fcedef836a
This commit creates more issues than resolves. It could happen that there are components with different reference types (e.g. U1 and IC1) that would be later converted to X1, causing a conflict.
416 lines
13 KiB
C++
416 lines
13 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2016 CERN
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 3
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, you may find one here:
|
|
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
* or you may search the http://www.gnu.org website for the version 3 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "dialog_sim_settings.h"
|
|
#include <sim/netlist_exporter_pspice_sim.h>
|
|
#include <confirm.h>
|
|
|
|
#include <wx/tokenzr.h>
|
|
|
|
/// @todo ngspice offers more types of analysis,
|
|
//so there are a few tabs missing (e.g. pole-zero, distortion, sensitivity)
|
|
|
|
// Helper function to shorten conditions
|
|
static bool empty( const wxTextEntryBase* aCtrl )
|
|
{
|
|
return aCtrl->GetValue().IsEmpty();
|
|
}
|
|
|
|
|
|
DIALOG_SIM_SETTINGS::DIALOG_SIM_SETTINGS( wxWindow* aParent )
|
|
: DIALOG_SIM_SETTINGS_BASE( aParent ), m_exporter( nullptr ), m_spiceEmptyValidator( true )
|
|
{
|
|
m_posIntValidator.SetMin( 1 );
|
|
|
|
m_acPointsNumber->SetValidator( m_posIntValidator );
|
|
m_acFreqStart->SetValidator( m_spiceValidator );
|
|
m_acFreqStop->SetValidator( m_spiceValidator );
|
|
|
|
m_dcStart1->SetValidator( m_spiceValidator );
|
|
m_dcStop1->SetValidator( m_spiceValidator );
|
|
m_dcIncr1->SetValidator( m_spiceValidator );
|
|
|
|
m_dcStart2->SetValidator( m_spiceValidator );
|
|
m_dcStop2->SetValidator( m_spiceValidator );
|
|
m_dcIncr2->SetValidator( m_spiceValidator );
|
|
|
|
m_noisePointsNumber->SetValidator( m_posIntValidator );
|
|
m_noiseFreqStart->SetValidator( m_spiceValidator );
|
|
m_noiseFreqStop->SetValidator( m_spiceValidator );
|
|
|
|
m_transStep->SetValidator( m_spiceValidator );
|
|
m_transFinal->SetValidator( m_spiceValidator );
|
|
m_transInitial->SetValidator( m_spiceEmptyValidator );
|
|
|
|
m_sdbSizerOK->SetDefault();
|
|
updateNetlistOpts();
|
|
|
|
FixOSXCancelButtonIssue();
|
|
}
|
|
|
|
|
|
bool DIALOG_SIM_SETTINGS::TransferDataFromWindow()
|
|
{
|
|
if( !wxDialog::TransferDataFromWindow() )
|
|
return false;
|
|
|
|
wxWindow* page = m_simPages->GetCurrentPage();
|
|
|
|
// AC analysis
|
|
if( page == m_pgAC )
|
|
{
|
|
if( !m_pgAC->Validate() )
|
|
return false;
|
|
|
|
m_simCommand = wxString::Format( ".ac %s %s %s %s",
|
|
scaleToString( m_acScale->GetSelection() ),
|
|
m_acPointsNumber->GetValue(),
|
|
SPICE_VALUE( m_acFreqStart->GetValue() ).ToSpiceString(),
|
|
SPICE_VALUE( m_acFreqStop->GetValue() ).ToSpiceString() );
|
|
}
|
|
|
|
|
|
// DC transfer analysis
|
|
else if( page == m_pgDC )
|
|
{
|
|
// At least one source has to be enabled
|
|
if( !m_dcEnable1->IsChecked() && !m_dcEnable1->IsChecked() )
|
|
{
|
|
DisplayError( this, wxT( "You need to enable at least one source" ) );
|
|
return false;
|
|
}
|
|
|
|
wxString simCmd = wxString( ".dc " );
|
|
|
|
if( m_dcEnable1->IsChecked() )
|
|
{
|
|
if( empty( m_dcSource1 ) )
|
|
{
|
|
DisplayError( this, wxT( "You need to select DC source (sweep 1)" ) );
|
|
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
|
|
{
|
|
simCmd += wxString::Format( "v%s %s %s %s",
|
|
m_dcSource1->GetValue(),
|
|
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;
|
|
}
|
|
}
|
|
|
|
if( m_dcEnable2->IsChecked() )
|
|
{
|
|
if( empty( m_dcSource2 ) )
|
|
{
|
|
DisplayError( this, wxT( "You need to select DC source (sweep 2)" ) );
|
|
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
|
|
{
|
|
simCmd += wxString::Format( "v%s %s %s %s",
|
|
m_dcSource2->GetValue(),
|
|
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;
|
|
}
|
|
}
|
|
|
|
m_simCommand = simCmd;
|
|
}
|
|
|
|
|
|
// Noise analysis
|
|
else if( page == m_pgNoise )
|
|
{
|
|
const NETLIST_EXPORTER_PSPICE::NET_INDEX_MAP& netMap = m_exporter->GetNetIndexMap();
|
|
|
|
if( empty( m_noiseMeas ) || empty( m_noiseSrc ) || empty( m_noisePointsNumber )
|
|
|| empty( m_noiseFreqStart ) || empty( m_noiseFreqStop ) )
|
|
return false;
|
|
|
|
wxString ref = empty( m_noiseRef )
|
|
? wxString() : wxString::Format( ", %d", netMap.at( m_noiseRef->GetValue() ) );
|
|
|
|
m_simCommand = wxString::Format( ".noise v(%d%s) v%s %s %s %s %s",
|
|
netMap.at( m_noiseMeas->GetValue() ), ref,
|
|
m_noiseSrc->GetValue(), scaleToString( m_noiseScale->GetSelection() ),
|
|
m_noisePointsNumber->GetValue(),
|
|
SPICE_VALUE( m_noiseFreqStart->GetValue() ).ToSpiceString(),
|
|
SPICE_VALUE( m_noiseFreqStop->GetValue() ).ToSpiceString() );
|
|
}
|
|
|
|
|
|
// DC operating point analysis
|
|
else if( page == m_pgOP )
|
|
{
|
|
m_simCommand = wxString( ".op" );
|
|
}
|
|
|
|
|
|
// Transient analysis
|
|
else if( page == m_pgTransient )
|
|
{
|
|
if( !m_pgTransient->Validate() )
|
|
return false;
|
|
|
|
wxString initial = empty( m_transInitial )
|
|
? "" : SPICE_VALUE( m_transInitial->GetValue() ).ToSpiceString();
|
|
|
|
m_simCommand = wxString::Format( ".tran %s %s %s",
|
|
SPICE_VALUE( m_transStep->GetValue() ).ToSpiceString(),
|
|
SPICE_VALUE( m_transFinal->GetValue() ).ToSpiceString(),
|
|
initial );
|
|
}
|
|
|
|
|
|
// Custom directives
|
|
else if( page == m_pgCustom )
|
|
{
|
|
m_simCommand = m_customTxt->GetValue();
|
|
}
|
|
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_simCommand.Trim();
|
|
updateNetlistOpts();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool DIALOG_SIM_SETTINGS::TransferDataToWindow()
|
|
{
|
|
/// @todo one day it could interpret the sim command and fill out appropriate fields..
|
|
if( empty( m_customTxt ) )
|
|
loadDirectives();
|
|
|
|
if( m_simCommand.IsEmpty() && !empty( m_customTxt ) )
|
|
return parseCommand( m_customTxt->GetValue() );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int DIALOG_SIM_SETTINGS::ShowModal()
|
|
{
|
|
// Fill out comboboxes that allow to select nets
|
|
// Map comoboxes to their current values
|
|
std::map<wxComboBox*, wxString> cmbNet = {
|
|
{ m_noiseMeas, m_noiseMeas->GetStringSelection() },
|
|
{ m_noiseRef, m_noiseRef->GetStringSelection() }
|
|
};
|
|
|
|
for( auto c : cmbNet )
|
|
c.first->Clear();
|
|
|
|
for( auto net : m_exporter->GetNetIndexMap() )
|
|
{
|
|
for( auto c : cmbNet )
|
|
c.first->Append( net.first );
|
|
}
|
|
|
|
// Try to restore the previous selection, if possible
|
|
for( auto c : cmbNet )
|
|
{
|
|
int idx = c.first->FindString( c.second );
|
|
|
|
if( idx != wxNOT_FOUND )
|
|
c.first->SetSelection( idx );
|
|
}
|
|
|
|
|
|
// Fill out comboboxes that allow 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();
|
|
|
|
for( auto item : m_exporter->GetSpiceItems() )
|
|
{
|
|
if( item.m_primitive == 'V' )
|
|
{
|
|
for( auto c : cmbSrc )
|
|
c.first->Append( item.m_refName );
|
|
}
|
|
}
|
|
|
|
// Try to restore the previous selection, if possible
|
|
for( auto c : cmbSrc )
|
|
{
|
|
int idx = c.first->FindString( c.second );
|
|
|
|
if( idx != wxNOT_FOUND )
|
|
c.first->SetSelection( idx );
|
|
}
|
|
|
|
return DIALOG_SIM_SETTINGS_BASE::ShowModal();
|
|
}
|
|
|
|
|
|
bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand )
|
|
{
|
|
if( aCommand.IsEmpty() )
|
|
return false;
|
|
|
|
wxStringTokenizer tokenizer( aCommand, " " );
|
|
wxString tkn = tokenizer.GetNextToken().Lower();
|
|
|
|
try {
|
|
if( tkn == ".ac" )
|
|
{
|
|
m_simPages->SetSelection( m_simPages->FindPage( m_pgAC ) );
|
|
|
|
tkn = tokenizer.GetNextToken().Lower();
|
|
|
|
if( tkn == "dec" )
|
|
m_acScale->SetSelection( 0 );
|
|
if( tkn == "oct" )
|
|
m_acScale->SetSelection( 1 );
|
|
if( tkn == "lin" )
|
|
m_acScale->SetSelection( 2 );
|
|
else
|
|
return false;
|
|
|
|
// If the fields below are empty, it will be caught by the exception handler
|
|
m_acPointsNumber->SetValue( tokenizer.GetNextToken() );
|
|
m_acFreqStart->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
|
|
m_acFreqStop->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
|
|
}
|
|
|
|
else if( tkn == ".dc" )
|
|
{
|
|
m_simPages->SetSelection( m_simPages->FindPage( m_pgDC ) );
|
|
|
|
tkn = tokenizer.GetNextToken();
|
|
|
|
if( !tkn.IsEmpty() )
|
|
{
|
|
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() );
|
|
|
|
// Check the 'Enabled' field, if all values are filled
|
|
m_dcEnable1->SetValue( !empty( m_dcSource1 ) && !empty( m_dcStart1 )
|
|
&& !empty( m_dcStop1 ) && !empty( m_dcIncr1 ) );
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
else if( tkn == ".tran" )
|
|
{
|
|
m_simPages->SetSelection( m_simPages->FindPage( m_pgTransient ) );
|
|
|
|
// If the fields below are empty, it will be caught by the exception handler
|
|
m_transStep->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
|
|
m_transFinal->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
|
|
|
|
// Initial time is an optional field
|
|
tkn = tokenizer.GetNextToken();
|
|
|
|
if( !tkn.IsEmpty() )
|
|
m_transInitial->SetValue( SPICE_VALUE( tkn ).ToSpiceString() );
|
|
}
|
|
|
|
// Custom directives
|
|
else if( !empty( m_customTxt ) )
|
|
{
|
|
m_simPages->SetSelection( m_simPages->FindPage( m_pgCustom ) );
|
|
}
|
|
}
|
|
catch( ... )
|
|
{
|
|
// Nothing really bad has happened
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void DIALOG_SIM_SETTINGS::loadDirectives()
|
|
{
|
|
if( m_exporter )
|
|
m_customTxt->SetValue( m_exporter->GetSheetSimCommand() );
|
|
}
|
|
|
|
|
|
void DIALOG_SIM_SETTINGS::updateNetlistOpts()
|
|
{
|
|
m_netlistOpts = NET_ALL_FLAGS;
|
|
|
|
if( !m_fixPassiveVals->IsChecked() )
|
|
m_netlistOpts &= ~NET_ADJUST_PASSIVE_VALS;
|
|
|
|
if( !m_fixIncludePaths->IsChecked() )
|
|
m_netlistOpts &= ~NET_ADJUST_INCLUDE_PATHS;
|
|
}
|