From 7d3fa8fb4ea3eb45b7e327cd09cd2aab8c35ca8c Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Mon, 3 Jul 2023 23:11:43 +0100 Subject: [PATCH] Plotting for noise simulations. Fixes https://gitlab.com/kicad/code/kicad/-/issues/2369 --- eeschema/dialogs/dialog_sim_command_base.cpp | 9 +- eeschema/dialogs/dialog_sim_command_base.fbp | 16 +--- eeschema/sim/ngspice.cpp | 4 +- eeschema/sim/sim_plot_panel.cpp | 23 ++++- eeschema/sim/sim_plot_panel_base.cpp | 1 + eeschema/sim/simulator_panel.cpp | 90 ++++++++++++++++---- eeschema/sim/simulator_panel.h | 2 + 7 files changed, 104 insertions(+), 41 deletions(-) diff --git a/eeschema/dialogs/dialog_sim_command_base.cpp b/eeschema/dialogs/dialog_sim_command_base.cpp index 8d77f5d170..df5894c496 100644 --- a/eeschema/dialogs/dialog_sim_command_base.cpp +++ b/eeschema/dialogs/dialog_sim_command_base.cpp @@ -264,17 +264,16 @@ DIALOG_SIM_COMMAND_BASE::DIALOG_SIM_COMMAND_BASE( wxWindow* parent, wxWindowID i int m_noiseScaleNChoices = sizeof( m_noiseScaleChoices ) / sizeof( wxString ); m_noiseScale = new wxRadioBox( m_pgNoise, wxID_ANY, _("Frequency scale"), wxDefaultPosition, wxDefaultSize, m_noiseScaleNChoices, m_noiseScaleChoices, 1, wxRA_SPECIFY_COLS ); m_noiseScale->SetSelection( 0 ); + m_noiseScale->Hide(); + bSizer10->Add( m_noiseScale, 0, wxALL, 5 ); - - bSizer10->Add( 30, 0, 0, wxEXPAND, 5 ); - wxFlexGridSizer* fgSizer11; fgSizer11 = new wxFlexGridSizer( 0, 3, 3, 0 ); fgSizer11->SetFlexibleDirection( wxBOTH ); fgSizer11->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - m_staticText11 = new wxStaticText( m_pgNoise, wxID_ANY, _("Number of points:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText11 = new wxStaticText( m_pgNoise, wxID_ANY, _("Number of points per decade:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText11->Wrap( -1 ); fgSizer11->Add( m_staticText11, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5 ); @@ -313,7 +312,7 @@ DIALOG_SIM_COMMAND_BASE::DIALOG_SIM_COMMAND_BASE( wxWindow* parent, wxWindowID i fgSizer11->Add( m_noiseFreqStopUnits, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - bSizer10->Add( fgSizer11, 0, wxALIGN_BOTTOM|wxBOTTOM|wxLEFT, 4 ); + bSizer10->Add( fgSizer11, 0, wxALIGN_BOTTOM|wxTOP|wxBOTTOM|wxLEFT, 5 ); bSizer15->Add( bSizer10, 0, wxEXPAND|wxTOP, 5 ); diff --git a/eeschema/dialogs/dialog_sim_command_base.fbp b/eeschema/dialogs/dialog_sim_command_base.fbp index 11f6b9a328..b143e9312b 100644 --- a/eeschema/dialogs/dialog_sim_command_base.fbp +++ b/eeschema/dialogs/dialog_sim_command_base.fbp @@ -3076,7 +3076,7 @@ 1 0 - 0 + 1 wxID_ANY Frequency scale 1 @@ -3113,17 +3113,7 @@ 5 - wxEXPAND - 0 - - 0 - protected - 30 - - - - 4 - wxALIGN_BOTTOM|wxBOTTOM|wxLEFT + wxALIGN_BOTTOM|wxTOP|wxBOTTOM|wxLEFT 0 3 @@ -3169,7 +3159,7 @@ 0 0 wxID_ANY - Number of points: + Number of points per decade: 0 0 diff --git a/eeschema/sim/ngspice.cpp b/eeschema/sim/ngspice.cpp index 3aa2ee8756..918df09b82 100644 --- a/eeschema/sim/ngspice.cpp +++ b/eeschema/sim/ngspice.cpp @@ -329,9 +329,11 @@ wxString NGSPICE::GetXAxis( SIM_TYPE aType ) const switch( aType ) { case ST_AC: - case ST_NOISE: return wxS( "frequency" ); + case ST_NOISE: + return wxS( "noise1.frequency" ); + case ST_DC: // find plot, which ends with "-sweep" for( wxString plot : AllPlots() ) diff --git a/eeschema/sim/sim_plot_panel.cpp b/eeschema/sim/sim_plot_panel.cpp index 0165ca4448..dd0a322175 100644 --- a/eeschema/sim/sim_plot_panel.cpp +++ b/eeschema/sim/sim_plot_panel.cpp @@ -528,13 +528,28 @@ void SIM_PLOT_PANEL::updateAxes( int aNewTraceType ) 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 ); + if( ( aNewTraceType & SPT_CURRENT ) == 0 ) + { + m_axis_y1 = new LIN_SCALE( wxEmptyString, wxT( "" ), mpALIGN_LEFT ); + m_axis_y1->SetNameAlign( mpALIGN_LEFT ); + m_plotWin->AddLayer( m_axis_y1 ); + } + else + { + m_axis_y2 = new LIN_SCALE( wxEmptyString, wxT( "" ), mpALIGN_RIGHT ); + m_axis_y2->SetNameAlign( mpALIGN_RIGHT ); + m_plotWin->AddLayer( m_axis_y2 ); + } } m_axis_x->SetName( _( "Frequency" ) ); - m_axis_y1->SetName( _( "noise [(V or A)^2/Hz]" ) ); + + if( m_axis_y1 ) + m_axis_y1->SetName( _( "Noise (V/√Hz)" ) ); + + if( m_axis_y2 ) + m_axis_y2->SetName( _( "Noise (A/√Hz)" ) ); + break; case ST_TRANSIENT: diff --git a/eeschema/sim/sim_plot_panel_base.cpp b/eeschema/sim/sim_plot_panel_base.cpp index c69842f6d0..b8c05e1504 100644 --- a/eeschema/sim/sim_plot_panel_base.cpp +++ b/eeschema/sim/sim_plot_panel_base.cpp @@ -59,6 +59,7 @@ bool SIM_PLOT_PANEL_BASE::IsPlottable( SIM_TYPE aSimType ) case ST_AC: case ST_DC: case ST_TRANSIENT: + case ST_NOISE: return true; default: diff --git a/eeschema/sim/simulator_panel.cpp b/eeschema/sim/simulator_panel.cpp index 9fd7a4b9ff..d14fc09b75 100644 --- a/eeschema/sim/simulator_panel.cpp +++ b/eeschema/sim/simulator_panel.cpp @@ -699,7 +699,8 @@ void SIMULATOR_PANEL::rebuildSignalsList() } }; - if( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES ) + if( ( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES ) + && ( simType == ST_TRANSIENT || simType == ST_DC || simType == ST_AC ) ) { for( const std::string& net : circuitModel()->GetNets() ) { @@ -738,6 +739,12 @@ void SIMULATOR_PANEL::rebuildSignalsList() } } + if( simType == ST_NOISE ) + { + addSignal( wxS( "inoise" ) ); + addSignal( wxS( "onoise" ) ); + } + // Add .PROBE directives for( const wxString& directive : circuitModel()->GetDirectives() ) { @@ -840,14 +847,26 @@ wxString SIMULATOR_PANEL::vectorNameFromSignalName( const wxString& aSignalName, if( aTraceType ) { - wxUniChar firstChar = aSignalName.Upper()[0]; + wxString name = aSignalName.Upper(); - if( firstChar == 'V' ) - *aTraceType = SPT_VOLTAGE; - else if( firstChar == 'I' ) - *aTraceType = SPT_CURRENT; - else if( firstChar == 'P' ) - *aTraceType = SPT_POWER; + if( name == wxS( "INOISE" ) || name == wxS( "ONOISE" ) ) + { + if( getNoiseSource().Upper().StartsWith( 'I' ) ) + *aTraceType = SPT_CURRENT; + else + *aTraceType = SPT_VOLTAGE; + } + else if( !name.IsEmpty() ) + { + wxUniChar firstChar = name[0]; + + if( firstChar == 'V' ) + *aTraceType = SPT_VOLTAGE; + else if( firstChar == 'I' ) + *aTraceType = SPT_CURRENT; + else if( firstChar == 'P' ) + *aTraceType = SPT_POWER; + } } wxString suffix; @@ -1432,6 +1451,10 @@ void SIMULATOR_PANEL::updateTrace( const wxString& aVectorName, int aTraceType, break; case ST_NOISE: + simVectorName = wxString::Format( wxS( "noise1.%s_spectrum" ), simVectorName ); + data_y = simulator()->GetMagPlot( (const char*) simVectorName.c_str() ); + break; + case ST_DC: case ST_TRANSIENT: data_y = simulator()->GetMagPlot( (const char*) simVectorName.c_str() ); @@ -2099,11 +2122,29 @@ SIM_TRACE_TYPE SIMULATOR_PANEL::getXAxisType( SIM_TYPE aType ) const case ST_AC: return SPT_LIN_FREQUENCY; case ST_DC: return SPT_SWEEP; case ST_TRANSIENT: return SPT_TIME; + case ST_NOISE: return SPT_LIN_FREQUENCY; default: wxFAIL_MSG( wxS( "Unhandled simulation type" ) ); return SPT_UNKNOWN; } } +wxString SIMULATOR_PANEL::getNoiseSource() const +{ + wxString output; + wxString ref; + wxString source; + wxString scale; + SPICE_VALUE pts; + SPICE_VALUE fStart; + SPICE_VALUE fStop; + + circuitModel()->ParseNoiseCommand( circuitModel()->GetSimCommand(), &output, &ref, &source, + &scale, &pts, &fStart, &fStop ); + + return source; +} + + void SIMULATOR_PANEL::ToggleDarkModePlots() { m_darkMode = !m_darkMode; @@ -2359,6 +2400,7 @@ void SIMULATOR_PANEL::OnSimRefresh( bool aFinal ) } std::vector oldSignals = m_signals; + wxString msg; applyUserDefinedSignals(); rebuildSignalsList(); @@ -2366,6 +2408,23 @@ void SIMULATOR_PANEL::OnSimRefresh( bool aFinal ) // If there are any signals plotted, update them if( SIM_PLOT_PANEL_BASE::IsPlottable( simType ) ) { + if( simType == ST_NOISE && aFinal ) + { + m_simConsole->AppendText( _( "\n\nSimulation results:\n\n" ) ); + m_simConsole->SetInsertionPointEnd(); + + for( const std::string& vec : simulator()->AllPlots() ) + { + std::vector val_list = simulator()->GetRealPlot( vec, 1 ); + wxString value = SPICE_VALUE( val_list[ 0 ] ).ToSpiceString(); + + msg.Printf( wxS( "%s: %sV\n" ), vec, value ); + + m_simConsole->AppendText( msg ); + m_simConsole->SetInsertionPointEnd(); + } + } + SIM_PLOT_PANEL* plotPanel = dynamic_cast( plotPanelWindow ); wxCHECK_RET( plotPanel, wxT( "not a SIM_PLOT_PANEL" ) ); @@ -2427,17 +2486,12 @@ void SIMULATOR_PANEL::OnSimRefresh( bool aFinal ) for( const std::string& vec : simulator()->AllPlots() ) { std::vector val_list = simulator()->GetRealPlot( vec, 1 ); + wxString value = SPICE_VALUE( val_list[ 0 ] ).ToSpiceString(); + wxString signal; + SIM_TRACE_TYPE type = circuitModel()->VectorToSignal( vec, signal ); - if( val_list.size() == 0 ) // The list of values can be empty! - continue; - - wxString value = SPICE_VALUE( val_list.at( 0 ) ).ToSpiceString(); - wxString msg; - wxString signal; - SIM_TRACE_TYPE type = circuitModel()->VectorToSignal( vec, signal ); - - const size_t tab = 25; //characters - size_t padding = ( signal.length() < tab ) ? ( tab - signal.length() ) : 1; + const size_t tab = 25; //characters + size_t padding = ( signal.length() < tab ) ? ( tab - signal.length() ) : 1; value.Append( type == SPT_CURRENT ? wxS( "A" ) : wxS( "V" ) ); diff --git a/eeschema/sim/simulator_panel.h b/eeschema/sim/simulator_panel.h index 546ffeeb87..301bdccc22 100644 --- a/eeschema/sim/simulator_panel.h +++ b/eeschema/sim/simulator_panel.h @@ -283,6 +283,8 @@ private: */ SIM_TRACE_TYPE getXAxisType( SIM_TYPE aType ) const; + wxString getNoiseSource() const; + void parseTraceParams( SIM_PLOT_PANEL* aPlotPanel, TRACE* aTrace, const wxString& aSignalName, const wxString& aParams );