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:
committed by
Ian McInerney
parent
6a8ace55c4
commit
fdccdd5cb9
+117
-74
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user