1026 wxString* aModelType, wxString* aModelParams, wxString* aPinMap )
1055 auto convertNotation =
1056 [&](
const wxString& units ) -> wxString
1060 if( units == wxS(
"µ" ) || units == wxS(
"μ" ) )
1065 if( units == wxT(
"M" ) )
1066 return wxT(
"Meg" );
1070 if( units.Capitalize() == wxT(
"Meg" ) )
1078 []( wxString* mantissa )
1080 mantissa->Replace( wxS(
" " ), wxEmptyString );
1082 wxChar ambiguousSeparator =
'?';
1083 wxChar thousandsSeparator =
'?';
1084 bool thousandsSeparatorFound =
false;
1085 wxChar decimalSeparator =
'?';
1086 bool decimalSeparatorFound =
false;
1089 for(
int ii = (
int) mantissa->length() - 1; ii >= 0; --ii )
1091 wxChar c = mantissa->GetChar( ii );
1093 if( c >=
'0' && c <=
'9' )
1097 else if( c ==
'.' || c ==
',' )
1099 if( decimalSeparator !=
'?' || thousandsSeparator !=
'?' )
1103 if( c == decimalSeparator )
1105 if( thousandsSeparatorFound )
1107 else if( decimalSeparatorFound )
1110 decimalSeparatorFound =
true;
1112 else if( c == thousandsSeparator )
1117 thousandsSeparatorFound =
true;
1120 else if( ambiguousSeparator !=
'?' )
1125 if( c == ambiguousSeparator )
1128 thousandsSeparator = ambiguousSeparator;
1129 thousandsSeparatorFound =
true;
1130 decimalSeparator = c ==
'.' ?
',' :
'.';
1136 decimalSeparator = ambiguousSeparator;
1137 decimalSeparatorFound =
true;
1138 thousandsSeparator = c;
1139 thousandsSeparatorFound =
true;
1152 if( ( ii == 1 && mantissa->GetChar( 0 ) ==
'0' ) || digits != 3 )
1154 decimalSeparator = c;
1155 decimalSeparatorFound =
true;
1156 thousandsSeparator = c ==
'.' ?
',' :
'.';
1160 ambiguousSeparator = c;
1173 if( decimalSeparator ==
'?' && thousandsSeparator ==
'?' )
1175 decimalSeparator =
'.';
1176 thousandsSeparator =
',';
1179 mantissa->Replace( thousandsSeparator, wxEmptyString );
1180 mantissa->Replace( decimalSeparator,
'.' );
1185 wxString prefix = aSymbol.GetPrefix();
1189 std::vector<SCH_PIN*> pins;
1191 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1193 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1200 std::sort( pins.begin(), pins.end(),
1203 return a->GetNumber() < b->GetNumber();
1211 if( pins.size() != 2 )
1214 if( ( ( *aDeviceType ==
"R" || *aDeviceType ==
"L" || *aDeviceType ==
"C" )
1215 && aModelType->IsEmpty() )
1217 (
library.IsEmpty() && modelName.IsEmpty()
1218 && aDeviceType->IsEmpty()
1219 && aModelType->IsEmpty()
1221 && ( prefix.StartsWith(
"R" ) || prefix.StartsWith(
"L" ) || prefix.StartsWith(
"C" ) ) ) )
1223 if( aModelParams->IsEmpty() )
1225 wxRegEx idealVal( wxT(
"^"
1227 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1228 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1230 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1233 if( idealVal.Matches( value ) )
1235 wxString valueMantissa( idealVal.GetMatch( value, 1 ) );
1236 wxString valueExponent( idealVal.GetMatch( value, 2 ) );
1237 wxString valueFraction( idealVal.GetMatch( value, 6 ) );
1242 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1244 aModelParams->Printf( wxT(
"%s=\"%s%s\"" ),
1245 prefix.Left(1).Lower(),
1246 std::move( valueMantissa ),
1247 convertNotation( valueExponent ) );
1251 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\"" ),
1252 prefix.Left(1).Lower(),
1253 std::move( valueMantissa ),
1254 std::move( valueFraction ),
1255 convertNotation( valueExponent ) );
1260 *aModelType = wxT(
"=" );
1261 aModelParams->Printf( wxT(
"%s=\"%s\"" ), prefix.Left(1).Lower(), std::move( value ) );
1265 if( aDeviceType->IsEmpty() )
1266 *aDeviceType = prefix.Left( 1 );
1268 if( aPinMap->IsEmpty() )
1269 aPinMap->Printf( wxT(
"%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1274 if( ( ( *aDeviceType == wxT(
"V" ) || *aDeviceType == wxT(
"I" ) )
1275 && ( aModelType->IsEmpty() || *aModelType == wxT(
"DC" ) ) )
1277 ( aDeviceType->IsEmpty()
1278 && aModelType->IsEmpty()
1280 && ( prefix.StartsWith(
"V" ) || prefix.StartsWith(
"I" ) ) ) )
1282 if( !value.IsEmpty() )
1284 wxString param =
"dc";
1286 if( value.StartsWith( wxT(
"DC " ) ) )
1288 value = value.Right( value.Length() - 3 );
1290 else if( value.StartsWith( wxT(
"AC " ) ) )
1292 value = value.Right( value.Length() - 3 );
1296 wxRegEx sourceVal( wxT(
"^"
1298 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1304 if( sourceVal.Matches( value ) )
1306 wxString valueMantissa( sourceVal.GetMatch( value, 1 ) );
1307 wxString valueExponent( sourceVal.GetMatch( value, 2 ) );
1308 wxString valueFraction( sourceVal.GetMatch( value, 6 ) );
1313 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1315 aModelParams->Printf( wxT(
"%s=\"%s%s\" %s" ),
1317 std::move( valueMantissa ),
1318 convertNotation( valueExponent ),
1323 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\" %s" ),
1325 std::move( valueMantissa ),
1326 std::move( valueFraction ),
1327 convertNotation( valueExponent ),
1333 aModelParams->Printf( wxT(
"%s=\"%s\" %s" ),
1340 if( aDeviceType->IsEmpty() )
1341 *aDeviceType = prefix.Left( 1 );
1343 if( aModelType->IsEmpty() )
1344 *aModelType = wxT(
"DC" );
1346 if( aPinMap->IsEmpty() )
1347 aPinMap->Printf( wxT(
"%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1381 FIELD_INFO(
const wxString& aText,
SCH_FIELD* aField ) :
1388 bool IsEmpty()
const {
return m_Text.IsEmpty(); }
1390 SCH_FIELD CreateField(
T* aSymbol,
const wxString& aFieldName )
1414 wxString existing_deviceSubtype;
1416 if( existing_deviceSubtypeField )
1417 existing_deviceSubtype = existing_deviceSubtypeField->
GetShownText(
false ).Upper();
1419 if( existing_deviceField
1420 || existing_deviceSubtypeField
1421 || existing_pinsField
1422 || existing_paramsField )
1428 if( existing_deviceSubtype == wxS(
"POT" ) )
1430 if( existing_pinsField )
1432 wxString pinMap = existing_pinsField->
GetText();
1433 pinMap.Replace( wxS(
"=+" ), wxS(
"=r1" ) );
1434 pinMap.Replace( wxS(
"=-" ), wxS(
"=r0" ) );
1435 existing_pinsField->
SetText( pinMap );
1440 if( existing_deviceSubtype.StartsWith( wxS(
"RAND" ) ) )
1444 existing_deviceSubtype = existing_deviceSubtypeField->
GetText().Upper();
1446 if( existing_deviceSubtype.Replace( wxS(
"NORMAL" ), wxS(
"GAUSSIAN" ) ) )
1447 existing_deviceSubtypeField->
SetText( existing_deviceSubtype );
1449 if( existing_paramsField )
1451 wxString params = existing_paramsField->
GetText().Lower();
1458 count += params.Replace( wxS(
"min=0 " ), wxEmptyString );
1459 count += params.Replace( wxS(
"max=0 " ), wxEmptyString );
1462 count += params.Replace( wxS(
"dt=" ), wxS(
"ts=" ) );
1465 existing_paramsField->
SetText( params );
1471 if( existing_deviceSubtype == wxS(
"MUTUAL" ) )
1473 if( existing_deviceSubtypeField )
1474 aSymbol.RemoveField( existing_deviceSubtypeField );
1476 if( existing_deviceField )
1478 existing_deviceField->
SetText( wxS(
"K" ) );
1482 FIELD_INFO deviceFieldInfo;
1483 deviceFieldInfo.m_Text = wxS(
"K" );
1486 aSymbol.AddField( deviceField );
1497 return wxString( wxEmptyString );
1499 wxRegEx regex( wxT(
"([^a-z])(M)(e|E)(g|G)($|[^a-z])" ) );
1500 wxString value = aField->GetText();
1503 regex.ReplaceAll( &value, wxT(
"\\1\\2\\5" ) );
1508 auto generateDefaultPinMapFromSymbol =
1509 [](
const std::vector<SCH_PIN*>& sourcePins )
1516 for(
unsigned ii = 0; ii < sourcePins.size(); ++ii )
1519 pinMap.Append( wxS(
" " ) );
1521 pinMap.Append( wxString::Format( wxT(
"%s=%u" ),
1522 sourcePins[ii]->GetNumber(),
1529 wxString prefix = aSymbol.GetPrefix();
1531 bool sourcePinsSorted =
false;
1532 std::vector<SCH_PIN*> sourcePins;
1534 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1536 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1539 auto lazySortSourcePins =
1540 [&sourcePins, &sourcePinsSorted]()
1542 if( !sourcePinsSorted )
1544 std::sort( sourcePins.begin(), sourcePins.end(),
1547 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
1551 sourcePinsSorted =
true;
1554 FIELD_INFO deviceInfo;
1555 FIELD_INFO modelInfo;
1556 FIELD_INFO deviceSubtypeInfo;
1558 FIELD_INFO spiceParamsInfo;
1559 FIELD_INFO pinMapInfo;
1560 bool modelFromValueField =
false;
1570 deviceInfo = FIELD_INFO( primitiveField->GetText(), primitiveField );
1571 aSymbol.RemoveField( primitiveField );
1576 const wxString delimiters(
"{:,; }" );
1577 const wxString& nodeSequence = nodeSequenceField->GetText();
1580 if( nodeSequence !=
"" )
1582 wxStringTokenizer tkz( nodeSequence, delimiters );
1584 for(
long modelPinNumber = 1; tkz.HasMoreTokens(); ++modelPinNumber )
1586 long symbolPinNumber = 1;
1587 tkz.GetNextToken().ToLong( &symbolPinNumber );
1589 if( modelPinNumber != 1 )
1590 pinMap.Append(
" " );
1592 pinMap.Append( wxString::Format(
"%ld=%ld", symbolPinNumber, modelPinNumber ) );
1596 pinMapInfo = FIELD_INFO( pinMap, nodeSequenceField );
1597 aSymbol.RemoveField( nodeSequenceField );
1602 modelInfo = FIELD_INFO( getSIValue( modelField ), modelField );
1603 aSymbol.RemoveField( modelField );
1605 else if( valueField )
1607 modelInfo = FIELD_INFO( getSIValue( valueField ), valueField );
1608 modelFromValueField =
true;
1613 libInfo = FIELD_INFO( libFileField->GetText(), libFileField );
1614 aSymbol.RemoveField( libFileField );
1621 if(
SCH_FIELD* legacyType = aSymbol.GetField( wxT(
"Sim_Type" ) ) )
1626 if(
SCH_FIELD* legacyDevice = aSymbol.GetField( wxT(
"Sim_Device" ) ) )
1631 if(
SCH_FIELD* legacyPins = aSymbol.GetField( wxT(
"Sim_Pins" ) ) )
1633 bool isPassive = prefix.StartsWith( wxT(
"R" ) )
1634 || prefix.StartsWith( wxT(
"L" ) )
1635 || prefix.StartsWith( wxT(
"C" ) );
1639 wxArrayString pinIndexes;
1643 lazySortSourcePins();
1645 if( isPassive && pinIndexes.size() == 2 && sourcePins.size() == 2 )
1647 if( pinIndexes[0] == wxT(
"2" ) )
1649 pinMap.Printf( wxT(
"%s=- %s=+" ),
1650 sourcePins[0]->GetNumber(),
1651 sourcePins[1]->GetNumber() );
1655 pinMap.Printf( wxT(
"%s=+ %s=-" ),
1656 sourcePins[0]->GetNumber(),
1657 sourcePins[1]->GetNumber() );
1662 for(
unsigned ii = 0; ii < pinIndexes.size() && ii < sourcePins.size(); ++ii )
1665 pinMap.Append( wxS(
" " ) );
1667 pinMap.Append( wxString::Format( wxT(
"%s=%s" ),
1668 sourcePins[ii]->GetNumber(),
1669 pinIndexes[ ii ] ) );
1674 legacyPins->SetText( pinMap );
1677 if(
SCH_FIELD* legacyParams = aSymbol.GetField( wxT(
"Sim_Params" ) ) )
1685 wxString device = deviceInfo.m_Text.Trim(
true ).Trim(
false );
1686 wxString lib = libInfo.m_Text.Trim(
true ).Trim(
false );
1687 wxString
model = modelInfo.m_Text.Trim(
true ).Trim(
false );
1688 wxString modelLineParams;
1690 bool libraryModel =
false;
1691 bool inferredModel =
false;
1692 bool internalModel =
false;
1694 if( !lib.IsEmpty() )
1698 std::vector<SCH_FIELD> emptyFields;
1699 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
1701 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1707 if(
EMBEDDED_FILES* symbolEmbeddedFiles = aSymbol.GetEmbeddedFiles() )
1709 embeddedFilesStack.push_back( symbolEmbeddedFiles );
1711 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1716 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1726 model =
model.BeforeFirst(
' ', &modelLineParams );
1727 modelInfo.m_Text =
model;
1729 lazySortSourcePins();
1732 emptyFields,
false, 0,
1733 sourcePins, reporter );
1736 libraryModel =
false;
1738 libraryModel =
true;
1740 if( pinMapInfo.IsEmpty() )
1746 if( pinMapInfo.IsEmpty() )
1747 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1750 else if( ( device == wxS(
"R" )
1751 || device == wxS(
"L" )
1752 || device == wxS(
"C" )
1753 || device == wxS(
"V" )
1754 || device == wxS(
"I" ) )
1755 && prefix.StartsWith( device )
1756 && modelFromValueField )
1758 inferredModel =
true;
1760 else if( device == wxS(
"V" ) || device == wxS(
"I" ) )
1765 wxStringTokenizer tokenizer(
model, wxT(
"() " ), wxTOKEN_STRTOK );
1767 if( tokenizer.HasMoreTokens() )
1769 deviceSubtypeInfo.m_Text = tokenizer.GetNextToken();
1770 deviceSubtypeInfo.m_Text.MakeUpper();
1772 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1781 if( deviceSubtypeInfo.m_Text == wxT(
"DC" ) && tokenizer.CountTokens() == 1 )
1783 wxCHECK( valueField, );
1784 valueField->
SetText( tokenizer.GetNextToken() );
1785 modelFromValueField =
false;
1789 for(
int ii = 0; tokenizer.HasMoreTokens(); ++ii )
1791 simModel->SetParamValue( ii, tokenizer.GetNextToken().ToStdString(),
1797 spiceParamsInfo = modelInfo;
1798 spiceParamsInfo.m_Text = wxString( simModel->Serializer().GenerateParams() );
1801 internalModel =
true;
1803 if( pinMapInfo.IsEmpty() )
1805 lazySortSourcePins();
1808 simModel->createPins( sourcePins );
1809 pinMapInfo.m_Text = wxString( simModel->Serializer().GeneratePins() );
1826 aSymbol.AddField( libField );
1829 aSymbol.AddField( nameField );
1831 if( !modelLineParams.IsEmpty() )
1833 spiceParamsInfo = modelInfo;
1835 spiceParamsInfo.m_Text = modelLineParams;
1838 int nameWidth = nameBBox.
GetWidth();
1844 spiceParamsInfo.m_Pos.x -= nameWidth;
1846 spiceParamsInfo.m_Pos.x += nameWidth;
1849 aSymbol.AddField( paramsField );
1852 if( modelFromValueField )
1853 valueField->
SetText( wxT(
"${SIM.NAME}" ) );
1855 else if( inferredModel )
1860 else if( internalModel )
1863 aSymbol.AddField( deviceField );
1865 if( !deviceSubtypeInfo.m_Text.IsEmpty() )
1868 aSymbol.AddField( subtypeField );
1871 if( !spiceParamsInfo.IsEmpty() )
1874 aSymbol.AddField( paramsField );
1877 if( modelFromValueField )
1878 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1882 if( device.IsEmpty() && lib.IsEmpty() )
1884 spiceParamsInfo = modelInfo;
1888 spiceParamsInfo.m_Text.Printf( wxT(
"type=\"%s\" model=\"%s\" lib=\"%s\"" ), device,
1895 aSymbol.AddField( deviceField );
1898 aSymbol.AddField( paramsField );
1900 if( modelFromValueField )
1906 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1912 if( pinMapInfo.IsEmpty() )
1914 lazySortSourcePins();
1915 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1919 if( !pinMapInfo.IsEmpty() )
1922 aSymbol.AddField( pinsField );