Eeschema: adding .op SPICE simulation support

ADDED new tab on simulation settings dialog, which allows to run .op analysis.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/2368
This commit is contained in:
Sylwester Kocjan
2020-04-15 01:51:58 +00:00
committed by Ian McInerney
parent 6a8ace55c4
commit fdccdd5cb9
24 changed files with 5923 additions and 7415 deletions
+117 -74
View File
@@ -118,7 +118,10 @@ TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxSt
wxString SIM_PLOT_FRAME::m_savedWorkbooksPath;
SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
: SIM_PLOT_FRAME_BASE( aParent ), m_lastSimPlot( nullptr )
: SIM_PLOT_FRAME_BASE( aParent ),
m_lastSimPlot( nullptr ),
m_welcomePanel( nullptr ),
m_plotNumber( 0 )
{
SetKiway( this, aKiway );
m_signalsIconColorList = NULL;
@@ -204,7 +207,9 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSettings, this, m_settings->GetId() );
m_toolBar->Realize();
m_plotNotebook->SetPageText( 0, _( "Welcome!" ) );
m_welcomePanel = new SIM_PANEL_BASE( ST_UNKNOWN, m_plotNotebook, wxID_ANY );
m_plotNotebook->AddPage( m_welcomePanel, _( "Welcome!" ), 1, true );
// the settings dialog will be created later, on demand.
// if created in the ctor, for some obscure reason, there is an issue
@@ -419,10 +424,9 @@ void SIM_PLOT_FRAME::fillDefaultColorList( bool aWhiteBg )
}
void SIM_PLOT_FRAME::StartSimulation()
void SIM_PLOT_FRAME::StartSimulation( const wxString& aSimCommand )
{
STRING_FORMATTER formatter;
SIM_PLOT_PANEL* plotPanel = CurrentPlot();
if( !m_settingsDlg )
m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
@@ -430,8 +434,16 @@ void SIM_PLOT_FRAME::StartSimulation()
m_simConsole->Clear();
updateNetlistExporter();
if( plotPanel )
m_exporter->SetSimCommand( m_plots[plotPanel].m_simCommand );
if( aSimCommand.IsEmpty() )
{
SIM_PANEL_BASE* plotPanel = currentPlotWindow();
if( plotPanel )
m_exporter->SetSimCommand( m_plots[plotPanel].m_simCommand );
}
else
{
m_exporter->SetSimCommand( aSimCommand );
}
if( !m_exporter->Format( &formatter, m_settingsDlg->GetNetlistOptions() ) )
{
@@ -464,12 +476,26 @@ bool SIM_PLOT_FRAME::IsSimulationRunning()
}
SIM_PLOT_PANEL* SIM_PLOT_FRAME::NewPlotPanel( SIM_TYPE aSimType )
SIM_PANEL_BASE* SIM_PLOT_FRAME::NewPlotPanel( SIM_TYPE aSimType )
{
SIM_PLOT_PANEL* plotPanel = new SIM_PLOT_PANEL( aSimType, m_plotNotebook, this, wxID_ANY );
SIM_PANEL_BASE* plotPanel;
plotPanel->EnableMouseWheelPan(
m_schematicFrame->GetCanvas()->GetViewControls()->IsMousewheelPanEnabled() );
if( SIM_PANEL_BASE::IsPlottable( aSimType ) )
{
SIM_PLOT_PANEL* panel;
panel = new SIM_PLOT_PANEL( aSimType, m_plotNotebook, this, wxID_ANY );
panel->GetPlotWin()->EnableMouseWheelPan(
m_schematicFrame->GetCanvas()->GetViewControls()->IsMousewheelPanEnabled() );
plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel );
}
else
{
SIM_NOPLOT_PANEL* panel;
panel = new SIM_NOPLOT_PANEL( aSimType, m_plotNotebook, wxID_ANY );
plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel );
}
if( m_welcomePanel )
{
@@ -477,9 +503,10 @@ SIM_PLOT_PANEL* SIM_PLOT_FRAME::NewPlotPanel( SIM_TYPE aSimType )
m_welcomePanel = nullptr;
}
m_plotNotebook->AddPage( plotPanel, wxString::Format( _( "Plot%u" ),
(unsigned int) m_plotNotebook->GetPageCount() + 1 ), true );
wxString pageTitle( m_simulator->TypeToName( aSimType, true ) );
pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), (unsigned int) ++m_plotNumber ) );
m_plotNotebook->AddPage( dynamic_cast<wxWindow*>( plotPanel ), pageTitle, true );
m_plots[plotPanel] = PLOT_INFO();
return plotPanel;
@@ -500,9 +527,9 @@ void SIM_PLOT_FRAME::AddCurrentPlot( const wxString& aDeviceName, const wxString
void SIM_PLOT_FRAME::AddTuner( SCH_COMPONENT* aComponent )
{
SIM_PLOT_PANEL* plotPanel = CurrentPlot();
SIM_PANEL_BASE* plotPanel = currentPlotWindow();
if( !plotPanel )
if( !plotPanel || plotPanel == m_welcomePanel )
return;
// For now limit the tuner tool to RLC components
@@ -550,9 +577,11 @@ void SIM_PLOT_FRAME::RemoveTuner( TUNER_SLIDER* aTuner, bool aErase )
SIM_PLOT_PANEL* SIM_PLOT_FRAME::CurrentPlot() const
{
wxWindow* curPage = m_plotNotebook->GetCurrentPage();
SIM_PANEL_BASE* curPage = currentPlotWindow();
return ( curPage == m_welcomePanel ) ? nullptr : static_cast<SIM_PLOT_PANEL*>( curPage );
return ( ( !curPage || curPage->GetType() == ST_UNKNOWN ) ?
nullptr :
dynamic_cast<SIM_PLOT_PANEL*>( curPage ) );
}
@@ -572,7 +601,7 @@ void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const
m_simConsole->SetInsertionPointEnd();
return;
}
else if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
else if( !SIM_PANEL_BASE::IsPlottable( simType ) )
{
m_simConsole->AppendText( _( "Error: simulation type doesn't support plotting!\n" ) );
m_simConsole->SetInsertionPointEnd();
@@ -583,7 +612,7 @@ void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const
SIM_PLOT_PANEL* plotPanel = CurrentPlot();
if( !plotPanel || plotPanel->GetType() != simType )
plotPanel = NewPlotPanel( simType );
plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( simType ) );
TRACE_DESC descriptor( *m_exporter, aName, aType, aParam );
@@ -630,10 +659,11 @@ void SIM_PLOT_FRAME::removePlot( const wxString& aPlotName, bool aErase )
wxASSERT( plotPanel->TraceShown( aPlotName ) );
plotPanel->DeleteTrace( aPlotName );
plotPanel->Fit();
plotPanel->GetPlotWin()->Fit();
updateSignalList();
updateCursors();
wxCommandEvent dummy;
onCursorUpdate( dummy );
}
@@ -646,10 +676,10 @@ void SIM_PLOT_FRAME::updateNetlistExporter()
bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* aPanel )
{
SIM_TYPE simType = m_exporter->GetSimType();
wxString spiceVector = m_exporter->GetSpiceVector( aDescriptor.GetName(),
aDescriptor.GetType(), aDescriptor.GetParam() );
wxString spiceVector = m_exporter->ComponentToVector(
aDescriptor.GetName(), aDescriptor.GetType(), aDescriptor.GetParam() );
if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
if( !SIM_PANEL_BASE::IsPlottable( simType ) )
{
// There is no plot to be shown
m_simulator->Command( wxString::Format( "print %s", spiceVector ).ToStdString() );
@@ -760,13 +790,13 @@ bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL*
void SIM_PLOT_FRAME::updateSignalList()
{
m_signals->ClearAll();
SIM_PLOT_PANEL* plotPanel = CurrentPlot();
if( !plotPanel )
return;
m_signals->ClearAll();
wxSize size = m_signals->GetClientSize();
m_signals->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x );
@@ -824,12 +854,6 @@ void SIM_PLOT_FRAME::updateSignalList()
}
void SIM_PLOT_FRAME::updateCursors()
{
wxQueueEvent( this, new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
}
void SIM_PLOT_FRAME::updateTuners()
{
const auto& spiceItems = m_exporter->GetSpiceItems();
@@ -891,9 +915,9 @@ bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath )
if( !file.GetNextLine().ToLong( &plotType ) )
return false;
SIM_PLOT_PANEL* plotPanel = NewPlotPanel( (SIM_TYPE) plotType );
SIM_PANEL_BASE* plotPanel = NewPlotPanel( (SIM_TYPE) plotType );
m_plots[plotPanel].m_simCommand = file.GetNextLine();
StartSimulation();
StartSimulation( m_plots[plotPanel].m_simCommand );
// Perform simulation, so plots can be added with values
do
@@ -952,19 +976,22 @@ bool SIM_PLOT_FRAME::saveWorkbook( const wxString& aPath )
file.Create();
}
file.AddLine( wxString::Format( "%lu", m_plots.size() ) );
file.AddLine( wxString::Format( "%llu", m_plots.size() ) );
for( const auto& plot : m_plots )
{
file.AddLine( wxString::Format( "%d", plot.first->GetType() ) );
file.AddLine( plot.second.m_simCommand );
file.AddLine( wxString::Format( "%lu", plot.second.m_traces.size() ) );
for( const auto& trace : plot.second.m_traces )
if( plot.first )
{
file.AddLine( wxString::Format( "%d", trace.second.GetType() ) );
file.AddLine( trace.second.GetName() );
file.AddLine( trace.second.GetParam() );
file.AddLine( wxString::Format( "%d", plot.first->GetType() ) );
file.AddLine( plot.second.m_simCommand );
file.AddLine( wxString::Format( "%llu", plot.second.m_traces.size() ) );
for( const auto& trace : plot.second.m_traces )
{
file.AddLine( wxString::Format( "%d", trace.second.GetType() ) );
file.AddLine( trace.second.GetName() );
file.AddLine( trace.second.GetParam() );
}
}
}
@@ -1000,10 +1027,10 @@ void SIM_PLOT_FRAME::menuNewPlot( wxCommandEvent& aEvent )
{
SIM_TYPE type = m_exporter->GetSimType();
if( SIM_PLOT_PANEL::IsPlottable( type ) )
if( SIM_PANEL_BASE::IsPlottable( type ) )
{
SIM_PLOT_PANEL* prevPlot = CurrentPlot();
SIM_PLOT_PANEL* newPlot = NewPlotPanel( type );
SIM_PLOT_PANEL* newPlot = dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( type ) );
// If the previous plot had the same type, copy the simulation command
if( prevPlot )
@@ -1056,7 +1083,7 @@ void SIM_PLOT_FRAME::menuSaveImage( wxCommandEvent& event )
if( saveDlg.ShowModal() == wxID_CANCEL )
return;
CurrentPlot()->SaveScreenshot( saveDlg.GetPath(), wxBITMAP_TYPE_PNG );
CurrentPlot()->GetPlotWin()->SaveScreenshot( saveDlg.GetPath(), wxBITMAP_TYPE_PNG );
}
@@ -1106,21 +1133,21 @@ void SIM_PLOT_FRAME::menuSaveCsv( wxCommandEvent& event )
void SIM_PLOT_FRAME::menuZoomIn( wxCommandEvent& event )
{
if( CurrentPlot() )
CurrentPlot()->ZoomIn();
CurrentPlot()->GetPlotWin()->ZoomIn();
}
void SIM_PLOT_FRAME::menuZoomOut( wxCommandEvent& event )
{
if( CurrentPlot() )
CurrentPlot()->ZoomOut();
CurrentPlot()->GetPlotWin()->ZoomOut();
}
void SIM_PLOT_FRAME::menuZoomFit( wxCommandEvent& event )
{
if( CurrentPlot() )
CurrentPlot()->Fit();
CurrentPlot()->GetPlotWin()->Fit();
}
@@ -1201,21 +1228,21 @@ void SIM_PLOT_FRAME::onPlotClose( wxAuiNotebookEvent& event )
if( idx == wxNOT_FOUND )
return;
SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetPage( idx ) );
if( !plotPanel )
return;
SIM_PANEL_BASE* plotPanel =
dynamic_cast<SIM_PANEL_BASE*>( m_plotNotebook->GetPage( idx ) );
m_plots.erase( plotPanel );
updateSignalList();
updateCursors();
wxCommandEvent dummy;
onCursorUpdate( dummy );
}
void SIM_PLOT_FRAME::onPlotChanged( wxAuiNotebookEvent& event )
{
updateSignalList();
updateCursors();
wxCommandEvent dummy;
onCursorUpdate( dummy );
}
@@ -1258,7 +1285,7 @@ void SIM_PLOT_FRAME::onSimulate( wxCommandEvent& event )
void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
{
SIM_PLOT_PANEL* plotPanel = CurrentPlot();
SIM_PANEL_BASE* plotPanelWindow = currentPlotWindow();
// Initial processing is required to e.g. display a list of power sources
updateNetlistExporter();
@@ -1272,8 +1299,8 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
if( !m_settingsDlg )
m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
if( plotPanel )
m_settingsDlg->SetSimCommand( m_plots[plotPanel].m_simCommand );
if( plotPanelWindow != m_welcomePanel )
m_settingsDlg->SetSimCommand( m_plots[plotPanelWindow].m_simCommand );
m_settingsDlg->SetNetlistExporter( m_exporter.get() );
@@ -1283,12 +1310,12 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
SIM_TYPE newSimType = NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( newCommand );
// If it is a new simulation type, open a new plot
if( !plotPanel || ( plotPanel && plotPanel->GetType() != newSimType ) )
if( !plotPanelWindow || ( plotPanelWindow && plotPanelWindow->GetType() != newSimType ) )
{
plotPanel = NewPlotPanel( newSimType );
plotPanelWindow = NewPlotPanel( newSimType );
}
m_plots[plotPanel].m_simCommand = newCommand;
m_plots[plotPanelWindow].m_simCommand = newCommand;
}
}
@@ -1299,7 +1326,7 @@ void SIM_PLOT_FRAME::onAddSignal( wxCommandEvent& event )
if( !plotPanel || !m_exporter || plotPanel->GetType() != m_exporter->GetSimType() )
{
DisplayInfoMessage( this, _( "You need to run simulation first." ) );
DisplayInfoMessage( this, _( "You need to run plot-providing simulation first." ) );
return;
}
@@ -1455,18 +1482,19 @@ void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
if( simType == ST_UNKNOWN )
return;
SIM_PLOT_PANEL* plotPanel = CurrentPlot();
SIM_PANEL_BASE* plotPanelWindow = currentPlotWindow();
if( !plotPanel || plotPanel->GetType() != simType )
plotPanel = NewPlotPanel( simType );
if( !plotPanelWindow || plotPanelWindow->GetType() != simType )
plotPanelWindow = NewPlotPanel( simType );
if( IsSimulationRunning() )
return;
// If there are any signals plotted, update them
if( SIM_PLOT_PANEL::IsPlottable( simType ) )
if( SIM_PANEL_BASE::IsPlottable( simType ) )
{
TRACE_MAP& traceMap = m_plots[plotPanel].m_traces;
TRACE_MAP& traceMap = m_plots[plotPanelWindow].m_traces;
SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( plotPanelWindow );
for( auto it = traceMap.begin(); it != traceMap.end(); /* iteration occurs in the loop */)
{
@@ -1482,18 +1510,33 @@ void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
}
updateSignalList();
plotPanel->UpdateAll();
plotPanel->GetPlotWin()->UpdateAll();
plotPanel->ResetScales();
}
else
else if( simType == ST_OP )
{
/// @todo do not make it hardcoded for ngspice
for( const auto& net : m_exporter->GetNetIndexMap() )
{
int node = net.second;
m_simConsole->AppendText( _( "\n\nSimulation results:\n\n" ) );
m_simConsole->SetInsertionPointEnd();
if( node > 0 )
m_simulator->Command( wxString::Format( "print v(%d)", node ).ToStdString() );
for( const auto& vec : m_simulator->AllPlots() )
{
double val = m_simulator->GetRealPlot( vec, 1 ).at( 0 );
wxString outLine, signal;
SIM_PLOT_TYPE type = m_exporter->VectorToSignal( vec, signal );
const size_t tab = 25; //characters
size_t padding = ( signal.length() < tab ) ? ( tab - signal.length() ) : 1;
outLine.Printf( wxT( "%s%s" ), ( signal + wxT( ":" ) ).Pad( padding, wxUniChar( ' ' ) ),
SPICE_VALUE( val ).ToSpiceString() );
outLine.Append( type == SPT_CURRENT ? "A\n" : "V\n" );
m_simConsole->AppendText( outLine );
m_simConsole->SetInsertionPointEnd();
// @todo display calculated values on the schematic
}
}
}