simulator: update dialog to handle R, I & TEMP DC sweeps

Fixes https://gitlab.com/kicad/code/kicad/-/issues/2370
Fixes https://gitlab.com/kicad/code/kicad/-/issues/6195
Fixes https://gitlab.com/kicad/code/kicad/-/issues/2386
This commit is contained in:
Sylwester Kocjan 2021-01-25 21:29:13 +00:00 committed by Seth Hillbrand
parent 96af236493
commit 6a48e21eb2
14 changed files with 1285 additions and 1230 deletions

View File

@ -28,6 +28,9 @@
#include <wx/tokenzr.h>
#include <vector>
#include <utility>
/// @todo ngspice offers more types of analysis,
//so there are a few tabs missing (e.g. pole-zero, distortion, sensitivity)
@ -63,6 +66,8 @@ DIALOG_SIM_SETTINGS::DIALOG_SIM_SETTINGS( wxWindow* aParent )
m_transFinal->SetValidator( m_spiceValidator );
m_transInitial->SetValidator( m_spiceEmptyValidator );
refreshUIControls();
// Hide pages that aren't fully implemented yet
// wxPanel::Hide() isn't enough on some platforms
m_simPages->RemovePage( m_simPages->FindPage( m_pgDistortion ) );
@ -76,6 +81,59 @@ DIALOG_SIM_SETTINGS::DIALOG_SIM_SETTINGS( wxWindow* aParent )
}
wxString DIALOG_SIM_SETTINGS::evaluateDCControls( wxChoice* aDcSource, wxTextCtrl* aDcStart,
wxTextCtrl* aDcStop, wxTextCtrl* aDcIncr )
{
wxString dcSource = aDcSource->GetString( aDcSource->GetSelection() );
wxWindow* ctrlWithError = nullptr;
if( dcSource.IsEmpty() )
{
DisplayError( this, _( "You need to select DC source" ) );
ctrlWithError = aDcSource;
}
/// @todo for some reason it does not trigger the assigned SPICE_VALIDATOR,
// hence try..catch below
else if( !aDcStart->Validate() )
ctrlWithError = aDcStart;
else if( !aDcStop->Validate() )
ctrlWithError = aDcStop;
else if( !aDcIncr->Validate() )
ctrlWithError = aDcIncr;
if( ctrlWithError )
{
ctrlWithError->SetFocus();
return wxEmptyString;
}
try
{
// pick device name from exporter when something different than temperature is selected
if( dcSource.Cmp( "TEMP" ) )
dcSource = m_exporter->GetSpiceDevice( dcSource );
return wxString::Format( "%s %s %s %s", dcSource,
SPICE_VALUE( aDcStart->GetValue() ).ToSpiceString(),
SPICE_VALUE( aDcStop->GetValue() ).ToSpiceString(),
SPICE_VALUE( aDcIncr->GetValue() ).ToSpiceString() );
}
catch( std::exception& e )
{
DisplayError( this, e.what() );
return wxEmptyString;
}
catch( const KI_PARAM_ERROR& e )
{
DisplayError( this, e.What() );
return wxEmptyString;
}
catch( ... )
{
return wxEmptyString;
}
}
bool DIALOG_SIM_SETTINGS::TransferDataFromWindow()
{
@ -101,101 +159,27 @@ bool DIALOG_SIM_SETTINGS::TransferDataFromWindow()
// DC transfer analysis
else if( page == m_pgDC )
{
// At least one source has to be enabled
if( !m_dcEnable1->IsChecked() && !m_dcEnable2->IsChecked() )
{
DisplayError( this, _( "You need to enable at least one source" ) );
return false;
}
wxString simCmd = wxString( ".dc " );
if( m_dcEnable1->IsChecked() )
{
if( empty( m_dcSource1 ) )
{
DisplayError( this, _( "You need to select DC source (sweep 1)" ) );
wxString src1 = evaluateDCControls( m_dcSource1, m_dcStart1, m_dcStop1, m_dcIncr1 );
if( src1.IsEmpty() )
return false;
}
/// @todo for some reason it does not trigger the assigned SPICE_VALIDATOR,
// hence try..catch below
if( !m_dcStart1->Validate() || !m_dcStop1->Validate() || !m_dcIncr1->Validate() )
return false;
try
{
wxString dcSource = m_exporter->GetSpiceDevice( m_dcSource1->GetValue() );
simCmd += wxString::Format( "%s %s %s %s",
dcSource,
SPICE_VALUE( m_dcStart1->GetValue() ).ToSpiceString(),
SPICE_VALUE( m_dcStop1->GetValue() ).ToSpiceString(),
SPICE_VALUE( m_dcIncr1->GetValue() ).ToSpiceString() );
}
catch( std::exception& e )
{
DisplayError( this, e.what() );
return false;
}
catch( const KI_PARAM_ERROR& e )
{
DisplayError( this, e.What() );
return false;
}
catch( ... )
{
return false;
}
}
else
simCmd += src1;
if( m_dcEnable2->IsChecked() )
{
if( empty( m_dcSource2 ) )
{
DisplayError( this, _( "You need to select DC source (sweep 2)" ) );
wxString src2 = evaluateDCControls( m_dcSource2, m_dcStart2, m_dcStop2, m_dcIncr2 );
if( src2.IsEmpty() )
return false;
}
else
simCmd += " " + src2;
if( m_dcEnable1->IsChecked() && m_dcSource1->GetValue() == m_dcSource2->GetValue() )
if( m_dcSource1->GetSelection() == m_dcSource2->GetSelection() )
{
DisplayError( this, _( "Source 1 and Source 2 must be different" ) );
return false;
}
/// @todo for some reason it does not trigger the assigned SPICE_VALIDATOR,
// hence try..catch below
if( !m_dcStart2->Validate() || !m_dcStop2->Validate() || !m_dcIncr2->Validate() )
return false;
try
{
wxString dcSource = m_exporter->GetSpiceDevice( m_dcSource2->GetValue() );
if( m_dcEnable1->IsChecked() )
simCmd += " ";
simCmd += wxString::Format( "%s %s %s %s",
dcSource,
SPICE_VALUE( m_dcStart2->GetValue() ).ToSpiceString(),
SPICE_VALUE( m_dcStop2->GetValue() ).ToSpiceString(),
SPICE_VALUE( m_dcIncr2->GetValue() ).ToSpiceString() );
}
catch( std::exception& e )
{
DisplayError( this, e.what() );
return false;
}
catch( const KI_PARAM_ERROR& e )
{
DisplayError( this, e.What() );
return false;
}
catch( ... )
{
return false;
}
}
m_simCommand = simCmd;
@ -310,36 +294,52 @@ int DIALOG_SIM_SETTINGS::ShowModal()
c.first->SetSelection( idx );
}
return DIALOG_SIM_SETTINGS_BASE::ShowModal();
}
// Fill out comboboxes that allows one to select power sources
std::map<wxComboBox*, wxString> cmbSrc = {
{ m_dcSource1, m_dcSource1->GetStringSelection() },
{ m_dcSource2, m_dcSource2->GetStringSelection() },
{ m_noiseSrc, m_noiseSrc->GetStringSelection() },
};
for( auto c : cmbSrc )
c.first->Clear();
void DIALOG_SIM_SETTINGS::updateDCSources( wxChar aType, wxChoice* aSource )
{
wxString prevSelection = aSource->GetString( aSource->GetSelection() );
std::vector<wxString> sourcesList;
bool enableSrcSelection = true;
if( aType != 'T' )
{
for( const auto& item : m_exporter->GetSpiceItems() )
{
if( item.m_primitive == 'V' )
if( item.m_primitive == aType )
{
for( auto c : cmbSrc )
c.first->Append( item.m_refName );
sourcesList.push_back( item.m_refName );
}
}
std::sort( sourcesList.begin(), sourcesList.end(),
[](wxString& a, wxString& b) -> bool
{
return a.Len() < b.Len() || b.Cmp( a ) > 0;
} );
if( aSource == m_dcSource2 && !m_dcEnable2->IsChecked() )
enableSrcSelection = false;
}
else
{
prevSelection = wxT( "TEMP" );
sourcesList.push_back( prevSelection );
enableSrcSelection = false;
}
aSource->Enable( enableSrcSelection );
aSource->Clear();
for( auto& src : sourcesList )
aSource->Append( src );
// Try to restore the previous selection, if possible
for( auto c : cmbSrc )
{
int idx = c.first->FindString( c.second );
int idx = aSource->FindString( prevSelection );
if( idx != wxNOT_FOUND )
c.first->SetSelection( idx );
}
return DIALOG_SIM_SETTINGS_BASE::ShowModal();
aSource->SetSelection( idx );
}
@ -375,39 +375,43 @@ bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand )
else if( tkn == ".dc" )
{
SPICE_DC_PARAMS src1, src2;
src2.m_vincrement = SPICE_VALUE( -1 );
if( !m_exporter->ParseDCCommand( aCommand, &src1, &src2 ) )
return false;
m_simPages->SetSelection( m_simPages->FindPage( m_pgDC ) );
tkn = tokenizer.GetNextToken();
if( !src1.m_source.IsSameAs( wxT( "TEMP" ), false ) == 0 )
m_dcSourceType1->SetSelection( m_dcSourceType1->FindString( src1.m_source ) );
else
m_dcSourceType1->SetSelection(
m_dcSourceType1->FindString( src1.m_source.GetChar( 0 ) ) );
if( !tkn.IsEmpty() )
updateDCSources( src1.m_source.GetChar( 0 ), m_dcSource1 );
m_dcSource1->SetSelection( m_dcSource1->FindString( src1.m_source ) );
m_dcStart1->SetValue( src1.m_vstart.ToSpiceString() );
m_dcStop1->SetValue( src1.m_vend.ToSpiceString() );
m_dcIncr1->SetValue( src1.m_vincrement.ToSpiceString() );
if( src2.m_vincrement.ToDouble() != -1 )
{
m_dcSource1->SetValue( tkn );
m_dcStart1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
m_dcStop1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
m_dcIncr1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
if( !src2.m_source.IsSameAs( wxT( "TEMP" ), false ) == 0 )
m_dcSourceType2->SetSelection( m_dcSourceType2->FindString( src2.m_source ) );
else
m_dcSourceType2->SetSelection(
m_dcSourceType2->FindString( src2.m_source.GetChar( 0 ) ) );
// Check the 'Enabled' field, if all values are filled
m_dcEnable1->SetValue( !empty( m_dcSource1 ) && !empty( m_dcStart1 )
&& !empty( m_dcStop1 ) && !empty( m_dcIncr1 ) );
updateDCSources( src2.m_source.GetChar( 0 ), m_dcSource2 );
m_dcSource2->SetSelection( m_dcSource2->FindString( src2.m_source ) );
m_dcStart2->SetValue( src2.m_vstart.ToSpiceString() );
m_dcStop2->SetValue( src2.m_vend.ToSpiceString() );
m_dcIncr2->SetValue( src2.m_vincrement.ToSpiceString() );
m_dcEnable2->SetValue( true );
}
tkn = tokenizer.GetNextToken();
if( !tkn.IsEmpty() )
{
m_dcSource2->SetValue( tkn );
m_dcStart2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
m_dcStop2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
m_dcIncr2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
// Check the 'Enabled' field, if all values are filled
m_dcEnable2->SetValue( !empty( m_dcSource2 ) && !empty( m_dcStart2 )
&& !empty( m_dcStop2 ) && !empty( m_dcIncr2 ) );
}
// Check if the directive is complete
if( !m_dcEnable1->IsChecked() || !m_dcEnable2->IsChecked() )
return false;
refreshUIControls();
}
else if( tkn == ".tran" )
@ -425,6 +429,11 @@ bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand )
m_transInitial->SetValue( SPICE_VALUE( tkn ).ToSpiceString() );
}
else if( tkn == ".op" )
{
m_simPages->SetSelection( m_simPages->FindPage( m_pgOP ) );
}
// Custom directives
else if( !empty( m_customTxt ) )
{
@ -441,6 +450,77 @@ bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand )
}
void DIALOG_SIM_SETTINGS::onSwapDCSources( wxCommandEvent& event )
{
std::vector<std::pair<wxTextEntry*, wxTextEntry*>> textCtrl = { { m_dcStart1, m_dcStart2 },
{ m_dcStop1, m_dcStop2 },
{ m_dcIncr1, m_dcIncr2 } };
for( auto& couple : textCtrl )
{
wxString tmp = couple.first->GetValue();
couple.first->SetValue( couple.second->GetValue() );
couple.second->SetValue( tmp );
}
int src1 = m_dcSource1->GetSelection();
int src2 = m_dcSource2->GetSelection();
int sel = m_dcSourceType1->GetSelection();
m_dcSourceType1->SetSelection( m_dcSourceType2->GetSelection() );
m_dcSourceType2->SetSelection( sel );
wxChar type1 =
m_dcSourceType1->GetString( m_dcSourceType1->GetSelection() ).Upper().GetChar( 0 );
updateDCSources( type1, m_dcSource1 );
wxChar type2 =
m_dcSourceType2->GetString( m_dcSourceType2->GetSelection() ).Upper().GetChar( 0 );
updateDCSources( type2, m_dcSource2 );
m_dcSource1->SetSelection( src2 );
m_dcSource2->SetSelection( src1 );
updateDCUnits( type1, m_dcSource1, m_src1DCStartValUnit, m_src1DCEndValUnit, m_src1DCStepUnit );
updateDCUnits( type2, m_dcSource2, m_src2DCStartValUnit, m_src2DCEndValUnit, m_src2DCStepUnit );
}
void DIALOG_SIM_SETTINGS::onDCEnableSecondSource( wxCommandEvent& event )
{
bool is2ndSrcEnabled = m_dcEnable2->IsChecked();
wxChar type =
m_dcSourceType2->GetString( m_dcSourceType2->GetSelection() ).Upper().GetChar( 0 );
m_dcSourceType2->Enable( is2ndSrcEnabled );
m_dcSource2->Enable( is2ndSrcEnabled && type != 'T' );
m_dcStart2->Enable( is2ndSrcEnabled );
m_dcStop2->Enable( is2ndSrcEnabled );
m_dcIncr2->Enable( is2ndSrcEnabled );
}
void DIALOG_SIM_SETTINGS::updateDCUnits( wxChar aType, wxChoice* aSource,
wxStaticText* aStartValUnit, wxStaticText* aEndValUnit,
wxStaticText* aStepUnit )
{
wxString unit;
switch( aType )
{
case 'V': unit = _( "Volts" ); break;
case 'I': unit = _( "Amperes" ); break;
case 'R': unit = _( "Ohms" ); break;
case 'T': unit = wxT( "\u00B0C" ); break;
}
aStartValUnit->SetLabel( unit );
aEndValUnit->SetLabel( unit );
aStepUnit->SetLabel( unit );
m_pgDC->Fit();
m_pgDC->Refresh();
}
void DIALOG_SIM_SETTINGS::loadDirectives()
{
if( m_exporter )

View File

@ -84,6 +84,32 @@ private:
LINEAR
};
///!> Generates events to update UI state
void refreshUIControls()
{
wxQueueEvent( m_dcEnable2, new wxCommandEvent( wxEVT_CHECKBOX ) );
wxQueueEvent( m_dcSourceType1, new wxCommandEvent( wxEVT_RADIOBOX ) );
wxQueueEvent( m_dcSourceType2, new wxCommandEvent( wxEVT_RADIOBOX ) );
}
/**
* @brief Reads values from one DC sweep source to form a part of sim command
* @return string of four SPICE values if values are correct, empty string upon error.
*/
wxString evaluateDCControls( wxChoice* aDcSource, wxTextCtrl* aDcStart, wxTextCtrl* aDcStop,
wxTextCtrl* aDcIncr );
/**
* @brief Updates DC sweep source with components from schematic
*/
void updateDCSources( wxChar aType, wxChoice* aSource );
/**
* @brief Updates units on labels depending on selected source
*/
void updateDCUnits( wxChar aType, wxChoice* aSource, wxStaticText* aStartValUnit,
wxStaticText* aEndValUnit, wxStaticText* aStepUnit );
virtual void onInitDlg( wxInitDialogEvent& event ) override
{
// Call the default wxDialog handler of a wxInitDialogEvent
@ -105,6 +131,26 @@ private:
loadDirectives();
}
void onDCEnableSecondSource( wxCommandEvent& event ) override;
void onSwapDCSources( wxCommandEvent& event ) override;
void onDCSource1Selected( wxCommandEvent& event ) override
{
wxChar type =
m_dcSourceType1->GetString( m_dcSourceType1->GetSelection() ).Upper().GetChar( 0 );
updateDCSources( type, m_dcSource1 );
updateDCUnits( type, m_dcSource1, m_src1DCStartValUnit, m_src1DCEndValUnit,
m_src1DCStepUnit );
}
void onDCSource2Selected( wxCommandEvent& event ) override
{
wxChar type =
m_dcSourceType2->GetString( m_dcSourceType2->GetSelection() ).Upper().GetChar( 0 );
updateDCSources( type, m_dcSource2 );
updateDCUnits( type, m_dcSource2, m_src2DCStartValUnit, m_src2DCEndValUnit,
m_src2DCStepUnit );
}
static wxString scaleToString( int aOption )
{
switch( aOption )

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.9.0 Jun 18 2020)
// C++ code generated with wxFormBuilder (version 3.9.0 Sep 3 2020)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -86,138 +86,127 @@ DIALOG_SIM_SETTINGS_BASE::DIALOG_SIM_SETTINGS_BASE( wxWindow* parent, wxWindowID
bSizer3->Fit( m_pgAC );
m_simPages->AddPage( m_pgAC, _("AC"), false );
m_pgDC = new wxPanel( m_simPages, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer4;
bSizer4 = new wxBoxSizer( wxVERTICAL );
wxStaticBoxSizer* sbSizer21;
sbSizer21 = new wxStaticBoxSizer( new wxStaticBox( m_pgDC, wxID_ANY, _("DC Sweep Source 1") ), wxVERTICAL );
m_dcEnable1 = new wxCheckBox( sbSizer21->GetStaticBox(), wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, 0 );
m_dcEnable1->SetValue(true);
sbSizer21->Add( m_dcEnable1, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
wxFlexGridSizer* fgSizer21;
fgSizer21 = new wxFlexGridSizer( 0, 3, 0, 0 );
fgSizer21->SetFlexibleDirection( wxBOTH );
fgSizer21->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText41 = new wxStaticText( sbSizer21->GetStaticBox(), wxID_ANY, _("DC source:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText41->Wrap( -1 );
fgSizer21->Add( m_staticText41, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_dcSource1 = new wxComboBox( sbSizer21->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
fgSizer21->Add( m_dcSource1, 0, wxALL, 5 );
wxBoxSizer* bSizer82;
bSizer82 = new wxBoxSizer( wxVERTICAL );
fgSizer21->Add( 0, 0, 1, wxEXPAND, 5 );
bSizer82->Add( 0, 0, 1, wxEXPAND, 5 );
m_staticText51 = new wxStaticText( sbSizer21->GetStaticBox(), wxID_ANY, _("Starting voltage:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText51->Wrap( -1 );
fgSizer21->Add( m_staticText51, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_dcStart1 = new wxTextCtrl( sbSizer21->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer21->Add( m_dcStart1, 0, wxALL, 5 );
m_staticText511 = new wxStaticText( sbSizer21->GetStaticBox(), wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText511->Wrap( -1 );
fgSizer21->Add( m_staticText511, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT, 5 );
m_staticText61 = new wxStaticText( sbSizer21->GetStaticBox(), wxID_ANY, _("Final voltage:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText61->Wrap( -1 );
fgSizer21->Add( m_staticText61, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_dcStop1 = new wxTextCtrl( sbSizer21->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer21->Add( m_dcStop1, 0, wxALL, 5 );
m_staticText512 = new wxStaticText( sbSizer21->GetStaticBox(), wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText512->Wrap( -1 );
fgSizer21->Add( m_staticText512, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT, 5 );
m_staticText71 = new wxStaticText( sbSizer21->GetStaticBox(), wxID_ANY, _("Increment step:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText71->Wrap( -1 );
fgSizer21->Add( m_staticText71, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_dcIncr1 = new wxTextCtrl( sbSizer21->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer21->Add( m_dcIncr1, 0, wxALL, 5 );
m_staticText513 = new wxStaticText( sbSizer21->GetStaticBox(), wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText513->Wrap( -1 );
fgSizer21->Add( m_staticText513, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT, 5 );
wxBoxSizer* bSizer151;
bSizer151 = new wxBoxSizer( wxHORIZONTAL );
sbSizer21->Add( fgSizer21, 1, wxALIGN_CENTER_HORIZONTAL, 5 );
bSizer151->Add( 0, 0, 1, wxEXPAND, 5 );
wxGridBagSizer* gbSizer1;
gbSizer1 = new wxGridBagSizer( 0, 0 );
gbSizer1->SetFlexibleDirection( wxBOTH );
gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
bSizer4->Add( sbSizer21, 0, wxEXPAND, 5 );
m_dcEnable2 = new wxCheckBox( m_pgDC, wxID_ANY, _("Enable second source"), wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( m_dcEnable2, wxGBPosition( 0, 3 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
wxStaticBoxSizer* sbSizer2;
sbSizer2 = new wxStaticBoxSizer( new wxStaticBox( m_pgDC, wxID_ANY, _("DC Sweep Source 2") ), wxVERTICAL );
wxString m_dcSourceType1Choices[] = { _("V"), _("I"), _("R"), _("TEMP") };
int m_dcSourceType1NChoices = sizeof( m_dcSourceType1Choices ) / sizeof( wxString );
m_dcSourceType1 = new wxRadioBox( m_pgDC, wxID_ANY, _("Type"), wxDefaultPosition, wxDefaultSize, m_dcSourceType1NChoices, m_dcSourceType1Choices, 4, wxRA_SPECIFY_COLS );
m_dcSourceType1->SetSelection( 0 );
gbSizer1->Add( m_dcSourceType1, wxGBPosition( 1, 1 ), wxGBSpan( 1, 2 ), wxALL, 5 );
m_dcEnable2 = new wxCheckBox( sbSizer2->GetStaticBox(), wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, 0 );
sbSizer2->Add( m_dcEnable2, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
wxString m_dcSourceType2Choices[] = { _("V"), _("I"), _("R"), _("TEMP") };
int m_dcSourceType2NChoices = sizeof( m_dcSourceType2Choices ) / sizeof( wxString );
m_dcSourceType2 = new wxRadioBox( m_pgDC, wxID_ANY, _("Type"), wxDefaultPosition, wxDefaultSize, m_dcSourceType2NChoices, m_dcSourceType2Choices, 4, wxRA_SPECIFY_COLS );
m_dcSourceType2->SetSelection( 0 );
gbSizer1->Add( m_dcSourceType2, wxGBPosition( 1, 3 ), wxGBSpan( 1, 2 ), wxALL, 5 );
wxFlexGridSizer* fgSizer2;
fgSizer2 = new wxFlexGridSizer( 0, 3, 0, 0 );
fgSizer2->SetFlexibleDirection( wxBOTH );
fgSizer2->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText4 = new wxStaticText( sbSizer2->GetStaticBox(), wxID_ANY, _("DC source:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText4 = new wxStaticText( m_pgDC, wxID_ANY, _("DC source:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText4->Wrap( -1 );
fgSizer2->Add( m_staticText4, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
gbSizer1->Add( m_staticText4, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_dcSource2 = new wxComboBox( sbSizer2->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
fgSizer2->Add( m_dcSource2, 0, wxALL, 5 );
wxArrayString m_dcSource1Choices;
m_dcSource1 = new wxChoice( m_pgDC, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_dcSource1Choices, 0 );
m_dcSource1->SetSelection( 0 );
gbSizer1->Add( m_dcSource1, wxGBPosition( 2, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
wxArrayString m_dcSource2Choices;
m_dcSource2 = new wxChoice( m_pgDC, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_dcSource2Choices, 0 );
m_dcSource2->SetSelection( 0 );
gbSizer1->Add( m_dcSource2, wxGBPosition( 2, 3 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
fgSizer2->Add( 0, 0, 1, wxEXPAND, 5 );
m_staticText5 = new wxStaticText( sbSizer2->GetStaticBox(), wxID_ANY, _("Starting voltage:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText5 = new wxStaticText( m_pgDC, wxID_ANY, _("Starting value:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText5->Wrap( -1 );
fgSizer2->Add( m_staticText5, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
gbSizer1->Add( m_staticText5, wxGBPosition( 3, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_dcStart2 = new wxTextCtrl( sbSizer2->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer2->Add( m_dcStart2, 0, wxALL, 5 );
m_dcStart1 = new wxTextCtrl( m_pgDC, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( m_dcStart1, wxGBPosition( 3, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticText52 = new wxStaticText( sbSizer2->GetStaticBox(), wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText52->Wrap( -1 );
fgSizer2->Add( m_staticText52, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT, 5 );
m_src1DCStartValUnit = new wxStaticText( m_pgDC, wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_src1DCStartValUnit->Wrap( -1 );
gbSizer1->Add( m_src1DCStartValUnit, wxGBPosition( 3, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticText6 = new wxStaticText( sbSizer2->GetStaticBox(), wxID_ANY, _("Final voltage:"), wxDefaultPosition, wxDefaultSize, 0 );
m_dcStart2 = new wxTextCtrl( m_pgDC, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( m_dcStart2, wxGBPosition( 3, 3 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_src2DCStartValUnit = new wxStaticText( m_pgDC, wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_src2DCStartValUnit->Wrap( -1 );
gbSizer1->Add( m_src2DCStartValUnit, wxGBPosition( 3, 4 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticText6 = new wxStaticText( m_pgDC, wxID_ANY, _("Final value:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText6->Wrap( -1 );
fgSizer2->Add( m_staticText6, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
gbSizer1->Add( m_staticText6, wxGBPosition( 4, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_dcStop2 = new wxTextCtrl( sbSizer2->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer2->Add( m_dcStop2, 0, wxALL, 5 );
m_dcStop1 = new wxTextCtrl( m_pgDC, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( m_dcStop1, wxGBPosition( 4, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticText53 = new wxStaticText( sbSizer2->GetStaticBox(), wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText53->Wrap( -1 );
fgSizer2->Add( m_staticText53, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT, 5 );
m_src1DCEndValUnit = new wxStaticText( m_pgDC, wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_src1DCEndValUnit->Wrap( -1 );
gbSizer1->Add( m_src1DCEndValUnit, wxGBPosition( 4, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticText7 = new wxStaticText( sbSizer2->GetStaticBox(), wxID_ANY, _("Increment step:"), wxDefaultPosition, wxDefaultSize, 0 );
m_dcStop2 = new wxTextCtrl( m_pgDC, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( m_dcStop2, wxGBPosition( 4, 3 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_src2DCEndValUnit = new wxStaticText( m_pgDC, wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_src2DCEndValUnit->Wrap( -1 );
gbSizer1->Add( m_src2DCEndValUnit, wxGBPosition( 4, 4 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticText7 = new wxStaticText( m_pgDC, wxID_ANY, _("Increment step:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText7->Wrap( -1 );
fgSizer2->Add( m_staticText7, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
gbSizer1->Add( m_staticText7, wxGBPosition( 5, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_dcIncr2 = new wxTextCtrl( sbSizer2->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer2->Add( m_dcIncr2, 0, wxALL, 5 );
m_dcIncr1 = new wxTextCtrl( m_pgDC, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( m_dcIncr1, wxGBPosition( 5, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticText54 = new wxStaticText( sbSizer2->GetStaticBox(), wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText54->Wrap( -1 );
fgSizer2->Add( m_staticText54, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT, 5 );
m_src1DCStepUnit = new wxStaticText( m_pgDC, wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_src1DCStepUnit->Wrap( -1 );
gbSizer1->Add( m_src1DCStepUnit, wxGBPosition( 5, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_dcIncr2 = new wxTextCtrl( m_pgDC, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( m_dcIncr2, wxGBPosition( 5, 3 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_src2DCStepUnit = new wxStaticText( m_pgDC, wxID_ANY, _("Volts"), wxDefaultPosition, wxDefaultSize, 0 );
m_src2DCStepUnit->Wrap( -1 );
gbSizer1->Add( m_src2DCStepUnit, wxGBPosition( 5, 4 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_swapDCSources = new wxButton( m_pgDC, wxID_ANY, _("Swap sources"), wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( m_swapDCSources, wxGBPosition( 6, 0 ), wxGBSpan( 1, 5 ), wxALL|wxEXPAND, 5 );
sbSizer2->Add( fgSizer2, 1, wxALIGN_CENTER_HORIZONTAL, 5 );
bSizer151->Add( gbSizer1, 1, wxEXPAND, 5 );
bSizer4->Add( sbSizer2, 0, wxEXPAND, 5 );
bSizer151->Add( 0, 0, 1, wxEXPAND, 5 );
m_pgDC->SetSizer( bSizer4 );
bSizer82->Add( bSizer151, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
bSizer82->Add( 0, 0, 1, wxEXPAND, 5 );
m_pgDC->SetSizer( bSizer82 );
m_pgDC->Layout();
bSizer4->Fit( m_pgDC );
bSizer82->Fit( m_pgDC );
m_simPages->AddPage( m_pgDC, _("DC Transfer"), true );
m_pgDistortion = new wxPanel( m_simPages, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_pgDistortion->Hide();
m_simPages->AddPage( m_pgDistortion, _("Distortion"), false );
m_pgNoise = new wxPanel( m_simPages, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_pgNoise->Hide();
@ -468,6 +457,10 @@ DIALOG_SIM_SETTINGS_BASE::DIALOG_SIM_SETTINGS_BASE( wxWindow* parent, wxWindowID
// Connect Events
this->Connect( wxEVT_INIT_DIALOG, wxInitDialogEventHandler( DIALOG_SIM_SETTINGS_BASE::onInitDlg ) );
m_dcEnable2->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onDCEnableSecondSource ), NULL, this );
m_dcSourceType1->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onDCSource1Selected ), NULL, this );
m_dcSourceType2->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onDCSource2Selected ), NULL, this );
m_swapDCSources->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onSwapDCSources ), NULL, this );
m_loadDirectives->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onLoadDirectives ), NULL, this );
}
@ -475,6 +468,10 @@ DIALOG_SIM_SETTINGS_BASE::~DIALOG_SIM_SETTINGS_BASE()
{
// Disconnect Events
this->Disconnect( wxEVT_INIT_DIALOG, wxInitDialogEventHandler( DIALOG_SIM_SETTINGS_BASE::onInitDlg ) );
m_dcEnable2->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onDCEnableSecondSource ), NULL, this );
m_dcSourceType1->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onDCSource1Selected ), NULL, this );
m_dcSourceType2->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onDCSource2Selected ), NULL, this );
m_swapDCSources->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onSwapDCSources ), NULL, this );
m_loadDirectives->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SIM_SETTINGS_BASE::onLoadDirectives ), NULL, this );
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.9.0 Jun 18 2020)
// C++ code generated with wxFormBuilder (version 3.9.0 Sep 3 2020)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -26,9 +26,10 @@
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/checkbox.h>
#include <wx/combobox.h>
#include <wx/statbox.h>
#include <wx/choice.h>
#include <wx/button.h>
#include <wx/gbsizer.h>
#include <wx/combobox.h>
#include <wx/notebook.h>
#include <wx/dialog.h>
@ -55,30 +56,28 @@ class DIALOG_SIM_SETTINGS_BASE : public DIALOG_SHIM
wxTextCtrl* m_acFreqStop;
wxStaticText* m_staticText110;
wxPanel* m_pgDC;
wxCheckBox* m_dcEnable1;
wxStaticText* m_staticText41;
wxComboBox* m_dcSource1;
wxStaticText* m_staticText51;
wxTextCtrl* m_dcStart1;
wxStaticText* m_staticText511;
wxStaticText* m_staticText61;
wxTextCtrl* m_dcStop1;
wxStaticText* m_staticText512;
wxStaticText* m_staticText71;
wxTextCtrl* m_dcIncr1;
wxStaticText* m_staticText513;
wxCheckBox* m_dcEnable2;
wxRadioBox* m_dcSourceType1;
wxRadioBox* m_dcSourceType2;
wxStaticText* m_staticText4;
wxComboBox* m_dcSource2;
wxChoice* m_dcSource1;
wxChoice* m_dcSource2;
wxStaticText* m_staticText5;
wxTextCtrl* m_dcStart1;
wxStaticText* m_src1DCStartValUnit;
wxTextCtrl* m_dcStart2;
wxStaticText* m_staticText52;
wxStaticText* m_src2DCStartValUnit;
wxStaticText* m_staticText6;
wxTextCtrl* m_dcStop1;
wxStaticText* m_src1DCEndValUnit;
wxTextCtrl* m_dcStop2;
wxStaticText* m_staticText53;
wxStaticText* m_src2DCEndValUnit;
wxStaticText* m_staticText7;
wxTextCtrl* m_dcIncr1;
wxStaticText* m_src1DCStepUnit;
wxTextCtrl* m_dcIncr2;
wxStaticText* m_staticText54;
wxStaticText* m_src2DCStepUnit;
wxButton* m_swapDCSources;
wxPanel* m_pgDistortion;
wxPanel* m_pgNoise;
wxStaticText* m_staticText14;
@ -123,6 +122,10 @@ class DIALOG_SIM_SETTINGS_BASE : public DIALOG_SHIM
// Virtual event handlers, overide them in your derived class
virtual void onInitDlg( wxInitDialogEvent& event ) { event.Skip(); }
virtual void onDCEnableSecondSource( wxCommandEvent& event ) { event.Skip(); }
virtual void onDCSource1Selected( wxCommandEvent& event ) { event.Skip(); }
virtual void onDCSource2Selected( wxCommandEvent& event ) { event.Skip(); }
virtual void onSwapDCSources( wxCommandEvent& event ) { event.Skip(); }
virtual void onLoadDirectives( wxCommandEvent& event ) { event.Skip(); }

View File

@ -71,7 +71,7 @@ void NGSPICE::Init()
}
vector<string> NGSPICE::AllPlots()
vector<string> NGSPICE::AllPlots() const
{
LOCALE_IO c_locale; // ngspice works correctly only with C locale
char* currentPlot = m_ngSpice_CurPlot();
@ -293,20 +293,28 @@ string NGSPICE::GetXAxis( SIM_TYPE aType ) const
case ST_AC:
case ST_NOISE:
return string( "frequency" );
break;
case ST_DC:
return string( "v-sweep" );
// find plot, which ends with "-sweep"
for( auto& plot : AllPlots() )
{
const string sweepEnding = "-sweep";
unsigned int len = sweepEnding.length();
if( plot.length() > len
&& plot.substr( plot.length() - len, len ).compare( sweepEnding ) == 0 )
{
return string( plot );
}
}
break;
case ST_TRANSIENT:
return string( "time" );
break;
default:
break;
}
return string( "" );
}

View File

@ -60,7 +60,7 @@ public:
std::string GetXAxis( SIM_TYPE aType ) const override;
///> @copydoc SPICE_SIMULATOR::AllPlots()
std::vector<std::string> AllPlots() override;
std::vector<std::string> AllPlots() const override;
///> @copydoc SPICE_SIMULATOR::GetPlot()
std::vector<COMPLEX> GetPlot( const std::string& aName, int aMaxLen = -1 ) override;

View File

@ -25,23 +25,24 @@
#include "sim_panel_base.h"
#include "sim_plot_frame.h"
#include <wx/sizer.h>
#include "netlist_exporter_pspice_sim.h"
SIM_PANEL_BASE::SIM_PANEL_BASE() : m_type( ST_UNKNOWN )
SIM_PANEL_BASE::SIM_PANEL_BASE() : m_simCommand( wxEmptyString )
{
}
SIM_PANEL_BASE::SIM_PANEL_BASE( SIM_TYPE aType ) : m_type( aType )
SIM_PANEL_BASE::SIM_PANEL_BASE( wxString aCommand ) : m_simCommand( aCommand )
{
}
SIM_PANEL_BASE::SIM_PANEL_BASE( SIM_TYPE aType, wxWindow* parent, wxWindowID id,
const wxPoint& pos, const wxSize& size, long style, const wxString& name )
: wxWindow( parent, id, pos, size, style, name ), m_type( aType )
SIM_PANEL_BASE::SIM_PANEL_BASE( wxString aCommand, wxWindow* parent, wxWindowID id,
const wxPoint& pos, const wxSize& size, long style,
const wxString& name ) :
wxWindow( parent, id, pos, size, style, name ),
m_simCommand( aCommand )
{
}
@ -66,9 +67,16 @@ bool SIM_PANEL_BASE::IsPlottable( SIM_TYPE aSimType )
}
SIM_NOPLOT_PANEL::SIM_NOPLOT_PANEL( SIM_TYPE aType, wxWindow* parent, wxWindowID id,
const wxPoint& pos, const wxSize& size, long style, const wxString& name )
: SIM_PANEL_BASE( aType, parent, id, pos, size, style, name )
SIM_TYPE SIM_PANEL_BASE::GetType() const
{
return NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( m_simCommand );
}
SIM_NOPLOT_PANEL::SIM_NOPLOT_PANEL( wxString aCommand, wxWindow* parent, wxWindowID id,
const wxPoint& pos, const wxSize& size, long style,
const wxString& name ) :
SIM_PANEL_BASE( aCommand, parent, id, pos, size, style, name )
{
m_sizer = new wxBoxSizer( wxVERTICAL );
m_sizer->Add( 0, 1, 1, wxEXPAND, 5 );
@ -81,9 +89,10 @@ SIM_NOPLOT_PANEL::SIM_NOPLOT_PANEL( SIM_TYPE aType, wxWindow* parent, wxWindowID
//ST_UNKNOWN serves purpose of a welcome panel
m_textInfo->SetLabel(
( aType == ST_UNKNOWN ) ?
_( "Start the simulation by clicking the Run Simulation button" ) :
_( "This simulation provide no plots. Please refer to console window for results" ) );
( GetType() == ST_UNKNOWN )
? _( "Start the simulation by clicking the Run Simulation button" )
: _( "This simulation provide no plots. Please refer to console window for "
"results" ) );
m_sizer->Add( m_textInfo, 1, wxALL | wxEXPAND, 5 );
m_sizer->Add( 0, 1, 1, wxEXPAND, 5 );

View File

@ -36,28 +36,25 @@ class SIM_PANEL_BASE : public wxWindow
{
public:
SIM_PANEL_BASE();
SIM_PANEL_BASE( SIM_TYPE );
SIM_PANEL_BASE( SIM_TYPE aType, wxWindow* parent, wxWindowID id,
SIM_PANEL_BASE( wxString aCommand );
SIM_PANEL_BASE( wxString aCommand, wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
long style = 0, const wxString& name = wxPanelNameStr );
virtual ~SIM_PANEL_BASE();
static bool IsPlottable( SIM_TYPE aSimType );
SIM_TYPE GetType() const
{
return m_type;
}
SIM_TYPE GetType() const;
private:
const SIM_TYPE m_type;
protected:
const wxString m_simCommand;
};
class SIM_NOPLOT_PANEL : public SIM_PANEL_BASE
{
public:
SIM_NOPLOT_PANEL( SIM_TYPE aType, wxWindow* parent, wxWindowID id,
SIM_NOPLOT_PANEL( wxString aCommand, wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
long style = 0, const wxString& name = wxPanelNameStr );

View File

@ -207,7 +207,7 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
m_toolBar->Realize();
m_welcomePanel = new SIM_PANEL_BASE( ST_UNKNOWN, m_plotNotebook, wxID_ANY );
m_welcomePanel = new SIM_PANEL_BASE( wxEmptyString, m_plotNotebook, wxID_ANY );
m_plotNotebook->AddPage( m_welcomePanel, _( "Welcome!" ), 1, true );
// the settings dialog will be created later, on demand.
@ -476,14 +476,15 @@ bool SIM_PLOT_FRAME::IsSimulationRunning()
}
SIM_PANEL_BASE* SIM_PLOT_FRAME::NewPlotPanel( SIM_TYPE aSimType )
SIM_PANEL_BASE* SIM_PLOT_FRAME::NewPlotPanel( wxString aSimCommand )
{
SIM_PANEL_BASE* plotPanel;
SIM_TYPE simType = NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( aSimCommand );
if( SIM_PANEL_BASE::IsPlottable( aSimType ) )
if( SIM_PANEL_BASE::IsPlottable( simType ) )
{
SIM_PLOT_PANEL* panel;
panel = new SIM_PLOT_PANEL( aSimType, m_plotNotebook, this, wxID_ANY );
panel = new SIM_PLOT_PANEL( aSimCommand, m_plotNotebook, this, wxID_ANY );
panel->GetPlotWin()->EnableMouseWheelPan(
Pgm().GetCommonSettings()->m_Input.scroll_modifier_zoom != 0 );
@ -493,7 +494,7 @@ SIM_PANEL_BASE* SIM_PLOT_FRAME::NewPlotPanel( SIM_TYPE aSimType )
else
{
SIM_NOPLOT_PANEL* panel;
panel = new SIM_NOPLOT_PANEL( aSimType, m_plotNotebook, wxID_ANY );
panel = new SIM_NOPLOT_PANEL( aSimCommand, m_plotNotebook, wxID_ANY );
plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel );
}
@ -503,7 +504,7 @@ SIM_PANEL_BASE* SIM_PLOT_FRAME::NewPlotPanel( SIM_TYPE aSimType )
m_welcomePanel = nullptr;
}
wxString pageTitle( m_simulator->TypeToName( aSimType, true ) );
wxString pageTitle( m_simulator->TypeToName( simType, true ) );
pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), (unsigned int) ++m_plotNumber ) );
m_plotNotebook->AddPage( dynamic_cast<wxWindow*>( plotPanel ), pageTitle, true );
@ -612,7 +613,8 @@ void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const
SIM_PLOT_PANEL* plotPanel = CurrentPlot();
if( !plotPanel || plotPanel->GetType() != simType )
plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( simType ) );
plotPanel =
dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( m_exporter->GetUsedSimCommand() ) );
wxASSERT( plotPanel );
@ -920,8 +922,9 @@ bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath )
if( !file.GetNextLine().ToLong( &plotType ) )
return false;
SIM_PANEL_BASE* plotPanel = NewPlotPanel( (SIM_TYPE) plotType );
m_plots[plotPanel].m_simCommand = file.GetNextLine();
wxString simCommand = file.GetNextLine();
SIM_PANEL_BASE* plotPanel = NewPlotPanel( simCommand );
m_plots[plotPanel].m_simCommand = simCommand;
StartSimulation( m_plots[plotPanel].m_simCommand );
// Perform simulation, so plots can be added with values
@ -1031,7 +1034,8 @@ void SIM_PLOT_FRAME::menuNewPlot( wxCommandEvent& aEvent )
if( SIM_PANEL_BASE::IsPlottable( type ) )
{
SIM_PLOT_PANEL* prevPlot = CurrentPlot();
SIM_PLOT_PANEL* newPlot = dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( type ) );
SIM_PLOT_PANEL* newPlot =
dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( m_exporter->GetUsedSimCommand() ) );
// If the previous plot had the same type, copy the simulation command
if( prevPlot )
@ -1314,13 +1318,18 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
if( m_settingsDlg->ShowModal() == wxID_OK )
{
wxString oldCommand = m_plots[plotPanelWindow].m_simCommand;
wxString newCommand = m_settingsDlg->GetSimCommand();
SIM_TYPE newSimType = NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( newCommand );
// If it is a new simulation type, open a new plot
if( !plotPanelWindow || ( plotPanelWindow && plotPanelWindow->GetType() != newSimType ) )
// 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
&& oldCommand.Lower().GetChar( 4 ) != newCommand.Lower().GetChar( 4 ) ) )
{
plotPanelWindow = NewPlotPanel( newSimType );
plotPanelWindow = NewPlotPanel( newCommand );
}
m_plots[plotPanelWindow].m_simCommand = newCommand;
@ -1501,7 +1510,7 @@ void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
SIM_PANEL_BASE* plotPanelWindow = currentPlotWindow();
if( !plotPanelWindow || plotPanelWindow->GetType() != simType )
plotPanelWindow = NewPlotPanel( simType );
plotPanelWindow = NewPlotPanel( m_exporter->GetUsedSimCommand() );
if( IsSimulationRunning() )
return;

View File

@ -136,10 +136,10 @@ public:
/**
* @brief Creates a new plot panel for a given simulation type and adds it to the main
* notebook.
* @param aSimType is requested simulation type.
* @param aSimCommand is the SPICE command used for simulation.
* @return The new plot panel.
*/
SIM_PANEL_BASE* NewPlotPanel( SIM_TYPE aSimType );
SIM_PANEL_BASE* NewPlotPanel( wxString aSimCommand );
/**
* @brief Adds a voltage plot for a given net name.

View File

@ -29,6 +29,8 @@
#include <algorithm>
#include <limits>
#include <wx/regex.h>
static wxString formatFloat( double x, int nDigits )
{
wxString rv, fmt;
@ -120,47 +122,60 @@ static int countDecimalDigits( double x, int maxDigits )
}
static void formatSILabels( mpScaleBase* scale, const wxString& aUnit, int nDigits )
template <typename parent>
class LIN_SCALE : public parent
{
double maxVis = scale->AbsVisibleMaxValue();
private:
const wxString m_unit;
public:
LIN_SCALE( wxString name, wxString unit, int flags ) : parent( name, flags ), m_unit( unit ){};
void formatLabels() override
{
double maxVis = parent::AbsVisibleMaxValue();
wxString suffix;
int power, digits = 0;
int constexpr DIGITS = 3;
getSISuffix( maxVis, aUnit, power, suffix );
getSISuffix( maxVis, m_unit, power, suffix );
double sf = pow( 10.0, power );
for( auto &l : scale->TickLabels() )
for( auto& l : parent::TickLabels() )
{
int k = countDecimalDigits( l.pos / sf, nDigits );
int k = countDecimalDigits( l.pos / sf, DIGITS );
digits = std::max( digits, k );
}
for( auto &l : scale->TickLabels() )
for( auto& l : parent::TickLabels() )
{
l.label = formatFloat ( l.pos / sf, digits ) + suffix;
l.label = formatFloat( l.pos / sf, digits ) + suffix;
l.visible = true;
}
}
}
};
class FREQUENCY_LOG_SCALE : public mpScaleXLog
template <typename parent>
class LOG_SCALE : public parent
{
private:
const wxString m_unit;
public:
FREQUENCY_LOG_SCALE( wxString name, int flags ) :
mpScaleXLog( name, flags ) {};
LOG_SCALE( wxString name, wxString unit, int flags ) : parent( name, flags ), m_unit( unit ){};
void formatLabels() override
{
const wxString unit = wxT( "Hz" );
wxString suffix;
int power;
for( auto &l : TickLabels() )
for( auto& l : parent::TickLabels() )
{
getSISuffix( l.pos, unit, power, suffix );
getSISuffix( l.pos, m_unit, power, suffix );
double sf = pow( 10.0, power );
int k = countDecimalDigits( l.pos / sf, 3 );
@ -171,98 +186,6 @@ public:
};
class FREQUENCY_LIN_SCALE : public mpScaleX
{
public:
FREQUENCY_LIN_SCALE( wxString name, int flags ) :
mpScaleX( name, flags, false , 0 ) {};
void formatLabels() override
{
formatSILabels( this, wxT( "Hz" ), 3 );
}
};
class TIME_SCALE : public mpScaleX
{
public:
TIME_SCALE( wxString name, int flags ) :
mpScaleX( name, flags, false, 0 ) {};
void formatLabels() override
{
formatSILabels( this, wxT( "s" ), 3 );
}
};
class VOLTAGE_SCALE_X : public mpScaleX
{
public:
VOLTAGE_SCALE_X( wxString name, int flags ) :
mpScaleX( name, flags, false, 0 ) {};
void formatLabels() override
{
formatSILabels( this, wxT( "V" ), 3 );
}
};
class GAIN_SCALE : public mpScaleY
{
public:
GAIN_SCALE( wxString name, int flags ) :
mpScaleY( name, flags, false ) {};
void formatLabels() override
{
formatSILabels( this, wxT( "dBV" ), 3 );
}
};
class PHASE_SCALE : public mpScaleY
{
public:
PHASE_SCALE( wxString name, int flags ) :
mpScaleY( name, flags, false ) {};
void formatLabels() override
{
formatSILabels( this, wxT( "\u00B0" ), 3 ); // degree sign
}
};
class VOLTAGE_SCALE_Y : public mpScaleY
{
public:
VOLTAGE_SCALE_Y( wxString name, int flags ) :
mpScaleY( name, flags, false ) {};
void formatLabels() override
{
formatSILabels( this, wxT( "V" ), 3 );
}
};
class CURRENT_SCALE : public mpScaleY
{
public:
CURRENT_SCALE( wxString name, int flags ) :
mpScaleY( name, flags, false ) {};
void formatLabels() override
{
formatSILabels( this, wxT( "A" ), 3 );
}
};
void CURSOR::Plot( wxDC& aDC, mpWindow& aWindow )
{
if( !m_window )
@ -363,9 +286,9 @@ void CURSOR::UpdateReference()
}
SIM_PLOT_PANEL::SIM_PLOT_PANEL( SIM_TYPE aType, wxWindow* parent, SIM_PLOT_FRAME* aMainFrame,
SIM_PLOT_PANEL::SIM_PLOT_PANEL( wxString aCommand, wxWindow* parent, SIM_PLOT_FRAME* aMainFrame,
wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name )
: SIM_PANEL_BASE( aType, parent, id, pos, size, style, name ),
: SIM_PANEL_BASE( aCommand, parent, id, pos, size, style, name ),
m_colorIdx( 0 ),
m_axis_x( nullptr ),
m_axis_y1( nullptr ),
@ -385,27 +308,26 @@ SIM_PLOT_PANEL::SIM_PLOT_PANEL( SIM_TYPE aType, wxWindow* parent, SIM_PLOT_FRAME
switch( GetType() )
{
case ST_AC:
m_axis_x = new FREQUENCY_LOG_SCALE( _( "Frequency" ), mpALIGN_BOTTOM );
m_axis_y1 = new GAIN_SCALE( _( "Gain" ), mpALIGN_LEFT );
m_axis_y2 = new PHASE_SCALE( _( "Phase" ), mpALIGN_RIGHT );
m_axis_x = new LOG_SCALE<mpScaleXLog>( _( "Frequency" ), wxT( "Hz" ), mpALIGN_BOTTOM );
m_axis_y1 = new LIN_SCALE<mpScaleY>( _( "Gain" ), wxT( "dBV" ), mpALIGN_LEFT );
m_axis_y2 = new LIN_SCALE<mpScaleY>( _( "Phase" ), wxT( "\u00B0" ),
mpALIGN_RIGHT ); // degree sign
m_axis_y2->SetMasterScale( m_axis_y1 );
break;
case ST_DC:
m_axis_x = new VOLTAGE_SCALE_X( _( "Voltage (swept)" ), mpALIGN_BOTTOM );
m_axis_y1 = new VOLTAGE_SCALE_Y( _( "Voltage (measured)" ), mpALIGN_LEFT );
m_axis_y2 = new CURRENT_SCALE( _( "Current" ), mpALIGN_RIGHT );
prepareDCAxes();
break;
case ST_NOISE:
m_axis_x = new FREQUENCY_LOG_SCALE( _( "Frequency" ), mpALIGN_BOTTOM );
m_axis_x = new LOG_SCALE<mpScaleXLog>( _( "Frequency" ), wxT( "Hz" ), mpALIGN_BOTTOM );
m_axis_y1 = new mpScaleY( _( "noise [(V or A)^2/Hz]" ), mpALIGN_LEFT );
break;
case ST_TRANSIENT:
m_axis_x = new TIME_SCALE( _( "Time" ), mpALIGN_BOTTOM );
m_axis_y1 = new VOLTAGE_SCALE_Y( _( "Voltage" ), mpALIGN_LEFT );
m_axis_y2 = new CURRENT_SCALE( _( "Current" ), mpALIGN_RIGHT );
m_axis_x = new LIN_SCALE<mpScaleX>( _( "Time" ), wxT( "s" ), mpALIGN_BOTTOM );
m_axis_y1 = new LIN_SCALE<mpScaleY>( _( "Voltage" ), wxT( "V" ), mpALIGN_LEFT );
m_axis_y2 = new LIN_SCALE<mpScaleY>( _( "Current" ), wxT( "A" ), mpALIGN_RIGHT );
m_axis_y2->SetMasterScale( m_axis_y1 );
break;
@ -455,6 +377,38 @@ SIM_PLOT_PANEL::~SIM_PLOT_PANEL()
}
void SIM_PLOT_PANEL::prepareDCAxes()
{
wxRegEx simCmd( "^.dc[[:space:]]+([[:alnum:]]+\\M).*", wxRE_ADVANCED | wxRE_ICASE );
if( simCmd.Matches( m_simCommand ) )
{
switch( static_cast<char>( simCmd.GetMatch( m_simCommand.Lower(), 1 ).GetChar( 0 ) ) )
{
case 'v':
m_axis_x =
new LIN_SCALE<mpScaleX>( _( "Voltage (swept)" ), wxT( "V" ), mpALIGN_BOTTOM );
break;
case 'i':
m_axis_x =
new LIN_SCALE<mpScaleX>( _( "Current (swept)" ), wxT( "A" ), mpALIGN_BOTTOM );
break;
case 'r':
m_axis_x = new LIN_SCALE<mpScaleX>( _( "Resistance (swept)" ), wxT( "\u03A9" ),
mpALIGN_BOTTOM );
break;
case 't':
m_axis_x = new LIN_SCALE<mpScaleX>( _( "Temperature (swept)" ), wxT( "\u00B0C" ),
mpALIGN_BOTTOM );
break;
}
m_axis_y1 = new LIN_SCALE<mpScaleY>( _( "Voltage (measured)" ), wxT( "V" ), mpALIGN_LEFT );
m_axis_y2 = new LIN_SCALE<mpScaleY>( _( "Current" ), wxT( "A" ), mpALIGN_RIGHT );
}
}
void SIM_PLOT_PANEL::UpdatePlotColors()
{
// Update bg and fg colors:

View File

@ -168,9 +168,9 @@ protected:
class SIM_PLOT_PANEL : public SIM_PANEL_BASE
{
public:
SIM_PLOT_PANEL( SIM_TYPE aType, wxWindow* parent, SIM_PLOT_FRAME* aMainFrame,
wxWindowID id, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxPanelNameStr );
SIM_PLOT_PANEL( wxString aCommand, wxWindow* parent, SIM_PLOT_FRAME* aMainFrame, wxWindowID id,
const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
long style = 0, const wxString& name = wxPanelNameStr );
virtual ~SIM_PLOT_PANEL();
@ -296,6 +296,9 @@ private:
///> @return a new color from the palette
wxColour generateColor();
///> @brief Constructs the plot axes for DC simulation plot
void prepareDCAxes();
// Color index to get a new color from the palette
unsigned int m_colorIdx;

View File

@ -95,7 +95,7 @@ public:
* @param none
* @return List of vector names. ?May not match to the net name elements.
*/
virtual std::vector<std::string> AllPlots() = 0;
virtual std::vector<std::string> AllPlots() const = 0;
/**
* @brief Returns a requested vector with complex values. If the vector is real, then