ADDED power dissipation plotting and cursors.

Also fixes a bug so that voltages, currents and power dissipations are
only probed if the flag is set -- this keeps ngspice from throwing an
error if you probe something twice (for instance, if you have .probe
commands in text and turn off the auto-probing).
This commit is contained in:
Jeff Young 2023-02-10 20:52:52 +00:00
parent bc108023b3
commit e5176ff4d6
17 changed files with 379 additions and 110 deletions

View File

@ -1436,6 +1436,9 @@ void mpScaleY::Plot( wxDC& dc, mpWindow& w )
orgx = w.GetScrX() - w.GetMarginRight();
}
if( m_flags == mpALIGN_FAR_RIGHT )
orgx = w.GetScrX() - ( w.GetMarginRight() / 2 );
if( m_flags == mpALIGN_BORDER_RIGHT )
orgx = w.GetScrX() - 1; // dc.LogicalToDeviceX(0) - 1;
@ -1514,7 +1517,7 @@ void mpScaleY::Plot( wxDC& dc, mpWindow& w )
s = getLabel( n );
dc.GetTextExtent( s, &tx, &ty );
if( (m_flags == mpALIGN_BORDER_LEFT) || (m_flags == mpALIGN_RIGHT) )
if( m_flags == mpALIGN_BORDER_LEFT || m_flags == mpALIGN_RIGHT || m_flags == mpALIGN_FAR_RIGHT )
dc.DrawText( s, orgx + 4, p - ty / 2 );
else
dc.DrawText( s, orgx - 4 - tx, p - ty / 2 ); // ( s, orgx+4, p-ty/2);
@ -1537,7 +1540,7 @@ void mpScaleY::Plot( wxDC& dc, mpWindow& w )
// if ((!m_drawOutsideMargins) && (w.GetMarginLeft() > (ty + labelW + 8))) {
// dc.DrawRotatedText( m_name, orgx - 6 - labelW - ty, (maxYpx + minYpx) / 2 + tx / 2, 90);
// } else {
dc.DrawText( m_name, orgx + 4, minYpx - ty - 4 );
dc.DrawText( m_name, orgx - ( tx / 2 ), minYpx - ty - 4 );
// }
}
break;
@ -1547,13 +1550,14 @@ void mpScaleY::Plot( wxDC& dc, mpWindow& w )
break;
case mpALIGN_RIGHT:
case mpALIGN_FAR_RIGHT:
{
// dc.DrawRotatedText( m_name, orgx + 6, (maxYpx + minYpx) / 2 + tx / 2, 90);
/*if ((!m_drawOutsideMargins) && (w.GetMarginRight() > (ty + labelW + 8))) {
* dc.DrawRotatedText( m_name, orgx + 6 + labelW, (maxYpx - minYpx + tx)>>1, 90);
* } else {*/
dc.DrawText( m_name, orgx - tx - 4, minYpx - ty - 4 );
dc.DrawText( m_name, orgx - ( tx / 2 ), minYpx - ty - 4 );
// }
}
break;

View File

