New type of simulation opens a new plot

This commit is contained in:
Maciej Suminski 2016-08-11 14:41:35 +02:00
parent fe92630f16
commit e90fcaa6d9
4 changed files with 203 additions and 37 deletions

View File

@ -83,8 +83,6 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
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 );
NewPlotPanel();
}
@ -121,9 +119,9 @@ void SIM_PLOT_FRAME::StopSimulation()
}
void SIM_PLOT_FRAME::NewPlotPanel()
void SIM_PLOT_FRAME::NewPlotPanel( SIM_TYPE aSimType )
{
SIM_PLOT_PANEL* plot = new SIM_PLOT_PANEL( m_plotNotebook, wxID_ANY );
SIM_PLOT_PANEL* plot = new SIM_PLOT_PANEL( aSimType, m_plotNotebook, wxID_ANY );
m_plotNotebook->AddPage( plot,
wxString::Format( wxT( "Plot%lu" ), m_plotNotebook->GetPageCount() + 1 ), true );
@ -140,6 +138,7 @@ void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName )
}
}
SIM_PLOT_PANEL* SIM_PLOT_FRAME::CurrentPlot() const
{
return static_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetCurrentPage() );
@ -161,13 +160,71 @@ void SIM_PLOT_FRAME::updateNetlistExporter()
void SIM_PLOT_FRAME::updatePlot( const wxString& aSpiceName, const wxString& aName, SIM_PLOT_PANEL* aPanel )
{
auto data_y = m_simulator->GetPlot( (const char*) aSpiceName.c_str() );
auto data_t = m_simulator->GetPlot( "time" );
// First, handle the x axis
wxString xAxisName;
SIM_TYPE simType = m_exporter->GetSimType();
if( data_y.empty() || data_t.empty() )
if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
{
// There is no plot to be shown
m_simulator->Command( wxString::Format( "print %s", aSpiceName ).ToStdString() );
return;
}
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;
case ST_DC:
xAxisName = "v-sweep";
break;
case ST_TRANSIENT:
xAxisName = "time";
break;
case ST_OP:
break;
default:
break;
}
auto data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() );
int size = data_x.size();
if( data_x.empty() )
return;
aPanel->AddTrace( aSpiceName, aName, data_t.size(), data_t.data(), data_y.data(), 0 );
// 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() );
aPanel->AddTrace( aSpiceName, aName + " (mag)", size, data_x.data(), data_mag.data(), 0 );
aPanel->AddTrace( aSpiceName, aName + " (phase)", size, data_x.data(), data_phase.data(), 0 );
}
break;
case ST_NOISE:
case ST_DC:
case ST_TRANSIENT:
{
auto data_y = m_simulator->GetMagPlot( (const char*) aSpiceName.c_str() );
aPanel->AddTrace( aSpiceName, aName, size, data_x.data(), data_y.data(), 0 );
}
break;
default:
return;
}
}
@ -186,10 +243,22 @@ int SIM_PLOT_FRAME::getNodeNumber( const wxString& aNetName )
}
void SIM_PLOT_FRAME::menuNewPlot( wxCommandEvent& aEvent )
{
if( m_exporter )
{
SIM_TYPE type = m_exporter->GetSimType();
if( SIM_PLOT_PANEL::IsPlottable( type ) )
NewPlotPanel( type );
}
}
void SIM_PLOT_FRAME::menuSaveImage( wxCommandEvent& event )
{
wxFileDialog saveDlg( this, wxT( "Save plot as image" ), "", "",
"PNG file (*.png)|*.png", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
"PNG file (*.png)|*.png", wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
if( saveDlg.ShowModal() == wxID_CANCEL )
return;
@ -203,7 +272,7 @@ void SIM_PLOT_FRAME::menuSaveCsv( wxCommandEvent& event )
const wxChar SEPARATOR = ';';
wxFileDialog saveDlg( this, wxT( "Save plot data" ), "", "",
"CSV file (*.csv)|*.csv", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
"CSV file (*.csv)|*.csv", wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
if( saveDlg.ShowModal() == wxID_CANCEL )
return;
@ -308,6 +377,14 @@ void SIM_PLOT_FRAME::onSignalDblClick( wxCommandEvent& event )
{
int idx = m_signals->GetSelection();
SIM_PLOT_PANEL* plot = CurrentPlot();
SIM_TYPE simType = m_exporter->GetSimType();
// Create a new plot if the current one displays a different type
if( SIM_PLOT_PANEL::IsPlottable( simType ) && ( plot == nullptr || plot->GetType() != simType ) )
{
NewPlotPanel( simType );
plot = CurrentPlot();
}
if( idx != wxNOT_FOUND )
{
@ -388,7 +465,7 @@ void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
const long SIGNAL_COL = m_cursors->AppendColumn( wxT( "Signal" ), wxLIST_FORMAT_LEFT, size.x / 2 );
const long X_COL = m_cursors->AppendColumn( CurrentPlot()->GetLabelX(), wxLIST_FORMAT_LEFT, size.x / 4 );
const long Y_COL = m_cursors->AppendColumn( CurrentPlot()->GetLabelY(), wxLIST_FORMAT_LEFT, size.x / 4 );
const long Y_COL = m_cursors->AppendColumn( CurrentPlot()->GetLabelY1(), wxLIST_FORMAT_LEFT, size.x / 4 );
// Update cursor values
for( const auto& trace : CurrentPlot()->GetTraces() )
@ -416,6 +493,8 @@ void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
m_simulateBtn->SetLabel( wxT( "Simulate" ) );
SetCursor( wxCURSOR_ARROW );
SIM_TYPE simType = m_exporter->GetSimType();
// Fill the signals listbox
m_signals->Clear();
@ -426,12 +505,28 @@ void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
}
// If there are any signals plotted, update them
SIM_PLOT_PANEL* plotPanel = CurrentPlot();
if( SIM_PLOT_PANEL::IsPlottable( simType ) )
{
SIM_PLOT_PANEL* plotPanel = CurrentPlot();
for( const auto& trace : plotPanel->GetTraces() )
updatePlot( trace.second->GetSpiceName(), trace.second->GetName(), plotPanel );
if( plotPanel == nullptr )
return;
plotPanel->UpdateAll();
for( const auto& trace : plotPanel->GetTraces() )
updatePlot( trace.second->GetSpiceName(), trace.second->GetName(), plotPanel );
plotPanel->UpdateAll();
}
else
{
/// @todo do not make it hardcoded for ngspice
for( const auto& net : m_exporter->GetNetIndexMap() )
{
int node = net.second;
if( node > 0 )
m_simulator->Command( wxString::Format( "print v(%d)", node ).ToStdString() );
}
}
}

View File

@ -30,6 +30,7 @@
@file Subclass of SIM_PLOT_FRAME_BASE, which is generated by wxFormBuilder. */
#include "sim_plot_frame_base.h"
#include "sim_types.h"
#include "kiway_player.h"
#include <netlist_exporters/netlist_exporter_pspice.h>
#include <dialogs/dialog_sim_settings.h>
@ -56,7 +57,7 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE
void StartSimulation();
void StopSimulation();
void NewPlotPanel();
void NewPlotPanel( SIM_TYPE aSimType );
void AddVoltagePlot( const wxString& aNetName );
SIM_PLOT_PANEL* CurrentPlot() const;
@ -84,17 +85,13 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE
int getNodeNumber( const wxString& aNetName );
// Menu handlers
void menuNewPlot( wxCommandEvent& aEvent ) override
{
NewPlotPanel();
}
void menuNewPlot( wxCommandEvent& aEvent ) override;
void menuExit( wxCommandEvent& event ) override
{
Close();
}
// Event handlers
void menuSaveImage( wxCommandEvent& event ) override;
void menuSaveCsv( wxCommandEvent& event ) override;
void menuZoomIn( wxCommandEvent& event ) override;
@ -107,6 +104,7 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE
void menuShowCoords( wxCommandEvent& event ) override;
void menuShowCoordsUpdate( wxUpdateUIEvent& event ) override;
// Event handlers
void onPlotChanged( wxNotebookEvent& event ) override;
void onSignalDblClick( wxCommandEvent& event ) override;

View File

@ -95,17 +95,58 @@ void CURSOR::Plot( wxDC& aDC, mpWindow& aWindow )
}
SIM_PLOT_PANEL::SIM_PLOT_PANEL( wxWindow* parent, wxWindowID id, const wxPoint& pos,
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 )
: mpWindow( parent, id, pos, size, style ), m_colorIdx( 0 ),
m_axis_x( nullptr ), m_axis_y1( nullptr ), m_axis_y2( nullptr ), m_type( aType )
{
m_axis_x = new mpScaleX( wxT( "T [s]" ) );
m_axis_x->SetTicks( false );
AddLayer( m_axis_x );
SetMargins( 10, 10, 10, 10 );
m_axis_y = new mpScaleY( wxT( "U [V]" ) );
m_axis_y->SetTicks( false );
AddLayer( m_axis_y );
switch( m_type )
{
case ST_AC:
m_axis_x = new mpScaleX( wxT( "frequency [Hz]" ), mpALIGN_BORDER_BOTTOM );
m_axis_y1 = new mpScaleY( wxT( "magnitude [V]" ), mpALIGN_BORDER_LEFT );
m_axis_y2 = new mpScaleY( wxT( "phase [rad]" ), mpALIGN_BORDER_RIGHT );
break;
case ST_DC:
m_axis_x = new mpScaleX( wxT( "voltage [V]" ), mpALIGN_BORDER_BOTTOM );
m_axis_y1 = new mpScaleY( wxT( "voltage [V]" ), mpALIGN_BORDER_LEFT );
break;
case ST_NOISE:
m_axis_x = new mpScaleX( wxT( "frequency [Hz]" ), mpALIGN_BORDER_BOTTOM );
m_axis_y1 = new mpScaleY( wxT( "noise [(V or A)^2/Hz]" ), mpALIGN_BORDER_LEFT );
break;
case ST_TRANSIENT:
m_axis_x = new mpScaleX( wxT( "time [s]" ), mpALIGN_BORDER_BOTTOM );
m_axis_y1 = new mpScaleY( wxT( "voltage [V]" ), mpALIGN_BORDER_LEFT );
break;
default:
// suppress warnings
break;
}
if( m_axis_x )
{
m_axis_x->SetTicks( false );
AddLayer( m_axis_x );
}
if( m_axis_y1 )
{
m_axis_y1->SetTicks( false );
AddLayer( m_axis_y1 );
}
if( m_axis_y2 )
{
m_axis_y2->SetTicks( false );
AddLayer( m_axis_y2 );
}
m_coords = new mpInfoCoords( wxRect( 0, 0, 100, 40 ), wxWHITE_BRUSH );
AddLayer( m_coords );
@ -123,6 +164,21 @@ SIM_PLOT_PANEL::~SIM_PLOT_PANEL()
}
bool SIM_PLOT_PANEL::IsPlottable( SIM_TYPE aSimType )
{
switch( aSimType )
{
case ST_AC:
case ST_DC:
case ST_TRANSIENT:
return true;
default:
return false;
}
}
bool SIM_PLOT_PANEL::AddTrace( const wxString& aSpiceName, const wxString& aName, int aPoints,
const double* aT, const double* aY, int aFlags )
{

View File

@ -28,6 +28,7 @@
#include <widgets/mathplot.h>
#include <map>
#include "sim_types.h"
class TRACE;
@ -143,19 +144,31 @@ private:
class SIM_PLOT_PANEL : public mpWindow
{
public:
SIM_PLOT_PANEL( wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition,
SIM_PLOT_PANEL( SIM_TYPE aType, wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxPanelNameStr );
~SIM_PLOT_PANEL();
const wxString& GetLabelX() const
SIM_TYPE GetType() const
{
return m_axis_x->GetName();
return m_type;
}
const wxString& GetLabelY() const
static bool IsPlottable( SIM_TYPE aSimType );
wxString GetLabelX() const
{
return m_axis_y->GetName();
return m_axis_x ? m_axis_x->GetName() : "";
}
wxString GetLabelY1() const
{
return m_axis_y1 ? m_axis_y1->GetName() : "";
}
wxString GetLabelY2() const
{
return m_axis_y2 ? m_axis_y2->GetName() : "";
}
bool AddTrace( const wxString& aSpiceName, const wxString& aName, int aPoints,
@ -185,13 +198,14 @@ public:
void ShowGrid( bool aEnable )
{
m_axis_x->SetTicks( !aEnable );
m_axis_y->SetTicks( !aEnable );
m_axis_y1->SetTicks( !aEnable );
m_axis_y2->SetTicks( !aEnable );
UpdateAll();
}
bool IsGridShown() const
{
assert( m_axis_x->GetTicks() == m_axis_y->GetTicks() );
assert( m_axis_x->GetTicks() == m_axis_y1->GetTicks() );
return !m_axis_x->GetTicks();
}
@ -230,11 +244,14 @@ private:
std::map<wxString, TRACE*> m_traces;
mpScaleX* m_axis_x;
mpScaleY* m_axis_y;
mpScaleY* m_axis_y1;
mpScaleY* m_axis_y2;
mpInfoLegend* m_legend;
mpInfoCoords* m_coords;
std::vector<mpLayer*> m_topLevel;
const SIM_TYPE m_type;
};
wxDECLARE_EVENT( EVT_SIM_CURSOR_UPDATE, wxCommandEvent );