KiCad PCB EDA Suite
dialog_sim_settings.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 CERN
5  * @author Maciej Suminski <maciej.suminski@cern.ch>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * https://www.gnu.org/licenses/gpl-3.0.html
20  * or you may search the http://www.gnu.org website for the version 3 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include "dialog_sim_settings.h"
27 #include <confirm.h>
28 
29 #include <wx/tokenzr.h>
30 
32 //so there are a few tabs missing (e.g. pole-zero, distortion, sensitivity)
33 
34 // Helper function to shorten conditions
35 static bool empty( const wxTextEntryBase* aCtrl )
36 {
37  return aCtrl->GetValue().IsEmpty();
38 }
39 
40 
42  : DIALOG_SIM_SETTINGS_BASE( aParent ), m_exporter( nullptr ), m_spiceEmptyValidator( true )
43 {
44  m_posIntValidator.SetMin( 1 );
45 
46  m_acPointsNumber->SetValidator( m_posIntValidator );
47  m_acFreqStart->SetValidator( m_spiceValidator );
48  m_acFreqStop->SetValidator( m_spiceValidator );
49 
50  m_dcStart1->SetValidator( m_spiceValidator );
51  m_dcStop1->SetValidator( m_spiceValidator );
52  m_dcIncr1->SetValidator( m_spiceValidator );
53 
54  m_dcStart2->SetValidator( m_spiceValidator );
55  m_dcStop2->SetValidator( m_spiceValidator );
56  m_dcIncr2->SetValidator( m_spiceValidator );
57 
58  m_noisePointsNumber->SetValidator( m_posIntValidator );
59  m_noiseFreqStart->SetValidator( m_spiceValidator );
60  m_noiseFreqStop->SetValidator( m_spiceValidator );
61 
62  m_transStep->SetValidator( m_spiceValidator );
63  m_transFinal->SetValidator( m_spiceValidator );
64  m_transInitial->SetValidator( m_spiceEmptyValidator );
65 
66  // Hide pages that aren't fully implemented yet
67  // wxPanel::Hide() isn't enough on some platforms
68  m_simPages->RemovePage( m_simPages->FindPage( m_pgDistortion ) );
69  m_simPages->RemovePage( m_simPages->FindPage( m_pgNoise ) );
70  m_simPages->RemovePage( m_simPages->FindPage( m_pgPoleZero ) );
71  m_simPages->RemovePage( m_simPages->FindPage( m_pgSensitivity ) );
72  m_simPages->RemovePage( m_simPages->FindPage( m_pgTransferFunction ) );
73 
74  m_sdbSizerOK->SetDefault();
76 
77 }
78 
79 
81 {
82  if( !wxDialog::TransferDataFromWindow() )
83  return false;
84 
85  wxWindow* page = m_simPages->GetCurrentPage();
86 
87  // AC analysis
88  if( page == m_pgAC )
89  {
90  if( !m_pgAC->Validate() )
91  return false;
92 
93  m_simCommand = wxString::Format( ".ac %s %s %s %s",
94  scaleToString( m_acScale->GetSelection() ),
95  m_acPointsNumber->GetValue(),
96  SPICE_VALUE( m_acFreqStart->GetValue() ).ToSpiceString(),
97  SPICE_VALUE( m_acFreqStop->GetValue() ).ToSpiceString() );
98  }
99 
100 
101  // DC transfer analysis
102  else if( page == m_pgDC )
103  {
104  // At least one source has to be enabled
105  if( !m_dcEnable1->IsChecked() && !m_dcEnable2->IsChecked() )
106  {
107  DisplayError( this, _( "You need to enable at least one source" ) );
108  return false;
109  }
110 
111  wxString simCmd = wxString( ".dc " );
112 
113  if( m_dcEnable1->IsChecked() )
114  {
115  if( empty( m_dcSource1 ) )
116  {
117  DisplayError( this, _( "You need to select DC source (sweep 1)" ) );
118  return false;
119  }
120 
122  // hence try..catch below
123  if( !m_dcStart1->Validate() || !m_dcStop1->Validate() || !m_dcIncr1->Validate() )
124  return false;
125 
126  try
127  {
128  wxString dcSource = m_exporter->GetSpiceDevice( m_dcSource1->GetValue() );
129 
130  simCmd += wxString::Format( "%s %s %s %s",
131  dcSource,
132  SPICE_VALUE( m_dcStart1->GetValue() ).ToSpiceString(),
133  SPICE_VALUE( m_dcStop1->GetValue() ).ToSpiceString(),
134  SPICE_VALUE( m_dcIncr1->GetValue() ).ToSpiceString() );
135  }
136  catch( std::exception& e )
137  {
138  DisplayError( this, e.what() );
139  return false;
140  }
141  catch( const KI_PARAM_ERROR& e )
142  {
143  DisplayError( this, e.What() );
144  return false;
145  }
146  catch( ... )
147  {
148  return false;
149  }
150  }
151 
152  if( m_dcEnable2->IsChecked() )
153  {
154  if( empty( m_dcSource2 ) )
155  {
156  DisplayError( this, _( "You need to select DC source (sweep 2)" ) );
157  return false;
158  }
159 
160  if( m_dcEnable1->IsChecked() && m_dcSource1->GetValue() == m_dcSource2->GetValue() )
161  {
162  DisplayError( this, _( "Source 1 and Source 2 must be different" ) );
163  return false;
164  }
165 
167  // hence try..catch below
168  if( !m_dcStart2->Validate() || !m_dcStop2->Validate() || !m_dcIncr2->Validate() )
169  return false;
170 
171  try
172  {
173  wxString dcSource = m_exporter->GetSpiceDevice( m_dcSource2->GetValue() );
174 
175  if( m_dcEnable1->IsChecked() )
176  simCmd += " ";
177 
178  simCmd += wxString::Format( "%s %s %s %s",
179  dcSource,
180  SPICE_VALUE( m_dcStart2->GetValue() ).ToSpiceString(),
181  SPICE_VALUE( m_dcStop2->GetValue() ).ToSpiceString(),
182  SPICE_VALUE( m_dcIncr2->GetValue() ).ToSpiceString() );
183  }
184  catch( std::exception& e )
185  {
186  DisplayError( this, e.what() );
187  return false;
188  }
189  catch( const KI_PARAM_ERROR& e )
190  {
191  DisplayError( this, e.What() );
192  return false;
193  }
194  catch( ... )
195  {
196  return false;
197  }
198 
199  }
200 
201  m_simCommand = simCmd;
202  }
203 
204 
205  // Noise analysis
206  else if( page == m_pgNoise )
207  {
209 
212  return false;
213 
214  wxString ref = empty( m_noiseRef )
215  ? wxString() : wxString::Format( ", %d", netMap.at( m_noiseRef->GetValue() ) );
216 
217  wxString noiseSource = m_exporter->GetSpiceDevice( m_noiseSrc->GetValue() );
218 
219  // Add voltage source prefix if needed
220  if( noiseSource[0] != 'v' && noiseSource[0] != 'V' )
221  noiseSource += 'v' + noiseSource;
222 
223  m_simCommand = wxString::Format( ".noise v(%d%s) %s %s %s %s %s",
224  netMap.at( m_noiseMeas->GetValue() ), ref,
225  noiseSource, scaleToString( m_noiseScale->GetSelection() ),
226  m_noisePointsNumber->GetValue(),
227  SPICE_VALUE( m_noiseFreqStart->GetValue() ).ToSpiceString(),
228  SPICE_VALUE( m_noiseFreqStop->GetValue() ).ToSpiceString() );
229  }
230 
231 
232  // DC operating point analysis
233  else if( page == m_pgOP )
234  {
235  m_simCommand = wxString( ".op" );
236  }
237 
238 
239  // Transient analysis
240  else if( page == m_pgTransient )
241  {
242  if( !m_pgTransient->Validate() )
243  return false;
244 
245  wxString initial = empty( m_transInitial )
246  ? "" : SPICE_VALUE( m_transInitial->GetValue() ).ToSpiceString();
247 
248  m_simCommand = wxString::Format( ".tran %s %s %s",
249  SPICE_VALUE( m_transStep->GetValue() ).ToSpiceString(),
250  SPICE_VALUE( m_transFinal->GetValue() ).ToSpiceString(),
251  initial );
252  }
253 
254 
255  // Custom directives
256  else if( page == m_pgCustom )
257  {
258  m_simCommand = m_customTxt->GetValue();
259  }
260 
261  else
262  {
263  return false;
264  }
265 
266  m_simCommand.Trim();
268 
269  return true;
270 }
271 
272 
274 {
276  if( empty( m_customTxt ) )
277  loadDirectives();
278 
279  if( m_simCommand.IsEmpty() && !empty( m_customTxt ) )
280  return parseCommand( m_customTxt->GetValue() );
281 
282  return true;
283 }
284 
285 
287 {
288  // Fill out comboboxes that allows one to select nets
289  // Map comoboxes to their current values
290  std::map<wxComboBox*, wxString> cmbNet = {
291  { m_noiseMeas, m_noiseMeas->GetStringSelection() },
292  { m_noiseRef, m_noiseRef->GetStringSelection() }
293  };
294 
295  for( auto c : cmbNet )
296  c.first->Clear();
297 
298  for( const auto& net : m_exporter->GetNetIndexMap() )
299  {
300  for( auto c : cmbNet )
301  c.first->Append( net.first );
302  }
303 
304  // Try to restore the previous selection, if possible
305  for( auto c : cmbNet )
306  {
307  int idx = c.first->FindString( c.second );
308 
309  if( idx != wxNOT_FOUND )
310  c.first->SetSelection( idx );
311  }
312 
313 
314  // Fill out comboboxes that allows one to select power sources
315  std::map<wxComboBox*, wxString> cmbSrc = {
316  { m_dcSource1, m_dcSource1->GetStringSelection() },
317  { m_dcSource2, m_dcSource2->GetStringSelection() },
318  { m_noiseSrc, m_noiseSrc->GetStringSelection() },
319  };
320 
321  for( auto c : cmbSrc )
322  c.first->Clear();
323 
324  for( const auto& item : m_exporter->GetSpiceItems() )
325  {
326  if( item.m_primitive == 'V' )
327  {
328  for( auto c : cmbSrc )
329  c.first->Append( item.m_refName );
330  }
331  }
332 
333  // Try to restore the previous selection, if possible
334  for( auto c : cmbSrc )
335  {
336  int idx = c.first->FindString( c.second );
337 
338  if( idx != wxNOT_FOUND )
339  c.first->SetSelection( idx );
340  }
341 
342  return DIALOG_SIM_SETTINGS_BASE::ShowModal();
343 }
344 
345 
346 bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand )
347 {
348  if( aCommand.IsEmpty() )
349  return false;
350 
351  wxStringTokenizer tokenizer( aCommand, " " );
352  wxString tkn = tokenizer.GetNextToken().Lower();
353 
354  try {
355  if( tkn == ".ac" )
356  {
357  m_simPages->SetSelection( m_simPages->FindPage( m_pgAC ) );
358 
359  tkn = tokenizer.GetNextToken().Lower();
360 
361  if( tkn == "dec" )
362  m_acScale->SetSelection( 0 );
363  if( tkn == "oct" )
364  m_acScale->SetSelection( 1 );
365  if( tkn == "lin" )
366  m_acScale->SetSelection( 2 );
367  else
368  return false;
369 
370  // If the fields below are empty, it will be caught by the exception handler
371  m_acPointsNumber->SetValue( tokenizer.GetNextToken() );
372  m_acFreqStart->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
373  m_acFreqStop->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
374  }
375 
376  else if( tkn == ".dc" )
377  {
378  m_simPages->SetSelection( m_simPages->FindPage( m_pgDC ) );
379 
380  tkn = tokenizer.GetNextToken();
381 
382  if( !tkn.IsEmpty() )
383  {
384  m_dcSource1->SetValue( tkn );
385  m_dcStart1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
386  m_dcStop1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
387  m_dcIncr1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
388 
389  // Check the 'Enabled' field, if all values are filled
390  m_dcEnable1->SetValue( !empty( m_dcSource1 ) && !empty( m_dcStart1 )
391  && !empty( m_dcStop1 ) && !empty( m_dcIncr1 ) );
392  }
393 
394  tkn = tokenizer.GetNextToken();
395 
396  if( !tkn.IsEmpty() )
397  {
398  m_dcSource2->SetValue( tkn );
399  m_dcStart2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
400  m_dcStop2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
401  m_dcIncr2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
402 
403  // Check the 'Enabled' field, if all values are filled
404  m_dcEnable2->SetValue( !empty( m_dcSource2 ) && !empty( m_dcStart2 )
405  && !empty( m_dcStop2 ) && !empty( m_dcIncr2 ) );
406  }
407 
408  // Check if the directive is complete
409  if( !m_dcEnable1->IsChecked() || !m_dcEnable2->IsChecked() )
410  return false;
411  }
412 
413  else if( tkn == ".tran" )
414  {
415  m_simPages->SetSelection( m_simPages->FindPage( m_pgTransient ) );
416 
417  // If the fields below are empty, it will be caught by the exception handler
418  m_transStep->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
419  m_transFinal->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
420 
421  // Initial time is an optional field
422  tkn = tokenizer.GetNextToken();
423 
424  if( !tkn.IsEmpty() )
425  m_transInitial->SetValue( SPICE_VALUE( tkn ).ToSpiceString() );
426  }
427 
428  // Custom directives
429  else if( !empty( m_customTxt ) )
430  {
431  m_simPages->SetSelection( m_simPages->FindPage( m_pgCustom ) );
432  }
433  }
434  catch( ... )
435  {
436  // Nothing really bad has happened
437  return false;
438  }
439 
440  return true;
441 }
442 
443 
445 {
446  if( m_exporter )
447  m_customTxt->SetValue( m_exporter->GetSheetSimCommand() );
448 }
449 
450 
452 {
454 
455  if( !m_fixPassiveVals->IsChecked() )
457 
458  if( !m_fixIncludePaths->IsChecked() )
460 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:240
const SPICE_ITEM_LIST & GetSpiceItems() const
Returns list of items representing schematic components in the Spice world.
This file is part of the common library.
SPICE_VALIDATOR m_spiceEmptyValidator
static wxString scaleToString(int aOption)
Class DIALOG_SIM_SETTINGS_BASE.
SPICE_VALIDATOR m_spiceValidator
wxString GetSheetSimCommand()
Returns simulation command directives placed in schematic sheets (if any).
Helper class to handle Spice way of expressing values (e.g. 10.5 Meg)
Definition: spice_value.h:32
bool parseCommand(const wxString &aCommand)
Parses a Spice directive.
const NET_INDEX_MAP & GetNetIndexMap() const
Returns a map of circuit nodes to net names.
std::map< wxString, int > NET_INDEX_MAP
Net name to circuit node number mapping
bool TransferDataFromWindow() override
const wxString What() const
Definition: ki_exception.h:58
NETLIST_EXPORTER_PSPICE_SIM * m_exporter
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
DIALOG_SIM_SETTINGS(wxWindow *aParent)
#define _(s)
Definition: 3d_actions.cpp:33
bool TransferDataToWindow() override
wxIntegerValidator< int > m_posIntValidator
static bool empty(const wxTextEntryBase *aCtrl)
wxString GetSpiceDevice(const wxString &aComponent) const
Returns name of Spice device corresponding to a schematic component.
KI_PARAM_ERROR is a class used to hold a translatable error message and may be used when throwing exc...
Definition: ki_exception.h:45