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