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