@ -98,6 +98,7 @@ public:
wxCheckBox* m_CurSheetAsRoot;
wxCheckBox* m_SaveAllVoltages;
wxCheckBox* m_SaveAllCurrents;
wxCheckBox* m_SaveAllDissipations;
wxCheckBox* m_RunExternalSpiceCommand;
wxTextCtrl* m_CommandStringCtrl;
wxTextCtrl* m_TitleStringCtrl;
@ -195,6 +196,7 @@ enum id_netlist {
ID_CUR_SHEET_AS_ROOT,
ID_SAVE_ALL_VOLTAGES,
ID_SAVE_ALL_CURRENTS,
ID_SAVE_ALL_DISSIPATIONS,
ID_RUN_SIMULATOR
};
@ -210,6 +212,7 @@ EXPORT_NETLIST_PAGE::EXPORT_NETLIST_PAGE( wxNotebook* aParent, const wxString& a
m_TitleStringCtrl = nullptr;
m_SaveAllVoltages = nullptr;
m_SaveAllCurrents = nullptr;
m_SaveAllDissipations = nullptr;
m_custom = aCustom;
aParent->AddPage( this, aTitle, false );
@ -298,6 +301,12 @@ void DIALOG_EXPORT_NETLIST::InstallPageSpice()
page->m_SaveAllCurrents->SetValue( settings.m_SpiceSaveAllCurrents );
page->m_LeftBoxSizer->Add( page->m_SaveAllCurrents, 0, wxBOTTOM | wxRIGHT, 5 );
page->m_SaveAllDissipations = new wxCheckBox( page, ID_SAVE_ALL_DISSIPATIONS,
_( "Save all power dissipations" ) );
page->m_SaveAllDissipations->SetToolTip( _( "Write directives to save power dissipation of all items (.probe p(<item>))" ) );
page->m_SaveAllDissipations->SetValue( settings.m_SpiceSaveAllDissipations );
page->m_LeftBoxSizer->Add( page->m_SaveAllDissipations, 0, wxBOTTOM | wxRIGHT, 5 );
page->m_RunExternalSpiceCommand = new wxCheckBox( page, ID_RUN_SIMULATOR,
_( "Run external simulator command:" ) );
@ -397,8 +406,9 @@ void DIALOG_EXPORT_NETLIST::OnNetlistTypeSelection( wxNotebookEvent& event )
void DIALOG_EXPORT_NETLIST::NetlistUpdateOpt()
{
bool saveAllVoltages = m_PanelNetType[ PANELSPICE ]->m_SaveAllVoltages->IsChecked();
bool saveAllCurrents = m_PanelNetType[ PANELSPICE ]->m_SaveAllCurrents->IsChecked();
bool saveAllVoltages = m_PanelNetType[ PANELSPICE ]->m_SaveAllVoltages->IsChecked();
bool saveAllCurrents = m_PanelNetType[ PANELSPICE ]->m_SaveAllCurrents->IsChecked();
bool saveAllDissipations = m_PanelNetType[ PANELSPICE ]->m_SaveAllDissipations->IsChecked();
wxString spiceCmdString = m_PanelNetType[ PANELSPICE ]->m_CommandStringCtrl->GetValue();
bool curSheetAsRoot = m_PanelNetType[ PANELSPICE ]->m_CurSheetAsRoot->GetValue();
bool spiceModelCurSheetAsRoot = m_PanelNetType[ PANELSPICEMODEL ]->m_CurSheetAsRoot->GetValue();
@ -407,6 +417,7 @@ void DIALOG_EXPORT_NETLIST::NetlistUpdateOpt()
settings.m_SpiceSaveAllVoltages = saveAllVoltages;
settings.m_SpiceSaveAllCurrents = saveAllCurrents;
settings.m_SpiceSaveAllDissipations = saveAllDissipations;
settings.m_SpiceCommandString = spiceCmdString;
settings.m_SpiceCurSheetAsRoot = curSheetAsRoot;
settings.m_SpiceModelCurSheetAsRoot = spiceModelCurSheetAsRoot;
@ -446,6 +457,9 @@ bool DIALOG_EXPORT_NETLIST::TransferDataFromWindow()
if( currPage->m_SaveAllCurrents->GetValue() )
netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
if( currPage->m_SaveAllDissipations->GetValue() )
netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
if( currPage->m_CurSheetAsRoot->GetValue() )
netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_CUR_SHEET_AS_ROOT;

View File

@ -79,6 +79,7 @@ public:
m_fixIncludePaths->SetValue( aOptions & NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS );
m_saveAllVoltages->SetValue( aOptions & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES );
m_saveAllCurrents->SetValue( aOptions & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS );
m_saveAllDissipations->SetValue( aOptions & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS );
}
bool TransferDataFromWindow() override;

View File

@ -438,6 +438,9 @@ DIALOG_SIM_COMMAND_BASE::DIALOG_SIM_COMMAND_BASE( wxWindow* parent, wxWindowID i
m_saveAllCurrents = new wxCheckBox( this, wxID_ANY, _("Save all currents"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer88->Add( m_saveAllCurrents, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_saveAllDissipations = new wxCheckBox( this, wxID_ANY, _("Save all power dissipations"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer88->Add( m_saveAllDissipations, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_compatibilityMode = new wxBoxSizer( wxHORIZONTAL );
wxStaticText* compatibilityLabel;
@ -452,7 +455,7 @@ DIALOG_SIM_COMMAND_BASE::DIALOG_SIM_COMMAND_BASE( wxWindow* parent, wxWindowID i
m_compatibilityMode->Add( m_compatibilityModeChoice, 0, wxALIGN_CENTER_VERTICAL, 5 );
bSizer88->Add( m_compatibilityMode, 1, wxEXPAND|wxBOTTOM, 5 );
bSizer88->Add( m_compatibilityMode, 1, wxEXPAND|wxTOP|wxBOTTOM, 5 );
bSizer1->Add( bSizer88, 0, wxEXPAND|wxTOP|wxLEFT, 10 );

View File

@ -5053,7 +5053,71 @@
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxBOTTOM</property>
<property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Save all power dissipations</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_saveAllDissipations</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxTOP|wxBOTTOM</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>

View File

@ -119,6 +119,7 @@ class DIALOG_SIM_COMMAND_BASE : public DIALOG_SHIM
wxCheckBox* m_fixIncludePaths;
wxCheckBox* m_saveAllVoltages;
wxCheckBox* m_saveAllCurrents;
wxCheckBox* m_saveAllDissipations;
wxBoxSizer* m_compatibilityMode;
wxChoice* m_compatibilityModeChoice;
wxStdDialogButtonSizer* m_sdbSizer;

View File

@ -609,6 +609,18 @@ void NETLIST_EXPORTER_SPICE::WriteDirectives( OUTPUTFORMATTER& aFormatter,
if( aNetlistOptions & OPTION_SAVE_ALL_CURRENTS )
aFormatter.Print( 0, ".probe alli\n" );
if( aNetlistOptions & OPTION_SAVE_ALL_DISSIPATIONS )
{
for( const SPICE_ITEM& item : m_items )
{
if( item.model->GetPinCount() >= 2 )
{
aFormatter.Print( 0, ".probe p(%s)\n",
item.model->SpiceGenerator().ItemName( item ).c_str() );
}
}
}
for( const wxString& directive : m_directives )
{
bool simCommand = false;

View File

@ -51,16 +51,18 @@ class NETLIST_EXPORTER_SPICE : public NETLIST_EXPORTER_BASE
public:
enum OPTIONS
{
OPTION_ADJUST_INCLUDE_PATHS = 0x0010,
OPTION_ADJUST_PASSIVE_VALS = 0x0020,
OPTION_SAVE_ALL_VOLTAGES = 0x0040,
OPTION_SAVE_ALL_CURRENTS = 0x0080,
OPTION_CUR_SHEET_AS_ROOT = 0x0100,
OPTION_SIM_COMMAND = 0x0200,
OPTION_ADJUST_INCLUDE_PATHS = 0x0010,
OPTION_ADJUST_PASSIVE_VALS = 0x0020,
OPTION_SAVE_ALL_VOLTAGES = 0x0040,
OPTION_SAVE_ALL_CURRENTS = 0x0080,
OPTION_SAVE_ALL_DISSIPATIONS = 0x0100,
OPTION_CUR_SHEET_AS_ROOT = 0x0200,
OPTION_SIM_COMMAND = 0x0400,
OPTION_DEFAULT_FLAGS = OPTION_ADJUST_INCLUDE_PATHS
| OPTION_ADJUST_PASSIVE_VALS
| OPTION_SAVE_ALL_VOLTAGES
| OPTION_SAVE_ALL_CURRENTS
| OPTION_SAVE_ALL_DISSIPATIONS
};
NETLIST_EXPORTER_SPICE( SCHEMATIC_IFACE* aSchematic, wxWindow* aDialogParent = nullptr );

View File

@ -59,6 +59,7 @@ SCHEMATIC_SETTINGS::SCHEMATIC_SETTINGS( JSON_SETTINGS* aParent, const std::strin
m_SpiceCurSheetAsRoot( false ),
m_SpiceSaveAllVoltages( false ),
m_SpiceSaveAllCurrents( false ),
m_SpiceSaveAllDissipations( false ),
m_SpiceModelCurSheetAsRoot( true ),
m_NgspiceSimulatorSettings( nullptr )
{
@ -204,6 +205,9 @@ SCHEMATIC_SETTINGS::SCHEMATIC_SETTINGS( JSON_SETTINGS* aParent, const std::strin
m_params.emplace_back( new PARAM<bool>( "spice_save_all_currents",
&m_SpiceSaveAllCurrents, false ) );
m_params.emplace_back( new PARAM<bool>( "spice_save_all_dissipations",
&m_SpiceSaveAllDissipations, false ) );
m_params.emplace_back( new PARAM<bool>( "spice_model_current_sheet_as_root",
&m_SpiceModelCurSheetAsRoot, true ) );

View File

@ -77,6 +77,7 @@ public:
bool m_SpiceCurSheetAsRoot;
bool m_SpiceSaveAllVoltages;
bool m_SpiceSaveAllCurrents;
bool m_SpiceSaveAllDissipations;
wxString m_SpiceCommandString; // A command string to run external spice
bool m_SpiceModelCurSheetAsRoot;

View File

@ -369,7 +369,7 @@ void SIM_PLOT_FRAME::ShowChangedLanguage()
m_cursorsGrid->SetColLabelValue( COL_CURSOR_NAME, _( "Cursor" ) );
m_cursorsGrid->SetColLabelValue( COL_CURSOR_SIGNAL, _( "Signal" ) );
m_cursorsGrid->SetColLabelValue( COL_CURSOR_X, _( "Time" ) );
m_cursorsGrid->SetColLabelValue( COL_CURSOR_Y, _( "Voltage / Current" ) );
m_cursorsGrid->SetColLabelValue( COL_CURSOR_Y, _( "Value" ) );
wxCommandEvent dummy;
onCursorUpdate( dummy );
@ -645,16 +645,19 @@ void SIM_PLOT_FRAME::StartSimulation( const wxString& aSimCommand )
unconnected.Replace( '(', '_' ); // Convert to SPICE markup
m_signals.clear();
auto simType = m_circuitModel->GetSimType();
int options = m_circuitModel->GetSimOptions();
SIM_TYPE simType = m_circuitModel->GetSimType();
// Add voltages
for( const std::string& net : m_circuitModel->GetNets() )
if( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES )
{
// netnames are escaped (can contain "{slash}" for '/') Unscape them:
wxString netname = UnescapeString( net );
if( netname != "GND" && netname != "0" && !netname.StartsWith( unconnected ) )
for( const std::string& net : m_circuitModel->GetNets() )
{
// netnames are escaped (can contain "{slash}" for '/') Unscape them:
wxString netname = UnescapeString( net );
if( netname == "GND" || netname == "0" || netname.StartsWith( unconnected ) )
continue;
if( simType == ST_AC )
{
m_signals.push_back( wxString::Format( _( "V(%s) (gain)" ), netname ) );
@ -667,17 +670,39 @@ void SIM_PLOT_FRAME::StartSimulation( const wxString& aSimCommand )
}
}
// Add currents
if( simType == ST_TRANSIENT || simType == ST_DC )
if( ( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS )
&& ( simType == ST_TRANSIENT || simType == ST_DC ) )
{
for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
{
// Add all possible currents for the primitive.
// Add all possible currents for the device.
for( const std::string& name : item.model->SpiceGenerator().CurrentNames( item ) )
m_signals.push_back( name );
}
}
if( ( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS )
&& ( simType == ST_TRANSIENT || simType == ST_DC ) )
{
for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
{
if( item.model->GetPinCount() >= 2 )
{
wxString name = item.model->SpiceGenerator().ItemName( item );
m_signals.push_back( wxString::Format( wxS( "P(%s)" ), name ) );
}
}
}
// Add .PROBE directives
for( const wxString& directive : m_circuitModel->GetDirectives() )
{
wxString directiveParams;
if( directive.Upper().StartsWith( wxS( ".PROBE" ), &directiveParams ) )
m_signals.push_back( directiveParams.Trim( false ) );
}
rebuildSignalsGrid( m_filter->GetValue() );
applyTuners();
@ -767,6 +792,8 @@ void SIM_PLOT_FRAME::onSignalsGridCellChanged( wxGridEvent& aEvent )
traceType = SPT_VOLTAGE;
else if( firstChar == 'I' || firstChar == 'i' )
traceType = SPT_CURRENT;
else if( firstChar == 'P' || firstChar == 'p' )
traceType = SPT_POWER;
if( signalName.EndsWith( gainSuffix ) )
{
@ -1059,12 +1086,16 @@ bool SIM_PLOT_FRAME::updateTrace( const wxString& aName, SIM_TRACE_TYPE aType,
SIM_TYPE simType = m_circuitModel->GetSimType();
wxString traceTitle = aName;
wxString vectorName = aName;
if( aType & SPT_AC_MAG )
traceTitle += _( " (gain)" );
else if( aType & SPT_AC_PHASE )
traceTitle += _( " (phase)" );
if( aType & SPT_POWER )
vectorName = vectorName.AfterFirst( '(' ).BeforeLast( ')' ) + wxS( ":power" );
if( !SIM_PANEL_BASE::IsPlottable( simType ) )
{
// There is no plot to be shown
@ -1092,9 +1123,9 @@ bool SIM_PLOT_FRAME::updateTrace( const wxString& aName, SIM_TRACE_TYPE aType,
wxT( "Cannot set both AC_PHASE and AC_MAG bits" ) );
if( aType & SPT_AC_MAG )
data_y = m_simulator->GetMagPlot( (const char*) aName.c_str() );
data_y = m_simulator->GetMagPlot( (const char*) vectorName.c_str() );
else if( aType & SPT_AC_PHASE )
data_y = m_simulator->GetPhasePlot( (const char*) aName.c_str() );
data_y = m_simulator->GetPhasePlot( (const char*) vectorName.c_str() );
else
wxFAIL_MSG( wxT( "Plot type missing AC_PHASE or AC_MAG bit" ) );
@ -1103,7 +1134,7 @@ bool SIM_PLOT_FRAME::updateTrace( const wxString& aName, SIM_TRACE_TYPE aType,
case ST_NOISE:
case ST_DC:
case ST_TRANSIENT:
data_y = m_simulator->GetMagPlot( (const char*) aName.c_str() );
data_y = m_simulator->GetMagPlot( (const char*) vectorName.c_str() );
break;
default:
@ -1332,6 +1363,10 @@ bool SIM_PLOT_FRAME::LoadWorkbook( const wxString& aPath )
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES;
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
}
else if( version >= 3 )
{
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
}
while( tokenizer.HasMoreTokens() )
{
@ -1343,6 +1378,8 @@ bool SIM_PLOT_FRAME::LoadWorkbook( const wxString& aPath )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES;
else if( line.StartsWith( wxT( ".probe alli" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
else if( line.StartsWith( wxT( ".probe allp" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
else
simCommand += line + wxT( "\n" );
}
@ -1378,6 +1415,33 @@ bool SIM_PLOT_FRAME::LoadWorkbook( const wxString& aPath )
return false;
}
if( version <= 2 )
{
long legacyTraceType = traceType;
traceType = 0;
if( legacyTraceType & LEGACY_SPT_VOLTAGE )
traceType |= SPT_VOLTAGE;
if( legacyTraceType & LEGACY_SPT_CURRENT )
traceType |= SPT_CURRENT;
if( legacyTraceType & LEGACY_SPT_AC_PHASE )
traceType |= SPT_AC_PHASE;
if( legacyTraceType & LEGACY_SPT_AC_MAG )
traceType |= SPT_AC_MAG;
if( legacyTraceType & LEGACY_SPT_TIME )
traceType |= SPT_TIME;
if( legacyTraceType & LEGACY_SPT_LIN_FREQUENCY )
traceType |= SPT_LIN_FREQUENCY;
if( legacyTraceType & LEGACY_SPT_SWEEP )
traceType |= SPT_SWEEP;
}
name = file.GetNextLine();
if( name.IsEmpty() )
@ -1437,7 +1501,7 @@ bool SIM_PLOT_FRAME::SaveWorkbook( const wxString& aPath )
file.Create();
}
file.AddLine( wxT( "version 2" ) );
file.AddLine( wxT( "version 3" ) );
file.AddLine( wxString::Format( wxT( "%llu" ), m_workbook->GetPageCount() ) );
@ -1465,6 +1529,9 @@ bool SIM_PLOT_FRAME::SaveWorkbook( const wxString& aPath )
if( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS )
command += wxT( "\n.probe alli" );
if( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS )
command += wxT( "\n.probe allp" );
file.AddLine( EscapeString( command, CTX_LINE ) );
const SIM_PLOT_PANEL* plotPanel = dynamic_cast<const SIM_PLOT_PANEL*>( basePanel );
@ -1700,33 +1767,36 @@ void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
if( !plotPanel )
return;
// Set up the labels
m_cursorsGrid->SetColLabelValue( COL_CURSOR_X, plotPanel->GetLabelX() );
wxString labelY1 = plotPanel->GetLabelY1();
wxString labelY2 = plotPanel->GetLabelY2();
if( labelY2.IsEmpty() )
m_cursorsGrid->SetColLabelValue( COL_CURSOR_Y, labelY1 );
else
m_cursorsGrid->SetColLabelValue( COL_CURSOR_Y, labelY1 + wxT( " / " ) + labelY2 );
// Update cursor values
wxString unitsX = plotPanel->GetUnitsX();
CURSOR* cursor1 = nullptr;
wxString unitsY1;
wxString cursor1Name;
wxString cursor1Units;
CURSOR* cursor2 = nullptr;
wxString unitsY2;
wxString cursor2Name;
wxString cursor2Units;
auto getUnitsY =
[&]( TRACE* aTrace ) -> wxString
{
if( ( aTrace->GetType() & SPT_AC_PHASE ) || ( aTrace->GetType() & SPT_CURRENT ) )
return plotPanel->GetUnitsY2();
else if( aTrace->GetType() & SPT_POWER )
return plotPanel->GetUnitsY3();
else
return plotPanel->GetUnitsY1();
};
auto getNameY =
[&]( TRACE* aTrace ) -> wxString
{
if( ( aTrace->GetType() & SPT_AC_PHASE ) || ( aTrace->GetType() & SPT_CURRENT ) )
return plotPanel->GetLabelY2();
else if( aTrace->GetType() & SPT_POWER )
return plotPanel->GetLabelY3();
else
return plotPanel->GetLabelY1();
};
auto updateRangeUnits =
[]( wxString* aRange, const wxString& aUnits )
{
@ -1750,13 +1820,14 @@ void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
if( CURSOR* cursor = trace->GetCursor( 1 ) )
{
cursor1 = cursor;
unitsY1 = getUnitsY( trace );
cursor1Name = getNameY( trace );
cursor1Units = getUnitsY( trace );
wxRealPoint coords = cursor->GetCoords();
int row = m_cursorsGrid->GetNumberRows();
updateRangeUnits( &m_cursorRange[0][0], unitsX );
updateRangeUnits( &m_cursorRange[0][1], unitsY1 );
updateRangeUnits( &m_cursorRange[0][0], plotPanel->GetUnitsX() );
updateRangeUnits( &m_cursorRange[0][1], cursor1Units );
m_cursorsGrid->AppendRows( 1 );
m_cursorsGrid->SetCellValue( row, COL_CURSOR_NAME, wxS( "1" ) );
@ -1772,13 +1843,14 @@ void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
if( CURSOR* cursor = trace->GetCursor( 2 ) )
{
cursor2 = cursor;
unitsY2 = getUnitsY( trace );
cursor2Name = getNameY( trace );
cursor2Units = getUnitsY( trace );
wxRealPoint coords = cursor->GetCoords();
int row = m_cursorsGrid->GetNumberRows();
updateRangeUnits( &m_cursorRange[1][0], unitsX );
updateRangeUnits( &m_cursorRange[1][1], unitsY2 );
updateRangeUnits( &m_cursorRange[1][0], plotPanel->GetUnitsX() );
updateRangeUnits( &m_cursorRange[1][1], cursor2Units );
m_cursorsGrid->AppendRows( 1 );
m_cursorsGrid->SetCellValue( row, COL_CURSOR_NAME, wxS( "2" ) );
@ -1789,13 +1861,13 @@ void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
}
}
if( cursor1 && cursor2 && unitsY1 == unitsY2 )
if( cursor1 && cursor2 && cursor1Units == cursor2Units )
{
wxRealPoint coords = cursor2->GetCoords() - cursor1->GetCoords();
wxString signal;
updateRangeUnits( &m_cursorRange[2][0], unitsX );
updateRangeUnits( &m_cursorRange[2][1], unitsY1 );
updateRangeUnits( &m_cursorRange[2][0], plotPanel->GetUnitsX() );
updateRangeUnits( &m_cursorRange[2][1], cursor1Units );
if( cursor1->GetName() == cursor2->GetName() )
signal = wxString::Format( wxS( "%s[2 - 1]" ), cursor2->GetName() );
@ -1808,6 +1880,21 @@ void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
m_cursorsGrid->SetCellValue( 2, COL_CURSOR_X, formatValue( coords.x, 2, 0 ) );
m_cursorsGrid->SetCellValue( 2, COL_CURSOR_Y, formatValue( coords.y, 2, 1 ) );
}
// Set up the labels
m_cursorsGrid->SetColLabelValue( COL_CURSOR_X, plotPanel->GetLabelX() );
wxString valColName = _( "Value" );
if( !cursor1Name.IsEmpty() && cursor2Name.IsEmpty() )
valColName = cursor1Name;
else if( !cursor2Name.IsEmpty() && cursor1Name.IsEmpty() )
valColName = cursor2Name;
else if( cursor1Name == cursor2Name )
valColName = cursor1Name;
m_cursorsGrid->SetColLabelValue( COL_CURSOR_Y, valColName );
}

View File

@ -114,8 +114,8 @@ SIM_PLOT_FRAME_BASE::SIM_PLOT_FRAME_BASE( wxWindow* parent, wxWindowID id, const
m_signalsGrid->SetMargins( 0, 0 );
// Columns
m_signalsGrid->SetColSize( 0, 204 );
m_signalsGrid->SetColSize( 1, 45 );
m_signalsGrid->SetColSize( 0, 212 );
m_signalsGrid->SetColSize( 1, 40 );
m_signalsGrid->SetColSize( 2, 45 );
m_signalsGrid->SetColSize( 3, 55 );
m_signalsGrid->SetColSize( 4, 55 );
@ -171,16 +171,16 @@ SIM_PLOT_FRAME_BASE::SIM_PLOT_FRAME_BASE( wxWindow* parent, wxWindowID id, const
m_cursorsGrid->SetMargins( 0, 0 );
// Columns
m_cursorsGrid->SetColSize( 0, 44 );
m_cursorsGrid->SetColSize( 1, 152 );
m_cursorsGrid->SetColSize( 2, 103 );
m_cursorsGrid->SetColSize( 3, 106 );
m_cursorsGrid->SetColSize( 0, 45 );
m_cursorsGrid->SetColSize( 1, 162 );
m_cursorsGrid->SetColSize( 2, 100 );
m_cursorsGrid->SetColSize( 3, 100 );
m_cursorsGrid->EnableDragColMove( false );
m_cursorsGrid->EnableDragColSize( true );
m_cursorsGrid->SetColLabelValue( 0, _("Cursor") );
m_cursorsGrid->SetColLabelValue( 1, _("Signal") );
m_cursorsGrid->SetColLabelValue( 2, _("Time") );
m_cursorsGrid->SetColLabelValue( 3, _("Voltage / Current") );
m_cursorsGrid->SetColLabelValue( 3, _("Value") );
m_cursorsGrid->SetColLabelSize( -1 );
m_cursorsGrid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );

View File

@ -843,7 +843,7 @@
<property name="col_label_values">&quot;Signal&quot; &quot;Plot&quot; &quot;Color&quot; &quot;Cursor 1&quot; &quot;Cursor 2&quot;</property>
<property name="col_label_vert_alignment">wxALIGN_CENTER</property>
<property name="cols">5</property>
<property name="column_sizes">204,45,45,55,55</property>
<property name="column_sizes">212,40,45,55,55</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
@ -1107,10 +1107,10 @@
<property name="close_button">1</property>
<property name="col_label_horiz_alignment">wxALIGN_CENTER</property>
<property name="col_label_size">-1</property>
<property name="col_label_values">&quot;Cursor&quot; &quot;Signal&quot; &quot;Time&quot; &quot;Voltage / Current&quot;</property>
<property name="col_label_values">&quot;Cursor&quot; &quot;Signal&quot; &quot;Time&quot; &quot;Value&quot;</property>
<property name="col_label_vert_alignment">wxALIGN_CENTER</property>
<property name="cols">4</property>
<property name="column_sizes">44,152,103,106</property>
<property name="column_sizes">45,162,100,100</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>

View File

@ -130,7 +130,7 @@ class LIN_SCALE : public parent
{
public:
LIN_SCALE( const wxString& name, const wxString& unit, int flags ) :
parent( name, flags ),
parent( name, flags, false ),
m_unit( unit )
{};
@ -174,7 +174,7 @@ class LOG_SCALE : public parent
{
public:
LOG_SCALE( const wxString& name, const wxString& unit, int flags ) :
parent( name, flags ),
parent( name, flags, false ),
m_unit( unit )
{};
@ -377,12 +377,13 @@ void CURSOR::UpdateReference()
SIM_PLOT_PANEL::SIM_PLOT_PANEL( const wxString& aCommand, int aOptions, wxWindow* parent,
wxWindowID id, const wxPoint& pos, const wxSize& size, long style,
const wxString& name )
: SIM_PANEL_BASE( aCommand, aOptions, parent, id, pos, size, style, name ),
m_axis_x( nullptr ),
m_axis_y1( nullptr ),
m_axis_y2( nullptr ),
m_dotted_cp( false )
const wxString& name ) :
SIM_PANEL_BASE( aCommand, aOptions, parent, id, pos, size, style, name ),
m_axis_x( nullptr ),
m_axis_y1( nullptr ),
m_axis_y2( nullptr ),
m_axis_y3( nullptr ),
m_dotted_cp( false )
{
m_sizer = new wxBoxSizer( wxVERTICAL );
m_plotWin = new mpWindow( this, wxID_ANY, pos, size, style );
@ -449,22 +450,36 @@ wxString SIM_PLOT_PANEL::GetUnitsY2() const
}
void SIM_PLOT_PANEL::updateAxes()
wxString SIM_PLOT_PANEL::GetUnitsY3() const
{
bool skipAddToView = false;
LIN_SCALE<mpScaleY>* linScale = dynamic_cast<LIN_SCALE<mpScaleY>*>( m_axis_y3 );
if( m_axis_x )
skipAddToView = true;
if( linScale )
return linScale->GetUnits();
else
return wxEmptyString;
}
void SIM_PLOT_PANEL::updateAxes( SIM_TRACE_TYPE aNewTraceType )
{
switch( GetType() )
{
case ST_AC:
if( !m_axis_x )
{
m_axis_x = new LOG_SCALE<mpScaleXLog>( wxEmptyString, wxT( "Hz" ), mpALIGN_BOTTOM );
m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
m_plotWin->AddLayer( m_axis_x );
m_axis_y1 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "dBV" ), mpALIGN_LEFT );
m_axis_y1->SetNameAlign( mpALIGN_LEFT );
m_plotWin->AddLayer( m_axis_y1 );
m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "°" ), mpALIGN_RIGHT );
m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
m_axis_y2->SetMasterScale( m_axis_y1 );
m_plotWin->AddLayer( m_axis_y2 );
}
m_axis_x->SetName( _( "Frequency" ) );
@ -480,7 +495,12 @@ void SIM_PLOT_PANEL::updateAxes()
if( !m_axis_x )
{
m_axis_x = new LOG_SCALE<mpScaleXLog>( wxEmptyString, wxT( "Hz" ), mpALIGN_BOTTOM );
m_axis_y1 = new mpScaleY( wxEmptyString, mpALIGN_LEFT );
m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
m_plotWin->AddLayer( m_axis_x );
m_axis_y1 = new mpScaleY( wxEmptyString, mpALIGN_LEFT, false );
m_axis_y1->SetNameAlign( mpALIGN_LEFT );
m_plotWin->AddLayer( m_axis_y1 );
}
m_axis_x->SetName( _( "Frequency" ) );
@ -491,45 +511,42 @@ void SIM_PLOT_PANEL::updateAxes()
if( !m_axis_x )
{
m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "s" ), mpALIGN_BOTTOM );
m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
m_plotWin->AddLayer( m_axis_x );
m_axis_y1 = new LIN_SCALE<mpScaleY>(wxEmptyString, wxT( "V" ), mpALIGN_LEFT );
m_axis_y1->SetNameAlign( mpALIGN_LEFT );
m_plotWin->AddLayer( m_axis_y1 );
m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "A" ), mpALIGN_RIGHT );
m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
m_axis_y2->SetMasterScale( m_axis_y1 );
m_plotWin->AddLayer( m_axis_y2 );
}
m_axis_x->SetName( _( "Time" ) );
m_axis_y1->SetName( _( "Voltage" ) );
m_axis_y2->SetName( _( "Current" ) );
if( aNewTraceType == SPT_POWER && !m_axis_y3 )
{
m_plotWin->SetMargins( 35, 140, 35, 70 );
m_axis_y3 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "W" ), mpALIGN_FAR_RIGHT );
m_axis_y3->SetNameAlign( mpALIGN_FAR_RIGHT );
m_axis_y3->SetMasterScale( m_axis_y1 );
m_plotWin->AddLayer( m_axis_y3 );
}
if( m_axis_y3 )
m_axis_y3->SetName( _( "Power" ) );
break;
default:
// suppress warnings
break;
}
if( skipAddToView )
return;
if( m_axis_x )
{
m_axis_x->SetTicks( false );
m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
m_plotWin->AddLayer( m_axis_x );
}
if( m_axis_y1 )
{
m_axis_y1->SetTicks( false );
m_axis_y1->SetNameAlign( mpALIGN_LEFT );
m_plotWin->AddLayer( m_axis_y1 );
}
if( m_axis_y2 )
{
m_axis_y2->SetTicks( false );
m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
m_plotWin->AddLayer( m_axis_y2 );
}
}
void SIM_PLOT_PANEL::prepareDCAxes()
@ -558,38 +575,62 @@ void SIM_PLOT_PANEL::prepareDCAxes()
default:
case 'v':
if( !m_axis_x )
{
m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "V" ), mpALIGN_BOTTOM );
m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
m_plotWin->AddLayer( m_axis_x );
}
m_axis_x->SetName( _( "Voltage (swept)" ) );
break;
case 'i':
if( !m_axis_x )
{
m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "A" ), mpALIGN_BOTTOM );
m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
m_plotWin->AddLayer( m_axis_x );
}
m_axis_x->SetName( _( "Current (swept)" ) );
break;
case 'r':
if( !m_axis_x )
{
m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "" ), mpALIGN_BOTTOM );
m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
m_plotWin->AddLayer( m_axis_x );
}
m_axis_x->SetName( _( "Resistance (swept)" ) );
break;
case 't':
if( !m_axis_x )
{
m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "°C" ), mpALIGN_BOTTOM );
m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
m_plotWin->AddLayer( m_axis_x );
}
m_axis_x->SetName( _( "Temperature (swept)" ) );
break;
}
if( !m_axis_y1 )
{
m_axis_y1 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "V" ), mpALIGN_LEFT );
m_axis_y1->SetNameAlign( mpALIGN_LEFT );
m_plotWin->AddLayer( m_axis_y1 );
}
if( !m_axis_y2 )
{
m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "A" ), mpALIGN_RIGHT );
m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
m_plotWin->AddLayer( m_axis_y2 );
}
m_axis_y1->SetName( _( "Voltage (measured)" ) );
m_axis_y2->SetName( _( "Current" ) );
@ -640,7 +681,7 @@ bool SIM_PLOT_PANEL::addTrace( const wxString& aTitle, const wxString& aName, in
{
TRACE* trace = nullptr;
updateAxes();
updateAxes( aType );
// Find previous entry, if there is one
auto prev = m_traces.find( aTitle );
@ -652,9 +693,9 @@ bool SIM_PLOT_PANEL::addTrace( const wxString& aTitle, const wxString& aName, in
{
bool hasVoltageTraces = false;
for( const auto& tr : m_traces )
for( const auto& [ name, candidate ] : m_traces )
{
if( !( tr.second->GetType() & SPT_CURRENT ) )
if( candidate->GetType() & SPT_VOLTAGE )
{
hasVoltageTraces = true;
break;
@ -662,9 +703,13 @@ bool SIM_PLOT_PANEL::addTrace( const wxString& aTitle, const wxString& aName, in
}
if( !hasVoltageTraces )
m_axis_y2->SetMasterScale( nullptr );
else
m_axis_y2->SetMasterScale( m_axis_y1 );
{
if( m_axis_y2 )
m_axis_y2->SetMasterScale( nullptr );
if( m_axis_y3 )
m_axis_y3->SetMasterScale( nullptr );
}
}
// New entry
@ -704,6 +749,8 @@ bool SIM_PLOT_PANEL::addTrace( const wxString& aTitle, const wxString& aName, in
if( ( aType & SPT_AC_PHASE ) || ( aType & SPT_CURRENT ) )
trace->SetScale( m_axis_x, m_axis_y2 );
else if( aType & SPT_POWER )
trace->SetScale( m_axis_x, m_axis_y3 );
else
trace->SetScale( m_axis_x, m_axis_y1 );
@ -782,6 +829,9 @@ void SIM_PLOT_PANEL::ResetScales()
if( m_axis_y2 )
m_axis_y2->ResetDataRange();
if( m_axis_y3 )
m_axis_y3->ResetDataRange();
for( auto& [ name, trace ] : m_traces )
trace->UpdateScales();
}

View File

@ -219,9 +219,15 @@ public:
return m_axis_y2 ? m_axis_y2->GetName() : wxS( "" );
}
wxString GetLabelY3() const
{
return m_axis_y3 ? m_axis_y3->GetName() : wxS( "" );
}
wxString GetUnitsX() const;
wxString GetUnitsY1() const;
wxString GetUnitsY2() const;
wxString GetUnitsY3() const;
bool TraceShown( const wxString& aName ) const
{
@ -245,6 +251,7 @@ public:
m_axis_x->SetTicks( !aEnable );
m_axis_y1->SetTicks( !aEnable );
m_axis_y2->SetTicks( !aEnable );
m_axis_y3->SetTicks( !aEnable );
m_plotWin->UpdateAll();
}
@ -317,7 +324,7 @@ private:
void prepareDCAxes();
///> Create/Ensure axes are available for plotting
void updateAxes();
void updateAxes( SIM_TRACE_TYPE aNewTraceType = SIM_TRACE_TYPE::SPT_UNKNOWN );
private:
SIM_PLOT_COLORS m_colors;
@ -332,6 +339,7 @@ private:
mpScaleXBase* m_axis_x;
mpScaleY* m_axis_y1;
mpScaleY* m_axis_y2;
mpScaleY* m_axis_y3;
mpInfoLegend* m_legend;
bool m_dotted_cp;

View File

@ -43,6 +43,21 @@ enum SIM_TYPE
};
///< Possible plot types
enum LEGACY_SIM_TRACE_TYPE
{
// Y axis
LEGACY_SPT_VOLTAGE = 0x01,
LEGACY_SPT_CURRENT = 0x02,
LEGACY_SPT_AC_PHASE = 0x04,
LEGACY_SPT_AC_MAG = 0x08,
// X axis
LEGACY_SPT_TIME = 0x10,
LEGACY_SPT_LIN_FREQUENCY = 0x20,
LEGACY_SPT_SWEEP = 0x40
};
enum SIM_TRACE_TYPE
{
// Y axis
@ -50,12 +65,13 @@ enum SIM_TRACE_TYPE
SPT_CURRENT = 0x02,
SPT_AC_PHASE = 0x04,
SPT_AC_MAG = 0x08,
SPT_POWER = 0x10,
// X axis
SPT_TIME = 0x10,
SPT_LIN_FREQUENCY = 0x20,
SPT_LOG_FREQUENCY = 0x20,
SPT_SWEEP = 0x40,
SPT_TIME = 0x0100,
SPT_LIN_FREQUENCY = 0x0200,
SPT_LOG_FREQUENCY = 0x0400,
SPT_SWEEP = 0x0800,
SPT_UNKNOWN = 0x00
};

View File

@ -487,6 +487,8 @@ protected:
#define mpALIGN_BORDER_BOTTOM 0x04
/** Aligns X axis to top border. For mpScaleX */
#define mpALIGN_BORDER_TOP 0x05
/** Aligns label to the right of mpALIGN_RIGHT */
#define mpALIGN_FAR_RIGHT 0x06
/** Set label for X axis in normal mode */
#define mpX_NORMAL 0x00
/** Set label for X axis in time mode: the value is represented as minutes:seconds.milliseconds if time is less than 2 minutes, hours:minutes:seconds otherwise. */