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 = aSymbol.GetPins();
1190 std::sort( pins.begin(), pins.end(),
1193 return a->GetNumber() < b->GetNumber();
1201 if( pins.size() != 2 )
1204 if( ( ( *aDeviceType ==
"R" || *aDeviceType ==
"L" || *aDeviceType ==
"C" )
1205 && aModelType->IsEmpty() )
1207 (
library.IsEmpty() && modelName.IsEmpty()
1208 && aDeviceType->IsEmpty()
1209 && aModelType->IsEmpty()
1211 && ( prefix.StartsWith(
"R" ) || prefix.StartsWith(
"L" ) || prefix.StartsWith(
"C" ) ) ) )
1213 if( aModelParams->IsEmpty() )
1215 wxRegEx idealVal( wxT(
"^"
1217 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1218 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1220 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1223 if( idealVal.Matches( value ) )
1225 wxString valueMantissa( idealVal.GetMatch( value, 1 ) );
1226 wxString valueExponent( idealVal.GetMatch( value, 2 ) );
1227 wxString valueFraction( idealVal.GetMatch( value, 6 ) );
1232 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1234 aModelParams->Printf( wxT(
"%s=\"%s%s\"" ),
1235 prefix.Left(1).Lower(),
1236 std::move( valueMantissa ),
1237 convertNotation( valueExponent ) );
1241 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\"" ),
1242 prefix.Left(1).Lower(),
1243 std::move( valueMantissa ),
1244 std::move( valueFraction ),
1245 convertNotation( valueExponent ) );
1250 *aModelType = wxT(
"=" );
1251 aModelParams->Printf( wxT(
"%s=\"%s\"" ), prefix.Left(1).Lower(), std::move( value ) );
1255 if( aDeviceType->IsEmpty() )
1256 *aDeviceType = prefix.Left( 1 );
1258 if( aPinMap->IsEmpty() )
1259 aPinMap->Printf( wxT(
"%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1264 if( ( ( *aDeviceType == wxT(
"V" ) || *aDeviceType == wxT(
"I" ) )
1265 && ( aModelType->IsEmpty() || *aModelType == wxT(
"DC" ) ) )
1267 ( aDeviceType->IsEmpty()
1268 && aModelType->IsEmpty()
1270 && ( prefix.StartsWith(
"V" ) || prefix.StartsWith(
"I" ) ) ) )
1272 if( !value.IsEmpty() )
1274 wxString param =
"dc";
1276 if( value.StartsWith( wxT(
"DC " ) ) )
1278 value = value.Right( value.Length() - 3 );
1280 else if( value.StartsWith( wxT(
"AC " ) ) )
1282 value = value.Right( value.Length() - 3 );
1286 wxRegEx sourceVal( wxT(
"^"
1288 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1294 if( sourceVal.Matches( value ) )
1296 wxString valueMantissa( sourceVal.GetMatch( value, 1 ) );
1297 wxString valueExponent( sourceVal.GetMatch( value, 2 ) );
1298 wxString valueFraction( sourceVal.GetMatch( value, 6 ) );
1303 if( valueMantissa.Contains( wxT(
"." ) ) || valueFraction.IsEmpty() )
1305 aModelParams->Printf( wxT(
"%s=\"%s%s\" %s" ),
1307 std::move( valueMantissa ),
1308 convertNotation( valueExponent ),
1313 aModelParams->Printf( wxT(
"%s=\"%s.%s%s\" %s" ),
1315 std::move( valueMantissa ),
1316 std::move( valueFraction ),
1317 convertNotation( valueExponent ),
1323 aModelParams->Printf( wxT(
"%s=\"%s\" %s" ),
1330 if( aDeviceType->IsEmpty() )
1331 *aDeviceType = prefix.Left( 1 );
1333 if( aModelType->IsEmpty() )
1334 *aModelType = wxT(
"DC" );
1336 if( aPinMap->IsEmpty() )
1337 aPinMap->Printf( wxT(
"%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1371 FIELD_INFO(
const wxString& aText,
SCH_FIELD* aField ) :
1378 bool IsEmpty()
const {
return m_Text.IsEmpty(); }
1380 SCH_FIELD CreateField(
T* aSymbol,
const wxString& aFieldName )
1404 wxString existing_deviceSubtype;
1406 if( existing_deviceSubtypeField )
1407 existing_deviceSubtype = existing_deviceSubtypeField->
GetShownText(
false ).Upper();
1409 if( existing_deviceField
1410 || existing_deviceSubtypeField
1411 || existing_pinsField
1412 || existing_paramsField )
1418 if( existing_deviceSubtype == wxS(
"POT" ) )
1420 if( existing_pinsField )
1422 wxString pinMap = existing_pinsField->
GetText();
1423 pinMap.Replace( wxS(
"=+" ), wxS(
"=r1" ) );
1424 pinMap.Replace( wxS(
"=-" ), wxS(
"=r0" ) );
1425 existing_pinsField->
SetText( pinMap );
1430 if( existing_deviceSubtype.StartsWith( wxS(
"RAND" ) ) )
1434 existing_deviceSubtype = existing_deviceSubtypeField->
GetText().Upper();
1436 if( existing_deviceSubtype.Replace( wxS(
"NORMAL" ), wxS(
"GAUSSIAN" ) ) )
1437 existing_deviceSubtypeField->
SetText( existing_deviceSubtype );
1439 if( existing_paramsField )
1441 wxString params = existing_paramsField->
GetText().Lower();
1448 count += params.Replace( wxS(
"min=0 " ), wxEmptyString );
1449 count += params.Replace( wxS(
"max=0 " ), wxEmptyString );
1452 count += params.Replace( wxS(
"dt=" ), wxS(
"ts=" ) );
1455 existing_paramsField->
SetText( params );
1461 if( existing_deviceSubtype == wxS(
"MUTUAL" ) )
1463 if( existing_deviceSubtypeField )
1464 aSymbol.RemoveField( existing_deviceSubtypeField );
1466 if( existing_deviceField )
1468 existing_deviceField->
SetText( wxS(
"K" ) );
1472 FIELD_INFO deviceFieldInfo;
1473 deviceFieldInfo.m_Text = wxS(
"K" );
1476 aSymbol.AddField( deviceField );
1487 return wxString( wxEmptyString );
1489 wxRegEx regex( wxT(
"([^a-z])(M)(e|E)(g|G)($|[^a-z])" ) );
1490 wxString value = aField->GetText();
1493 regex.ReplaceAll( &value, wxT(
"\\1\\2\\5" ) );
1498 auto generateDefaultPinMapFromSymbol =
1499 [](
const std::vector<SCH_PIN*>& sourcePins )
1506 for(
unsigned ii = 0; ii < sourcePins.size(); ++ii )
1509 pinMap.Append( wxS(
" " ) );
1511 pinMap.Append( wxString::Format( wxT(
"%s=%u" ),
1512 sourcePins[ii]->GetNumber(),
1519 wxString prefix = aSymbol.GetPrefix();
1521 std::vector<SCH_PIN*> sourcePins = aSymbol.GetPins();
1522 bool sourcePinsSorted =
false;
1524 auto lazySortSourcePins =
1525 [&sourcePins, &sourcePinsSorted]()
1527 if( !sourcePinsSorted )
1529 std::sort( sourcePins.begin(), sourcePins.end(),
1532 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
1536 sourcePinsSorted =
true;
1539 FIELD_INFO deviceInfo;
1540 FIELD_INFO modelInfo;
1541 FIELD_INFO deviceSubtypeInfo;
1543 FIELD_INFO spiceParamsInfo;
1544 FIELD_INFO pinMapInfo;
1545 bool modelFromValueField =
false;
1555 deviceInfo = FIELD_INFO( primitiveField->GetText(), primitiveField );
1556 aSymbol.RemoveField( primitiveField );
1561 const wxString delimiters(
"{:,; }" );
1562 const wxString& nodeSequence = nodeSequenceField->GetText();
1565 if( nodeSequence !=
"" )
1567 wxStringTokenizer tkz( nodeSequence, delimiters );
1569 for(
long modelPinNumber = 1; tkz.HasMoreTokens(); ++modelPinNumber )
1571 long symbolPinNumber = 1;
1572 tkz.GetNextToken().ToLong( &symbolPinNumber );
1574 if( modelPinNumber != 1 )
1575 pinMap.Append(
" " );
1577 pinMap.Append( wxString::Format(
"%ld=%ld", symbolPinNumber, modelPinNumber ) );
1581 pinMapInfo = FIELD_INFO( pinMap, nodeSequenceField );
1582 aSymbol.RemoveField( nodeSequenceField );
1587 modelInfo = FIELD_INFO( getSIValue( modelField ), modelField );
1588 aSymbol.RemoveField( modelField );
1590 else if( valueField )
1592 modelInfo = FIELD_INFO( getSIValue( valueField ), valueField );
1593 modelFromValueField =
true;
1598 libInfo = FIELD_INFO( libFileField->GetText(), libFileField );
1599 aSymbol.RemoveField( libFileField );
1606 if(
SCH_FIELD* legacyType = aSymbol.GetField( wxT(
"Sim_Type" ) ) )
1611 if(
SCH_FIELD* legacyDevice = aSymbol.GetField( wxT(
"Sim_Device" ) ) )
1616 if(
SCH_FIELD* legacyPins = aSymbol.GetField( wxT(
"Sim_Pins" ) ) )
1618 bool isPassive = prefix.StartsWith( wxT(
"R" ) )
1619 || prefix.StartsWith( wxT(
"L" ) )
1620 || prefix.StartsWith( wxT(
"C" ) );
1624 wxArrayString pinIndexes;
1628 lazySortSourcePins();
1630 if( isPassive && pinIndexes.size() == 2 && sourcePins.size() == 2 )
1632 if( pinIndexes[0] == wxT(
"2" ) )
1634 pinMap.Printf( wxT(
"%s=- %s=+" ),
1635 sourcePins[0]->GetNumber(),
1636 sourcePins[1]->GetNumber() );
1640 pinMap.Printf( wxT(
"%s=+ %s=-" ),
1641 sourcePins[0]->GetNumber(),
1642 sourcePins[1]->GetNumber() );
1647 for(
unsigned ii = 0; ii < pinIndexes.size() && ii < sourcePins.size(); ++ii )
1650 pinMap.Append( wxS(
" " ) );
1652 pinMap.Append( wxString::Format( wxT(
"%s=%s" ),
1653 sourcePins[ii]->GetNumber(),
1654 pinIndexes[ ii ] ) );
1659 legacyPins->SetText( pinMap );
1662 if(
SCH_FIELD* legacyParams = aSymbol.GetField( wxT(
"Sim_Params" ) ) )
1670 wxString device = deviceInfo.m_Text.Trim(
true ).Trim(
false );
1671 wxString lib = libInfo.m_Text.Trim(
true ).Trim(
false );
1672 wxString model = modelInfo.m_Text.Trim(
true ).Trim(
false );
1673 wxString modelLineParams;
1675 bool libraryModel =
false;
1676 bool inferredModel =
false;
1677 bool internalModel =
false;
1679 if( !lib.IsEmpty() )
1683 std::vector<SCH_FIELD> emptyFields;
1684 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
1686 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
1692 if(
EMBEDDED_FILES* symbolEmbeddedFiles = aSymbol.GetEmbeddedFiles() )
1693 embeddedFilesStack.push_back( symbolEmbeddedFiles );
1698 model = model.BeforeFirst(
' ', &modelLineParams );
1699 modelInfo.m_Text = model;
1701 lazySortSourcePins();
1704 emptyFields,
false, 0,
1705 sourcePins, reporter );
1708 libraryModel =
false;
1710 libraryModel =
true;
1712 if( pinMapInfo.IsEmpty() )
1718 if( pinMapInfo.IsEmpty() )
1719 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1722 else if( ( device == wxS(
"R" )
1723 || device == wxS(
"L" )
1724 || device == wxS(
"C" )
1725 || device == wxS(
"V" )
1726 || device == wxS(
"I" ) )
1727 && prefix.StartsWith( device )
1728 && modelFromValueField )
1730 inferredModel =
true;
1732 else if( device == wxS(
"V" ) || device == wxS(
"I" ) )
1737 wxStringTokenizer tokenizer( model, wxT(
"() " ), wxTOKEN_STRTOK );
1739 if( tokenizer.HasMoreTokens() )
1741 deviceSubtypeInfo.m_Text = tokenizer.GetNextToken();
1742 deviceSubtypeInfo.m_Text.MakeUpper();
1744 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1753 if( deviceSubtypeInfo.m_Text == wxT(
"DC" ) && tokenizer.CountTokens() == 1 )
1755 wxCHECK( valueField, );
1756 valueField->
SetText( tokenizer.GetNextToken() );
1757 modelFromValueField =
false;
1761 for(
int ii = 0; tokenizer.HasMoreTokens(); ++ii )
1763 simModel->SetParamValue( ii, tokenizer.GetNextToken().ToStdString(),
1769 spiceParamsInfo = modelInfo;
1770 spiceParamsInfo.m_Text = wxString( simModel->Serializer().GenerateParams() );
1773 internalModel =
true;
1775 if( pinMapInfo.IsEmpty() )
1777 lazySortSourcePins();
1780 simModel->createPins( sourcePins );
1781 pinMapInfo.m_Text = wxString( simModel->Serializer().GeneratePins() );
1798 aSymbol.AddField( libField );
1801 aSymbol.AddField( nameField );
1803 if( !modelLineParams.IsEmpty() )
1805 spiceParamsInfo = modelInfo;
1807 spiceParamsInfo.m_Text = modelLineParams;
1810 int nameWidth = nameBBox.
GetWidth();
1816 spiceParamsInfo.m_Pos.x -= nameWidth;
1818 spiceParamsInfo.m_Pos.x += nameWidth;
1821 aSymbol.AddField( paramsField );
1824 if( modelFromValueField )
1825 valueField->
SetText( wxT(
"${SIM.NAME}" ) );
1827 else if( inferredModel )
1832 else if( internalModel )
1835 aSymbol.AddField( deviceField );
1837 if( !deviceSubtypeInfo.m_Text.IsEmpty() )
1840 aSymbol.AddField( subtypeField );
1843 if( !spiceParamsInfo.IsEmpty() )
1846 aSymbol.AddField( paramsField );
1849 if( modelFromValueField )
1850 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1854 if( device.IsEmpty() && lib.IsEmpty() )
1856 spiceParamsInfo = modelInfo;
1860 spiceParamsInfo.m_Text.Printf( wxT(
"type=\"%s\" model=\"%s\" lib=\"%s\"" ), device,
1867 aSymbol.AddField( deviceField );
1870 aSymbol.AddField( paramsField );
1872 if( modelFromValueField )
1878 valueField->
SetText( wxT(
"${SIM.PARAMS}" ) );
1884 if( pinMapInfo.IsEmpty() )
1886 lazySortSourcePins();
1887 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1891 if( !pinMapInfo.IsEmpty() )
1894 aSymbol.AddField( pinsField );