diff --git a/eeschema/dialogs/dialog_signal_list.cpp b/eeschema/dialogs/dialog_signal_list.cpp index 6b1ff2cc02..90d7b8d9b0 100644 --- a/eeschema/dialogs/dialog_signal_list.cpp +++ b/eeschema/dialogs/dialog_signal_list.cpp @@ -52,10 +52,21 @@ bool DIALOG_SIGNAL_LIST::TransferDataToWindow() if( m_exporter ) { + // Voltage list for( const auto& net : m_exporter->GetNetIndexMap() ) { if( net.first != "GND" ) - m_signals->Append( net.first ); + m_signals->Append( wxString::Format( "V(%s)", net.first ) ); + } + + for( const auto& item : m_exporter->GetSpiceItems() ) + { + // Add all possible currents for the primitive + for( const auto& current : + NETLIST_EXPORTER_PSPICE_SIM::GetCurrents( (SPICE_PRIMITIVE) item.m_primitive ) ) + { + m_signals->Append( wxString::Format( "%s(%s)", current, item.m_refName ) ); + } } } @@ -68,6 +79,24 @@ void DIALOG_SIGNAL_LIST::addSelectionToPlotFrame() for( unsigned int i = 0; i < m_signals->GetCount(); ++i ) { if( m_signals->IsSelected( i ) ) - m_plotFrame->AddVoltagePlot( m_signals->GetString( i ) ); + { + const wxString& plotName = m_signals->GetString( i ); + + // Get the part in the parentheses + wxString name = plotName.AfterFirst( '(' ).BeforeLast( ')' ); + + if( plotName[0] == 'V' ) + { + m_plotFrame->AddVoltagePlot( name ); + } + else if( plotName[0] == 'I' ) + { + m_plotFrame->AddCurrentPlot( name, plotName.BeforeFirst( '(' ) ); + } + else + { + wxASSERT_MSG( false, "Unhandled plot type" ); + } + } } } diff --git a/eeschema/sim/netlist_exporter_pspice_sim.cpp b/eeschema/sim/netlist_exporter_pspice_sim.cpp index a317fefe87..e75f9edb94 100644 --- a/eeschema/sim/netlist_exporter_pspice_sim.cpp +++ b/eeschema/sim/netlist_exporter_pspice_sim.cpp @@ -23,7 +23,84 @@ */ #include "netlist_exporter_pspice_sim.h" -#include "sim_types.h" + +wxString NETLIST_EXPORTER_PSPICE_SIM::GetSpiceVector( const wxString& aName, SIM_PLOT_TYPE aType, + const wxString& aParam ) const +{ + wxString res; + + // Some of the flags should exclude mutually + assert( ( ( aType & SPT_VOLTAGE ) == 0 ) != ( ( aType & SPT_CURRENT ) == 0 ) ); + assert( ( ( aType & SPT_AC_PHASE ) == 0 ) || ( ( aType & SPT_AC_MAG ) == 0 ) ); + + if( aType & SPT_VOLTAGE ) + { + const auto& netMapping = GetNetIndexMap(); + auto it = netMapping.find( aName ); + + if( it == netMapping.end() ) + return ""; + + return wxString::Format( "v(%d)", it->second ); + } + + + /// @todo check is Lower() is required + else if( aType & SPT_CURRENT ) + { + return wxString::Format( "@%s[%s]", GetSpiceDevice( aName ).Lower(), + aParam.IsEmpty() ? "i" : aParam.Lower() ); + } + + return res; +} + + +const std::vector& NETLIST_EXPORTER_PSPICE_SIM::GetCurrents( SPICE_PRIMITIVE aPrimitive ) +{ + static const std::vector passive = { "I" }; + static const std::vector diode = { "Id" }; + static const std::vector bjt = { "Ib", "Ic", "Ie" }; + static const std::vector mos = { "Ig", "Id", "Is" }; + static const std::vector empty; + + switch( aPrimitive ) + { + case SP_RESISTOR: + case SP_CAPACITOR: + case SP_INDUCTOR: + case SP_VSOURCE: + return passive; + + case SP_DIODE: + return diode; + + case SP_BJT: + return bjt; + + case SP_MOSFET: + return mos; + + default: + return empty; + } +} + + +wxString NETLIST_EXPORTER_PSPICE_SIM::GetSpiceDevice( const wxString& aComponent ) const +{ + const auto& spiceItems = GetSpiceItems(); + + auto it = std::find_if( spiceItems.begin(), spiceItems.end(), [&]( const SPICE_ITEM& item ) { + return item.m_refName == aComponent; + } ); + + if( it == spiceItems.end() ) + return wxEmptyString; + + return wxString( it->m_primitive + it->m_refName ); +} + wxString NETLIST_EXPORTER_PSPICE_SIM::GetSheetSimCommand() { @@ -68,19 +145,43 @@ SIM_TYPE NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( const wxString& aCmd ) void NETLIST_EXPORTER_PSPICE_SIM::writeDirectives( OUTPUTFORMATTER* aFormatter, unsigned aCtl ) const { + // Add a directive to obtain currents + //aFormatter->Print( 0, ".options savecurrents\n" ); // does not work :( + + for( const auto& item : GetSpiceItems() ) + { + for( const auto& current : + NETLIST_EXPORTER_PSPICE_SIM::GetCurrents( (SPICE_PRIMITIVE) item.m_primitive ) ) + { + /// @todo is it required to switch to lowercase + aFormatter->Print( 0, ".save %s\n", + (const char*) GetSpiceVector( item.m_refName, SPT_CURRENT, current ).c_str() ); + } + } + + // If we print out .save directives for currents, then it needs to be done for voltages as well + for( const auto& netMap : GetNetIndexMap() ) + { + aFormatter->Print( 0, ".save %s\n", + (const char*) GetSpiceVector( netMap.first, SPT_VOLTAGE ).c_str() ); + } + if( m_simCommand.IsEmpty() ) { // Fallback to the default behavior and just write all directives NETLIST_EXPORTER_PSPICE::writeDirectives( aFormatter, aCtl ); } - - // Dump all directives, but simulation commands - for( const auto& dir : GetDirectives() ) + else { - if( !IsSimCommand( dir ) ) - aFormatter->Print( 0, "%s\n", (const char*) dir.c_str() ); + // Dump all directives but simulation commands + for( const auto& dir : GetDirectives() ) + { + if( !IsSimCommand( dir ) ) + aFormatter->Print( 0, "%s\n", (const char*) dir.c_str() ); + } + + // Finish with our custom simulation command + aFormatter->Print( 0, "%s\n", (const char*) m_simCommand.c_str() ); } - // Finish with our custom simulation command - aFormatter->Print( 0, "%s\n", (const char*) m_simCommand.c_str() ); } diff --git a/eeschema/sim/netlist_exporter_pspice_sim.h b/eeschema/sim/netlist_exporter_pspice_sim.h index 27cb0e8369..6cd76c81d7 100644 --- a/eeschema/sim/netlist_exporter_pspice_sim.h +++ b/eeschema/sim/netlist_exporter_pspice_sim.h @@ -26,6 +26,8 @@ #define NETLIST_EXPORTER_PSPICE_SIM_H #include +#include + #include "sim_types.h" /// Special netlist exporter flavor that allows to override simulation commands @@ -38,6 +40,30 @@ public: { } + /** + * @brief Returns name of Spice dataset for a specific plot. + * @param aName is name of the measured net or device + * @param aType describes the type of expected plot + * @param aParam is an optional parameter for devices, if absent it will return current (only + * for passive devices). + * @return Empty string if query is invalid, otherwise a plot name that + * can be requested from the simulator. + */ + wxString GetSpiceVector( const wxString& aName, SIM_PLOT_TYPE aType, + const wxString& aParam = wxEmptyString ) const; + + /** + * @brief Returns name of Spice device corresponding to a schematic component. + * @param aComponent is the component reference. + * @return Spice device name or empty string if there is no such component in the netlist. + */ + wxString GetSpiceDevice( const wxString& aComponent ) const; + + /** + * @brief Returns a list of currents that can be probed in a Spice primitive. + */ + static const std::vector& GetCurrents( SPICE_PRIMITIVE aPrimitive ); + void SetSimCommand( const wxString& aCmd ) { m_simCommand = aCmd; diff --git a/eeschema/sim/sim_plot_frame.cpp b/eeschema/sim/sim_plot_frame.cpp index 15d5ec9ecf..fd9646ee21 100644 --- a/eeschema/sim/sim_plot_frame.cpp +++ b/eeschema/sim/sim_plot_frame.cpp @@ -37,6 +37,13 @@ #include "spice_simulator.h" #include "spice_reporter.h" +SIM_PLOT_TYPE operator|( SIM_PLOT_TYPE aFirst, SIM_PLOT_TYPE aSecond ) +{ + int res = (int) aFirst | (int) aSecond; + + return (SIM_PLOT_TYPE) res; +} + class SIM_THREAD_REPORTER : public SPICE_REPORTER { public: @@ -76,6 +83,23 @@ 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 ) +{ + // Spice vector generation + m_spiceVector = aExporter.GetSpiceVector( aName, aType, aParam ); + + // Title generation + m_title = wxString::Format( "%s(%s)", aParam, aName ); + + if( aType & SPT_AC_MAG ) + m_title += " (mag)"; + else if( aType & SPT_AC_PHASE ) + m_title += " (phase)"; +} + + SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : SIM_PLOT_FRAME_BASE( aParent ), m_settingsDlg( this ) { @@ -157,27 +181,13 @@ SIM_PLOT_PANEL* SIM_PLOT_FRAME::NewPlotPanel( SIM_TYPE aSimType ) void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName ) { - SIM_TYPE simType = m_exporter->GetSimType(); + addPlot( aNetName, SPT_VOLTAGE, "V" ); +} - if( !SIM_PLOT_PANEL::IsPlottable( simType ) ) - return; // TODO else write out in console? - int nodeNumber = getNodeNumber( aNetName ); - - if( nodeNumber >= -1 ) - { - // Create a new plot if the current one displays a different type - SIM_PLOT_PANEL* plotPanel = CurrentPlot(); - - if( plotPanel == nullptr || plotPanel->GetType() != simType ) - plotPanel = NewPlotPanel( simType ); - - if( updatePlot( wxString::Format( "V(%d)", nodeNumber ), aNetName, plotPanel ) ) - { - updateSignalList(); - plotPanel->Fit(); - } - } +void SIM_PLOT_FRAME::AddCurrentPlot( const wxString& aDeviceName, const wxString& aParam ) +{ + addPlot( aDeviceName, SPT_CURRENT, aParam ); } @@ -195,7 +205,7 @@ void SIM_PLOT_FRAME::AddTuner( SCH_COMPONENT* aComponent ) return; const wxString& componentName = aComponent->GetField( REFERENCE )->GetText(); - auto& tunerList = m_tuners[plotPanel]; + auto& tunerList = m_plots[plotPanel].m_tuners; // Do not add multiple instances for the same component auto tunerIt = std::find_if( tunerList.begin(), tunerList.end(), [&]( const TUNER_SLIDER* t ) @@ -228,7 +238,7 @@ void SIM_PLOT_FRAME::RemoveTuner( TUNER_SLIDER* aTuner ) if( !plotPanel ) return; - m_tuners[plotPanel].remove( aTuner ); + m_plots[plotPanel].m_tuners.remove( aTuner ); aTuner->Destroy(); Layout(); } @@ -240,6 +250,45 @@ SIM_PLOT_PANEL* SIM_PLOT_FRAME::CurrentPlot() const } +void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam ) +{ + SIM_TYPE simType = m_exporter->GetSimType(); + + if( !SIM_PLOT_PANEL::IsPlottable( simType ) ) + return; // TODO else write out in console? + + // Create a new plot if the current one displays a different type + SIM_PLOT_PANEL* plotPanel = CurrentPlot(); + + if( plotPanel == nullptr || plotPanel->GetType() != simType ) + plotPanel = NewPlotPanel( simType ); + + TRACE_DESC descriptor( *m_exporter, aName, aType, aParam ); + + bool updated = false; + + if( GetXAxisType( simType ) == SPT_FREQUENCY ) + { + // Add two plots: magnitude & phase + TRACE_DESC mag_desc( *m_exporter, descriptor, descriptor.GetType() | SPT_AC_MAG ); + TRACE_DESC phase_desc( *m_exporter, descriptor, descriptor.GetType() | SPT_AC_PHASE ); + + updated |= updatePlot( mag_desc, plotPanel ); + updated |= updatePlot( phase_desc, plotPanel ); + } + else + { + updated = updatePlot( descriptor, plotPanel ); + } + + if( updated ) + { + updateSignalList(); + plotPanel->Fit(); + } +} + + void SIM_PLOT_FRAME::updateNetlistExporter() { m_exporter.reset( new NETLIST_EXPORTER_PSPICE_SIM( m_schematicFrame->BuildNetListBase(), @@ -247,65 +296,51 @@ void SIM_PLOT_FRAME::updateNetlistExporter() } -bool SIM_PLOT_FRAME::updatePlot( const wxString& aName, SIM_PLOT_TYPE aType, SIM_PLOT_PANEL* aPanel ) +bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* aPanel ) { if( !m_simulator ) return false; - // First, handle the x axis - wxString xAxisName; SIM_TYPE simType = m_exporter->GetSimType(); + wxString spiceVector = aDescriptor.GetSpiceVector(); if( !SIM_PLOT_PANEL::IsPlottable( simType ) ) { // There is no plot to be shown - m_simulator->Command( wxString::Format( "print %s", aSpiceName ).ToStdString() ); + m_simulator->Command( wxString::Format( "print %s", spiceVector ).ToStdString() ); + return false; } - switch( simType ) - { - /// @todo x axis names should be moved to simulator iface, so they are not hardcoded for ngspice - case ST_AC: - case ST_NOISE: - xAxisName = "frequency"; - break; + // First, handle the x axis + wxString xAxisName( m_simulator->GetXAxis( simType ) ); - case ST_DC: - xAxisName = "v-sweep"; - break; - - case ST_TRANSIENT: - xAxisName = "time"; - break; - - case ST_OP: - break; - - default: - break; - } + if( xAxisName.IsEmpty() ) + return false; auto data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() ); - int size = data_x.size(); + unsigned int size = data_x.size(); if( data_x.empty() ) return false; + SIM_PLOT_TYPE plotType = aDescriptor.GetType(); + std::vector data_y; + // Now, Y axis data switch( m_exporter->GetSimType() ) { - /// @todo x axis names should be moved to simulator iface case ST_AC: { - auto data_mag = m_simulator->GetMagPlot( (const char*) aSpiceName.c_str() ); - auto data_phase = m_simulator->GetPhasePlot( (const char*) aSpiceName.c_str() ); + wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ), + "Cannot set both AC_PHASE and AC_MAG bits" ); - if( data_mag.empty() || data_phase.empty() ) - return false; - - aPanel->AddTrace( aSpiceName, aName, size, data_x.data(), data_mag.data(), SPF_AC_MAG ); - aPanel->AddTrace( aSpiceName, aName, size, data_x.data(), data_phase.data(), SPF_AC_PHASE ); + 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; @@ -313,12 +348,7 @@ bool SIM_PLOT_FRAME::updatePlot( const wxString& aName, SIM_PLOT_TYPE aType, SIM case ST_DC: case ST_TRANSIENT: { - auto data_y = m_simulator->GetMagPlot( (const char*) aSpiceName.c_str() ); - - if( data_y.empty() ) - return false; - - aPanel->AddTrace( aSpiceName, aName, size, data_x.data(), data_y.data(), 0 ); + data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() ); } break; @@ -327,6 +357,15 @@ bool SIM_PLOT_FRAME::updatePlot( const wxString& aName, SIM_PLOT_TYPE aType, SIM return false; } + if( data_y.size() != size ) + return false; + + if( aPanel->AddTrace( aDescriptor.GetTitle(), size, + data_x.data(), data_y.data(), aDescriptor.GetType() ) ) + { + m_plots[aPanel].m_traces.insert( std::make_pair( aDescriptor.GetTitle(), aDescriptor ) ); + } + return true; } @@ -341,8 +380,8 @@ void SIM_PLOT_FRAME::updateSignalList() // Fill the signals listbox m_signals->Clear(); - for( const auto& trace : plotPanel->GetTraces() ) - m_signals->Append( trace.second->GetName() ); + for( const auto& trace : m_plots[plotPanel].m_traces ) + m_signals->Append( trace.first ); } @@ -358,7 +397,7 @@ void SIM_PLOT_FRAME::updateTuners() m_tuneSizer->Clear(); - for( auto tuner : m_tuners[plotPanel] ) + for( auto tuner : m_plots[plotPanel].m_tuners ) { m_tuneSizer->Add( tuner ); tuner->Show(); @@ -368,15 +407,23 @@ void SIM_PLOT_FRAME::updateTuners() } -int SIM_PLOT_FRAME::getNodeNumber( const wxString& aNetName ) +SIM_PLOT_TYPE SIM_PLOT_FRAME::GetXAxisType( SIM_TYPE aType ) const { - const auto& netMapping = m_exporter->GetNetIndexMap(); - auto it = netMapping.find( aNetName ); + switch( aType ) + { + case ST_AC: + return SPT_FREQUENCY; - if( it == netMapping.end() ) - return -1; + case ST_DC: + return SPT_SWEEP; - return it->second; + case ST_TRANSIENT: + return SPT_TIME; + + default: + wxASSERT_MSG( false, "Unhandled simulation type" ); + return (SIM_PLOT_TYPE) 0; + } } @@ -429,7 +476,7 @@ void SIM_PLOT_FRAME::menuSaveCsv( wxCommandEvent& event ) timeWritten = true; } - out.Write( wxString::Format( "%s%c", t.first.GetDescription(), SEPARATOR ) ); + out.Write( wxString::Format( "%s%c", t.first, SEPARATOR ) ); for( double v : trace->GetDataY() ) out.Write( wxString::Format( "%f%c", v, SEPARATOR ) ); @@ -515,14 +562,22 @@ void SIM_PLOT_FRAME::onSignalDblClick( wxCommandEvent& event ) { // Remove signal from the plot on double click int idx = m_signals->GetSelection(); - SIM_PLOT_PANEL* plot = CurrentPlot(); + SIM_PLOT_PANEL* plotPanel = CurrentPlot(); if( idx != wxNOT_FOUND ) { - const wxString& netName = m_signals->GetString( idx ); + const wxString& plotName = m_signals->GetString( idx ); + auto& traceMap = m_plots[plotPanel].m_traces; + + auto traceIt = traceMap.find( plotName ); + wxASSERT( traceIt != traceMap.end() ); + traceMap.erase( traceIt ); + + wxASSERT( plotPanel->IsShown( plotName ) ); + plotPanel->DeleteTrace( plotName ); + plotPanel->Fit(); + m_signals->Delete( idx ); - wxASSERT( plot->IsShown( netName ) ); - plot->DeleteTrace( netName ); } } @@ -565,6 +620,14 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event ) void SIM_PLOT_FRAME::onAddSignal( wxCommandEvent& event ) { + SIM_PLOT_PANEL* plotPanel = CurrentPlot(); + + if( !plotPanel || !m_exporter || plotPanel->GetType() != m_exporter->GetSimType() ) + { + DisplayInfoMessage( this, wxT( "You need to run simulation first" ) ); + return; + } + DIALOG_SIGNAL_LIST dialog( this, m_exporter.get() ); dialog.ShowModal(); } @@ -612,7 +675,7 @@ void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event ) if( CURSOR* cursor = trace.second->GetCursor() ) { const wxRealPoint coords = cursor->GetCoords(); - long idx = m_cursors->InsertItem( SIGNAL_COL, trace.first.GetDescription() ); + long idx = m_cursors->InsertItem( SIGNAL_COL, trace.first ); m_cursors->SetItem( idx, X_COL, wxString::Format( "%f", coords.x ) ); m_cursors->SetItem( idx, Y_COL, wxString::Format( "%f", coords.y ) ); } @@ -645,8 +708,8 @@ void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent ) // If there are any signals plotted, update them if( SIM_PLOT_PANEL::IsPlottable( simType ) ) { - for( const auto& trace : plotPanel->GetTraces() ) - updatePlot( trace.second->GetSpiceName(), trace.first.GetName(), plotPanel ); + for( const auto& trace : m_plots[plotPanel].m_traces ) + updatePlot( trace.second, plotPanel ); plotPanel->UpdateAll(); } @@ -676,7 +739,7 @@ void SIM_PLOT_FRAME::onSimUpdate( wxCommandEvent& aEvent ) // Apply tuned values if( SIM_PLOT_PANEL* plotPanel = CurrentPlot() ) { - for( auto tuner : m_tuners[plotPanel] ) + for( auto tuner : m_plots[plotPanel].m_tuners ) { /// @todo no ngspice hardcoding std::string command( "alter @" + tuner->GetSpiceName() @@ -703,21 +766,14 @@ SIM_PLOT_FRAME::SIGNAL_CONTEXT_MENU::SIGNAL_CONTEXT_MENU( const wxString& aSigna { SIM_PLOT_PANEL* plot = m_plotFrame->CurrentPlot(); - if( plot->IsShown( m_signal ) ) - { - Append( HIDE_SIGNAL, wxT( "Hide signal" ) ); + Append( HIDE_SIGNAL, wxT( "Hide signal" ) ); - TRACE* trace = plot->GetTrace( m_signal ); + TRACE* trace = plot->GetTrace( m_signal ); - if( trace->HasCursor() ) - Append( HIDE_CURSOR, wxT( "Hide cursor" ) ); - else - Append( SHOW_CURSOR, wxT( "Show cursor" ) ); - } + if( trace->HasCursor() ) + Append( HIDE_CURSOR, wxT( "Hide cursor" ) ); else - { - Append( SHOW_SIGNAL, wxT( "Show signal" ) ); - } + Append( SHOW_CURSOR, wxT( "Show cursor" ) ); Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ), NULL, this ); } @@ -729,11 +785,6 @@ void SIM_PLOT_FRAME::SIGNAL_CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent ) switch( aEvent.GetId() ) { - case SHOW_SIGNAL: - m_plotFrame->AddVoltagePlot( m_signal ); - break; - - break; case HIDE_SIGNAL: plot->DeleteTrace( m_signal ); break; diff --git a/eeschema/sim/sim_plot_frame.h b/eeschema/sim/sim_plot_frame.h index 899a5d5bbe..7a2786ae92 100644 --- a/eeschema/sim/sim_plot_frame.h +++ b/eeschema/sim/sim_plot_frame.h @@ -49,6 +49,64 @@ class NETLIST_EXPORTER_PSPICE_SIM; class SIM_PLOT_PANEL; class TUNER_SLIDER; +/// @todo description +class TRACE_DESC +{ +public: + TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName, + SIM_PLOT_TYPE aType, const wxString& aParam ); + + ///> Modifies an existing TRACE_DESC simulation type + TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, + const TRACE_DESC& aDescription, SIM_PLOT_TYPE aNewType ) + : TRACE_DESC( aExporter, aDescription.GetName(), aNewType, aDescription.GetParam() ) + { + } + + const wxString& GetTitle() const + { + return m_title; + } + + const wxString& GetName() const + { + return m_name; + } + + const wxString& GetParam() const + { + return m_param; + } + + SIM_PLOT_TYPE GetType() const + { + return m_type; + } + + const wxString& GetSpiceVector() const + { + return m_spiceVector; + } + +private: + // Three basic parameters + ///> Name of the measured net/device + wxString m_name; + + ///> Type of the signal + SIM_PLOT_TYPE m_type; + + ///> Name of the signal parameter + wxString m_param; + + // Generated data + ///> Title displayed in the signal list/plot legend + wxString m_title; + + ///> Spice vector name + wxString m_spiceVector; +}; + /** Implementing SIM_PLOT_FRAME_BASE */ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE { @@ -70,26 +128,28 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE SIM_PLOT_PANEL* NewPlotPanel( SIM_TYPE aSimType ); void AddVoltagePlot( const wxString& aNetName ); + void AddCurrentPlot( const wxString& aDeviceName, const wxString& aParam ); void AddTuner( SCH_COMPONENT* aComponent ); - void RemoveTuner( TUNER_SLIDER* aTuner ); SIM_PLOT_PANEL* CurrentPlot() const; private: + void addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam ); + void updateNetlistExporter(); /** * @brief Updates plot in a particular SIM_PLOT_PANEL. If the panel does not contain * the plot, it will be added. - * @param aSpiceName is the plot name in the format accepted by the current simulator instance - * (for NGSPICE it is e.g. "V(1)"). - * @param aName is the name used in the legend. + * @param aName is the net/device name. + * @param aType is the plot type (@see SIM_PLOT_TYPES). * @param aPanel is the panel that should receive the update. * @return True if a plot was successfully added/updated. + * TODO update description */ - bool updatePlot( const wxString& aSpiceName, const wxString& aName, SIM_PLOT_PANEL* aPanel ); + bool updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* aPanel ); /** * @brief Updates the list of currently plotted signals. @@ -101,12 +161,7 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE */ void updateTuners(); - /** - * @brief Returns node number for a given net. - * @param aNetName is the net number. - * @return Corresponding net number or -1 if there is no such net. - */ - int getNodeNumber( const wxString& aNetName ); + SIM_PLOT_TYPE GetXAxisType( SIM_TYPE aType ) const; // Menu handlers void menuNewPlot( wxCommandEvent& aEvent ) override; @@ -151,7 +206,17 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE SCH_EDIT_FRAME* m_schematicFrame; std::unique_ptr m_exporter; std::unique_ptr m_simulator; - std::map > m_tuners; + + typedef std::map TRACE_MAP; + typedef std::list TUNER_LIST; + + struct PLOT_INFO + { + TUNER_LIST m_tuners; + TRACE_MAP m_traces; + }; + + std::map m_plots; // Trick to preserve settings between runs DIALOG_SIM_SETTINGS m_settingsDlg; @@ -170,12 +235,12 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE enum SIGNAL_CONTEXT_MENU_EVENTS { - SHOW_SIGNAL, HIDE_SIGNAL, SHOW_CURSOR, HIDE_CURSOR }; }; + }; // Commands diff --git a/eeschema/sim/sim_plot_panel.cpp b/eeschema/sim/sim_plot_panel.cpp index 2c9cecd8df..df41726190 100644 --- a/eeschema/sim/sim_plot_panel.cpp +++ b/eeschema/sim/sim_plot_panel.cpp @@ -237,44 +237,6 @@ void CURSOR::Plot( wxDC& aDC, mpWindow& aWindow ) } -TRACE_DESC::TRACE_DESC( const wxString& aDescription ) - : m_name( aDescription ) -{ - for( const auto& desc : m_descMap ) - { - if( m_name.EndsWith( desc.second ) ) - { - m_type = desc.first; - m_name.Replace( desc.second, "" ); - } - } -} - - -wxString TRACE_DESC::GetDescription() const -{ - wxString res( m_name ); - - for( const auto& desc : m_descMap ) - { - if( m_type == desc.first ) - { - res += desc.second; - break; - } - } - - return res; -} - - -const std::map TRACE_DESC::m_descMap = -{ - { SPF_AC_PHASE, wxT( " (phase)" ) }, - { SPF_AC_MAG, wxT( " (mag)" ) } -}; - - SIM_PLOT_PANEL::SIM_PLOT_PANEL( SIM_TYPE aType, wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : mpWindow( parent, id, pos, size, style ), m_colorIdx( 0 ), @@ -374,20 +336,13 @@ bool SIM_PLOT_PANEL::IsPlottable( SIM_TYPE aSimType ) } -bool SIM_PLOT_PANEL::AddTrace( const wxString& aSpiceName, const wxString& aName, int aPoints, - const double* aT, const double* aY, int aFlags ) +bool SIM_PLOT_PANEL::AddTrace( const wxString& aName, int aPoints, + const double* aX, const double* aY, SIM_PLOT_TYPE aFlags ) { TRACE* t = NULL; - wxString name( aName ); - - if( aFlags == SPF_AC_MAG ) - name += " (mag)"; - else if( aFlags == SPF_AC_PHASE ) - name += " (phase)"; - // Find previous entry, if there is one - auto prev = m_traces.find( TRACE_DESC( aName, (SIM_PLOT_FLAGS) aFlags ) ); + auto prev = m_traces.find( aName ); bool addedNewEntry = ( prev == m_traces.end() ); if( addedNewEntry ) @@ -396,18 +351,18 @@ bool SIM_PLOT_PANEL::AddTrace( const wxString& aSpiceName, const wxString& aName switch ( m_type ) { case ST_TRANSIENT: - t = new TRACE_TRANSIENT( name, aSpiceName ); + t = new TRACE_TRANSIENT( aName ); break; case ST_AC: //printf("makeFreqResp!\n"); - t = new TRACE_FREQ_RESPONSE( name, aSpiceName ); + t = new TRACE_FREQ_RESPONSE( aName ); break; default: assert(false); } t->SetPen( wxPen( generateColor(), 2, wxSOLID ) ); - m_traces[TRACE_DESC( aName, (SIM_PLOT_FLAGS) aFlags )] = t; + m_traces[aName] = t; // It is a trick to keep legend & coords always on the top for( mpLayer* l : m_topLevel ) @@ -427,7 +382,7 @@ bool SIM_PLOT_PANEL::AddTrace( const wxString& aSpiceName, const wxString& aName if( m_type == ST_AC ) { - if( aFlags & SPF_AC_PHASE ) + if( aFlags & SPT_AC_PHASE ) { for(int i = 0; i < aPoints; i++ ) tmp[i] = tmp[i] * 180.0 / M_PI; @@ -439,9 +394,9 @@ bool SIM_PLOT_PANEL::AddTrace( const wxString& aSpiceName, const wxString& aName } } - t->SetData( std::vector( aT, aT + aPoints ), tmp ); + t->SetData( std::vector( aX, aX + aPoints ), tmp ); - if( aFlags & SPF_AC_PHASE ) + if( aFlags & SPT_AC_PHASE ) t->SetScale( m_axis_x, m_axis_y2 ); else t->SetScale( m_axis_x, m_axis_y1 ); @@ -454,7 +409,7 @@ bool SIM_PLOT_PANEL::AddTrace( const wxString& aSpiceName, const wxString& aName bool SIM_PLOT_PANEL::DeleteTrace( const wxString& aName ) { - auto it = m_traces.find( TRACE_DESC( aName ) ); + auto it = m_traces.find( aName ); if( it != m_traces.end() ) { @@ -477,7 +432,7 @@ void SIM_PLOT_PANEL::DeleteAllTraces() { for( auto& t : m_traces ) { - DeleteTrace( t.first.GetDescription() ); + DeleteTrace( t.first ); } m_traces.clear(); diff --git a/eeschema/sim/sim_plot_panel.h b/eeschema/sim/sim_plot_panel.h index a4394f4c5c..27d1981cf2 100644 --- a/eeschema/sim/sim_plot_panel.h +++ b/eeschema/sim/sim_plot_panel.h @@ -90,8 +90,8 @@ private: class TRACE : public mpFXYVector { public: - TRACE( const wxString& aName, const wxString& aSpiceName ) : - mpFXYVector( aName ), m_spiceName( aSpiceName ), m_cursor( nullptr ) + TRACE( const wxString& aName ) : + mpFXYVector( aName ), m_cursor( nullptr ) { SetContinuity( true ); SetDrawOutsideMargins( false ); @@ -99,11 +99,6 @@ public: } - const wxString& GetSpiceName() const - { - return m_spiceName; - } - const std::vector& GetDataX() const { return m_xs; @@ -130,15 +125,14 @@ public: } protected: - wxString m_spiceName; CURSOR* m_cursor; }; class TRACE_FREQ_RESPONSE : public TRACE { public: - TRACE_FREQ_RESPONSE( const wxString& aName, const wxString& aSpiceName ) : - TRACE( aName, aSpiceName ) + TRACE_FREQ_RESPONSE( const wxString& aName ) : + TRACE( aName ) { printf("makeFreqResponse!\n"); } @@ -148,52 +142,13 @@ public: class TRACE_TRANSIENT : public TRACE { public: - TRACE_TRANSIENT( const wxString& aName, const wxString& aSpiceName ) : - TRACE( aName, aSpiceName ) + TRACE_TRANSIENT( const wxString& aName ) : + TRACE( aName ) { } }; -enum SIM_PLOT_FLAGS { - SPF_AC_PHASE = 0x01, - SPF_AC_MAG = 0x02 -}; - -class TRACE_DESC -{ -public: - TRACE_DESC( const wxString& aName, SIM_PLOT_FLAGS aType ) - : m_name( aName ), m_type( aType ) - { - } - - TRACE_DESC( const wxString& aDescription ); - - wxString GetDescription() const; - - const wxString& GetName() const - { - return m_name; - } - - SIM_PLOT_FLAGS GetType() const - { - return m_type; - } - - bool operator<( const TRACE_DESC& aOther ) const - { - return ( m_name < aOther.m_name ) || ( m_name == aOther.m_name && m_type < aOther.m_type ); - } - -private: - wxString m_name; - SIM_PLOT_FLAGS m_type; - - static const std::map m_descMap; -}; - class SIM_PLOT_PANEL : public mpWindow { public: @@ -224,8 +179,8 @@ public: return m_axis_y2 ? m_axis_y2->GetName() : ""; } - bool AddTrace( const wxString& aSpiceName, const wxString& aName, int aPoints, - const double* aT, const double* aY, int aFlags ); + bool AddTrace( const wxString& aName, int aPoints, + const double* aX, const double* aY, SIM_PLOT_TYPE aFlags ); bool DeleteTrace( const wxString& aName ); @@ -233,17 +188,17 @@ public: bool IsShown( const wxString& aName ) const { - return m_traces.count( TRACE_DESC( aName ) ) > 0; + return m_traces.count( aName ) > 0; } - const std::map& GetTraces() const + const std::map& GetTraces() const { return m_traces; } TRACE* GetTrace( const wxString& aName ) const { - auto trace = m_traces.find( TRACE_DESC( aName ) ); + auto trace = m_traces.find( aName ); return trace == m_traces.end() ? NULL : trace->second; } @@ -294,7 +249,7 @@ private: unsigned int m_colorIdx; // Traces to be plotted - std::map m_traces; + std::map m_traces; mpScaleXBase* m_axis_x; mpScaleY* m_axis_y1;