1025 wxString* aModelType, wxString* aModelParams, wxString* aPinMap )
1054 auto convertNotation =
1055 [&](
const wxString& units ) -> wxString
1059 if( units == wxS(
"µ" ) || units == wxS(
"μ" ) )
1064 if( units == wxT(
"M" ) )
1065 return wxT(
"Meg" );
1069 if( units.Capitalize() == wxT(
"Meg" ) )
1077 []( wxString* mantissa )
1079 mantissa->Replace( wxS(
" " ), wxEmptyString );
1081 wxChar ambiguousSeparator =
'?';
1082 wxChar thousandsSeparator =
'?';
1083 bool thousandsSeparatorFound =
false;
1084 wxChar decimalSeparator =
'?';
1085 bool decimalSeparatorFound =
false;
1088 for(
int ii = (
int) mantissa->length() - 1; ii >= 0; --ii )
1090 wxChar c = mantissa->GetChar( ii );
1092 if( c >=
'0' && c <=
'9' )
1096 else if( c ==
'.' || c ==
',' )
1098 if( decimalSeparator !=
'?' || thousandsSeparator !=
'?' )
1102 if( c == decimalSeparator )
1104 if( thousandsSeparatorFound )
1106 else if( decimalSeparatorFound )
1109 decimalSeparatorFound =
true;
1111 else if( c == thousandsSeparator )
1116 thousandsSeparatorFound =
true;
1119 else if( ambiguousSeparator !=
'?' )
1124 if( c == ambiguousSeparator )
1127 thousandsSeparator = ambiguousSeparator;
1128 thousandsSeparatorFound =
true;
1129 decimalSeparator = c ==
'.' ?
',' :
'.';
1135 decimalSeparator = ambiguousSeparator;
1136 decimalSeparatorFound =
true;
1137 thousandsSeparator = c;
1138 thousandsSeparatorFound =
true;
1151 if( ( ii == 1 && mantissa->GetChar( 0 ) ==
'0' ) || digits != 3 )
1153 decimalSeparator = c;
1154 decimalSeparatorFound =
true;
1155 thousandsSeparator = c ==
'.' ?
',' :
'.';
1159 ambiguousSeparator = c;
1172 if( decimalSeparator ==
'?' && thousandsSeparator ==
'?' )
1174 decimalSeparator =
'.';
1175 thousandsSeparator =
',';
1178 mantissa->Replace( thousandsSeparator, wxEmptyString );
1179 mantissa->Replace( decimalSeparator,
'.' );
1184 wxString prefix = aSymbol.GetPrefix();
1188 std::vector<SCH_PIN*> pins;
1190 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1192 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
1199 std::sort( pins.begin(), pins.end(),
1202 return a->GetNumber() < b->GetNumber();
1210 if( pins.size() != 2 )
1213 if( ( ( *aDeviceType ==
"R" || *aDeviceType ==
"L" || *aDeviceType ==
"C" )
1214 && aModelType->IsEmpty() )
1216 (
library.IsEmpty() && modelName.IsEmpty()
1217 && aDeviceType->IsEmpty()
1218 && aModelType->IsEmpty()
1220 && ( prefix.StartsWith(
"R" ) || prefix.StartsWith(
"L" ) || prefix.StartsWith(
"C" ) ) ) )
1222 if( aModelParams->IsEmpty() )
1224 wxRegEx idealVal( wxT(
"^"
1226 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1227 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1229 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1232 if( idealVal.Matches( value ) )
1234 wxString valueMantissa( idealVal.GetMatch( value, 1 ) );
1235 wxString valueExponent( idealVal.GetMatch( value, 2 ) );
1236 wxString valueFraction( idealVal.GetMatch( value, 6 ) );
1241 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1243 aModelParams->Printf( wxT(
"%s=\"%s%s\"" ),
1244 prefix.Left(1).Lower(),
1245 std::move( valueMantissa ),
1246 convertNotation( valueExponent ) );
1250 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\"" ),
1251 prefix.Left(1).Lower(),
1252 std::move( valueMantissa ),
1253 std::move( valueFraction ),
1254 convertNotation( valueExponent ) );
1259 *aModelType = wxT(
"=" );
1260 aModelParams->Printf( wxT(
"%s=\"%s\"" ), prefix.Left(1).Lower(), std::move( value ) );
1264 if( aDeviceType->IsEmpty() )
1265 *aDeviceType = prefix.Left( 1 );
1267 if( aPinMap->IsEmpty() )
1268 aPinMap->Printf( wxT(
"%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1273 if( ( ( *aDeviceType == wxT(
"V" ) || *aDeviceType == wxT(
"I" ) )
1274 && ( aModelType->IsEmpty() || *aModelType == wxT(
"DC" ) ) )
1276 ( aDeviceType->IsEmpty()
1277 && aModelType->IsEmpty()
1279 && ( prefix.StartsWith(
"V" ) || prefix.StartsWith(
"I" ) ) ) )
1281 if( !value.IsEmpty() )
1283 wxString param =
"dc";
1285 if( value.StartsWith( wxT(
"DC " ) ) )
1287 value = value.Right( value.Length() - 3 );
1289 else if( value.StartsWith( wxT(
"AC " ) ) )
1291 value = value.Right( value.Length() - 3 );
1295 wxRegEx sourceVal( wxT(
"^"
1297 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1303 if( sourceVal.Matches( value ) )
1305 wxString valueMantissa( sourceVal.GetMatch( value, 1 ) );
1306 wxString valueExponent( sourceVal.GetMatch( value, 2 ) );
1307 wxString valueFraction( sourceVal.GetMatch( value, 6 ) );
1312 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1314 aModelParams->Printf( wxT(
"%s=\"%s%s\" %s" ),
1316 std::move( valueMantissa ),
1317 convertNotation( valueExponent ),
1322 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\" %s" ),
1324 std::move( valueMantissa ),
1325 std::move( valueFraction ),
1326 convertNotation( valueExponent ),
1332 aModelParams->Printf( wxT(
"%s=\"%s\" %s" ),
1339 if( aDeviceType->IsEmpty() )
1340 *aDeviceType = prefix.Left( 1 );
1342 if( aModelType->IsEmpty() )
1343 *aModelType = wxT(
"DC" );
1345 if( aPinMap->IsEmpty() )
1346 aPinMap->Printf( wxT(
"%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
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 )
1416 existing_deviceSubtype = existing_deviceSubtypeField->
GetShownText(
false ).Upper();
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,
1732 sourcePins, reporter );
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( 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\"" ), device,
1894 aSymbol.AddField( deviceField );
1897 aSymbol.AddField( paramsField );
1899 if( modelFromValueField )
1905 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1911 if( pinMapInfo.IsEmpty() )
1913 lazySortSourcePins();
1914 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1918 if( !pinMapInfo.IsEmpty() )
1921 aSymbol.AddField( pinsField );