1039 std::vector<SCH_FIELD>* aFields,
bool aResolve,
int aDepth,
1041 wxString* aModelType, wxString* aModelParams, wxString* aPinMap )
1070 auto convertNotation =
1071 [&](
const wxString& units ) -> wxString
1075 if( units == wxS(
"µ" ) || units == wxS(
"μ" ) )
1080 if( units == wxT(
"M" ) )
1081 return wxT(
"Meg" );
1085 if( units.Capitalize() == wxT(
"Meg" ) )
1093 []( wxString* mantissa )
1095 mantissa->Replace( wxS(
" " ), wxEmptyString );
1097 wxChar ambiguousSeparator =
'?';
1098 wxChar thousandsSeparator =
'?';
1099 bool thousandsSeparatorFound =
false;
1100 wxChar decimalSeparator =
'?';
1101 bool decimalSeparatorFound =
false;
1104 for(
int ii = (
int) mantissa->length() - 1; ii >= 0; --ii )
1106 wxChar c = mantissa->GetChar( ii );
1108 if( c >=
'0' && c <=
'9' )
1112 else if( c ==
'.' || c ==
',' )
1114 if( decimalSeparator !=
'?' || thousandsSeparator !=
'?' )
1118 if( c == decimalSeparator )
1120 if( thousandsSeparatorFound )
1122 else if( decimalSeparatorFound )
1125 decimalSeparatorFound =
true;
1127 else if( c == thousandsSeparator )
1132 thousandsSeparatorFound =
true;
1135 else if( ambiguousSeparator !=
'?' )
1140 if( c == ambiguousSeparator )
1143 thousandsSeparator = ambiguousSeparator;
1144 thousandsSeparatorFound =
true;
1145 decimalSeparator = c ==
'.' ?
',' :
'.';
1151 decimalSeparator = ambiguousSeparator;
1152 decimalSeparatorFound =
true;
1153 thousandsSeparator = c;
1154 thousandsSeparatorFound =
true;
1167 if( ( ii == 1 && mantissa->GetChar( 0 ) ==
'0' ) || digits != 3 )
1169 decimalSeparator = c;
1170 decimalSeparatorFound =
true;
1171 thousandsSeparator = c ==
'.' ?
',' :
'.';
1175 ambiguousSeparator = c;
1188 if( decimalSeparator ==
'?' && thousandsSeparator ==
'?' )
1190 decimalSeparator =
'.';
1191 thousandsSeparator =
',';
1194 mantissa->Replace( thousandsSeparator, wxEmptyString );
1195 mantissa->Replace( decimalSeparator,
'.' );
1208 if( aPins.size() != 2 )
1211 if( ( ( *aDeviceType ==
"R" || *aDeviceType ==
"L" || *aDeviceType ==
"C" )
1212 && aModelType->IsEmpty() )
1214 (
library.IsEmpty() && modelName.IsEmpty()
1215 && aDeviceType->IsEmpty()
1216 && aModelType->IsEmpty()
1218 && ( aPrefix.StartsWith(
"R" ) || aPrefix.StartsWith(
"L" ) || aPrefix.StartsWith(
"C" ) ) ) )
1220 if( aModelParams->IsEmpty() )
1222 wxRegEx idealVal( wxT(
"^"
1224 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1225 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1227 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1230 if( idealVal.Matches( value ) )
1232 wxString valueMantissa( idealVal.GetMatch( value, 1 ) );
1233 wxString valueExponent( idealVal.GetMatch( value, 2 ) );
1234 wxString valueFraction( idealVal.GetMatch( value, 6 ) );
1239 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1241 aModelParams->Printf( wxT(
"%s=\"%s%s\"" ),
1242 aPrefix.Left(1).Lower(),
1243 std::move( valueMantissa ),
1244 convertNotation( valueExponent ) );
1248 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\"" ),
1249 aPrefix.Left(1).Lower(),
1250 std::move( valueMantissa ),
1251 std::move( valueFraction ),
1252 convertNotation( valueExponent ) );
1257 *aModelType = wxT(
"=" );
1258 aModelParams->Printf( wxT(
"%s=\"%s\"" ), aPrefix.Left(1).Lower(), std::move( value ) );
1262 if( aDeviceType->IsEmpty() )
1263 *aDeviceType = aPrefix.Left( 1 );
1265 if( aPinMap->IsEmpty() )
1266 aPinMap->Printf( wxT(
"%s=+ %s=-" ), aPins[0], aPins[1] );
1271 if( ( ( *aDeviceType == wxT(
"V" ) || *aDeviceType == wxT(
"I" ) )
1272 && ( aModelType->IsEmpty() || *aModelType == wxT(
"DC" ) ) )
1274 ( aDeviceType->IsEmpty()
1275 && aModelType->IsEmpty()
1277 && ( aPrefix.StartsWith(
"V" ) || aPrefix.StartsWith(
"I" ) ) ) )
1279 if( !value.IsEmpty() )
1281 wxString param =
"dc";
1283 if( value.StartsWith( wxT(
"DC " ) ) )
1285 value = value.Right( value.Length() - 3 );
1287 else if( value.StartsWith( wxT(
"AC " ) ) )
1289 value = value.Right( value.Length() - 3 );
1293 wxRegEx sourceVal( wxT(
"^"
1295 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1301 if( sourceVal.Matches( value ) )
1303 wxString valueMantissa( sourceVal.GetMatch( value, 1 ) );
1304 wxString valueExponent( sourceVal.GetMatch( value, 2 ) );
1305 wxString valueFraction( sourceVal.GetMatch( value, 6 ) );
1310 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1312 aModelParams->Printf( wxT(
"%s=\"%s%s\" %s" ),
1314 std::move( valueMantissa ),
1315 convertNotation( valueExponent ),
1320 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\" %s" ),
1322 std::move( valueMantissa ),
1323 std::move( valueFraction ),
1324 convertNotation( valueExponent ),
1330 aModelParams->Printf( wxT(
"%s=\"%s\" %s" ),
1337 if( aDeviceType->IsEmpty() )
1338 *aDeviceType = aPrefix.Left( 1 );
1340 if( aModelType->IsEmpty() )
1341 *aModelType = wxT(
"DC" );
1343 if( aPinMap->IsEmpty() )
1344 aPinMap->Printf( wxT(
"%s=+ %s=-" ), aPins[0], aPins[1] );
1380 FIELD_INFO(
const wxString& aText,
SCH_FIELD* aField ) :
1387 bool IsEmpty()
const {
return m_Text.IsEmpty(); }
1389 SCH_FIELD CreateField(
T* aSymbol,
const wxString& aFieldName )
1413 wxString existing_deviceSubtype;
1415 if( existing_deviceSubtypeField )
1418 if( existing_deviceField
1419 || existing_deviceSubtypeField
1420 || existing_pinsField
1421 || existing_paramsField )
1427 if( existing_deviceSubtype == wxS(
"POT" ) )
1429 if( existing_pinsField )
1431 wxString pinMap = existing_pinsField->
GetText();
1432 pinMap.Replace( wxS(
"=+" ), wxS(
"=r1" ) );
1433 pinMap.Replace( wxS(
"=-" ), wxS(
"=r0" ) );
1434 existing_pinsField->
SetText( pinMap );
1439 if( existing_deviceSubtype.StartsWith( wxS(
"RAND" ) ) )
1443 existing_deviceSubtype = existing_deviceSubtypeField->
GetText().Upper();
1445 if( existing_deviceSubtype.Replace( wxS(
"NORMAL" ), wxS(
"GAUSSIAN" ) ) )
1446 existing_deviceSubtypeField->
SetText( existing_deviceSubtype );
1448 if( existing_paramsField )
1450 wxString params = existing_paramsField->
GetText().Lower();
1457 count += params.Replace( wxS(
"min=0 " ), wxEmptyString );
1458 count += params.Replace( wxS(
"max=0 " ), wxEmptyString );
1461 count += params.Replace( wxS(
"dt=" ), wxS(
"ts=" ) );
1464 existing_paramsField->
SetText( params );
1470 if( existing_deviceSubtype == wxS(
"MUTUAL" ) )
1472 if( existing_deviceSubtypeField )
1473 aSymbol.RemoveField( existing_deviceSubtypeField );
1475 if( existing_deviceField )
1477 existing_deviceField->
SetText( wxS(
"K" ) );
1481 FIELD_INFO deviceFieldInfo;
1482 deviceFieldInfo.m_Text = wxS(
"K" );
1485 aSymbol.AddField( deviceField );
1496 return wxString( wxEmptyString );
1498 wxRegEx regex( wxT(
"([^a-z])(M)(e|E)(g|G)($|[^a-z])" ) );
1499 wxString value = aField->GetText();
1502 regex.ReplaceAll( &value, wxT(
"\\1\\2\\5" ) );
1507 auto generateDefaultPinMapFromSymbol =
1508 [](
const std::vector<SCH_PIN*>& sourcePins )
1515 for(
unsigned ii = 0; ii < sourcePins.size(); ++ii )
1518 pinMap.Append( wxS(
" " ) );
1520 pinMap.Append( wxString::Format( wxT(
"%s=%u" ),
1521 sourcePins[ii]->GetNumber(),
1528 wxString prefix = aSymbol.GetPrefix();
1530 bool sourcePinsSorted =
false;
1531 std::vector<SCH_PIN*> sourcePins;
1533 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1535 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1538 auto lazySortSourcePins =
1539 [&sourcePins, &sourcePinsSorted]()
1541 if( !sourcePinsSorted )
1543 std::sort( sourcePins.begin(), sourcePins.end(),
1546 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
1550 sourcePinsSorted =
true;
1553 FIELD_INFO deviceInfo;
1554 FIELD_INFO modelInfo;
1555 FIELD_INFO deviceSubtypeInfo;
1557 FIELD_INFO spiceParamsInfo;
1558 FIELD_INFO pinMapInfo;
1559 bool modelFromValueField =
false;
1569 deviceInfo = FIELD_INFO( primitiveField->GetText(), primitiveField );
1570 aSymbol.RemoveField( primitiveField );
1575 const wxString delimiters(
"{:,; }" );
1576 const wxString& nodeSequence = nodeSequenceField->GetText();
1579 if( nodeSequence !=
"" )
1581 wxStringTokenizer tkz( nodeSequence, delimiters );
1583 for(
long modelPinNumber = 1; tkz.HasMoreTokens(); ++modelPinNumber )
1585 long symbolPinNumber = 1;
1586 tkz.GetNextToken().ToLong( &symbolPinNumber );
1588 if( modelPinNumber != 1 )
1589 pinMap.Append(
" " );
1591 pinMap.Append( wxString::Format(
"%ld=%ld", symbolPinNumber, modelPinNumber ) );
1595 pinMapInfo = FIELD_INFO( pinMap, nodeSequenceField );
1596 aSymbol.RemoveField( nodeSequenceField );
1601 modelInfo = FIELD_INFO( getSIValue( modelField ), modelField );
1602 aSymbol.RemoveField( modelField );
1604 else if( valueField )
1606 modelInfo = FIELD_INFO( getSIValue( valueField ), valueField );
1607 modelFromValueField =
true;
1612 libInfo = FIELD_INFO( libFileField->GetText(), libFileField );
1613 aSymbol.RemoveField( libFileField );
1620 if(
SCH_FIELD* legacyType = aSymbol.GetField( wxT(
"Sim_Type" ) ) )
1625 if(
SCH_FIELD* legacyDevice = aSymbol.GetField( wxT(
"Sim_Device" ) ) )
1630 if(
SCH_FIELD* legacyPins = aSymbol.GetField( wxT(
"Sim_Pins" ) ) )
1632 bool isPassive = prefix.StartsWith( wxT(
"R" ) )
1633 || prefix.StartsWith( wxT(
"L" ) )
1634 || prefix.StartsWith( wxT(
"C" ) );
1638 wxArrayString pinIndexes;
1642 lazySortSourcePins();
1644 if( isPassive && pinIndexes.size() == 2 && sourcePins.size() == 2 )
1646 if( pinIndexes[0] == wxT(
"2" ) )
1648 pinMap.Printf( wxT(
"%s=- %s=+" ),
1649 sourcePins[0]->GetNumber(),
1650 sourcePins[1]->GetNumber() );
1654 pinMap.Printf( wxT(
"%s=+ %s=-" ),
1655 sourcePins[0]->GetNumber(),
1656 sourcePins[1]->GetNumber() );
1661 for(
unsigned ii = 0; ii < pinIndexes.size() && ii < sourcePins.size(); ++ii )
1664 pinMap.Append( wxS(
" " ) );
1666 pinMap.Append( wxString::Format( wxT(
"%s=%s" ),
1667 sourcePins[ii]->GetNumber(),
1668 pinIndexes[ ii ] ) );
1673 legacyPins->SetText( pinMap );
1676 if(
SCH_FIELD* legacyParams = aSymbol.GetField( wxT(
"Sim_Params" ) ) )
1684 wxString device = deviceInfo.m_Text.Trim(
true ).Trim(
false );
1685 wxString lib = libInfo.m_Text.Trim(
true ).Trim(
false );
1686 wxString
model = modelInfo.m_Text.Trim(
true ).Trim(
false );
1687 wxString modelLineParams;
1689 bool libraryModel =
false;
1690 bool inferredModel =
false;
1691 bool internalModel =
false;
1693 if( !lib.IsEmpty() )
1697 std::vector<SCH_FIELD> emptyFields;
1698 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
1700 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1706 if(
EMBEDDED_FILES* symbolEmbeddedFiles = aSymbol.GetEmbeddedFiles() )
1708 embeddedFilesStack.push_back( symbolEmbeddedFiles );
1710 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1715 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1725 model =
model.BeforeFirst(
' ', &modelLineParams );
1726 modelInfo.m_Text =
model;
1728 lazySortSourcePins();
1731 emptyFields,
false, 0,
1735 libraryModel =
false;
1737 libraryModel =
true;
1739 if( pinMapInfo.IsEmpty() )
1745 if( pinMapInfo.IsEmpty() )
1746 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1749 else if( ( device == wxS(
"R" )
1750 || device == wxS(
"L" )
1751 || device == wxS(
"C" )
1752 || device == wxS(
"V" )
1753 || device == wxS(
"I" ) )
1754 && prefix.StartsWith( device )
1755 && modelFromValueField )
1757 inferredModel =
true;
1759 else if( device == wxS(
"V" ) || device == wxS(
"I" ) )
1764 wxStringTokenizer tokenizer(
model, wxT(
"() " ), wxTOKEN_STRTOK );
1766 if( tokenizer.HasMoreTokens() )
1768 deviceSubtypeInfo.m_Text = tokenizer.GetNextToken();
1769 deviceSubtypeInfo.m_Text.MakeUpper();
1771 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1780 if( deviceSubtypeInfo.m_Text == wxT(
"DC" ) && tokenizer.CountTokens() == 1 )
1782 wxCHECK( valueField, );
1783 valueField->
SetText( tokenizer.GetNextToken() );
1784 modelFromValueField =
false;
1788 for(
int ii = 0; tokenizer.HasMoreTokens(); ++ii )
1790 simModel->SetParamValue( ii, tokenizer.GetNextToken().ToStdString(),
1796 spiceParamsInfo = modelInfo;
1797 spiceParamsInfo.m_Text = wxString( simModel->Serializer().GenerateParams() );
1800 internalModel =
true;
1802 if( pinMapInfo.IsEmpty() )
1804 lazySortSourcePins();
1807 simModel->createPins(
PinNumbers( sourcePins ) );
1808 pinMapInfo.m_Text = wxString( simModel->Serializer().GeneratePins() );
1825 aSymbol.AddField( libField );
1828 aSymbol.AddField( nameField );
1830 if( !modelLineParams.IsEmpty() )
1832 spiceParamsInfo = modelInfo;
1834 spiceParamsInfo.m_Text = modelLineParams;
1837 int nameWidth = nameBBox.
GetWidth();
1843 spiceParamsInfo.m_Pos.x -= nameWidth;
1845 spiceParamsInfo.m_Pos.x += nameWidth;
1848 aSymbol.AddField( paramsField );
1851 if( modelFromValueField )
1852 valueField->
SetText( wxT(
"${SIM.NAME}" ) );
1854 else if( inferredModel )
1859 else if( internalModel )
1862 aSymbol.AddField( deviceField );
1864 if( !deviceSubtypeInfo.m_Text.IsEmpty() )
1867 aSymbol.AddField( subtypeField );
1870 if( !spiceParamsInfo.IsEmpty() )
1873 aSymbol.AddField( paramsField );
1876 if( modelFromValueField )
1877 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1881 if( device.IsEmpty() && lib.IsEmpty() )
1883 spiceParamsInfo = modelInfo;
1887 spiceParamsInfo.m_Text.Printf( wxT(
"type=\"%s\" model=\"%s\" lib=\"%s\"" ),
1896 aSymbol.AddField( deviceField );
1899 aSymbol.AddField( paramsField );
1901 if( modelFromValueField )
1907 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1913 if( pinMapInfo.IsEmpty() )
1915 lazySortSourcePins();
1916 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1920 if( !pinMapInfo.IsEmpty() )
1923 aSymbol.AddField( pinsField );