28#include <fmt/format.h>
29#include <wx/wfstream.h>
30#include <wx/stdstream.h>
32#include <wx/clipbrd.h>
34#include <wx/tokenzr.h>
55#include <magic_enum.hpp>
61 int res =
static_cast<int>( aFirst ) |
static_cast<int>( aSecond);
122 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
override;
173 menu.AppendSeparator();
174 menu.Append(
MYID_FOURIER,
_(
"Perform Fourier Analysis..." ) );
177 menu.AppendSeparator();
180 menu.AppendSeparator();
183 m_grid->PopupMenu( &menu );
190 menu.AppendSeparator();
192 wxString msg =
m_grid->GetColLabelValue(
m_grid->GetNumberCols() - 1 );
194 menu.AppendSeparator();
197 m_grid->PopupMenu( &menu );
203 m_grid->PopupMenu( &menu );
210 std::vector<wxString> signals;
212 wxGridCellCoordsArray cells1 =
m_grid->GetSelectionBlockTopLeft();
213 wxGridCellCoordsArray cells2 =
m_grid->GetSelectionBlockBottomRight();
215 for(
size_t i = 0; i < cells1.Count(); i++ )
219 for(
int j = cells1[i].GetRow(); j < cells2[i].GetRow() + 1; j++ )
221 signals.push_back(
m_grid->GetCellValue( j, cells1[i].GetCol() ) );
226 wxGridCellCoordsArray cells3 =
m_grid->GetSelectedCells();
228 for(
size_t i = 0; i < cells3.Count(); i++ )
231 signals.push_back(
m_grid->GetCellValue( cells3[i].GetRow(), cells3[i].GetCol() ) );
234 if( signals.size() < 1 )
237 auto addMeasurement =
238 [
this](
const wxString& cmd, wxString signal )
240 if( signal.EndsWith(
_(
" (phase)" ) ) )
243 if( signal.EndsWith(
_(
" (gain)" ) ) || signal.EndsWith(
_(
" (amplitude)" ) ) )
245 signal = signal.Left( signal.length() - 7 );
247 if( signal.Upper().StartsWith( wxS(
"V(" ) ) )
248 signal = wxS(
"vdb" ) + signal.Mid( 1 );
251 m_parent->AddMeasurement( cmd + wxS(
" " ) + signal );
256 for(
const wxString& signal : signals )
257 addMeasurement( wxS(
"MIN" ), signal );
261 for(
const wxString& signal : signals )
262 addMeasurement( wxS(
"MAX" ), signal );
266 for(
const wxString& signal : signals )
267 addMeasurement( wxS(
"AVG" ), signal );
271 for(
const wxString& signal : signals )
272 addMeasurement( wxS(
"RMS" ), signal );
276 for(
const wxString& signal : signals )
277 addMeasurement( wxS(
"PP" ), signal );
281 for(
const wxString& signal : signals )
282 addMeasurement( wxS(
"MIN_AT" ), signal );
286 for(
const wxString& signal : signals )
287 addMeasurement( wxS(
"MAX_AT" ), signal );
291 for(
const wxString& signal : signals )
292 addMeasurement( wxS(
"INTEG" ), signal );
297 wxString fundamental = wxT(
"1K" );
299 if( signals.size() == 1 )
300 title.Printf(
_(
"Fourier Analysis of %s" ), signals[0] );
302 title =
_(
"Fourier Analyses of Multiple Signals" );
312 for(
const wxString& signal : signals )
313 m_parent->DoFourier( signal, fundamental );
320 for(
const wxString& signal : signals )
328 if( wxTheClipboard->Open() )
330 wxTheClipboard->SetData(
new wxTextDataObject( txt ) );
331 wxTheClipboard->Flush();
332 wxTheClipboard->Close();
357 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
override;
377 menu.AppendSeparator();
387 [
this](
int row ) -> wxString
391 if( signal.EndsWith(
"[2 - 1]" ) )
392 signal = signal.Left( signal.length() - 7 );
403 if( formatDialog.
ShowModal() == wxID_OK )
405 for(
int row = 0; row <
m_grid->GetNumberRows(); ++row )
407 if( getSignalName( row ) == getSignalName(
m_menuRow ) )
408 m_parent->SetCursorFormat( row, axis, format );
430 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
override;
456 menu.AppendSeparator();
469 if( formatDialog.
ShowModal() == wxID_OK )
478 std::vector<int> measurements;
480 wxGridCellCoordsArray cells1 =
m_grid->GetSelectionBlockTopLeft();
481 wxGridCellCoordsArray cells2 =
m_grid->GetSelectionBlockBottomRight();
483 for(
size_t i = 0; i < cells1.Count(); i++ )
487 for(
int j = cells1[i].GetRow(); j < cells2[i].GetRow() + 1; j++ )
488 measurements.push_back( j );
492 wxGridCellCoordsArray cells3 =
m_grid->GetSelectedCells();
494 for(
size_t i = 0; i < cells3.Count(); i++ )
497 measurements.push_back( cells3[i].GetRow() );
500 if( measurements.size() < 1 )
505 sort( measurements.begin(), measurements.end(), std::greater<>() );
507 for(
int row : measurements )
527 m_frame->m_SuppressGridEvents++;
532 m_frame->m_SuppressGridEvents--;
540#define ID_SIM_REFRESH 10207
541#define REFRESH_INTERVAL 50
566 wxGridCellAttr* attr =
new wxGridCellAttr;
570 attr =
new wxGridCellAttr;
574 attr =
new wxGridCellAttr;
578 attr =
new wxGridCellAttr;
584 attr =
new wxGridCellAttr;
594 [&]( wxTimerEvent& aEvent )
603#ifndef wxHAS_NATIVE_TABART
623 m_cursorFormat[0] = { 3, wxS(
"~s" ) };
624 m_cursorFormat[1] = { 3, wxS(
"~V" ) };
635 for(
size_t index2 = 0; index2 < std::size(
m_cursorFormats[0] ); index2++ )
649 maxCursor.Replace(
_(
"Cursor " ),
"" );
651 int tmpMax = wxAtoi( maxCursor );
653 if( nameMax < tmpMax )
663 std::vector<SPICE_VALUE_FORMAT> tmp;
675 wxGridCellAttr* attr =
new wxGridCellAttr;
695 for(
int i = 0; i < rows; i++ )
697 if(
m_signalsGrid->GetCellValue( i, col - 1 ) == wxS(
"1" ) )
700 wxGridEvent aDummy( wxID_ANY, wxEVT_GRID_CELL_CHANGED,
m_signalsGrid, i, col - 1 );
720 for(
int ii = 0; ii < static_cast<int>(
m_plotNotebook->GetPageCount() ); ++ii )
724 simTab->OnLanguageChanged();
726 wxString pageTitle(
simulator()->TypeToName( simTab->GetSimType(),
true ) );
727 pageTitle.Prepend( wxString::Format(
_(
"Analysis %u - " ), ii+1 ) );
748 tuner->ShowChangedLanguage();
788 simTab->ApplyPreferences( aPrefs );
796 bool loadFromSchematic =
false;
798 if( !workbookFilename.IsEmpty() )
800 wxFileName filename = workbookFilename;
803 if( !filename.FileExists() )
806 wxString::Format(
_(
"Workbook file '%s' not found. "
807 "Loading simulation settings from schematic." ),
808 filename.GetFullPath() ),
809 8000, wxICON_WARNING );
812 loadFromSchematic =
true;
821 loadFromSchematic =
true;
824 if( loadFromSchematic &&
m_simulatorFrame->LoadSimulator( wxEmptyString, 0 ) )
828 if( !schTextSimCommand.IsEmpty() )
861 std::sort( signals.begin(), signals.end(),
862 [](
const wxString& lhs,
const wxString& rhs )
865 if( lhs.Upper().StartsWith(
'V' ) && !rhs.Upper().StartsWith(
'V' ) )
867 else if( !lhs.Upper().StartsWith(
'V' ) && rhs.Upper().StartsWith(
'V' ) )
870 return StrNumCmp( lhs, rhs, true ) < 0;
887 std::vector<wxString> signals;
891 wxStringTokenizer tokenizer( plotPanel->
GetSimCommand(),
" \t\r\n", wxTOKEN_STRTOK );
893 while( tokenizer.HasMoreTokens() && tokenizer.GetNextToken().Lower() != wxT(
"fft" ) )
896 while( tokenizer.HasMoreTokens() )
897 signals.emplace_back( tokenizer.GetNextToken() );
904 for(
const wxString& signal :
m_signals )
905 signals.push_back( signal );
909 if( simType ==
ST_AC )
911 signals.push_back( signal +
_(
" (gain)" ) );
912 signals.push_back( signal +
_(
" (phase)" ) );
914 else if( simType ==
ST_SP )
918 signals.push_back( signal );
922 signals.push_back( signal +
_(
" (amplitude)" ) );
923 signals.push_back( signal +
_(
" (phase)" ) );
928 signals.push_back( signal );
935 if( aFilter.IsEmpty() )
936 aFilter = wxS(
"*" );
941 for(
const wxString& signal : signals )
943 if( matcher.
Find( signal.Upper() ) )
952 wxGridCellAttr* attr =
new wxGridCellAttr;
953 attr->SetRenderer(
new wxGridCellBoolRenderer() );
955 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
960 attr =
new wxGridCellAttr;
965 attr =
new wxGridCellAttr;
969 attr =
new wxGridCellAttr;
977 attr =
new wxGridCellAttr;
987 attr =
new wxGridCellAttr;
990 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
995 attr =
new wxGridCellAttr;
996 attr->SetRenderer(
new wxGridCellBoolRenderer() );
998 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1002 attr =
new wxGridCellAttr;
1003 attr->SetRenderer(
new wxGridCellBoolRenderer() );
1004 attr->SetReadOnly();
1005 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1013 attr =
new wxGridCellAttr;
1014 attr->SetRenderer(
new wxGridCellBoolRenderer() );
1015 attr->SetReadOnly();
1016 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1034 wxString unconnected = wxString( wxS(
"unconnected-(" ) );
1039 unconnected.Replace(
'(',
'_' );
1045 [&](
const wxString& aSignalName )
1047 if( simType ==
ST_AC )
1049 m_signals.push_back( aSignalName +
_(
" (gain)" ) );
1050 m_signals.push_back( aSignalName +
_(
" (phase)" ) );
1052 else if( simType ==
ST_SP )
1060 m_signals.push_back( aSignalName +
_(
" (amplitude)" ) );
1061 m_signals.push_back( aSignalName +
_(
" (phase)" ) );
1079 if( netname ==
"GND" || netname ==
"0" || netname.StartsWith( unconnected ) )
1083 addSignal( wxString::Format( wxS(
"V(%s)" ), netname ) );
1093 for(
const std::string&
name : item.model->SpiceGenerator().CurrentNames( item ) )
1103 if( item.model->GetPinCount() >= 2 )
1105 wxString
name = item.model->SpiceGenerator().ItemName( item );
1106 addSignal( wxString::Format( wxS(
"P(%s)" ),
name ) );
1113 addSignal( wxS(
"inoise_spectrum" ) );
1114 addSignal( wxS(
"onoise_spectrum" ) );
1117 if( simType ==
ST_SP )
1119 std::vector<std::string> portnums;
1123 wxString
name = item.model->SpiceGenerator().ItemName( item );
1126 if( !
name.StartsWith(
"V" ) )
1129 std::string portnum =
"";
1131 if(
const SIM_MODEL::PARAM* portnum_param = item.model->FindParam(
"portnum" ) )
1135 portnums.push_back( portnum );
1138 for(
const std::string& portnum1 : portnums )
1140 for(
const std::string& portnum2 : portnums )
1144 if( smithMode && portnum1 != portnum2 )
1147 addSignal( wxString::Format( wxS(
"S_%s_%s" ), portnum1, portnum2 ) );
1153 for(
const wxString& directive :
circuitModel()->GetDirectives() )
1155 wxStringTokenizer directivesTokenizer( directive,
"\r\n", wxTOKEN_STRTOK );
1157 while( directivesTokenizer.HasMoreTokens() )
1159 wxString line = directivesTokenizer.GetNextToken().Upper();
1160 wxString directiveParams;
1162 if( line.StartsWith( wxS(
".SAVE" ), &directiveParams )
1163 || line.StartsWith( wxS(
".PROBE" ), &directiveParams ) )
1165 wxStringTokenizer paramsTokenizer( directiveParams,
" \t", wxTOKEN_STRTOK );
1167 while( paramsTokenizer.HasMoreTokens() )
1168 addSignal( paramsTokenizer.GetNextToken() );
1191 wxString pageTitle(
simulator()->TypeToName( simType,
true ) );
1192 pageTitle.Prepend( wxString::Format(
_(
"Analysis %u - " ),
static_cast<unsigned int>( ++
m_plotNumber ) ) );
1208#if defined( __WXOSX__ )
1209 wxPoint pos = aEvent.GetPosition();
1210 wxRect ctrlRect =
m_filter->GetScreenRect();
1211 int buttonWidth = ctrlRect.GetHeight();
1213 if(
m_filter->IsSearchButtonVisible() && pos.x < buttonWidth )
1214 SetCursor( wxCURSOR_ARROW );
1215 else if(
m_filter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
1216 SetCursor( wxCURSOR_ARROW );
1218 SetCursor( wxCURSOR_IBEAM );
1225 return wxString::Format( wxS(
"user%d" ), aUserDefinedSignalId );
1236 auto looksLikePower = [](
const wxString& aExpression ) ->
bool
1238 wxString exprUpper = aExpression.Upper();
1240 if( exprUpper.Contains( wxS(
":POWER" ) ) )
1243 if( exprUpper.Find(
'*' ) == wxNOT_FOUND )
1246 if( !exprUpper.Contains( wxS(
"V(" ) ) )
1249 if( !exprUpper.Contains( wxS(
"I(" ) ) )
1255 std::map<wxString, int> suffixes;
1271 wxUniChar firstChar = aSignalName.Upper()[0];
1273 if( firstChar ==
'V' )
1275 else if( firstChar ==
'I' )
1277 else if( firstChar ==
'P' )
1282 wxString
name = aSignalName;
1284 for(
const auto& [ candidate, type ] : suffixes )
1286 if(
name.EndsWith( candidate ) )
1288 name =
name.Left(
name.Length() - candidate.Length() );
1291 *aTraceType |= type;
1306 if(
name == signal )
1308 if( aTraceType && looksLikePower( signal ) )
1332 int row = aEvent.GetRow();
1333 int col = aEvent.GetCol();
1341 if(
text == wxS(
"1" ) )
1376 TRACE* activeTrace =
nullptr;
1378 if(
text == wxS(
"1" ) )
1382 activeTrace = plotTab->
GetTrace( vectorName, traceType );
1393 if( trace != activeTrace && trace->
HasCursor(
id ) )
1416 int row = aEvent.GetRow();
1417 int col = aEvent.GetCol();
1425 CURSOR* cursor1 =
nullptr;
1426 CURSOR* cursor2 =
nullptr;
1428 std::vector<CURSOR*> cursorsVec;
1450 cursorsVec.emplace_back(
cursor );
1452 if( cursorName == ( wxString(
"" ) << i ) &&
cursor )
1453 cursor->SetCoordX( value );
1460 if( cursorName == wxS(
"1" ) && cursor1 )
1462 else if( cursorName == wxS(
"2" ) && cursor2 )
1464 else if( cursorName ==
_(
"Diff" ) && cursor1 && cursor2 )
1472 wxFAIL_MSG( wxT(
"All other columns are supposed to be read-only!" ) );
1505 int row = aEvent.GetRow();
1506 int col = aEvent.GetCol();
1516 wxFAIL_MSG( wxT(
"All other columns are supposed to be read-only!" ) );
1524 for( row = rowCount - 1; row >= 0; row-- )
1534 int killRows = emptyRows - 1;
1537 else if( emptyRows == 0 )
1548 if( plotTab->GetLegendPosition() != plotTab->m_LastLegendPosition )
1550 plotTab->m_LastLegendPosition = plotTab->GetLegendPosition();
1573 static wxRegEx measureParamsRegEx( wxT(
"^"
1577 "([a-zA-Z]*)\\(([^\\)]+)\\)" ) );
1586 if(
text.IsEmpty() )
1593 wxString resultName = wxString::Format( wxS(
"meas_result_%u" ), aRow );
1594 wxString
result = wxS(
"?" );
1596 if( measureParamsRegEx.Matches(
text ) )
1598 wxString func = measureParamsRegEx.GetMatch(
text, 1 ).Upper();
1599 wxString signalType = measureParamsRegEx.GetMatch(
text, 2 ).Upper();
1600 wxString deviceName = measureParamsRegEx.GetMatch(
text, 3 );
1604 if( signalType.EndsWith( wxS(
"DB" ) ) )
1606 units = wxS(
"dB" );
1608 else if( signalType.StartsWith(
'I' ) )
1612 else if( signalType.StartsWith(
'P' ) )
1616 text = func +
" " + deviceName +
":power";
1623 if( func.EndsWith( wxS(
"_AT" ) ) )
1626 units = wxS(
"Hz" );
1630 else if( func.StartsWith( wxS(
"INTEG" ) ) )
1635 if ( signalType.StartsWith(
'P' ) )
1638 units += wxS(
".s" );
1648 units += wxS(
"·Hz" );
1656 units += wxS(
"·?" );
1669 wxString cmd = wxString::Format( wxS(
"meas %s %s %s" ), simType, resultName,
text );
1675 if( resultVec.size() > 0 )
1690 "the value of a passive R, L, C model or voltage or "
1691 "current source." ) );
1695 wxString ref = aSymbol->
GetRef( &aSheetPath );
1700 if( tuner->GetSymbolRef() == ref )
1723 const wxString& aRef,
const wxString& aValue )
1726 wxString variant = schematic.GetCurrentVariant();
1733 + wxString::Format(
_(
"%s not found" ), aRef ) );
1740 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
1741 embeddedFilesStack.push_back(
m_schematicFrame->Schematic().GetEmbeddedFiles() );
1745 embeddedFilesStack.push_back( symbolEmbeddedFiles );
1758 + wxString::Format(
_(
"%s is not tunable" ), aRef ) );
1762 model.SetParamValue( tunerParam->
info.
name, std::string( aValue.ToUTF8() ) );
1832 wxString cmd = wxString::Format( wxS(
"fourier %s %s" ),
1850 m_simConsole->AppendText(
_(
"Error: no current simulation.\n" ) );
1859 m_simConsole->AppendText(
_(
"Error: simulation type not defined.\n" ) );
1865 m_simConsole->AppendText(
_(
"Error: simulation type doesn't support plotting.\n" ) );
1872 if( simType ==
ST_AC )
1877 else if( simType ==
ST_SP )
1879 if( plotTab->IsSmithMode() )
1894 plotTab->GetPlotWin()->UpdateAll();
1916 if( aNewSignals.count(
id ) == 0 )
1921 plotTab->
DeleteTrace( vectorName, traceType | subType );
1926 plotTab->
DeleteTrace( vectorName, traceType | subType );
1939 if(
TRACE* trace = plotTab->
GetTrace( vectorName, traceType | subType ) )
1940 trace->SetName( aNewSignals.at(
id ) );
1947 if(
TRACE* trace = plotTab->
GetTrace( vectorName, traceType | subType ) )
1948 trace->SetName( aNewSignals.at(
id ) );
1953 if(
TRACE* trace = plotTab->
GetTrace( vectorName, traceType ) )
1954 trace->SetName( aNewSignals.at(
id ) );
1975 long responsePort, drivePort;
1991 || portnum != responsePort )
2012 std::vector<double>* aDataX,
bool aClearData )
2025 wxString simVectorName = aVectorName;
2028 simVectorName = simVectorName.AfterFirst(
'(' ).BeforeLast(
')' ) + wxS(
":power" );
2038 std::vector<double> data_x;
2039 std::vector<double> data_y;
2040 std::vector<double> frequencies;
2042 if( !aDataX || aClearData )
2046 if( aDataX->empty() && !aClearData )
2048 wxString xAxisName(
simulator()->GetXAxis( simType ) );
2050 if( xAxisName.IsEmpty() )
2056 unsigned int size = aDataX->size();
2066 wxFAIL_MSG( wxT(
"Plot type missing AC_PHASE or AC_MAG bit" ) );
2073 frequencies = *aDataX;
2087 wxFAIL_MSG( wxT(
"Plot type missing AC_PHASE, SPT_SP_AMP or SPT_SP_SMITH bit" ) );
2103 wxFAIL_MSG( wxT(
"Unhandled plot type" ) );
2108 size_t sweepSize = std::numeric_limits<size_t>::max();
2110 if( simType ==
ST_DC
2117 sweepSize = aDataX->size() / sweepCount;
2136 size_t sweepSizeMulti = traceData.
xValues.size();
2137 size_t runCount = traceData.
yValues.size();
2139 if( sweepSizeMulti > 0 && runCount > 0 )
2141 std::vector<double> combinedX;
2142 std::vector<double> combinedY;
2143 std::vector<double> combinedFreq;
2145 combinedX.reserve( sweepSizeMulti * runCount );
2146 combinedY.reserve( sweepSizeMulti * runCount );
2148 for(
size_t run = 0; run < traceData.
yValues.size(); run++ )
2150 const std::vector<double>& runY = traceData.
yValues[run];
2152 if( runY.size() != sweepSizeMulti )
2158 if( run >= traceData.
xRuns.size() || traceData.
xRuns[run].size() != sweepSizeMulti )
2161 combinedX.insert( combinedX.end(), traceData.
xRuns[run].begin(),
2162 traceData.
xRuns[run].end() );
2166 combinedX.insert( combinedX.end(), traceData.
xValues.begin(), traceData.
xValues.end() );
2169 combinedY.insert( combinedY.end(), runY.begin(), runY.end() );
2172 if( frequencies.size() == sweepSizeMulti )
2173 combinedFreq.insert( combinedFreq.end(), frequencies.begin(), frequencies.end() );
2183 if( combinedFreq.size() == combinedX.size() )
2184 smith->SetFrequencies( combinedFreq );
2187 if( combinedY.size() >= combinedX.size() && sweepSizeMulti > 0 )
2189 int sweepCountCombined = combinedX.empty() ? 0 :
static_cast<int>( combinedY.size() / sweepSizeMulti );
2191 if( sweepCountCombined > 0 )
2194 std::vector<wxString> labels;
2195 labels.reserve( sweepCountCombined );
2197 for(
int i = 0; i < sweepCountCombined && i < (int)
m_multiRunState.steps.size(); ++i )
2205 label += wxS(
", " );
2208 double value = it->second;
2214 labels.push_back( label );
2217 aPlotTab->
SetTraceData( trace, combinedX, combinedY, sweepCountCombined,
2218 sweepSizeMulti,
true, labels );
2231 if( data_y.size() >= size )
2235 smith->SetFrequencies( frequencies );
2240 aPlotTab->
SetTraceData( trace, *aDataX, data_y, sweepCount, sweepSize );
2251template <
typename T,
typename U,
typename R>
2259 wxGridCellAttrPtr attr =
m_signalsGrid->GetOrCreateCellAttrPtr( r,
static_cast<int>( t ) );
2261 if(
TRACE* trace = plotTab ? plotTab->
GetTrace( vectorName, traceType ) :
nullptr )
2263 attr->SetReadOnly();
2267 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
2270 if constexpr ( std::is_enum<T>::value )
2274 m_signalsGrid->SetCellValue( r,
static_cast<int>( t ), wxS(
"1" ) );
2278 if( !attr->HasRenderer() )
2281 if( !attr->HasEditor() )
2284 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
2285 attr->SetReadOnly(
false );
2294 if( !attr->HasRenderer() )
2295 attr->SetRenderer(
new wxGridCellBoolRenderer() );
2297 if( u > 0 && trace->HasCursor( u ) )
2298 m_signalsGrid->SetCellValue( r,
static_cast<int>( t ), wxS(
"1" ) );
2300 m_signalsGrid->SetCellValue( r,
static_cast<int>( t ), wxEmptyString );
2306 if constexpr ( std::is_enum<T>::value )
2310 m_signalsGrid->SetCellValue( r,
static_cast<int>( t ), wxEmptyString );
2317 attr->SetEditor(
nullptr );
2318 attr->SetRenderer(
nullptr );
2319 attr->SetReadOnly();
2320 m_signalsGrid->SetCellValue( r,
static_cast<int>( t ), wxEmptyString );
2329 for(
int row = 0; row <
m_signalsGrid->GetNumberRows(); ++row )
2351 auto quoteNetNames =
2352 [&]( wxString aExpression ) -> wxString
2354 std::vector<bool> mask( aExpression.length(),
false );
2356 auto isNetnameChar =
2357 []( wxUniChar aChar ) ->
bool
2359 wxUint32 value = aChar.GetValue();
2361 if( ( value >=
'0' && value <=
'9' ) || ( value >=
'A' && value <=
'Z' )
2362 || ( value >=
'a' && value <=
'z' ) )
2385 size_t pos = aExpression.find( netname );
2387 while( pos != wxString::npos )
2389 for(
size_t i = 0; i < netname.length(); ++i )
2390 mask[pos + i] =
true;
2392 pos = aExpression.find( netname, pos + 1 );
2396 for(
size_t i = 0; i < aExpression.length(); ++i )
2398 if( !mask[i] || ( i > 0 && mask[i - 1] ) )
2403 while( j < aExpression.length() )
2411 if( isNetnameChar( aExpression[j] ) )
2423 wxString quotedNetnames =
"";
2424 bool startQuote =
true;
2427 for(
size_t i = 0; i < aExpression.length(); i++ )
2429 if( mask[i] && startQuote )
2431 quotedNetnames = quotedNetnames +
"\"";
2434 else if( !mask[i] && !startQuote )
2436 quotedNetnames = quotedNetnames +
"\"";
2440 wxString ch = aExpression[i];
2441 quotedNetnames = quotedNetnames + ch;
2445 quotedNetnames = quotedNetnames +
"\"";
2447 return quotedNetnames;
2452 constexpr const char* cmd =
"let user{} = {}";
2454 simulator()->
Command(
"echo " + fmt::format( cmd,
id, signal.ToStdString() ) );
2467 wxString ref = tuner->GetSymbolRef();
2468 KIID symbolId = tuner->GetSymbol( &sheetPath );
2474 reporter.Report( wxString::Format(
_(
"%s not found" ), ref ) );
2482 reporter.Report( wxString::Format(
_(
"%s is not tunable" ), ref ) );
2491 floatVal = overrideIt->second;
2493 floatVal = tuner->GetValue().ToDouble();
2504 wxTextFile file( aPath );
2509 wxString firstLine = file.GetFirstLine();
2511 bool legacy = firstLine.StartsWith( wxT(
"version " ) ) || firstLine.ToLong( &
dummy );
2536 wxFileName filename( aPath );
2548 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
2549 wxStdInputStream fstream( fp );
2556 nlohmann::json js = nlohmann::json::parse( fstream,
nullptr,
true,
true );
2558 std::map<SIM_PLOT_TAB*, nlohmann::json> traceInfo;
2560 for(
const nlohmann::json& tab_js : js[
"tabs" ] )
2562 wxString simCommand;
2566 if( !tab_js.contains(
"commands" ) || !tab_js[
"commands"].is_array() )
2569 for(
const nlohmann::json& cmd : tab_js[
"commands" ] )
2571 if( cmd ==
".kicad adjustpaths" )
2573 else if( cmd ==
".save all" )
2575 else if( cmd ==
".probe alli" )
2577 else if( cmd ==
".probe allp" )
2579 else if( cmd ==
".kicad esavenone" )
2582 simCommand += wxString( cmd.get<wxString>() ).Trim();
2592 if( tab_js.contains(
"traces" ) )
2593 traceInfo[plotTab] = tab_js[
"traces" ];
2595 if( tab_js.contains(
"measurements" ) && tab_js[
"measurements"].is_array() )
2597 for(
const nlohmann::json& m_js : tab_js[
"measurements" ] )
2599 plotTab->
Measurements().emplace_back( wxString( m_js.value(
"expr",
"" ) ),
2600 wxString( m_js.value(
"format",
"" ) ) );
2605 plotTab->
ShowGrid( tab_js.value(
"showGrid",
true ) );
2607 if( tab_js.value(
"smithMode",
false ) )
2610 plotTab->
SetSmithView( tab_js.value(
"smithZoom", 1.0 ), tab_js.value(
"smithPanX", 0.0 ),
2611 tab_js.value(
"smithPanY", 0.0 ) );
2613 if( tab_js.contains(
"smithStashedTraces" ) && tab_js[
"smithStashedTraces"].is_array() )
2615 for(
const nlohmann::json& stash_js : tab_js[
"smithStashedTraces"] )
2617 wxString vector( stash_js.value(
"vector",
"" ) );
2618 wxString
name( stash_js.value(
"name",
"" ) );
2620 if( vector.IsEmpty() )
2624 { vector,
name.IsEmpty() ? vector :
name, stash_js.value(
"base_type", 0 ) } );
2628 if( tab_js.contains(
"smithStashedCursors" ) && tab_js[
"smithStashedCursors"].is_array() )
2630 for(
const nlohmann::json& stash_js : tab_js[
"smithStashedCursors"] )
2632 wxString vector( stash_js.value(
"vector",
"" ) );
2633 int id = stash_js.value(
"id", 0 );
2635 if( vector.IsEmpty() || (
id < 1 &&
id != -1 ) )
2639 { id, vector, stash_js.value(
"base_type", 0 ), stash_js.value(
"sub_type", 0 ),
2640 stash_js.value(
"frequency", std::nan(
"" ) ) } );
2645 auto loadScale = [&](
const char* aKey,
auto&& aSetter )
2647 if( !tab_js.contains( aKey ) )
2651 nlohmann::json min_js = tab_js[aKey].value(
"min", nlohmann::json() );
2652 nlohmann::json max_js = tab_js[aKey].value(
"max", nlohmann::json() );
2654 if( !min_js.is_number() || !max_js.is_number() )
2657 double min = min_js.get<
double>();
2658 double max = max_js.get<
double>();
2662 aSetter( min, max );
2667 loadScale(
"fixedY1scale",
2668 [&](
double min,
double max )
2673 loadScale(
"fixedY2scale",
2674 [&](
double min,
double max )
2679 loadScale(
"fixedY3scale",
2680 [&](
double min,
double max )
2686 if( tab_js.contains(
"legend" ) )
2688 const nlohmann::json& legend_js = tab_js[
"legend" ];
2689 plotTab->
SetLegendPosition( wxPoint( legend_js.value(
"x", 0 ), legend_js.value(
"y", 0 ) ) );
2693 if( tab_js.contains(
"margins" ) )
2695 const nlohmann::json& margins_js = tab_js[
"margins" ];
2696 plotTab->
GetPlotWin()->
SetMargins( margins_js.value(
"top", 30 ), margins_js.value(
"right", 70 ),
2697 margins_js.value(
"bottom", 45 ),
2698 margins_js.value(
"left", 70 ) );
2705 if( js.contains(
"user_defined_signals" ) )
2707 for(
const nlohmann::json& signal_js : js[
"user_defined_signals" ] )
2713 m_simulatorFrame->LoadSimulator( simTab->GetSimCommand(), simTab->GetSimOptions() );
2716 firstTab->SetLastSchTextSimCommand( wxString( js.value(
"last_sch_text_sim_command",
"" ) ) );
2720 int tempCustomCursorsCnt = js.value(
"custom_cursors", 2 );
2730 int aCursorId,
const nlohmann::json& aCursor_js )
2732 if( aCursorId >= 1 )
2734 aPlotTab->
EnableCursor( aTrace, aCursorId, aSignalName );
2738 nlohmann::json position = aCursor_js.value(
"position", nlohmann::json() );
2740 if( position.is_number() )
2743 cursor->SetCoordX( position.get<
double>() );
2747 wxString xFormat( aCursor_js.value(
"x_format",
"" ) );
2748 wxString yFormat( aCursor_js.value(
"y_format",
"" ) );
2751 if( aCursorId == -1 )
2753 else if( aCursorId < 3 )
2754 formatSlot = aCursorId - 1;
2756 formatSlot = aCursorId;
2758 if( !xFormat.IsEmpty() )
2761 if( !yFormat.IsEmpty() )
2765 for(
const auto& [ plotTab, traces_js ] : traceInfo )
2767 for(
const nlohmann::json& trace_js : traces_js )
2769 int traceType = trace_js.value(
"trace_type", (
int)
SPT_UNKNOWN );
2770 wxString signalName( trace_js.value(
"signal",
"" ) );
2772 if( signalName.IsEmpty() || traceType ==
SPT_UNKNOWN )
2776 TRACE* trace = plotTab->GetOrAddTrace( vectorName, traceType );
2780 if( trace_js.contains(
"cursorD" ) )
2781 addCursor( plotTab, trace, signalName, -1, trace_js[
"cursorD" ] );
2783 std::vector<const char*> aVec;
2786 for(
int i = 1; i <= tempCustomCursorsCnt; i++ )
2788 wxString str =
"cursor" + std::to_string( i );
2789 aVec.emplace_back( str.c_str() );
2791 if( trace_js.contains( aVec[i - 1] ) )
2792 addCursor( plotTab, trace, signalName, i, trace_js[aVec[i - 1]] );
2795 if( trace_js.contains(
"color" ) )
2798 color.Set( wxString( trace_js[
"color"].get<wxString>() ) );
2800 plotTab->UpdateTraceStyle( trace );
2805 plotTab->UpdatePlotColors();
2808 catch( nlohmann::json::parse_error& error )
2810 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
2814 catch( nlohmann::json::type_error& error )
2816 wxLogTrace(
traceSettings, wxT(
"Json type error reading %s: %s" ), aPath, error.what() );
2820 catch( nlohmann::json::invalid_iterator& error )
2822 wxLogTrace(
traceSettings, wxT(
"Json invalid_iterator error reading %s: %s" ), aPath, error.what() );
2826 catch( nlohmann::json::out_of_range& error )
2828 wxLogTrace(
traceSettings, wxT(
"Json out_of_range error reading %s: %s" ), aPath, error.what() );
2834 wxLogTrace(
traceSettings, wxT(
"Error reading %s" ), aPath );
2843 int cursorIdAfterD = aCursorId;
2846 cursorIdAfterD = cursorIdAfterD - 1;
2851 nlohmann::json cursor_js =
2857 double position =
cursor->GetCoords().x;
2859 if( std::isfinite( position ) )
2860 cursor_js[
"position"] = position;
2862 aTraceJs[
"cursor" + wxString(
"" ) << aCursorId] = cursor_js;
2867 aTraceJs[
"cursorD"] =
2878 wxFileName filename = aPath;
2883 file.Create( filename.GetFullPath(),
true );
2885 if( !file.IsOpened() )
2888 nlohmann::json tabs_js = nlohmann::json::array();
2899 nlohmann::json commands_js = nlohmann::json::array();
2906 commands_js.push_back(
".kicad adjustpaths" );
2909 commands_js.push_back(
".save all" );
2912 commands_js.push_back(
".probe alli" );
2915 commands_js.push_back(
".probe allp" );
2918 commands_js.push_back(
".kicad esavenone" );
2920 nlohmann::json tab_js = nlohmann::json(
2922 {
"commands", commands_js } } );
2926 nlohmann::json traces_js = nlohmann::json::array();
2928 auto findSignalName =
2929 [&](
const wxString& aVectorName ) -> wxString
2931 wxString vectorName;
2934 if( aVectorName.EndsWith(
_(
" (phase)" ) ) )
2935 suffix =
_(
" (phase)" );
2936 else if( aVectorName.EndsWith(
_(
" (gain)" ) ) )
2937 suffix =
_(
" (gain)" );
2939 vectorName = aVectorName.Left( aVectorName.Length() - suffix.Length() );
2944 return signal + suffix;
2950 for(
const auto& [
name, trace] : plotTab->GetTraces() )
2952 nlohmann::json trace_js = nlohmann::json(
2953 { {
"trace_type", (int) trace->GetType() },
2954 {
"signal", findSignalName( trace->GetDisplayName() ) },
2960 if( trace->GetCursor( 1 ) || trace->GetCursor( 2 ) )
2962 trace_js[
"cursorD"] = nlohmann::json(
2967 traces_js.push_back( trace_js );
2970 nlohmann::json measurements_js = nlohmann::json::array();
2972 for(
const auto& [
measurement, format ] : plotTab->Measurements() )
2974 measurements_js.push_back( nlohmann::json( { {
"expr",
measurement },
2975 {
"format", format } } ) );
2978 tab_js[
"traces" ] = traces_js;
2979 tab_js[
"measurements" ] = measurements_js;
2980 tab_js[
"dottedSecondary" ] = plotTab->GetDottedSecondary();
2981 tab_js[
"showGrid" ] = plotTab->IsGridShown();
2982 tab_js[
"smithMode" ] = plotTab->IsSmithMode();
2984 if( plotTab->IsSmithMode() )
2986 tab_js[
"smithZoom"] = plotTab->GetSmithZoom();
2987 tab_js[
"smithPanX"] = plotTab->GetSmithPan().x;
2988 tab_js[
"smithPanY"] = plotTab->GetSmithPan().y;
2992 nlohmann::json stashedTraces_js = nlohmann::json::array();
2996 stashedTraces_js.push_back( nlohmann::json( { {
"vector", stashed.vectorName },
2997 {
"name", stashed.displayName },
2998 {
"base_type", stashed.baseType } } ) );
3001 if( !stashedTraces_js.empty() )
3002 tab_js[
"smithStashedTraces"] = stashedTraces_js;
3004 nlohmann::json stashedCursors_js = nlohmann::json::array();
3008 nlohmann::json cursor_js = nlohmann::json( { {
"id", stashed.id },
3009 {
"vector", stashed.vectorName },
3010 {
"base_type", stashed.baseType },
3011 {
"sub_type", stashed.subType } } );
3013 if( std::isfinite( stashed.frequency ) )
3014 cursor_js[
"frequency"] = stashed.frequency;
3016 stashedCursors_js.push_back( cursor_js );
3019 if( !stashedCursors_js.empty() )
3020 tab_js[
"smithStashedCursors"] = stashedCursors_js;
3026 auto saveScale = [&](
const char* aKey,
double aMin,
double aMax )
3028 if( std::isfinite( aMin ) && std::isfinite( aMax ) )
3029 tab_js[aKey] = nlohmann::json( { {
"min", aMin }, {
"max", aMax } } );
3032 if( plotTab->GetY1Scale( &min, &max ) )
3033 saveScale(
"fixedY1scale", min, max );
3035 if( plotTab->GetY2Scale( &min, &max ) )
3036 saveScale(
"fixedY2scale", min, max );
3038 if( plotTab->GetY3Scale( &min, &max ) )
3039 saveScale(
"fixedY3scale", min, max );
3041 if( plotTab->IsLegendShown() )
3043 tab_js[
"legend" ] = nlohmann::json( { {
"x", plotTab->GetLegendPosition().x },
3044 {
"y", plotTab->GetLegendPosition().y } } );
3047 mpWindow* plotWin = plotTab->GetPlotWin();
3049 tab_js[
"margins" ] = nlohmann::json( { {
"left", plotWin->
GetMarginLeft() },
3055 tabs_js.push_back( tab_js );
3058 nlohmann::json userDefinedSignals_js = nlohmann::json::array();
3061 userDefinedSignals_js.push_back( signal );
3064 nlohmann::json js = nlohmann::json( { {
"version", 8 },
3065 {
"tabs", tabs_js },
3066 {
"user_defined_signals", userDefinedSignals_js },
3079 std::stringstream buffer;
3080 buffer << std::setw( 2 ) << js << std::endl;
3082 bool res = file.Write( buffer.str() );
3109 wxFAIL_MSG( wxString::Format( wxS(
"Unhandled simulation type: %d" ), (
int) aType ) );
3129 &source, &
scale, &pts, &fStart, &fStop, &saveAll );
3137 int& aSashPosition )
3139 bool isShown = aPanel->IsShown();
3142 aSashPosition = aSplitterWindow->GetSashPosition();
3144 aPanel->Show( !isShown );
3146 aSplitterWindow->SetSashInvisible( isShown );
3147 aSplitterWindow->SetSashPosition( isShown ? -1 : aSashPosition,
true );
3149 aSplitterWindow->UpdateSize();
3150 m_parent->Refresh();
3187 for(
size_t page = 0; page <
m_plotNotebook->GetPageCount(); page++ )
3214 wxString vectorName;
3215 wxString displayName;
3219 std::vector<SHOWN_VECTOR> shownVectors;
3225 wxString vectorName;
3231 std::vector<SAVED_CURSOR> savedCursors;
3233 for(
const auto& [
id, trace] : plotTab->
GetTraces() )
3242 for(
const SHOWN_VECTOR& sv : shownVectors )
3243 seen |= sv.vectorName == vectorName;
3246 shownVectors.push_back( { vectorName, trace->GetName(), baseType } );
3248 for(
const auto& [cursorId,
cursor] : trace->GetCursors() )
3251 savedCursors.push_back( { cursorId, vectorName, baseType, subType,
cursor->GetCoords().x } );
3256 auto isReflection = [](
const wxString& aName ) ->
bool
3258 long response, drive;
3261 return response == drive;
3272 stashedTraces.clear();
3273 stashedCursors.clear();
3275 for(
const SHOWN_VECTOR& sv : shownVectors )
3277 if( !isReflection( sv.vectorName ) )
3278 stashedTraces.push_back( { sv.vectorName, sv.displayName, sv.baseType } );
3281 for(
const SAVED_CURSOR& saved : savedCursors )
3282 stashedCursors.push_back( { saved.id, saved.vectorName, saved.baseType, saved.subType, saved.coordX } );
3291 for(
const SHOWN_VECTOR& sv : shownVectors )
3292 seen |= sv.vectorName == stashed.vectorName;
3295 shownVectors.push_back( { stashed.vectorName, stashed.displayName, stashed.baseType } );
3299 for(
const SHOWN_VECTOR& sv : shownVectors )
3302 plotTab->
DeleteTrace( sv.vectorName, sv.baseType | subType );
3307 for(
const SHOWN_VECTOR& sv : shownVectors )
3309 std::vector<int> subTypes;
3313 if( !isReflection( sv.vectorName ) )
3323 for(
int subType : subTypes )
3325 updateTrace( sv.vectorName, sv.baseType | subType, plotTab );
3327 if(
TRACE* trace = plotTab->
GetTrace( sv.vectorName, sv.baseType | subType ) )
3328 trace->SetName( sv.displayName );
3335 for(
const SAVED_CURSOR& saved : savedCursors )
3341 if( !trace || trace->
GetCursor( saved.id ) )
3348 if( std::isfinite( saved.coordX ) )
3349 cursor->SetCoordX( saved.coordX );
3359 if( stashed.id == aId && stashed.vectorName == aVectorName )
3366 auto restoreCursor = [&](
int aId,
const wxString& aVectorName,
int aBaseType,
int aSubType,
3367 double aFreq ) ->
bool
3371 if(
TRACE* trace = plotTab->
GetTrace( aVectorName, aBaseType | wantSubType ) )
3376 if( std::isfinite( aFreq ) )
3379 cursor->SetCoordX( aFreq );
3388 std::vector<int> restoredIds;
3391 for(
const SAVED_CURSOR& saved : savedCursors )
3395 if( restoreCursor( saved.id, saved.vectorName, saved.baseType,
3398 restoredIds.push_back( saved.id );
3406 if( isReflection( stashed.vectorName ) )
3409 if( std::find( restoredIds.begin(), restoredIds.end(), stashed.id ) != restoredIds.end() )
3412 restoreCursor( stashed.id, stashed.vectorName, stashed.baseType, stashed.subType, stashed.frequency );
3415 stashedTraces.clear();
3416 stashedCursors.clear();
3451 schematic.ClearOperatingPoints();
3463 std::vector<std::pair<wxString, wxString>>& measurements = plotTab->Measurements();
3465 measurements.clear();
3503 simulator()->
Command(
"setplot " + simTab->GetSpicePlotName().ToStdString() );
3520 for(
const auto& [
measurement, format ] : plotTab->Measurements() )
3528 if( plotTab->GetSimType() ==
ST_TRAN || plotTab->GetSimType() ==
ST_AC
3529 || plotTab->GetSimType() ==
ST_DC || plotTab->GetSimType() ==
ST_SP )
3566 CURSOR* cursor1 =
nullptr;
3567 wxString cursor1Name;
3568 wxString cursor1Units;
3569 CURSOR* cursor2 =
nullptr;
3570 wxString cursor2Name;
3571 wxString cursor2Units;
3573 auto getUnitsY = [&](
TRACE* aTrace ) -> wxString
3597 auto getNameY = [&](
TRACE* aTrace ) -> wxString
3600 return _(
"Refl. Coeff." );
3621 [
this](
double aValue,
int aCursorId,
int aCol ) -> wxString
3623 if( ( !
m_simulatorFrame->SimFinished() && aCol == 1 ) || std::isnan( aValue ) )
3634 cursor1Name = getNameY( trace );
3635 cursor1Units = getUnitsY( trace );
3637 wxRealPoint coords =
cursor->GetCoords();
3657 cursor2Name = getNameY( trace );
3658 cursor2Units = getUnitsY( trace );
3660 wxRealPoint coords =
cursor->GetCoords();
3675 if( cursor1 && cursor2 && cursor1Units == cursor2Units )
3684 signal = wxString::Format( wxS(
"%s[2 - 1]" ), cursor2->
GetName() );
3686 signal = wxString::Format( wxS(
"%s - %s" ), cursor2->
GetName(), cursor1->
GetName() );
3697 wxString valColName =
_(
"Value" );
3699 if( !cursor1Name.IsEmpty() )
3701 if( cursor2Name.IsEmpty() || cursor1Name == cursor2Name )
3702 valColName = cursor1Name;
3704 else if( !cursor2Name.IsEmpty() )
3706 valColName = cursor2Name;
3720 wxString cursName = getNameY( trace );
3721 wxString cursUnits = getUnitsY( trace );
3723 wxRealPoint coords =
cursor->GetCoords();
3738 valColName =
_(
"Value" );
3741 valColName = cursName;
3762 plotTab->ResetScales(
true );
3782 if( messages.IsEmpty() )
3792 std::vector<wxString> signals;
3794 for(
const std::string& vec :
simulator()->AllVectors() )
3795 signals.emplace_back( vec );
3803 std::vector<wxString> signals;
3805 for(
const wxString& signal :
m_signals )
3806 signals.emplace_back( signal );
3809 signals.emplace_back( signal );
3829 bool storeMultiRun =
false;
3835 storeMultiRun =
true;
3850 if( aFinal && simType ==
ST_FFT )
3854 if( !spicePlotName.IsEmpty() )
3869 if( simType ==
ST_NOISE && aFinal )
3871 m_simConsole->AppendText(
_(
"\n\nSimulation results:\n\n" ) );
3880 for(
const std::string& vec :
simulator()->AllVectors() )
3885 msg.Printf( wxS(
"%s: %sV\n" ), vec, value );
3896 wxCHECK_RET( plotTab, wxString::Format( wxT(
"No SIM_PLOT_TAB for: %s" ),
3897 magic_enum::enum_name( simType ) ) );
3906 std::map<TRACE*, TRACE_INFO> traceMap;
3909 traceMap[ trace ] = { wxEmptyString,
SPT_UNKNOWN,
false };
3914 for(
const wxString& signal :
m_signals )
3919 if(
TRACE* trace = plotTab->
GetTrace( vectorName, traceType ) )
3920 traceMap[ trace ] = { vectorName, traceType,
false };
3928 if( simType ==
ST_AC )
3934 if(
TRACE* trace = plotTab->
GetTrace( vectorName, subType ) )
3935 traceMap[ trace ] = { vectorName, subType, !aFinal };
3938 else if( simType ==
ST_SP )
3944 if(
TRACE* trace = plotTab->
GetTrace( vectorName, subType ) )
3945 traceMap[trace] = { vectorName, subType, !aFinal };
3950 if(
TRACE* trace = plotTab->
GetTrace( vectorName, traceType ) )
3951 traceMap[ trace ] = { vectorName, traceType, !aFinal };
3957 for(
const auto& [ trace, traceInfo ] : traceMap )
3959 if( traceInfo.Vector.IsEmpty() )
3963 for(
const auto& [ trace,
info ] : traceMap )
3965 std::vector<double> data_x;
3967 if( !
info.Vector.IsEmpty() )
3985 else if( simType ==
ST_OP && aFinal )
3987 m_simConsole->AppendText(
_(
"\n\nSimulation results:\n\n" ) );
3990 for(
const std::string& vec :
simulator()->AllVectors() )
3994 if( val_list.empty() )
4001 const size_t tab = 25;
4002 size_t padding = ( signal.length() < tab ) ? ( tab - signal.length() ) : 1;
4006 case SPT_VOLTAGE: value.Append( wxS(
"V" ) );
break;
4007 case SPT_CURRENT: value.Append( wxS(
"A" ) );
break;
4008 case SPT_POWER: value.Append( wxS(
"W" ) );
break;
4009 default: value.Append( wxS(
"?" ) );
break;
4012 msg.Printf( wxT(
"%s%s\n" ),
4013 ( signal + wxT(
":" ) ).Pad( padding, wxUniChar(
' ' ) ),
4020 signal = signal.SubString( 2, signal.Length() - 2 );
4023 signal += wxS(
":power" );
4025 m_schematicFrame->Schematic().SetOperatingPoint( signal, val_list.at( 0 ) );
4028 else if( simType ==
ST_PZ && aFinal )
4030 m_simConsole->AppendText(
_(
"\n\nSimulation results:\n\n" ) );
4098 std::vector<TUNER_SLIDER*> multiTuners;
4103 multiTuners.push_back( tuner );
4106 if( multiTuners.empty() )
4138 else if( tunersChanged )
4147 for(
const auto& entry : step.
overrides )
4154 const std::vector<TUNER_SLIDER*>& aTuners )
const
4156 std::vector<MULTI_RUN_STEP> steps;
4158 if( aTuners.empty() )
4161 std::vector<std::vector<double>> tunerValues;
4162 tunerValues.reserve( aTuners.size() );
4169 double startValue = tuner->GetMin().ToDouble();
4170 double endValue = tuner->GetMax().ToDouble();
4171 int stepCount = std::max( 2, tuner->GetStepCount() );
4176 double increment = ( endValue - startValue ) /
static_cast<double>( stepCount - 1 );
4178 std::vector<double> values;
4179 values.reserve( stepCount );
4181 for(
int ii = 0; ii < stepCount; ++ii )
4182 values.push_back( startValue + increment * ii );
4184 tunerValues.push_back( std::move( values ) );
4192 std::vector<double> currentValues( aTuners.size(), 0.0 );
4194 auto generate = [&](
auto&& self,
size_t depth ) ->
void
4196 if( steps.size() >=
static_cast<size_t>( limit ) )
4199 if( depth == aTuners.size() )
4203 for(
size_t ii = 0; ii < aTuners.size(); ++ii )
4204 step.
overrides.emplace( aTuners[ii], currentValues[ii] );
4206 steps.push_back( std::move( step ) );
4210 for(
double value : tunerValues[depth] )
4212 currentValues[depth] = value;
4213 self( self, depth + 1 );
4215 if( steps.size() >=
static_cast<size_t>( limit ) )
4220 generate( generate, 0 );
4228 return fmt::format(
"{}|{}", aVectorName.ToStdString(), aTraceType );
4233 const std::vector<double>& aX,
4234 const std::vector<double>& aY )
4236 if( aX.empty() || aY.empty() )
4247 if( trace.
xValues.size() != aX.size() )
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void doPopupSelection(wxCommandEvent &event) override
SIMULATOR_FRAME_UI * m_parent
CURSORS_GRID_TRICKS(SIMULATOR_FRAME_UI *aParent, WX_GRID *aGrid)
The SIMULATOR_FRAME holds the main user-interface for running simulations.
const wxRealPoint & GetCoords() const
virtual void SetCoordX(double aValue)
bool Find(const wxString &aTerm, int &aMatchersTriggered, int &aPosition)
Look in all existing matchers, return the earliest match of any of the existing.
GRID_TRICKS(WX_GRID *aGrid)
virtual void doPopupSelection(wxCommandEvent &event)
virtual void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent)
WX_GRID * m_grid
I don't own the grid, but he owns me.
A color representation with 4 components: red, green, blue, alpha.
wxString ToCSSString() const
wxColour ToColour() const
Hold a translatable error message and may be used when throwing exceptions containing a translated er...
const wxString What() const
void AppendParentEmbeddedFiles(std::vector< EMBEDDED_FILES * > &aStack) const
void doPopupSelection(wxCommandEvent &event) override
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
SIMULATOR_FRAME_UI * m_parent
MEASUREMENTS_GRID_TRICKS(SIMULATOR_FRAME_UI *aParent, WX_GRID *aGrid)
static void ConvertToSpiceMarkup(wxString *aNetName)
Remove formatting wrappers and replace illegal spice net name characters with underscores.
@ OPTION_SAVE_ALL_CURRENTS
@ OPTION_SAVE_ALL_VOLTAGES
@ OPTION_SAVE_ALL_DISSIPATIONS
@ OPTION_ADJUST_INCLUDE_PATHS
@ OPTION_ADJUST_PASSIVE_VALS
const SPICE_ITEM * FindItem(const wxString &aRefName) const
Find and return the item corresponding to aRefName.
A singleton reporter that reports to nowhere.
Holds all the data relating to one schematic.
Schematic editor (Eeschema) main window.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_ITEM * ResolveItem(const KIID &aID) const
Fetch a SCH_ITEM by ID.
EMBEDDED_FILES * GetEmbeddedFiles() override
SCH_SYMBOLs don't currently support embedded files, but their LIB_SYMBOL counterparts do.
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
void doPopupSelection(wxCommandEvent &event) override
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
SIMULATOR_FRAME_UI * m_parent
SIGNALS_GRID_TRICKS(SIMULATOR_FRAME_UI *aParent, WX_GRID *aGrid)
wxBoxSizer * m_sizerTuners
WX_GRID * m_measurementsGrid
wxSplitterWindow * m_splitterLeftRight
wxSplitterWindow * m_splitterMeasurements
wxTextCtrl * m_simConsole
wxSplitterWindow * m_splitterCursors
wxSplitterWindow * m_splitterPlotAndConsole
SIMULATOR_FRAME_UI_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
wxSplitterWindow * m_splitterSignals
wxAuiNotebook * m_plotNotebook
The SIMULATOR_FRAME_UI holds the main user-interface for running simulations.
SIM_TAB * NewSimTab(const wxString &aSimCommand)
Create a new simulation tab for a given simulation type.
std::string multiRunTraceKey(const wxString &aVectorName, int aTraceType) const
void SetUserDefinedSignals(const std::map< int, wxString > &aSignals)
void updatePlotCursors()
Update the cursor values (in the grid) and graphics (in the plot window).
void OnSimRefresh(bool aFinal)
void onPlotClose(wxAuiNotebookEvent &event) override
void CustomCursorsInit()
Init handler for custom cursors.
void recordMultiRunData(const wxString &aVectorName, int aTraceType, const std::vector< double > &aX, const std::vector< double > &aY)
std::vector< MULTI_RUN_STEP > calculateMultiRunSteps(const std::vector< TUNER_SLIDER * > &aTuners) const
int m_splitterTuneValuesSashPosition
void TogglePanel(wxPanel *aPanel, wxSplitterWindow *aSplitterWindow, int &aSashPosition)
A common toggler for the two main wxSplitterWindow s.
void onPlotChanged(wxAuiNotebookEvent &event) override
void rebuildSignalsGrid(wxString aFilter)
Rebuild the filtered list of signals in the signals grid.
std::vector< std::vector< SPICE_VALUE_FORMAT > > m_cursorFormatsDyn
void DoFourier(const wxString &aSignal, const wxString &aFundamental)
void rebuildMeasurementsGrid()
Rebuild the measurements grid for the current plot.
std::list< TUNER_SLIDER * > m_tuners
void rebuildSignalsList()
Rebuild the list of signals available from the netlist.
bool loadLegacyWorkbook(const wxString &aPath)
int m_splitterCursorsSashPosition
MULTI_RUN_STATE m_multiRunState
SPICE expressions need quoted versions of the netnames since KiCad allows '-' and '/' in netnames.
void ToggleSimSidePanel()
void UpdateMeasurement(int aRow)
Update a measurement in the measurements grid.
wxString getNoiseSource() const
std::vector< wxString > SimPlotVectors() const
unsigned int m_plotNumber
void SetSubWindowsSashSize()
Adjust the sash dimension of splitter windows after reading the config settings must be called after ...
void applyUserDefinedSignals()
Apply user-defined signals to the SPICE session.
SIM_PREFERENCES m_preferences
void DeleteMeasurement(int aRow)
Delete a row from the measurements grid.
SCH_EDIT_FRAME * m_schematicFrame
wxString vectorNameFromSignalName(SIM_PLOT_TAB *aPlotTab, const wxString &aSignalName, int *aTraceType)
Get the simulator output vector name for a given signal name and type.
void updateSignalsGrid()
Update the values in the signals grid.
int m_splitterLeftRightSashPosition
void onCursorsGridCellChanged(wxGridEvent &aEvent) override
void onPlotDragged(wxAuiNotebookEvent &event) override
std::vector< wxString > Signals() const
bool SaveWorkbook(const wxString &aPath)
Save plot, signal, cursor, measurement, etc.
bool hasMultiRunTrace(const wxString &aVectorName, int aTraceType) const
std::vector< wxString > m_signals
SIM_TAB * GetCurrentSimTab() const
Return the currently opened plot panel (or NULL if there is none).
void SaveCursorToWorkbook(nlohmann::json &aTraceJs, TRACE *aTrace, int aCursorId)
void updateMeasurementsFromGrid()
bool LoadWorkbook(const wxString &aPath)
Load plot, signal, cursor, measurement, etc.
SPICE_VALUE_FORMAT GetMeasureFormat(int aRow) const
Get/Set the format of a value in the measurements grid.
std::map< int, wxString > m_userDefinedSignals
void UpdateTunerValue(const SCH_SHEET_PATH &aSheetPath, const KIID &aSymbol, const wxString &aRef, const wxString &aValue)
Safely update a field of the associated symbol without dereferencing the symbol.
std::vector< wxString > m_netnames
void AddTrace(const wxString &aName, SIM_TRACE_TYPE aType)
Add a new trace to the current plot.
void onPlotClosed(wxAuiNotebookEvent &event) override
void RemoveTuner(TUNER_SLIDER *aTuner)
Remove an existing tuner.
void SaveSettings(EESCHEMA_SETTINGS *aCfg)
std::shared_ptr< SPICE_CIRCUIT_MODEL > circuitModel() const
void OnFilterText(wxCommandEvent &aEvent) override
void onMeasurementsGridCellChanged(wxGridEvent &aEvent) override
void DeleteCursor()
Deletes last m_signalsGrid "Cursor n" column, removes vector's m_cursorFormatsDyn last entry,...
void CreateNewCursor()
Creates a column at the end of m_signalsGrid named "Cursor n" ( n = m_customCursorsCnt ),...
void applyTuners()
Apply component values specified using tuner sliders to the current netlist.
bool loadJsonWorkbook(const wxString &aPath)
void clearMultiRunState(bool aClearTraces)
void OnFilterMouseMoved(wxMouseEvent &aEvent) override
void AddMeasurement(const wxString &aCmd)
Add a measurement to the measurements grid.
void onPlotChanging(wxAuiNotebookEvent &event) override
std::map< const TUNER_SLIDER *, double > m_tunerOverrides
std::shared_ptr< SPICE_SIMULATOR > simulator() const
void onPlotCursorUpdate(wxCommandEvent &aEvent)
void onSignalsGridCellChanged(wxGridEvent &aEvent) override
void InitWorkbook()
Load the currently active workbook stored in the project settings.
void ToggleDarkModePlots()
Toggle the current S-parameter tab between Smith chart and amplitude/phase views.
SIM_TRACE_TYPE getXAxisType(SIM_TYPE aType) const
Return X axis for a given simulation type.
void prepareMultiRunState()
void OnPlotSettingsChanged()
void signalsGridCursorUpdate(T t, U u, R r)
Updates m_signalsGrid cursor widget, column rendering and attributes.
void SetMeasureFormat(int aRow, const SPICE_VALUE_FORMAT &aFormat)
int m_splitterSignalsSashPosition
double getSmithPortImpedance(const wxString &aVectorName)
void ApplyPreferences(const SIM_PREFERENCES &aPrefs)
Called when settings are changed via the common Preferences dialog.
const SPICE_CIRCUIT_MODEL * GetExporter() const
Return the netlist exporter object used for simulations.
int m_splitterPlotAndConsoleSashPosition
SIMULATOR_FRAME * m_simulatorFrame
void ShowChangedLanguage()
void AddTuner(const SCH_SHEET_PATH &aSheetPath, SCH_SYMBOL *aSymbol)
Add a tuner for a symbol.
bool IsSimSidePanelShown()
void OnUpdateUI(wxUpdateUIEvent &event) override
void updateTrace(const wxString &aVectorName, int aTraceType, SIM_PLOT_TAB *aPlotTab, std::vector< double > *aDataX=nullptr, bool aClearData=false)
Update a trace in a particular SIM_PLOT_TAB.
SIMULATOR_FRAME_UI(SIMULATOR_FRAME *aSimulatorFrame, SCH_EDIT_FRAME *aSchematicFrame)
SPICE_VALUE_FORMAT m_cursorFormats[3][2]
void LoadSettings(EESCHEMA_SETTINGS *aCfg)
The SIMULATOR_FRAME holds the main user-interface for running simulations.
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
void SetFilesStack(std::vector< EMBEDDED_FILES * > aFilesStack)
virtual const PARAM * GetTunerParam() const
const SPICE_GENERATOR & SpiceGenerator() const
static void FillDefaultColorList(bool aWhiteBg)
Fills m_colorList by a default set of colors.
bool DeleteTrace(const wxString &aVectorName, int aTraceType)
void UpdateSmithReferenceImpedance()
wxString GetLabelY1() const
mpWindow * GetPlotWin() const
void SetSmithView(double aZoom, double aPanX, double aPanY)
void ShowGrid(bool aEnable)
void SetTraceData(TRACE *aTrace, std::vector< double > &aX, std::vector< double > &aY, int aSweepCount, size_t aSweepSize, bool aIsMultiRun=false, const std::vector< wxString > &aMultiRunLabels={})
wxString GetUnitsY2() const
void SetY2Scale(bool aLock, double aMin, double aMax)
TRACE * GetTrace(const wxString &aVecName, int aType) const
void SetSmithMode(bool aEnable)
wxString GetLabelX() const
const std::map< wxString, TRACE * > & GetTraces() const
wxString GetLabelY3() const
void SetY1Scale(bool aLock, double aMin, double aMax)
void SetY3Scale(bool aLock, double aMin, double aMax)
std::vector< std::pair< wxString, wxString > > & Measurements()
void UpdateTraceStyle(TRACE *trace)
Update plot colors.
void SetLegendPosition(const wxPoint &aPosition)
void ResetScales(bool aIncludeX)
Update trace line style.
std::vector< SMITH_STASHED_TRACE > & SmithStashedTraces()
void ShowLegend(bool aEnable)
wxString GetLabelY2() const
void EnableCursor(TRACE *aTrace, int aCursorId, const wxString &aSignalName)
bool IsSmithMode() const
Refresh the grid z0 from the shown Smith traces.
wxString GetUnitsX() const
void EnsureThirdYAxisExists()
TRACE * GetOrAddTrace(const wxString &aVectorName, int aType)
std::vector< SMITH_STASHED_CURSOR > & SmithStashedCursors()
Turn on/off the cursor for a particular trace.
void SetDottedSecondary(bool aEnable)
Draw secondary signal traces (current or phase) with dotted lines.
void ApplyPreferences(const SIM_PREFERENCES &aPrefs) override
wxString GetUnitsY1() const
void DisableCursor(TRACE *aTrace, int aCursorId)
Reset scale ranges to fit the current traces.
wxString GetUnitsY3() const
int GetSimOptions() const
const wxString & GetSpicePlotName() const
SIM_TYPE GetSimType() const
const wxString & GetSimCommand() const
static bool IsPlottable(SIM_TYPE aSimType)
void SetSimOptions(int aOptions)
wxString GetLastSchTextSimCommand() const
void SetSpicePlotName(const wxString &aPlotName)
static double ToDouble(const std::string &aString, double aDefault=NAN)
static std::string ToSpice(const std::string &aString)
Cursor that snaps along a Smith chart locus, keyed by frequency.
Special netlist exporter flavor that allows one to override simulation commands.
static SIM_TYPE CommandToSimType(const wxString &aCmd)
Return simulation type basing on a simulation command directive.
bool ParseNoiseCommand(const wxString &aCmd, wxString *aOutput, wxString *aRef, wxString *aSource, wxString *aScale, SPICE_VALUE *aPts, SPICE_VALUE *aFStart, SPICE_VALUE *aFStop, bool *aSaveAll)
wxString GetSchTextSimCommand()
Return simulation command directives placed in schematic sheets (if any).
SIM_TRACE_TYPE VectorToSignal(const std::string &aVector, wxString &aSignal) const
Return name of Spice dataset for a specific trace.
virtual std::string TunerCommand(const SPICE_ITEM &aItem, double aValue) const
wxString GetWorkbookFilename() const
void SetWorkbookFilename(const wxString &aFilename)
virtual std::vector< double > GetImaginaryVector(const std::string &aName, int aMaxLen=-1)=0
Return a requested vector with imaginary values.
virtual bool Command(const std::string &aCmd)=0
Execute a Spice command as if it was typed into console.
static wxString TypeToName(SIM_TYPE aType, bool aShortName)
Return a string with simulation name based on enum.
std::shared_ptr< SPICE_SETTINGS > & Settings()
Return the simulator configuration settings.
virtual wxString CurrentPlotName() const =0
virtual std::vector< double > GetRealVector(const std::string &aName, int aMaxLen=-1)=0
Return a requested vector with real values.
virtual std::vector< double > GetGainVector(const std::string &aName, int aMaxLen=-1)=0
Return a requested vector with magnitude values.
virtual std::vector< double > GetPhaseVector(const std::string &aName, int aMaxLen=-1)=0
Return a requested vector with phase values.
Helper class to recognize Spice formatted values.
wxString ToString() const
Return string value as when converting double to string (e.g.
wxString ToSpiceString() const
Return string value in Spice format (e.g.
~SUPPRESS_GRID_CELL_EVENTS()
SUPPRESS_GRID_CELL_EVENTS(SIMULATOR_FRAME_UI *aFrame)
SIMULATOR_FRAME_UI * m_frame
Overlay layer drawing the Smith chart grid (constant resistance and reactance circles)
void SetTraceColour(const wxColour &aColour)
bool HasCursor(int aCursorId)
CURSOR * GetCursor(int aCursorId)
Custom widget to handle quick component values modification and simulation on the fly.
wxString GetSymbolRef() const
A wrapper for reporting to a wxString object.
A KICAD version of wxTextEntryDialog which supports the various improvements/work-arounds from DIALOG...
wxString GetValue() const
const wxString & GetName() const
Get layer name.
const wxPen & GetPen() const
Get pen set for this layer.
Canvas for plotting mpLayer implementations.
int GetMarginLeft() const
void SetMargins(int top, int right, int bottom, int left)
Set window margins, creating a blank area where some kinds of layers cannot draw.
void UpdateAll()
Refresh display.
int GetMarginRight() const
int GetMarginBottom() const
void Fit() override
Set view to fit global bounding box of all plot layers and refresh display.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
This file is part of the common library.
static std::string ToStdString(const wxString &aStr)
Abstract pattern-matching tool and implementations.
@ GRIDTRICKS_FIRST_CLIENT_ID
int m_SimulatorMultiRunCombinationLimit
Maximum number of tuner combinations simulated when using multi-run mode.
static const std::string WorkbookFileExtension
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
bool ParseSParamPorts(const wxString &aVectorName, long *aResponsePort, long *aDrivePort)
SIM_TYPE
< Possible simulation types
void sortSignals(std::vector< wxString > &signals)
wxString vectorNameFromSignalId(int aUserDefinedSignalId)
MEASUREMENTS_GIRD_COLUMNS
@ MYID_DELETE_MEASUREMENT
SIM_TRACE_TYPE operator|(SIM_TRACE_TYPE aFirst, SIM_TRACE_TYPE aSecond)
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
int measurements_panel_height
SIM_PREFERENCES preferences
std::map< const TUNER_SLIDER *, double > overrides
std::vector< std::vector< double > > xRuns
std::vector< std::vector< double > > yValues
std::vector< double > xValues
Contains preferences pertaining to the simulator.
Cursor recorded when entering Smith mode, so leaving restores it to its original trace.
IbisParser parser & reporter
wxString result
Test unit parsing edge cases and error handling.
Definition of file extensions used in Kicad.