Spice simulator: add Ngspce model behavior mode settings.

ADDED: Ngspice simulator model behavior mode settings for PSpice, LTSpice,
       PSpice and LTSpice, and HSpice.

Fixes https://gitlab.com/kicad/code/kicad/issues/5810
This commit is contained in:
Wayne Stambaugh
2021-03-18 15:31:02 -04:00
parent 1641d6da79
commit acec6ad7fc
16 changed files with 1065 additions and 198 deletions
+113 -73
View File
@@ -26,6 +26,7 @@
#include <wx/stc/stc.h>
#include <project/project_file.h>
#include <sch_edit_frame.h>
#include <eeschema_id.h>
#include <kiway.h>
@@ -36,6 +37,7 @@
#include <dialogs/dialog_signal_list.h>
#include "netlist_exporter_pspice_sim.h"
#include <pgm_base.h>
#include "ngspice.h"
#include "sim_plot_colors.h"
#include "sim_plot_frame.h"
#include "sim_plot_panel.h"
@@ -59,8 +61,8 @@ SIM_PLOT_TYPE operator|( SIM_PLOT_TYPE aFirst, SIM_PLOT_TYPE aSecond )
class SIM_THREAD_REPORTER : public SPICE_REPORTER
{
public:
SIM_THREAD_REPORTER( SIM_PLOT_FRAME* aParent )
: m_parent( aParent )
SIM_THREAD_REPORTER( SIM_PLOT_FRAME* aParent ) :
m_parent( aParent )
{
}
@@ -83,17 +85,17 @@ public:
switch( aNewState )
{
case SIM_IDLE:
event = new wxCommandEvent( EVT_SIM_FINISHED );
break;
case SIM_IDLE:
event = new wxCommandEvent( EVT_SIM_FINISHED );
break;
case SIM_RUNNING:
event = new wxCommandEvent( EVT_SIM_STARTED );
break;
case SIM_RUNNING:
event = new wxCommandEvent( EVT_SIM_STARTED );
break;
default:
wxFAIL;
return;
default:
wxFAIL;
return;
}
wxQueueEvent( m_parent, event );
@@ -105,8 +107,10 @@ private:
TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName,
SIM_PLOT_TYPE aType, const wxString& aParam )
: m_name( aName ), m_type( aType ), m_param( aParam )
SIM_PLOT_TYPE aType, const wxString& aParam ) :
m_name( aName ),
m_type( aType ),
m_param( aParam )
{
// Title generation
m_title = wxString::Format( "%s(%s)", aParam, aName );
@@ -117,14 +121,16 @@ TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxSt
m_title += " (phase)";
}
// Store the path of saved workbooks during the session
wxString SIM_PLOT_FRAME::m_savedWorkbooksPath;
SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
: SIM_PLOT_FRAME_BASE( aParent ),
m_lastSimPlot( nullptr ),
m_welcomePanel( nullptr ),
m_plotNumber( 0 )
SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
SIM_PLOT_FRAME_BASE( aParent ),
m_lastSimPlot( nullptr ),
m_welcomePanel( nullptr ),
m_plotNumber( 0 )
{
SetKiway( this, aKiway );
m_signalsIconColorList = NULL;
@@ -139,6 +145,14 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
icon.CopyFromBitmap( KiBitmap( BITMAPS::simulator ) );
SetIcon( icon );
m_simulator = SPICE_SIMULATOR::CreateInstance( "ngspice" );
if( !m_simulator )
{
throw std::runtime_error( "Could not create simulator instance" );
return;
}
// Get the previous size and position of windows:
LoadSettings( config() );
@@ -148,14 +162,6 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
// Give icons to menuitems
setIconsForMenuItems();
m_simulator = SPICE_SIMULATOR::CreateInstance( "ngspice" );
if( !m_simulator )
{
throw std::runtime_error( "Could not create simulator instance" );
return;
}
m_simulator->Init();
if( m_savedWorkbooksPath.IsEmpty() )
@@ -178,7 +184,8 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
Connect( EVT_SIM_REPORT, wxCommandEventHandler( SIM_PLOT_FRAME::onSimReport ), NULL, this );
Connect( EVT_SIM_STARTED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimStarted ), NULL, this );
Connect( EVT_SIM_FINISHED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimFinished ), NULL, this );
Connect( EVT_SIM_CURSOR_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onCursorUpdate ), NULL, this );
Connect( EVT_SIM_CURSOR_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onCursorUpdate ),
NULL, this );
// Toolbar buttons
m_toolSimulate = m_toolBar->AddTool( ID_SIM_RUN, _( "Run/Stop Simulation" ),
@@ -204,12 +211,14 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
wxCommandEventHandler( SIM_PLOT_FRAME::onSettings ), NULL, this );
// Bind toolbar buttons event to existing menu event handlers, so they behave the same
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSimulate, this, m_runSimulation->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onAddSignal, this, m_addSignals->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onProbe, this, m_probeSignals->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onTune, this, m_tuneValue->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onShowNetlist, this, m_showNetlist->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSettings, this, m_settings->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSimulate, this,
m_runSimulation->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onAddSignal, this, m_addSignals->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onProbe, this, m_probeSignals->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onTune, this, m_tuneValue->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onShowNetlist, this,
m_showNetlist->GetId() );
Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSettings, this, m_settings->GetId() );
m_toolBar->Realize();
@@ -258,6 +267,14 @@ void SIM_PLOT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
cfg->m_Simulator.cursors_panel_height = m_splitterTuneValues->GetSashPosition();
cfg->m_Simulator.white_background = m_plotUseWhiteBg;
}
if( m_schematicFrame )
{
PROJECT_FILE& project = Prj().GetProjectFile();
project.m_SchematicSettings->m_NgspiceSimulatorSettings->SaveToFile();
m_schematicFrame->SaveProjectSettings();
}
}
@@ -277,6 +294,13 @@ void SIM_PLOT_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
m_splitterTuneValuesSashPosition = cfg->m_Simulator.cursors_panel_height;
m_plotUseWhiteBg = cfg->m_Simulator.white_background;
}
PROJECT_FILE& project = Prj().GetProjectFile();
NGSPICE* currentSim = dynamic_cast<NGSPICE*>( m_simulator.get() );
if( currentSim )
m_simulator->Settings() = project.m_SchematicSettings->m_NgspiceSimulatorSettings;
}
@@ -345,7 +369,8 @@ void SIM_PLOT_FRAME::setIconsForMenuItems()
continue;
wxMenu* menu = item->GetMenu();
// Calculate the initial index of item inside the wxMenu parent
// Calculate the initial index of item inside the wxMenu parent.
wxMenuItemList& mlist = menu->GetMenuItems();
int mpos = mlist.IndexOf( item );
@@ -354,6 +379,7 @@ void SIM_PLOT_FRAME::setIconsForMenuItems()
// Modify the bitmap
menu->Remove( item );
AddBitmapToMenuItem( item, KiBitmap( bm_list[ii].m_Bitmap ) );
// Insert item to its the initial index
menu->Insert( mpos, item );
}
@@ -382,7 +408,7 @@ void SIM_PLOT_FRAME::StartSimulation( const wxString& aSimCommand )
STRING_FORMATTER formatter;
if( !m_settingsDlg )
m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
m_settingsDlg = new DIALOG_SIM_SETTINGS( this, m_simulator->Settings() );
m_simConsole->Clear();
updateNetlistExporter();
@@ -490,7 +516,8 @@ void SIM_PLOT_FRAME::AddTuner( SCH_COMPONENT* aComponent )
// For now limit the tuner tool to RLC components
char primitiveType = NETLIST_EXPORTER_PSPICE::GetSpiceField( SF_PRIMITIVE, aComponent, 0 )[0];
if( primitiveType != SP_RESISTOR && primitiveType != SP_CAPACITOR && primitiveType != SP_INDUCTOR )
if( primitiveType != SP_RESISTOR && primitiveType != SP_CAPACITOR
&& primitiveType != SP_INDUCTOR )
return;
const wxString componentName = aComponent->GetField( REFERENCE_FIELD )->GetText();
@@ -546,6 +573,14 @@ const NETLIST_EXPORTER_PSPICE_SIM* SIM_PLOT_FRAME::GetExporter() const
}
std::shared_ptr<SPICE_SIMULATOR_SETTINGS>& SIM_PLOT_FRAME::GetSimulatorSettings()
{
wxASSERT( m_simulator->Settings() );
return m_simulator->Settings();
}
void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam )
{
SIM_TYPE simType = m_exporter->GetSimType();
@@ -586,7 +621,8 @@ void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const
// Add two plots: magnitude & phase
TRACE_DESC mag_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_MAG ) );
TRACE_DESC phase_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_PHASE ) );
TRACE_DESC phase_desc( *m_exporter, descriptor,
(SIM_PLOT_TYPE)( baseType | SPT_AC_PHASE ) );
updated |= updatePlot( mag_desc, plotPanel );
updated |= updatePlot( phase_desc, plotPanel );
@@ -631,6 +667,7 @@ void SIM_PLOT_FRAME::removePlot( const wxString& aPlotName, bool aErase )
void SIM_PLOT_FRAME::updateNetlistExporter()
{
m_exporter.reset( new NETLIST_EXPORTER_PSPICE_SIM( &m_schematicFrame->Schematic() ) );
if( m_settingsDlg )
m_settingsDlg->SetNetlistExporter( m_exporter.get() );
}
@@ -668,31 +705,28 @@ bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL*
// Now, Y axis data
switch( m_exporter->GetSimType() )
{
case ST_AC:
{
wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ),
"Cannot set both AC_PHASE and AC_MAG bits" );
case ST_AC:
wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ),
"Cannot set both AC_PHASE and AC_MAG bits" );
if( plotType & SPT_AC_MAG )
data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
else if( plotType & SPT_AC_PHASE )
data_y = m_simulator->GetPhasePlot( (const char*) spiceVector.c_str() );
else
wxASSERT_MSG( false, "Plot type missing AC_PHASE or AC_MAG bit" );
}
break;
case ST_NOISE:
case ST_DC:
case ST_TRANSIENT:
{
if( plotType & SPT_AC_MAG )
data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
}
else if( plotType & SPT_AC_PHASE )
data_y = m_simulator->GetPhasePlot( (const char*) spiceVector.c_str() );
else
wxASSERT_MSG( false, "Plot type missing AC_PHASE or AC_MAG bit" );
break;
default:
wxASSERT_MSG( false, "Unhandled plot type" );
return false;
case ST_NOISE:
case ST_DC:
case ST_TRANSIENT:
data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
break;
default:
wxASSERT_MSG( false, "Unhandled plot type" );
return false;
}
if( data_y.size() != size )
@@ -1177,7 +1211,7 @@ void SIM_PLOT_FRAME::menuWhiteBackground( wxCommandEvent& event )
if( curPage == m_welcomePanel )
continue;
// ensure it is truely a plot panel and not the (zero plots) placeholder
// ensure it is truly a plot panel and not the (zero plots) placeholder
// which is only SIM_PLOT_PANEL_BASE
SIM_PLOT_PANEL* panel = dynamic_cast<SIM_PLOT_PANEL*>( curPage );
@@ -1256,7 +1290,7 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
SIM_PANEL_BASE* plotPanelWindow = currentPlotWindow();
if( !m_settingsDlg )
m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
m_settingsDlg = new DIALOG_SIM_SETTINGS( this, m_simulator->Settings() );
// Initial processing is required to e.g. display a list of power sources
updateNetlistExporter();
@@ -1277,7 +1311,8 @@ 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
// For the DC sim, check if sweep source type has changed (char 4 will contain 'v', 'i', 'r' or 't'
// For the DC sim, check if sweep source type has changed (char 4 will contain 'v',
// 'i', 'r' or 't'.
if( !plotPanelWindow
|| ( plotPanelWindow && plotPanelWindow->GetType() != newSimType )
|| ( newSimType == ST_DC
@@ -1287,6 +1322,7 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
}
m_plots[plotPanelWindow].m_simCommand = newCommand;
m_simulator->Init();
}
}
@@ -1325,6 +1361,7 @@ void SIM_PLOT_FRAME::onTune( wxCommandEvent& event )
m_schematicFrame->Raise();
}
void SIM_PLOT_FRAME::onShowNetlist( wxCommandEvent& event )
{
class NETLIST_VIEW_DIALOG : public DIALOG_SHIM
@@ -1369,7 +1406,7 @@ void SIM_PLOT_FRAME::onShowNetlist( wxCommandEvent& event )
SetSizer( sizer );
Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( NETLIST_VIEW_DIALOG::onClose ), NULL,
this );
this );
finishDialogSettings();
}
@@ -1411,7 +1448,8 @@ void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
// Fill the signals listctrl
m_cursors->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x / 2 );
const long X_COL = m_cursors->AppendColumn( plotPanel->GetLabelX(), wxLIST_FORMAT_LEFT, size.x / 4 );
const long X_COL = m_cursors->AppendColumn( plotPanel->GetLabelX(), wxLIST_FORMAT_LEFT,
size.x / 4 );
wxString labelY1 = plotPanel->GetLabelY1();
wxString labelY2 = plotPanel->GetLabelY2();
@@ -1570,7 +1608,8 @@ SIM_PLOT_FRAME::SIGNAL_CONTEXT_MENU::SIGNAL_CONTEXT_MENU( const wxString& aSigna
else
AddMenuItem( this, SHOW_CURSOR, _( "Show Cursor" ), KiBitmap( BITMAPS::pcb_target ) );
Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ), NULL, this );
Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ),
NULL, this );
}
@@ -1580,20 +1619,21 @@ void SIM_PLOT_FRAME::SIGNAL_CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent )
switch( aEvent.GetId() )
{
case HIDE_SIGNAL:
m_plotFrame->removePlot( m_signal );
break;
case HIDE_SIGNAL:
m_plotFrame->removePlot( m_signal );
break;
case SHOW_CURSOR:
plot->EnableCursor( m_signal, true );
break;
case SHOW_CURSOR:
plot->EnableCursor( m_signal, true );
break;
case HIDE_CURSOR:
plot->EnableCursor( m_signal, false );
break;
case HIDE_CURSOR:
plot->EnableCursor( m_signal, false );
break;
}
}
wxDEFINE_EVENT( EVT_SIM_UPDATE, wxCommandEvent );
wxDEFINE_EVENT( EVT_SIM_REPORT, wxCommandEvent );