1022 wxString* aModelType, wxString* aModelParams, wxString* aPinMap )
1051 auto convertNotation =
1052 [&](
const wxString& units ) -> wxString
1056 if( units == wxS(
"µ" ) || units == wxS(
"μ" ) )
1061 if( units == wxT(
"M" ) )
1062 return wxT(
"Meg" );
1066 if( units.Capitalize() == wxT(
"Meg" ) )
1074 []( wxString* mantissa )
1076 mantissa->Replace( wxS(
" " ), wxEmptyString );
1078 wxChar ambiguousSeparator =
'?';
1079 wxChar thousandsSeparator =
'?';
1080 bool thousandsSeparatorFound =
false;
1081 wxChar decimalSeparator =
'?';
1082 bool decimalSeparatorFound =
false;
1085 for(
int ii = (
int) mantissa->length() - 1; ii >= 0; --ii )
1087 wxChar c = mantissa->GetChar( ii );
1089 if( c >=
'0' && c <=
'9' )
1093 else if( c ==
'.' || c ==
',' )
1095 if( decimalSeparator !=
'?' || thousandsSeparator !=
'?' )
1099 if( c == decimalSeparator )
1101 if( thousandsSeparatorFound )
1103 else if( decimalSeparatorFound )
1106 decimalSeparatorFound =
true;
1108 else if( c == thousandsSeparator )
1113 thousandsSeparatorFound =
true;
1116 else if( ambiguousSeparator !=
'?' )
1121 if( c == ambiguousSeparator )
1124 thousandsSeparator = ambiguousSeparator;
1125 thousandsSeparatorFound =
true;
1126 decimalSeparator = c ==
'.' ?
',' :
'.';
1132 decimalSeparator = ambiguousSeparator;
1133 decimalSeparatorFound =
true;
1134 thousandsSeparator = c;
1135 thousandsSeparatorFound =
true;
1148 if( ( ii == 1 && mantissa->GetChar( 0 ) ==
'0' ) || digits != 3 )
1150 decimalSeparator = c;
1151 decimalSeparatorFound =
true;
1152 thousandsSeparator = c ==
'.' ?
',' :
'.';
1156 ambiguousSeparator = c;
1169 if( decimalSeparator ==
'?' && thousandsSeparator ==
'?' )
1171 decimalSeparator =
'.';
1172 thousandsSeparator =
',';
1175 mantissa->Replace( thousandsSeparator, wxEmptyString );
1176 mantissa->Replace( decimalSeparator,
'.' );
1181 wxString prefix = aSymbol.GetPrefix();
1185 std::vector<SCH_PIN*> pins;
1187 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1189 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1196 std::sort( pins.begin(), pins.end(),
1199 return a->GetNumber() < b->GetNumber();
1207 if( pins.size() != 2 )
1210 if( ( ( *aDeviceType ==
"R" || *aDeviceType ==
"L" || *aDeviceType ==
"C" )
1211 && aModelType->IsEmpty() )
1213 (
library.IsEmpty() && modelName.IsEmpty()
1214 && aDeviceType->IsEmpty()
1215 && aModelType->IsEmpty()
1217 && ( prefix.StartsWith(
"R" ) || prefix.StartsWith(
"L" ) || prefix.StartsWith(
"C" ) ) ) )
1219 if( aModelParams->IsEmpty() )
1221 wxRegEx idealVal( wxT(
"^"
1223 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1224 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1226 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1229 if( idealVal.Matches( value ) )
1231 wxString valueMantissa( idealVal.GetMatch( value, 1 ) );
1232 wxString valueExponent( idealVal.GetMatch( value, 2 ) );
1233 wxString valueFraction( idealVal.GetMatch( value, 6 ) );
1238 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1240 aModelParams->Printf( wxT(
"%s=\"%s%s\"" ),
1241 prefix.Left(1).Lower(),
1242 std::move( valueMantissa ),
1243 convertNotation( valueExponent ) );
1247 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\"" ),
1248 prefix.Left(1).Lower(),
1249 std::move( valueMantissa ),
1250 std::move( valueFraction ),
1251 convertNotation( valueExponent ) );
1256 *aModelType = wxT(
"=" );
1257 aModelParams->Printf( wxT(
"%s=\"%s\"" ), prefix.Left(1).Lower(), std::move( value ) );
1261 if( aDeviceType->IsEmpty() )
1262 *aDeviceType = prefix.Left( 1 );
1264 if( aPinMap->IsEmpty() )
1265 aPinMap->Printf( wxT(
"%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1270 if( ( ( *aDeviceType == wxT(
"V" ) || *aDeviceType == wxT(
"I" ) )
1271 && ( aModelType->IsEmpty() || *aModelType == wxT(
"DC" ) ) )
1273 ( aDeviceType->IsEmpty()
1274 && aModelType->IsEmpty()
1276 && ( prefix.StartsWith(
"V" ) || prefix.StartsWith(
"I" ) ) ) )
1278 if( !value.IsEmpty() )
1280 wxString param =
"dc";
1282 if( value.StartsWith( wxT(
"DC " ) ) )
1284 value = value.Right( value.Length() - 3 );
1286 else if( value.StartsWith( wxT(
"AC " ) ) )
1288 value = value.Right( value.Length() - 3 );
1292 wxRegEx sourceVal( wxT(
"^"
1294 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1300 if( sourceVal.Matches( value ) )
1302 wxString valueMantissa( sourceVal.GetMatch( value, 1 ) );
1303 wxString valueExponent( sourceVal.GetMatch( value, 2 ) );
1304 wxString valueFraction( sourceVal.GetMatch( value, 6 ) );
1309 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1311 aModelParams->Printf( wxT(
"%s=\"%s%s\" %s" ),
1313 std::move( valueMantissa ),
1314 convertNotation( valueExponent ),
1319 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\" %s" ),
1321 std::move( valueMantissa ),
1322 std::move( valueFraction ),
1323 convertNotation( valueExponent ),
1329 aModelParams->Printf( wxT(
"%s=\"%s\" %s" ),
1336 if( aDeviceType->IsEmpty() )
1337 *aDeviceType = prefix.Left( 1 );
1339 if( aModelType->IsEmpty() )
1340 *aModelType = wxT(
"DC" );
1342 if( aPinMap->IsEmpty() )
1343 aPinMap->Printf( wxT(
"%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1377 FIELD_INFO(
const wxString& aText,
SCH_FIELD* aField ) :
1384 bool IsEmpty()
const {
return m_Text.IsEmpty(); }
1386 SCH_FIELD CreateField(
T* aSymbol,
const wxString& aFieldName )
1410 wxString existing_deviceSubtype;
1412 if( existing_deviceSubtypeField )
1413 existing_deviceSubtype = existing_deviceSubtypeField->
GetShownText(
false ).Upper();
1415 if( existing_deviceField
1416 || existing_deviceSubtypeField
1417 || existing_pinsField
1418 || existing_paramsField )
1424 if( existing_deviceSubtype == wxS(
"POT" ) )
1426 if( existing_pinsField )
1428 wxString pinMap = existing_pinsField->
GetText();
1429 pinMap.Replace( wxS(
"=+" ), wxS(
"=r1" ) );
1430 pinMap.Replace( wxS(
"=-" ), wxS(
"=r0" ) );
1431 existing_pinsField->
SetText( pinMap );
1436 if( existing_deviceSubtype.StartsWith( wxS(
"RAND" ) ) )
1440 existing_deviceSubtype = existing_deviceSubtypeField->
GetText().Upper();
1442 if( existing_deviceSubtype.Replace( wxS(
"NORMAL" ), wxS(
"GAUSSIAN" ) ) )
1443 existing_deviceSubtypeField->
SetText( existing_deviceSubtype );
1445 if( existing_paramsField )
1447 wxString params = existing_paramsField->
GetText().Lower();
1454 count += params.Replace( wxS(
"min=0 " ), wxEmptyString );
1455 count += params.Replace( wxS(
"max=0 " ), wxEmptyString );
1458 count += params.Replace( wxS(
"dt=" ), wxS(
"ts=" ) );
1461 existing_paramsField->
SetText( params );
1467 if( existing_deviceSubtype == wxS(
"MUTUAL" ) )
1469 if( existing_deviceSubtypeField )
1470 aSymbol.RemoveField( existing_deviceSubtypeField );
1472 if( existing_deviceField )
1474 existing_deviceField->
SetText( wxS(
"K" ) );
1478 FIELD_INFO deviceFieldInfo;
1479 deviceFieldInfo.m_Text = wxS(
"K" );
1482 aSymbol.AddField( deviceField );
1493 return wxString( wxEmptyString );
1495 wxRegEx regex( wxT(
"([^a-z])(M)(e|E)(g|G)($|[^a-z])" ) );
1496 wxString value = aField->GetText();
1499 regex.ReplaceAll( &value, wxT(
"\\1\\2\\5" ) );
1504 auto generateDefaultPinMapFromSymbol =
1505 [](
const std::vector<SCH_PIN*>& sourcePins )
1512 for(
unsigned ii = 0; ii < sourcePins.size(); ++ii )
1515 pinMap.Append( wxS(
" " ) );
1517 pinMap.Append( wxString::Format( wxT(
"%s=%u" ),
1518 sourcePins[ii]->GetNumber(),
1525 wxString prefix = aSymbol.GetPrefix();
1527 bool sourcePinsSorted =
false;
1528 std::vector<SCH_PIN*> sourcePins;
1530 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1532 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1535 auto lazySortSourcePins =
1536 [&sourcePins, &sourcePinsSorted]()
1538 if( !sourcePinsSorted )
1540 std::sort( sourcePins.begin(), sourcePins.end(),
1543 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
1547 sourcePinsSorted =
true;
1550 FIELD_INFO deviceInfo;
1551 FIELD_INFO modelInfo;
1552 FIELD_INFO deviceSubtypeInfo;
1554 FIELD_INFO spiceParamsInfo;
1555 FIELD_INFO pinMapInfo;
1556 bool modelFromValueField =
false;
1566 deviceInfo = FIELD_INFO( primitiveField->GetText(), primitiveField );
1567 aSymbol.RemoveField( primitiveField );
1572 const wxString delimiters(
"{:,; }" );
1573 const wxString& nodeSequence = nodeSequenceField->GetText();
1576 if( nodeSequence !=
"" )
1578 wxStringTokenizer tkz( nodeSequence, delimiters );
1580 for(
long modelPinNumber = 1; tkz.HasMoreTokens(); ++modelPinNumber )
1582 long symbolPinNumber = 1;
1583 tkz.GetNextToken().ToLong( &symbolPinNumber );
1585 if( modelPinNumber != 1 )
1586 pinMap.Append(
" " );
1588 pinMap.Append( wxString::Format(
"%ld=%ld", symbolPinNumber, modelPinNumber ) );
1592 pinMapInfo = FIELD_INFO( pinMap, nodeSequenceField );
1593 aSymbol.RemoveField( nodeSequenceField );
1598 modelInfo = FIELD_INFO( getSIValue( modelField ), modelField );
1599 aSymbol.RemoveField( modelField );
1601 else if( valueField )
1603 modelInfo = FIELD_INFO( getSIValue( valueField ), valueField );
1604 modelFromValueField =
true;
1609 libInfo = FIELD_INFO( libFileField->GetText(), libFileField );
1610 aSymbol.RemoveField( libFileField );
1617 if(
SCH_FIELD* legacyType = aSymbol.GetField( wxT(
"Sim_Type" ) ) )
1622 if(
SCH_FIELD* legacyDevice = aSymbol.GetField( wxT(
"Sim_Device" ) ) )
1627 if(
SCH_FIELD* legacyPins = aSymbol.GetField( wxT(
"Sim_Pins" ) ) )
1629 bool isPassive = prefix.StartsWith( wxT(
"R" ) )
1630 || prefix.StartsWith( wxT(
"L" ) )
1631 || prefix.StartsWith( wxT(
"C" ) );
1635 wxArrayString pinIndexes;
1639 lazySortSourcePins();
1641 if( isPassive && pinIndexes.size() == 2 && sourcePins.size() == 2 )
1643 if( pinIndexes[0] == wxT(
"2" ) )
1645 pinMap.Printf( wxT(
"%s=- %s=+" ),
1646 sourcePins[0]->GetNumber(),
1647 sourcePins[1]->GetNumber() );
1651 pinMap.Printf( wxT(
"%s=+ %s=-" ),
1652 sourcePins[0]->GetNumber(),
1653 sourcePins[1]->GetNumber() );
1658 for(
unsigned ii = 0; ii < pinIndexes.size() && ii < sourcePins.size(); ++ii )
1661 pinMap.Append( wxS(
" " ) );
1663 pinMap.Append( wxString::Format( wxT(
"%s=%s" ),
1664 sourcePins[ii]->GetNumber(),
1665 pinIndexes[ ii ] ) );
1670 legacyPins->SetText( pinMap );
1673 if(
SCH_FIELD* legacyParams = aSymbol.GetField( wxT(
"Sim_Params" ) ) )
1681 wxString device = deviceInfo.m_Text.Trim(
true ).Trim(
false );
1682 wxString lib = libInfo.m_Text.Trim(
true ).Trim(
false );
1683 wxString
model = modelInfo.m_Text.Trim(
true ).Trim(
false );
1684 wxString modelLineParams;
1686 bool libraryModel =
false;
1687 bool inferredModel =
false;
1688 bool internalModel =
false;
1690 if( !lib.IsEmpty() )
1694 std::vector<SCH_FIELD> emptyFields;
1695 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
1697 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1703 if(
EMBEDDED_FILES* symbolEmbeddedFiles = aSymbol.GetEmbeddedFiles() )
1705 embeddedFilesStack.push_back( symbolEmbeddedFiles );
1707 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1712 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1722 model =
model.BeforeFirst(
' ', &modelLineParams );
1723 modelInfo.m_Text =
model;
1725 lazySortSourcePins();
1728 emptyFields,
false, 0,
1732 libraryModel =
false;
1734 libraryModel =
true;
1736 if( pinMapInfo.IsEmpty() )
1742 if( pinMapInfo.IsEmpty() )
1743 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1746 else if( ( device == wxS(
"R" )
1747 || device == wxS(
"L" )
1748 || device == wxS(
"C" )
1749 || device == wxS(
"V" )
1750 || device == wxS(
"I" ) )
1751 && prefix.StartsWith( device )
1752 && modelFromValueField )
1754 inferredModel =
true;
1756 else if( device == wxS(
"V" ) || device == wxS(
"I" ) )
1761 wxStringTokenizer tokenizer(
model, wxT(
"() " ), wxTOKEN_STRTOK );
1763 if( tokenizer.HasMoreTokens() )
1765 deviceSubtypeInfo.m_Text = tokenizer.GetNextToken();
1766 deviceSubtypeInfo.m_Text.MakeUpper();
1768 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1777 if( deviceSubtypeInfo.m_Text == wxT(
"DC" ) && tokenizer.CountTokens() == 1 )
1779 wxCHECK( valueField, );
1780 valueField->
SetText( tokenizer.GetNextToken() );
1781 modelFromValueField =
false;
1785 for(
int ii = 0; tokenizer.HasMoreTokens(); ++ii )
1787 simModel->SetParamValue( ii, tokenizer.GetNextToken().ToStdString(),
1793 spiceParamsInfo = modelInfo;
1794 spiceParamsInfo.m_Text = wxString( simModel->Serializer().GenerateParams() );
1797 internalModel =
true;
1799 if( pinMapInfo.IsEmpty() )
1801 lazySortSourcePins();
1804 simModel->createPins( sourcePins );
1805 pinMapInfo.m_Text = wxString( simModel->Serializer().GeneratePins() );
1822 aSymbol.AddField( libField );
1825 aSymbol.AddField( nameField );
1827 if( !modelLineParams.IsEmpty() )
1829 spiceParamsInfo = modelInfo;
1831 spiceParamsInfo.m_Text = modelLineParams;
1834 int nameWidth = nameBBox.
GetWidth();
1840 spiceParamsInfo.m_Pos.x -= nameWidth;
1842 spiceParamsInfo.m_Pos.x += nameWidth;
1845 aSymbol.AddField( paramsField );
1848 if( modelFromValueField )
1849 valueField->
SetText( wxT(
"${SIM.NAME}" ) );
1851 else if( inferredModel )
1856 else if( internalModel )
1859 aSymbol.AddField( deviceField );
1861 if( !deviceSubtypeInfo.m_Text.IsEmpty() )
1864 aSymbol.AddField( subtypeField );
1867 if( !spiceParamsInfo.IsEmpty() )
1870 aSymbol.AddField( paramsField );
1873 if( modelFromValueField )
1874 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1878 if( device.IsEmpty() && lib.IsEmpty() )
1880 spiceParamsInfo = modelInfo;
1884 spiceParamsInfo.m_Text.Printf( wxT(
"type=\"%s\" model=\"%s\" lib=\"%s\"" ), device,
1891 aSymbol.AddField( deviceField );
1894 aSymbol.AddField( paramsField );
1896 if( modelFromValueField )
1902 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1908 if( pinMapInfo.IsEmpty() )
1910 lazySortSourcePins();
1911 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1915 if( !pinMapInfo.IsEmpty() )
1918 aSymbol.AddField( pinsField );