1040 std::vector<SCH_FIELD>* aFields,
bool aResolve,
int aDepth,
1042 wxString* aModelType, wxString* aModelParams, wxString* aPinMap )
1071 auto convertNotation =
1072 [&](
const wxString& units ) -> wxString
1076 if( units == wxS(
"µ" ) || units == wxS(
"μ" ) )
1081 if( units == wxT(
"M" ) )
1082 return wxT(
"Meg" );
1086 if( units.Capitalize() == wxT(
"Meg" ) )
1094 []( wxString* mantissa )
1096 mantissa->Replace( wxS(
" " ), wxEmptyString );
1098 wxChar ambiguousSeparator =
'?';
1099 wxChar thousandsSeparator =
'?';
1100 bool thousandsSeparatorFound =
false;
1101 wxChar decimalSeparator =
'?';
1102 bool decimalSeparatorFound =
false;
1105 for(
int ii = (
int) mantissa->length() - 1; ii >= 0; --ii )
1107 wxChar c = mantissa->GetChar( ii );
1109 if( c >=
'0' && c <=
'9' )
1113 else if( c ==
'.' || c ==
',' )
1115 if( decimalSeparator !=
'?' || thousandsSeparator !=
'?' )
1119 if( c == decimalSeparator )
1121 if( thousandsSeparatorFound )
1123 else if( decimalSeparatorFound )
1126 decimalSeparatorFound =
true;
1128 else if( c == thousandsSeparator )
1133 thousandsSeparatorFound =
true;
1136 else if( ambiguousSeparator !=
'?' )
1141 if( c == ambiguousSeparator )
1144 thousandsSeparator = ambiguousSeparator;
1145 thousandsSeparatorFound =
true;
1146 decimalSeparator = c ==
'.' ?
',' :
'.';
1152 decimalSeparator = ambiguousSeparator;
1153 decimalSeparatorFound =
true;
1154 thousandsSeparator = c;
1155 thousandsSeparatorFound =
true;
1168 if( ( ii == 1 && mantissa->GetChar( 0 ) ==
'0' ) || digits != 3 )
1170 decimalSeparator = c;
1171 decimalSeparatorFound =
true;
1172 thousandsSeparator = c ==
'.' ?
',' :
'.';
1176 ambiguousSeparator = c;
1189 if( decimalSeparator ==
'?' && thousandsSeparator ==
'?' )
1191 decimalSeparator =
'.';
1192 thousandsSeparator =
',';
1195 mantissa->Replace( thousandsSeparator, wxEmptyString );
1196 mantissa->Replace( decimalSeparator,
'.' );
1209 if( aPins.size() != 2 )
1212 if( ( ( *aDeviceType ==
"R" || *aDeviceType ==
"L" || *aDeviceType ==
"C" )
1213 && aModelType->IsEmpty() )
1215 (
library.IsEmpty() && modelName.IsEmpty()
1216 && aDeviceType->IsEmpty()
1217 && aModelType->IsEmpty()
1219 && ( aPrefix.StartsWith(
"R" ) || aPrefix.StartsWith(
"L" ) || aPrefix.StartsWith(
"C" ) ) ) )
1221 if( aModelParams->IsEmpty() )
1223 wxRegEx idealVal( wxT(
"^"
1225 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1226 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1228 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1231 if( idealVal.Matches( value ) )
1233 wxString valueMantissa( idealVal.GetMatch( value, 1 ) );
1234 wxString valueExponent( idealVal.GetMatch( value, 2 ) );
1235 wxString valueFraction( idealVal.GetMatch( value, 6 ) );
1240 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1242 aModelParams->Printf( wxT(
"%s=\"%s%s\"" ),
1243 aPrefix.Left(1).Lower(),
1244 std::move( valueMantissa ),
1245 convertNotation( valueExponent ) );
1249 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\"" ),
1250 aPrefix.Left(1).Lower(),
1251 std::move( valueMantissa ),
1252 std::move( valueFraction ),
1253 convertNotation( valueExponent ) );
1258 *aModelType = wxT(
"=" );
1259 aModelParams->Printf( wxT(
"%s=\"%s\"" ), aPrefix.Left(1).Lower(), std::move( value ) );
1263 if( aDeviceType->IsEmpty() )
1264 *aDeviceType = aPrefix.Left( 1 );
1266 if( aPinMap->IsEmpty() )
1267 aPinMap->Printf( wxT(
"%s=+ %s=-" ), aPins[0], aPins[1] );
1272 if( ( ( *aDeviceType == wxT(
"V" ) || *aDeviceType == wxT(
"I" ) )
1273 && ( aModelType->IsEmpty() || *aModelType == wxT(
"DC" ) ) )
1275 ( aDeviceType->IsEmpty()
1276 && aModelType->IsEmpty()
1278 && ( aPrefix.StartsWith(
"V" ) || aPrefix.StartsWith(
"I" ) ) ) )
1280 if( !value.IsEmpty() )
1282 wxString param =
"dc";
1284 if( value.StartsWith( wxT(
"DC " ) ) )
1286 value = value.Right( value.Length() - 3 );
1288 else if( value.StartsWith( wxT(
"AC " ) ) )
1290 value = value.Right( value.Length() - 3 );
1294 wxRegEx sourceVal( wxT(
"^"
1296 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1302 if( sourceVal.Matches( value ) )
1304 wxString valueMantissa( sourceVal.GetMatch( value, 1 ) );
1305 wxString valueExponent( sourceVal.GetMatch( value, 2 ) );
1306 wxString valueFraction( sourceVal.GetMatch( value, 6 ) );
1311 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1313 aModelParams->Printf( wxT(
"%s=\"%s%s\" %s" ),
1315 std::move( valueMantissa ),
1316 convertNotation( valueExponent ),
1321 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\" %s" ),
1323 std::move( valueMantissa ),
1324 std::move( valueFraction ),
1325 convertNotation( valueExponent ),
1331 aModelParams->Printf( wxT(
"%s=\"%s\" %s" ),
1338 if( aDeviceType->IsEmpty() )
1339 *aDeviceType = aPrefix.Left( 1 );
1341 if( aModelType->IsEmpty() )
1342 *aModelType = wxT(
"DC" );
1344 if( aPinMap->IsEmpty() )
1345 aPinMap->Printf( wxT(
"%s=+ %s=-" ), aPins[0], aPins[1] );
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 )
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;
1534 auto lazySortSourcePins =
1535 [&sourcePins, &sourcePinsSorted]()
1537 if( !sourcePinsSorted )
1539 std::sort( sourcePins.begin(), sourcePins.end(),
1542 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
1546 sourcePinsSorted =
true;
1549 FIELD_INFO deviceInfo;
1550 FIELD_INFO modelInfo;
1551 FIELD_INFO deviceSubtypeInfo;
1553 FIELD_INFO spiceParamsInfo;
1554 FIELD_INFO pinMapInfo;
1555 bool modelFromValueField =
false;
1565 deviceInfo = FIELD_INFO( primitiveField->GetText(), primitiveField );
1566 aSymbol.RemoveField( primitiveField );
1571 const wxString delimiters(
"{:,; }" );
1572 const wxString& nodeSequence = nodeSequenceField->GetText();
1575 if( nodeSequence !=
"" )
1577 wxStringTokenizer tkz( nodeSequence, delimiters );
1579 for(
long modelPinNumber = 1; tkz.HasMoreTokens(); ++modelPinNumber )
1581 long symbolPinNumber = 1;
1582 tkz.GetNextToken().ToLong( &symbolPinNumber );
1584 if( modelPinNumber != 1 )
1585 pinMap.Append(
" " );
1587 pinMap.Append( wxString::Format(
"%ld=%ld", symbolPinNumber, modelPinNumber ) );
1591 pinMapInfo = FIELD_INFO( pinMap, nodeSequenceField );
1592 aSymbol.RemoveField( nodeSequenceField );
1597 modelInfo = FIELD_INFO( getSIValue( modelField ), modelField );
1598 aSymbol.RemoveField( modelField );
1600 else if( valueField )
1602 modelInfo = FIELD_INFO( getSIValue( valueField ), valueField );
1603 modelFromValueField =
true;
1608 libInfo = FIELD_INFO( libFileField->GetText(), libFileField );
1609 aSymbol.RemoveField( libFileField );
1616 if(
SCH_FIELD* legacyType = aSymbol.GetField( wxT(
"Sim_Type" ) ) )
1621 if(
SCH_FIELD* legacyDevice = aSymbol.GetField( wxT(
"Sim_Device" ) ) )
1626 if(
SCH_FIELD* legacyPins = aSymbol.GetField( wxT(
"Sim_Pins" ) ) )
1628 bool isPassive = prefix.StartsWith( wxT(
"R" ) )
1629 || prefix.StartsWith( wxT(
"L" ) )
1630 || prefix.StartsWith( wxT(
"C" ) );
1634 wxArrayString pinIndexes;
1638 lazySortSourcePins();
1640 if( isPassive && pinIndexes.size() == 2 && sourcePins.size() == 2 )
1642 if( pinIndexes[0] == wxT(
"2" ) )
1644 pinMap.Printf( wxT(
"%s=- %s=+" ),
1645 sourcePins[0]->GetNumber(),
1646 sourcePins[1]->GetNumber() );
1650 pinMap.Printf( wxT(
"%s=+ %s=-" ),
1651 sourcePins[0]->GetNumber(),
1652 sourcePins[1]->GetNumber() );
1657 for(
unsigned ii = 0; ii < pinIndexes.size() && ii < sourcePins.size(); ++ii )
1660 pinMap.Append( wxS(
" " ) );
1662 pinMap.Append( wxString::Format( wxT(
"%s=%s" ),
1663 sourcePins[ii]->GetNumber(),
1664 pinIndexes[ ii ] ) );
1669 legacyPins->SetText( pinMap );
1672 if(
SCH_FIELD* legacyParams = aSymbol.GetField( wxT(
"Sim_Params" ) ) )
1680 wxString device = deviceInfo.m_Text.Trim(
true ).Trim(
false );
1681 wxString lib = libInfo.m_Text.Trim(
true ).Trim(
false );
1682 wxString
model = modelInfo.m_Text.Trim(
true ).Trim(
false );
1683 wxString modelLineParams;
1685 bool libraryModel =
false;
1686 bool inferredModel =
false;
1687 bool internalModel =
false;
1689 if( !lib.IsEmpty() )
1693 std::vector<SCH_FIELD> emptyFields;
1694 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
1696 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1702 if(
EMBEDDED_FILES* symbolEmbeddedFiles = aSymbol.GetEmbeddedFiles() )
1704 embeddedFilesStack.push_back( symbolEmbeddedFiles );
1706 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1711 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1721 model =
model.BeforeFirst(
' ', &modelLineParams );
1722 modelInfo.m_Text =
model;
1724 lazySortSourcePins();
1727 emptyFields,
false, 0,
1731 libraryModel =
false;
1733 libraryModel =
true;
1735 if( pinMapInfo.IsEmpty() )
1741 if( pinMapInfo.IsEmpty() )
1742 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1745 else if( ( device == wxS(
"R" )
1746 || device == wxS(
"L" )
1747 || device == wxS(
"C" )
1748 || device == wxS(
"V" )
1749 || device == wxS(
"I" ) )
1750 && prefix.StartsWith( device )
1751 && modelFromValueField )
1753 inferredModel =
true;
1755 else if( device == wxS(
"V" ) || device == wxS(
"I" ) )
1760 wxStringTokenizer tokenizer(
model, wxT(
"() " ), wxTOKEN_STRTOK );
1762 if( tokenizer.HasMoreTokens() )
1764 deviceSubtypeInfo.m_Text = tokenizer.GetNextToken();
1765 deviceSubtypeInfo.m_Text.MakeUpper();
1767 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1776 if( deviceSubtypeInfo.m_Text == wxT(
"DC" ) && tokenizer.CountTokens() == 1 )
1778 wxCHECK( valueField, );
1779 valueField->
SetText( tokenizer.GetNextToken() );
1780 modelFromValueField =
false;
1784 for(
int ii = 0; tokenizer.HasMoreTokens(); ++ii )
1786 simModel->SetParamValue( ii, tokenizer.GetNextToken().ToStdString(),
1792 spiceParamsInfo = modelInfo;
1793 spiceParamsInfo.m_Text = wxString( simModel->Serializer().GenerateParams() );
1796 internalModel =
true;
1798 if( pinMapInfo.IsEmpty() )
1800 lazySortSourcePins();
1803 simModel->createPins(
PinNumbers( sourcePins ) );
1804 pinMapInfo.m_Text = wxString( simModel->Serializer().GeneratePins() );
1821 aSymbol.AddField( libField );
1824 aSymbol.AddField( nameField );
1826 if( !modelLineParams.IsEmpty() )
1828 spiceParamsInfo = modelInfo;
1830 spiceParamsInfo.m_Text = modelLineParams;
1833 int nameWidth = nameBBox.
GetWidth();
1839 spiceParamsInfo.m_Pos.x -= nameWidth;
1841 spiceParamsInfo.m_Pos.x += nameWidth;
1844 aSymbol.AddField( paramsField );
1847 if( modelFromValueField )
1848 valueField->
SetText( wxT(
"${SIM.NAME}" ) );
1850 else if( inferredModel )
1855 else if( internalModel )
1858 aSymbol.AddField( deviceField );
1860 if( !deviceSubtypeInfo.m_Text.IsEmpty() )
1863 aSymbol.AddField( subtypeField );
1866 if( !spiceParamsInfo.IsEmpty() )
1869 aSymbol.AddField( paramsField );
1872 if( modelFromValueField )
1873 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1877 if( device.IsEmpty() && lib.IsEmpty() )
1879 spiceParamsInfo = modelInfo;
1883 spiceParamsInfo.m_Text.Printf( wxT(
"type=\"%s\" model=\"%s\" lib=\"%s\"" ),
1892 aSymbol.AddField( deviceField );
1895 aSymbol.AddField( paramsField );
1897 if( modelFromValueField )
1903 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1909 if( pinMapInfo.IsEmpty() )
1911 lazySortSourcePins();
1912 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1916 if( !pinMapInfo.IsEmpty() )
1919 aSymbol.AddField( pinsField );