KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_sim_command.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2016-2022 CERN
5 * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * https://www.gnu.org/licenses/gpl-3.0.html
21 * or you may search the http://www.gnu.org website for the version 3 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "dialog_sim_command.h"
28#include <sim/ngspice.h>
29#include <sim/simulator_frame.h>
30#include <sim/sim_plot_tab.h>
31#include <confirm.h>
32
33#include <wx/tokenzr.h>
34
35#include <vector>
36#include <utility>
37
38
39// Helper function to shorten conditions
40static bool empty( const wxTextEntryBase* aCtrl )
41{
42 return aCtrl->GetValue().IsEmpty();
43}
44
45
46static void setStringSelection( wxChoice* aCtrl, const wxString& aStr )
47{
48 aCtrl->SetSelection( aCtrl->FindString( aStr ) );
49}
50
51
52static wxString getStringSelection( const wxChoice* aCtrl )
53{
54 return aCtrl->GetString( aCtrl->GetSelection() );
55}
56
57
59 std::shared_ptr<SPICE_CIRCUIT_MODEL> aCircuitModel,
60 std::shared_ptr<SPICE_SETTINGS>& aSettings ) :
61 DIALOG_SIM_COMMAND_BASE( aParent ),
62 m_simulatorFrame( aParent ),
63 m_circuitModel( aCircuitModel ),
64 m_settings( aSettings ),
65 m_spiceEmptyValidator( true )
66{
67 m_posIntValidator.SetMin( 1 );
68
69 m_acPointsNumber->SetValidator( m_posIntValidator );
70 m_acFreqStart->SetValidator( m_spiceValidator );
71 m_acFreqStop->SetValidator( m_spiceValidator );
72
73 m_spPointsNumber->SetValidator( m_posIntValidator );
74 m_spFreqStart->SetValidator( m_spiceValidator );
75 m_spFreqStop->SetValidator( m_spiceValidator );
76
77 m_dcStart1->SetValidator( m_spiceValidator );
78 m_dcStop1->SetValidator( m_spiceValidator );
79 m_dcIncr1->SetValidator( m_spiceValidator );
80
81 m_dcStart2->SetValidator( m_spiceValidator );
82 m_dcStop2->SetValidator( m_spiceValidator );
83 m_dcIncr2->SetValidator( m_spiceValidator );
84
86 m_noiseFreqStart->SetValidator( m_spiceValidator );
87 m_noiseFreqStop->SetValidator( m_spiceValidator );
88
89 m_transStep->SetValidator( m_spiceValidator );
90 m_transFinal->SetValidator( m_spiceValidator );
93
94 m_inputSignalsFilter->SetDescriptiveText( _( "Filter" ) );
95
96 wxChar type1 = getStringSelection( m_dcSourceType1 ).Upper().GetChar( 0 );
98
99 wxChar type2 = getStringSelection( m_dcSourceType2 ).Upper().GetChar( 0 );
101
102 // NoiseRef is optional
103 m_noiseRef->Append( wxEmptyString );
104
105 for( const std::string& net : m_circuitModel->GetNets() )
106 {
107 m_pzInput->Append( net );
108 m_pzInputRef->Append( net );
109 m_pzOutput->Append( net );
110 m_pzOutputRef->Append( net );
111
112 m_noiseMeas->Append( net );
113 m_noiseRef->Append( net );
114 }
115
116 for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
117 {
118 if( item.model->GetDeviceType() == SIM_MODEL::DEVICE_T::V )
119 m_noiseSrc->Append( item.refName );
120 }
121
122 if( !dynamic_cast<NGSPICE_SETTINGS*>( aSettings.get() ) )
123 m_compatibilityModeSizer->Show( false );
124
125 int minWidth = GetTextExtent( wxS( "XXX.XXXXXXX" ) ).x;
126 m_y1Min->SetMinSize( wxSize( minWidth, -1 ) );
127 m_y1Max->SetMinSize( wxSize( minWidth, -1 ) );
128 m_y2Min->SetMinSize( wxSize( minWidth, -1 ) );
129 m_y2Max->SetMinSize( wxSize( minWidth, -1 ) );
130 m_y3Min->SetMinSize( wxSize( minWidth, -1 ) );
131 m_y3Max->SetMinSize( wxSize( minWidth, -1 ) );
132
133 m_bSizerY1->Show( false );
134 m_bSizerY2->Show( false );
135 m_bSizerY3->Show( false );
136
138}
139
141{
143 if( empty( m_customTxt ) )
145
146 m_fixIncludePaths->SetValue( m_settings->GetFixIncludePaths() );
147
148 if( NGSPICE_SETTINGS* settings = dynamic_cast<NGSPICE_SETTINGS*>( m_settings.get() ) )
149 {
150 switch( settings->GetCompatibilityMode() )
151 {
152 case NGSPICE_COMPATIBILITY_MODE::USER_CONFIG: m_compatibilityMode->SetSelection( 0 ); break;
153 case NGSPICE_COMPATIBILITY_MODE::NGSPICE: m_compatibilityMode->SetSelection( 1 ); break;
154 case NGSPICE_COMPATIBILITY_MODE::PSPICE: m_compatibilityMode->SetSelection( 2 ); break;
155 case NGSPICE_COMPATIBILITY_MODE::LTSPICE: m_compatibilityMode->SetSelection( 3 ); break;
156 case NGSPICE_COMPATIBILITY_MODE::LT_PSPICE: m_compatibilityMode->SetSelection( 4 ); break;
157 case NGSPICE_COMPATIBILITY_MODE::HSPICE: m_compatibilityMode->SetSelection( 5 ); break;
158 default: wxFAIL_MSG( wxString::Format( "Unknown NGSPICE_COMPATIBILITY_MODE %d.",
159 settings->GetCompatibilityMode() ) ); break;
160 }
161 }
162
163 return true;
164}
165
166
167void DIALOG_SIM_COMMAND::OnUpdateUILockY1( wxUpdateUIEvent& event )
168{
169 event.Enable( m_lockY1->GetValue() );
170}
171
172
173void DIALOG_SIM_COMMAND::OnUpdateUILockY2( wxUpdateUIEvent& event )
174{
175 event.Enable( m_lockY2->GetValue() );
176}
177
178
179void DIALOG_SIM_COMMAND::OnUpdateUILockY3( wxUpdateUIEvent& event )
180{
181 event.Enable( m_lockY3->GetValue() );
182}
183
184
186{
187 if( const SIM_PLOT_TAB* plotTab = dynamic_cast<const SIM_PLOT_TAB*>( aSimTab ) )
188 {
189 if( !plotTab->GetLabelY1().IsEmpty() )
190 {
191 m_bSizerY1->Show( true );
192 m_lockY1->SetLabel( wxString::Format( m_lockY1->GetLabel(), plotTab->GetLabelY1() ) );
193 m_y1Units->SetLabel( plotTab->GetUnitsY1() );
194
195 double min, max;
196 bool locked = plotTab->GetY1Scale( &min, &max );
197 m_lockY1->SetValue( locked );
198
199 if( !std::isnan( min ) )
200 m_y1Min->SetValue( SIM_VALUE::Normalize( min ) );
201
202 if( !std::isnan( max ) )
203 m_y1Max->SetValue( SIM_VALUE::Normalize( max ) );
204 }
205
206 if( !plotTab->GetLabelY2().IsEmpty() )
207 {
208 m_bSizerY2->Show( true );
209 m_lockY2->SetLabel( wxString::Format( m_lockY2->GetLabel(), plotTab->GetLabelY2() ) );
210 m_y2Units->SetLabel( plotTab->GetUnitsY2() );
211
212 double min, max;
213 bool locked = plotTab->GetY2Scale( &min, &max );
214 m_lockY2->SetValue( locked );
215
216 if( !std::isnan( min ) )
217 m_y2Min->SetValue( SIM_VALUE::Normalize( min ) );
218
219 if( !std::isnan( max ) )
220 m_y2Max->SetValue( SIM_VALUE::Normalize( max ) );
221 }
222
223 if( !plotTab->GetLabelY3().IsEmpty() )
224 {
225 m_bSizerY3->Show( true );
226 m_lockY3->SetLabel( wxString::Format( m_lockY3->GetLabel(), plotTab->GetLabelY3() ) );
227 m_y3Units->SetLabel( plotTab->GetUnitsY3() );
228
229 double min, max;
230 bool locked = plotTab->GetY3Scale( &min, &max );
231 m_lockY3->SetValue( locked );
232
233 if( !std::isnan( min ) )
234 m_y3Min->SetValue( SIM_VALUE::Normalize( min ) );
235
236 if( !std::isnan( max ) )
237 m_y3Max->SetValue( SIM_VALUE::Normalize( max ) );
238 }
239
240 m_grid->SetValue( plotTab->IsGridShown() );
241 m_legend->SetValue( plotTab->IsLegendShown() );
242 m_dottedSecondary->SetValue( plotTab->GetDottedSecondary() );
243
244#define GET_STR( val ) EDA_UNIT_UTILS::UI::MessageTextFromValue( unityScale, EDA_UNITS::UNSCALED, \
245 val, false /* no units */ )
246
247 m_marginLeft->SetValue( GET_STR( plotTab->GetPlotWin()->GetMarginLeft() ) );
248 m_marginTop->SetValue( GET_STR( plotTab->GetPlotWin()->GetMarginTop() ) );
249 m_marginRight->SetValue( GET_STR( plotTab->GetPlotWin()->GetMarginRight() ) );
250 m_marginBottom->SetValue( GET_STR( plotTab->GetPlotWin()->GetMarginBottom() ) );
251 }
252}
253
254
255wxString DIALOG_SIM_COMMAND::evaluateDCControls( wxChoice* aDcSource, wxTextCtrl* aDcStart,
256 wxTextCtrl* aDcStop, wxTextCtrl* aDcIncr )
257{
258 wxString dcSource;
259 wxWindow* ctrlWithError = nullptr;
260
261 if( aDcSource->GetSelection() >= 0 )
262 dcSource = aDcSource->GetString( aDcSource->GetSelection() );
263
264 if( dcSource.IsEmpty() )
265 {
266 DisplayError( this, _( "A DC source must be specified." ) );
267 ctrlWithError = aDcSource;
268 }
269 else if( !aDcStart->Validate() )
270 ctrlWithError = aDcStart;
271 else if( !aDcStop->Validate() )
272 ctrlWithError = aDcStop;
273 else if( !aDcIncr->Validate() )
274 ctrlWithError = aDcIncr;
275
276 if( ctrlWithError )
277 {
278 ctrlWithError->SetFocus();
279 return wxEmptyString;
280 }
281
282 // pick device name from exporter when something different than temperature is selected
283 if( dcSource.Cmp( "TEMP" ) )
284 dcSource = m_circuitModel->GetItemName( std::string( dcSource.ToUTF8() ) );
285
286 return wxString::Format( "%s %s %s %s", dcSource,
287 SPICE_VALUE( aDcStart->GetValue() ).ToSpiceString(),
288 SPICE_VALUE( aDcStop->GetValue() ).ToSpiceString(),
289 SPICE_VALUE( aDcIncr->GetValue() ).ToSpiceString() );
290}
291
292
294{
295 if( !wxDialog::TransferDataFromWindow() )
296 return false;
297
298 // The simulator dependent settings always get transferred.
299 if( NGSPICE_SETTINGS* settings = dynamic_cast<NGSPICE_SETTINGS*>( m_settings.get() ) )
300 {
301 switch( m_compatibilityMode->GetSelection() )
302 {
303 case 0: settings->SetCompatibilityMode( NGSPICE_COMPATIBILITY_MODE::USER_CONFIG ); break;
304 case 1: settings->SetCompatibilityMode( NGSPICE_COMPATIBILITY_MODE::NGSPICE ); break;
305 case 2: settings->SetCompatibilityMode( NGSPICE_COMPATIBILITY_MODE::PSPICE ); break;
306 case 3: settings->SetCompatibilityMode( NGSPICE_COMPATIBILITY_MODE::LTSPICE ); break;
307 case 4: settings->SetCompatibilityMode( NGSPICE_COMPATIBILITY_MODE::LT_PSPICE ); break;
308 case 5: settings->SetCompatibilityMode( NGSPICE_COMPATIBILITY_MODE::HSPICE ); break;
309 }
310 }
311
312 wxString previousSimCommand = m_simCommand;
313 wxWindow* page = m_simPages->GetCurrentPage();
314
315 if( page == m_pgAC ) // AC small-signal analysis
316 {
317 if( !m_pgAC->Validate() )
318 return false;
319
320 m_simCommand.Printf( ".ac %s %s %s %s",
321 scaleToString( m_acScale->GetSelection() ),
322 m_acPointsNumber->GetValue(),
323 SPICE_VALUE( m_acFreqStart->GetValue() ).ToSpiceString(),
324 SPICE_VALUE( m_acFreqStop->GetValue() ).ToSpiceString() );
325 }
326 else if( page == m_pgSP ) // S-params analysis
327 {
328 if( !m_pgSP->Validate() )
329 return false;
330
331 m_simCommand.Printf( ".sp %s %s %s %s %s", scaleToString( m_spScale->GetSelection() ),
332 m_spPointsNumber->GetValue(),
333 SPICE_VALUE( m_spFreqStart->GetValue() ).ToSpiceString(),
334 SPICE_VALUE( m_spFreqStop->GetValue() ).ToSpiceString(),
335 m_spDoNoise ? "1" : "" );
336 }
337 else if( page == m_pgDC ) // DC transfer analysis
338 {
339 wxString simCmd = wxString( ".dc " );
340
342
343 if( src1.IsEmpty() )
344 return false;
345 else
346 simCmd += src1;
347
348 if( m_dcEnable2->IsChecked() )
349 {
351
352 if( src2.IsEmpty() )
353 return false;
354 else
355 simCmd += " " + src2;
356
357 if( m_dcSource1->GetStringSelection() == m_dcSource2->GetStringSelection() )
358 {
359 DisplayError( this, _( "Source 1 and Source 2 must be different." ) );
360 return false;
361 }
362 }
363
364 m_simCommand = simCmd;
365 }
366 else if( page == m_pgFFT ) // Fast Fourier transform
367 {
368 wxString vectors;
369
370 for( int ii = 0; ii < (int) m_inputSignalsList->GetCount(); ++ii )
371 {
372 if( m_inputSignalsList->IsChecked( ii ) )
373 vectors += wxS( " " ) + m_inputSignalsList->GetString( ii );
374 }
375
376 if( m_linearize->IsChecked() )
377 m_simCommand = wxT( "linearize" ) + vectors + wxS( "\n" );
378
379 m_simCommand = wxT( "fft" ) + vectors;
380 }
381 else if( page == m_pgPZ ) // Pole-zero analyses
382 {
383 wxString input = m_pzInput->GetStringSelection();
384 wxString inputRef = m_pzInputRef->GetStringSelection();
385 wxString output = m_pzOutput->GetStringSelection();
386 wxString outputRef = m_pzOutputRef->GetStringSelection();
387 wxString transferFunction = wxS( "vol" );
388 wxString analyses = wxS( "pz" );
389
390 if( m_pzFunctionType->GetSelection() == 1 )
391 transferFunction = wxS( "cur" );
392
393 if( m_pzAnalyses->GetSelection() == 1 )
394 analyses = wxS( "pol" );
395 else if( m_pzAnalyses->GetSelection() == 2 )
396 analyses = wxS( "zer" );
397
398 m_simCommand.Printf( ".pz %s %s %s %s %s %s",
399 input,
400 inputRef,
401 output,
402 outputRef,
403 transferFunction,
404 analyses );
405 }
406 else if( page == m_pgNOISE ) // Noise analysis
407 {
408 wxString output = m_noiseMeas->GetStringSelection();
409 wxString ref = m_noiseRef->GetStringSelection();
410 wxString noiseSource = m_noiseSrc->GetStringSelection();
411
412 if( m_noiseFreqStart->IsEmpty() || m_noiseFreqStop->IsEmpty() )
413 {
414 DisplayError( this, _( "A frequency range must be specified." ) );
415 return false;
416 }
417
418 if( !ref.IsEmpty() )
419 ref = wxS( "," ) + m_circuitModel->GetItemName( std::string( ref.ToUTF8() ) );
420
421 m_simCommand.Printf( ".noise v(%s%s) %s %s %s %s %s %s",
422 output,
423 ref,
424 noiseSource,
425 scaleToString( m_noiseScale->GetSelection() ),
426 m_noisePointsNumber->GetValue(),
429 m_saveAllNoise->GetValue() ? "1" : "" );
430 }
431 else if( page == m_pgOP ) // DC operating point analysis
432 {
433 m_simCommand = wxString( ".op" );
434 }
435 else if( page == m_pgTRAN ) // Transient analysis
436 {
437 if( !m_pgTRAN->Validate() )
438 return false;
439
440 const wxString spc = wxS( " " );
441 const SPICE_VALUE timeStep( m_transStep->GetValue() );
442 const SPICE_VALUE finalTime( m_transFinal->GetValue() );
443
444 SPICE_VALUE startTime( 0 );
445
446 if( !empty( m_transInitial ) )
447 startTime = SPICE_VALUE( m_transInitial->GetValue() );
448
449 wxString optionals;
450
451 if( m_useInitialConditions->GetValue() )
452 optionals = wxS( "uic" );
453
454 if( !empty( m_transMaxStep ) )
455 {
456 optionals = SPICE_VALUE( m_transMaxStep->GetValue() ).ToSpiceString() + spc + optionals;
457 }
458 else if( !optionals.IsEmpty() )
459 {
460 SPICE_VALUE maxStep = ( finalTime - startTime ) / 50.0;
461
462 if( maxStep > timeStep )
463 maxStep = timeStep;
464
465 optionals = maxStep.ToSpiceString() + spc + optionals;
466 }
467
468 if( !empty( m_transInitial ) )
469 optionals = startTime.ToSpiceString() + spc + optionals;
470 else if( !optionals.IsEmpty() )
471 optionals = wxS( "0 " ) + optionals;
472
473 m_simCommand.Printf( wxS( ".tran %s %s %s" ), timeStep.ToSpiceString(),
474 finalTime.ToSpiceString(), optionals );
475 }
476 else if( page == m_pgCustom ) // Custom directives
477 {
478 m_simCommand = m_customTxt->GetValue();
479 }
480 else
481 {
482 return false;
483 }
484
485 if( previousSimCommand != m_simCommand )
486 m_simCommand.Trim();
487
488 m_settings->SetFixIncludePaths( m_fixIncludePaths->GetValue() );
489
490 return true;
491}
492
493
495{
497
498 if( !m_fixIncludePaths->GetValue() )
499 options &= ~NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS;
500
501 if( !m_saveAllVoltages->GetValue() )
502 options &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES;
503
504 if( !m_saveAllCurrents->GetValue() )
505 options &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
506
507 if( !m_saveAllDissipations->GetValue() )
508 options &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
509
510 aTab->SetSimOptions( options );
511
512#define TO_INT( ctrl ) (int) EDA_UNIT_UTILS::UI::ValueFromString( unityScale, EDA_UNITS::UNSCALED, \
513 ctrl->GetValue() )
514
515 if( SIM_PLOT_TAB* plotTab = dynamic_cast<SIM_PLOT_TAB*>( aTab ) )
516 {
517 if( !plotTab->GetLabelY1().IsEmpty() )
518 {
519 plotTab->SetY1Scale( m_lockY1->GetValue(),
520 SIM_VALUE::ToDouble( m_y1Min->GetValue().ToStdString() ),
521 SIM_VALUE::ToDouble( m_y1Max->GetValue().ToStdString() ) );
522 }
523
524 if( !plotTab->GetLabelY2().IsEmpty() )
525 {
526 plotTab->SetY2Scale( m_lockY2->GetValue(),
527 SIM_VALUE::ToDouble( m_y2Min->GetValue().ToStdString() ),
528 SIM_VALUE::ToDouble( m_y2Max->GetValue().ToStdString() ) );
529 }
530
531 if( !plotTab->GetLabelY3().IsEmpty() )
532 {
533 plotTab->SetY3Scale( m_lockY3->GetValue(),
534 SIM_VALUE::ToDouble( m_y3Min->GetValue().ToStdString() ),
535 SIM_VALUE::ToDouble( m_y3Max->GetValue().ToStdString() ) );
536 }
537
538 plotTab->GetPlotWin()->LockY( m_lockY1->GetValue()
539 || m_lockY2->GetValue()
540 || m_lockY3->GetValue() );
541
542 plotTab->ShowGrid( m_grid->GetValue() );
543 plotTab->ShowLegend( m_legend->GetValue() );
544 plotTab->SetDottedSecondary( m_dottedSecondary->GetValue() );
545
546 plotTab->GetPlotWin()->SetMarginLeft( TO_INT( m_marginLeft ) );
547 plotTab->GetPlotWin()->SetMarginRight( TO_INT( m_marginRight ) );
548 plotTab->GetPlotWin()->SetMarginTop( TO_INT( m_marginTop ) );
549 plotTab->GetPlotWin()->SetMarginBottom( TO_INT( m_marginBottom ) );
550
551 plotTab->GetPlotWin()->AdjustLimitedView();
552 plotTab->GetPlotWin()->UpdateAll();
553 }
554}
555
556
557void DIALOG_SIM_COMMAND::updateDCSources( wxChar aType, wxChoice* aSource )
558{
559 wxString prevSelection;
560
561 if( !aSource->IsEmpty() )
562 prevSelection = aSource->GetString( aSource->GetSelection() );
563
564 std::set<wxString> sourcesList;
565 bool enableSrcSelection = true;
566
567 if( aType != 'T' )
568 {
569 for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
570 {
571 if( ( aType == 'R' && item.model->GetDeviceType() == SIM_MODEL::DEVICE_T::R )
572 || ( aType == 'V' && item.model->GetDeviceType() == SIM_MODEL::DEVICE_T::V )
573 || ( aType == 'I' && item.model->GetDeviceType() == SIM_MODEL::DEVICE_T::I ) )
574 {
575 sourcesList.insert( item.refName );
576 }
577 }
578
579 if( aSource == m_dcSource2 && !m_dcEnable2->IsChecked() )
580 enableSrcSelection = false;
581 }
582 else
583 {
584 prevSelection = wxT( "TEMP" );
585 sourcesList.insert( prevSelection );
586 enableSrcSelection = false;
587 }
588
589 aSource->Enable( enableSrcSelection );
590
591 aSource->Clear();
592
593 for( const wxString& src : sourcesList )
594 aSource->Append( src );
595
596 // Try to restore the previous selection, if possible
597 aSource->SetStringSelection( prevSelection );
598}
599
600
601void DIALOG_SIM_COMMAND::parseCommand( const wxString& aCommand )
602{
603 if( aCommand.IsEmpty() )
604 return;
605
606 if( aCommand == wxT( "*" ) )
607 {
608 SetTitle( _( "New Simulation Tab" ) );
609
610 m_commandType->Clear();
611
612 for( SIM_TYPE type : { ST_OP, ST_DC, ST_AC, ST_TRAN, ST_PZ, ST_NOISE, ST_SP, ST_FFT } )
613 {
614 m_commandType->Append( SPICE_SIMULATOR::TypeToName( type, true )
615 + wxT( " \u2014 " )
616 + SPICE_SIMULATOR::TypeToName( type, false ) );
617 }
618
619 m_commandTypeSizer->Show( true );
620 m_commandType->SetSelection( 0 );
621 m_simPages->SetSelection( m_simPages->FindPage( m_pgOP ) );
622 return;
623 }
624
626
627 SetTitle( SPICE_SIMULATOR::TypeToName( simType, true )
628 + wxT( " \u2014 " )
629 + SPICE_SIMULATOR::TypeToName( simType, false ) );
630
631 m_commandTypeSizer->Show( false );
632
633 wxStringTokenizer tokenizer( aCommand, wxS( " \t\n\r" ), wxTOKEN_STRTOK );
634 wxString token = tokenizer.GetNextToken().Lower();
635
636 switch( simType )
637 {
638 case ST_AC:
639 m_simPages->SetSelection( m_simPages->FindPage( m_pgAC ) );
640
641 token = tokenizer.GetNextToken().Lower();
642
643 for( SCALE_TYPE candidate : { DECADE, OCTAVE, LINEAR } )
644 {
645 if( scaleToString( candidate ) == token )
646 {
647 m_acScale->SetSelection( candidate );
648 break;
649 }
650 }
651
652 m_acPointsNumber->SetValue( tokenizer.GetNextToken() );
653 m_acFreqStart->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
654 m_acFreqStop->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
655 break;
656
657 case ST_SP:
658 m_simPages->SetSelection( m_simPages->FindPage( m_pgSP ) );
659
660 token = tokenizer.GetNextToken().Lower();
661
662 for( SCALE_TYPE candidate : { DECADE, OCTAVE, LINEAR } )
663 {
664 if( scaleToString( candidate ) == token )
665 {
666 m_spScale->SetSelection( candidate );
667 break;
668 }
669 }
670
671 m_spPointsNumber->SetValue( tokenizer.GetNextToken() );
672 m_spFreqStart->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
673 m_spFreqStop->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
674
675 if( tokenizer.HasMoreTokens() )
676 m_spDoNoise->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() == "1" );
677
678 break;
679
680 case ST_DC:
681 {
682 m_simPages->SetSelection( m_simPages->FindPage( m_pgDC ) );
683
684 SPICE_DC_PARAMS src1, src2;
685 src2.m_vincrement = SPICE_VALUE( -1 );
686
687 m_circuitModel->ParseDCCommand( aCommand, &src1, &src2 );
688
689 if( src1.m_source.IsSameAs( wxT( "TEMP" ), false ) )
690 setStringSelection( m_dcSourceType1, wxT( "TEMP" ) );
691 else
692 setStringSelection( m_dcSourceType1, src1.m_source.GetChar( 0 ) );
693
694 updateDCSources( src1.m_source.GetChar( 0 ), m_dcSource1 );
695 m_dcSource1->SetStringSelection( src1.m_source );
696 m_dcStart1->SetValue( src1.m_vstart.ToSpiceString() );
697 m_dcStop1->SetValue( src1.m_vend.ToSpiceString() );
698 m_dcIncr1->SetValue( src1.m_vincrement.ToSpiceString() );
699
700 if( src2.m_vincrement.ToDouble() != -1 )
701 {
702 if( src2.m_source.IsSameAs( wxT( "TEMP" ), false ) )
703 setStringSelection( m_dcSourceType2, wxT( "TEMP" ) );
704 else
705 setStringSelection( m_dcSourceType2, src2.m_source.GetChar( 0 ) );
706
707 updateDCSources( src2.m_source.GetChar( 0 ), m_dcSource2 );
708 m_dcSource2->SetStringSelection( src2.m_source );
709 m_dcStart2->SetValue( src2.m_vstart.ToSpiceString() );
710 m_dcStop2->SetValue( src2.m_vend.ToSpiceString() );
711 m_dcIncr2->SetValue( src2.m_vincrement.ToSpiceString() );
712
713 m_dcEnable2->SetValue( true );
714 }
715
716 break;
717 }
718
719 case ST_PZ:
720 {
721 m_simPages->SetSelection( m_simPages->FindPage( m_pgPZ ) );
722
723 wxString transferFunction;
724 wxString input, inputRef;
725 wxString output, outputRef;
726 SPICE_PZ_ANALYSES analyses;
727
728 m_circuitModel->ParsePZCommand( aCommand, &transferFunction, &input, &inputRef, &output,
729 &outputRef, &analyses );
730
731 m_pzInput->SetStringSelection( input );
732 m_pzInputRef->SetStringSelection( inputRef );
733 m_pzOutput->SetStringSelection( output );
734 m_pzOutputRef->SetStringSelection( outputRef );
735
736 m_pzFunctionType->SetSelection( transferFunction.Lower() == "cur" ? 1 : 0 );
737
738 if( analyses.m_Poles && analyses.m_Zeros )
739 m_pzAnalyses->SetSelection( 0 );
740 else if( analyses.m_Poles )
741 m_pzAnalyses->SetSelection( 1 );
742 else
743 m_pzAnalyses->SetSelection( 2 );
744
745 break;
746 }
747
748 case ST_NOISE:
749 {
750 m_simPages->SetSelection( m_simPages->FindPage( m_pgNOISE ) );
751
752 wxString output;
753 wxString ref;
754 wxString source;
755 wxString scale;
756 SPICE_VALUE pts;
757 SPICE_VALUE fStart;
758 SPICE_VALUE fStop;
759 bool saveAll;
760
761 m_circuitModel->ParseNoiseCommand( aCommand, &output, &ref, &source, &scale, &pts,
762 &fStart, &fStop, &saveAll );
763
764 m_noiseMeas->SetStringSelection( output );
765 m_noiseRef->SetStringSelection( ref );
766 m_noiseSrc->SetStringSelection( source );
767
768 for( SCALE_TYPE candidate : { DECADE, OCTAVE, LINEAR } )
769 {
770 if( scaleToString( candidate ) == scale )
771 {
772 m_noiseScale->SetSelection( candidate );
773 break;
774 }
775 }
776
777 m_noisePointsNumber->SetValue( pts.ToSpiceString() );
778 m_noiseFreqStart->SetValue( fStart.ToSpiceString() );
779 m_noiseFreqStop->SetValue( fStop.ToSpiceString() );
780
781 m_saveAllNoise->SetValue( saveAll );
782 break;
783 }
784
785 case ST_TRAN:
786 m_simPages->SetSelection( m_simPages->FindPage( m_pgTRAN ) );
787
788 // If the fields below are empty, it will be caught by the exception handler
789 m_transStep->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
790 m_transFinal->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
791
792 // Initial time is an optional field
793 token = tokenizer.GetNextToken();
794
795 if( !token.IsEmpty() )
796 m_transInitial->SetValue( SPICE_VALUE( token ).ToSpiceString() );
797
798 // Max step is an optional field
799 token = tokenizer.GetNextToken();
800
801 if( !token.IsEmpty() )
802 m_transMaxStep->SetValue( SPICE_VALUE( token ).ToSpiceString() );
803
804 // uic is an optional field
805 token = tokenizer.GetNextToken();
806
807 if( token.IsSameAs( wxS( "uic" ) ) )
808 m_useInitialConditions->SetValue( true );
809
810 break;
811
812 case ST_OP:
813 m_simPages->SetSelection( m_simPages->FindPage( m_pgOP ) );
814 break;
815
816 case ST_FFT:
817 {
818 m_simPages->SetSelection( m_simPages->FindPage( m_pgFFT ) );
819
820 while( tokenizer.HasMoreTokens() )
821 m_fftInputSignals.insert( tokenizer.GetNextToken() );
822
823 break;
824 }
825
826 default:
827 m_simPages->SetSelection( m_simPages->FindPage( m_pgCustom ) );
828 break;
829 }
830}
831
832
833void DIALOG_SIM_COMMAND::OnCommandType( wxCommandEvent& event )
834{
835 int sel = ST_UNKNOWN;
836 wxString str = m_commandType->GetString( event.GetSelection() );
837
838 for( int type = ST_UNKNOWN; type < ST_LAST; ++type )
839 {
840 if( str.StartsWith( SPICE_SIMULATOR::TypeToName( (SIM_TYPE) type, true ) ) )
841 sel = type;
842 }
843
844 switch( sel )
845 {
846 case ST_AC: m_simPages->SetSelection( m_simPages->FindPage( m_pgAC ) ); break;
847 case ST_SP: m_simPages->SetSelection( m_simPages->FindPage( m_pgSP ) ); break;
848 case ST_DC: m_simPages->SetSelection( m_simPages->FindPage( m_pgDC ) ); break;
849 case ST_PZ: m_simPages->SetSelection( m_simPages->FindPage( m_pgPZ ) ); break;
850 case ST_NOISE: m_simPages->SetSelection( m_simPages->FindPage( m_pgNOISE ) ); break;
851 case ST_TRAN: m_simPages->SetSelection( m_simPages->FindPage( m_pgTRAN ) ); break;
852 case ST_OP: m_simPages->SetSelection( m_simPages->FindPage( m_pgOP ) ); break;
853 case ST_FFT: m_simPages->SetSelection( m_simPages->FindPage( m_pgFFT ) ); break;
854 default: m_simPages->SetSelection( m_simPages->FindPage( m_pgCustom ) ); break;
855 }
856}
857
858
859void DIALOG_SIM_COMMAND::onSwapDCSources( wxCommandEvent& event )
860{
861 std::vector<std::pair<wxTextEntry*, wxTextEntry*>> textCtrl = { { m_dcStart1, m_dcStart2 },
862 { m_dcStop1, m_dcStop2 },
863 { m_dcIncr1, m_dcIncr2 } };
864
865 for( auto& couple : textCtrl )
866 {
867 wxString tmp = couple.first->GetValue();
868 couple.first->SetValue( couple.second->GetValue() );
869 couple.second->SetValue( tmp );
870 }
871
872 int src1 = m_dcSource1->GetSelection();
873 int src2 = m_dcSource2->GetSelection();
874
875 int sel = m_dcSourceType1->GetSelection();
876 m_dcSourceType1->SetSelection( m_dcSourceType2->GetSelection() );
877 m_dcSourceType2->SetSelection( sel );
878
879 wxChar type1 = getStringSelection( m_dcSourceType1 ).Upper().GetChar( 0 );
881 wxChar type2 = getStringSelection( m_dcSourceType2 ).Upper().GetChar( 0 );
883
884 m_dcSource1->SetSelection( src2 );
885 m_dcSource2->SetSelection( src1 );
886
889}
890
891
892void DIALOG_SIM_COMMAND::onDCSource1Selected( wxCommandEvent& event )
893{
894 wxChar type = m_dcSourceType1->GetString( m_dcSourceType1->GetSelection() ).Upper()[ 0 ];
897}
898
899
900void DIALOG_SIM_COMMAND::onDCSource2Selected( wxCommandEvent& event )
901{
902 wxChar type = m_dcSourceType2->GetString( m_dcSourceType2->GetSelection() ).Upper()[ 0 ];
905}
906
907
909{
910 bool is2ndSrcEnabled = m_dcEnable2->IsChecked();
911 wxChar type = getStringSelection( m_dcSourceType2 ).Upper().GetChar( 0 );
912
913 m_dcSourceType2->Enable( is2ndSrcEnabled );
914 m_dcSource2->Enable( is2ndSrcEnabled && type != 'T' );
915 m_dcStart2->Enable( is2ndSrcEnabled );
916 m_dcStop2->Enable( is2ndSrcEnabled );
917 m_dcIncr2->Enable( is2ndSrcEnabled );
918}
919
920
921void DIALOG_SIM_COMMAND::updateDCUnits( wxChar aType, wxStaticText* aStartValUnit,
922 wxStaticText* aEndValUnit, wxStaticText* aStepUnit )
923{
924 wxString unit;
925
926 switch( aType )
927 {
928 case 'V': unit = wxS( "V" ); break;
929 case 'I': unit = wxS( "A" ); break;
930 case 'R': unit = wxS( "Ω" ); break;
931 case 'T': unit = wxS( "°C" ); break;
932 }
933
934 aStartValUnit->SetLabel( unit );
935 aEndValUnit->SetLabel( unit );
936 aStepUnit->SetLabel( unit );
937
938 m_pgDC->Refresh();
939}
940
941
943{
944 if( m_circuitModel )
945 m_customTxt->SetValue( m_circuitModel->GetSchTextSimCommand() );
946}
947
948
949void DIALOG_SIM_COMMAND::OnFilterText( wxCommandEvent& aEvent )
950{
951 for( int ii = 0; ii < (int) m_inputSignalsList->GetCount(); ++ii )
952 {
953 if( m_inputSignalsList->IsChecked( ii ) )
954 m_fftInputSignals.insert( m_inputSignalsList->GetString( ii ) );
955 else
956 m_fftInputSignals.erase( m_inputSignalsList->GetString( ii ) );
957 }
958
959 m_inputSignalsList->Clear();
960
961 wxString aFilter = m_inputSignalsFilter->GetValue();
962
963 if( aFilter.IsEmpty() )
964 aFilter = wxS( "*" );
965
966 EDA_COMBINED_MATCHER matcher( aFilter.Upper(), CTX_SIGNAL );
967
968 for( const wxString& signal : m_simulatorFrame->Signals() )
969 {
970 if( matcher.Find( signal.Upper() ) )
971 {
972 m_inputSignalsList->Append( signal );
973
974 if( m_fftInputSignals.count( signal ) )
975 m_inputSignalsList->Check( m_inputSignalsList->GetCount() - 1 );
976 }
977 }
978}
979
980
981void DIALOG_SIM_COMMAND::OnFilterMouseMoved( wxMouseEvent& aEvent )
982{
983 wxPoint pos = aEvent.GetPosition();
984 wxRect ctrlRect = m_inputSignalsFilter->GetScreenRect();
985 int buttonWidth = ctrlRect.GetHeight(); // Presume buttons are square
986
987 if( m_inputSignalsFilter->IsSearchButtonVisible() && pos.x < buttonWidth )
988 SetCursor( wxCURSOR_ARROW );
989 else if( m_inputSignalsFilter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
990 SetCursor( wxCURSOR_ARROW );
991 else
992 SetCursor( wxCURSOR_IBEAM );
993}
994
995
void SetupStandardButtons(std::map< int, wxString > aLabels={})
Class DIALOG_SIM_COMMAND_BASE.
wxString evaluateDCControls(wxChoice *aDcSource, wxTextCtrl *aDcStart, wxTextCtrl *aDcStop, wxTextCtrl *aDcIncr)
Read values from one DC sweep source to form a part of simulation command.
void onSwapDCSources(wxCommandEvent &event) override
static wxString scaleToString(int aOption)
void SetPlotSettings(const SIM_TAB *aSimTab)
void OnCommandType(wxCommandEvent &event) override
void OnFilterMouseMoved(wxMouseEvent &event) override
wxIntegerValidator< int > m_posIntValidator
void onDCSource2Selected(wxCommandEvent &event) override
void OnUpdateUILockY3(wxUpdateUIEvent &event) override
bool TransferDataFromWindow() override
void updateDCSources(wxChar aType, wxChoice *aSource)
Update DC sweep source with symbols from schematic.
void OnUpdateUILockY1(wxUpdateUIEvent &event) override
std::set< wxString > m_fftInputSignals
SPICE_VALIDATOR m_spiceValidator
void onDCEnableSecondSource(wxCommandEvent &event) override
void ApplySettings(SIM_TAB *aTab)
SIMULATOR_FRAME * m_simulatorFrame
void onDCSource1Selected(wxCommandEvent &event) override
void parseCommand(const wxString &aCommand)
Parse a Spice directive.
bool TransferDataToWindow() override
void updateDCUnits(wxChar aType, wxStaticText *aStartValUnit, wxStaticText *aEndValUnit, wxStaticText *aStepUnit)
Update units on labels depending on selected source.
void OnFilterText(wxCommandEvent &event) override
std::shared_ptr< SPICE_CIRCUIT_MODEL > m_circuitModel
DIALOG_SIM_COMMAND(SIMULATOR_FRAME *aParent, std::shared_ptr< SPICE_CIRCUIT_MODEL > aCircuitModel, std::shared_ptr< SPICE_SETTINGS > &aSettings)
std::shared_ptr< SPICE_SETTINGS > m_settings
void OnUpdateUILockY2(wxUpdateUIEvent &event) override
SPICE_VALIDATOR m_spiceEmptyValidator
bool Find(const wxString &aTerm, int &aMatchersTriggered, int &aPosition)
Container for Ngspice simulator settings.
The SIMULATOR_FRAME holds the main user-interface for running simulations.
const std::vector< wxString > Signals()
void SetSimOptions(int aOptions)
Definition: sim_tab.h:53
static std::string Normalize(double aValue)
Definition: sim_value.cpp:405
static double ToDouble(const std::string &aString, double aDefault=NAN)
Definition: sim_value.cpp:418
static SIM_TYPE CommandToSimType(const wxString &aCmd)
Return simulation type basing on a simulation command directive.
static wxString TypeToName(SIM_TYPE aType, bool aShortName)
Return a string with simulation name based on enum.
Helper class to recognize Spice formatted values.
Definition: spice_value.h:56
wxString ToSpiceString() const
Return string value in Spice format (e.g.
double ToDouble() const
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:280
This file is part of the common library.
static void setStringSelection(wxChoice *aCtrl, const wxString &aStr)
static wxString getStringSelection(const wxChoice *aCtrl)
#define TO_INT(ctrl)
static bool empty(const wxTextEntryBase *aCtrl)
#define GET_STR(val)
#define _(s)
@ CTX_SIGNAL
SIM_TYPE
< Possible simulation types
Definition: sim_types.h:32
@ ST_SP
Definition: sim_types.h:43
@ ST_LAST
Definition: sim_types.h:45
@ ST_TRAN
Definition: sim_types.h:42
@ ST_UNKNOWN
Definition: sim_types.h:33
@ ST_NOISE
Definition: sim_types.h:37
@ ST_AC
Definition: sim_types.h:34
@ ST_DC
Definition: sim_types.h:35
@ ST_OP
Definition: sim_types.h:38
@ ST_FFT
Definition: sim_types.h:44
@ ST_PZ
Definition: sim_types.h:39
const int scale
SPICE_VALUE m_vincrement