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