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_pgOP ) );
71  m_simPages->RemovePage( m_simPages->FindPage( m_pgPoleZero ) );
72  m_simPages->RemovePage( m_simPages->FindPage( m_pgSensitivity ) );
73  m_simPages->RemovePage( m_simPages->FindPage( m_pgTransferFunction ) );
74 
75  m_sdbSizerOK->SetDefault();
77 
78 }
79 
80 
82 {
83  if( !wxDialog::TransferDataFromWindow() )
84  return false;
85 
86  wxWindow* page = m_simPages->GetCurrentPage();
87 
88  // AC analysis
89  if( page == m_pgAC )
90  {
91  if( !m_pgAC->Validate() )
92  return false;
93 
94  m_simCommand = wxString::Format( ".ac %s %s %s %s",
95  scaleToString( m_acScale->GetSelection() ),
96  m_acPointsNumber->GetValue(),
97  SPICE_VALUE( m_acFreqStart->GetValue() ).ToSpiceString(),
98  SPICE_VALUE( m_acFreqStop->GetValue() ).ToSpiceString() );
99  }
100 
101 
102  // DC transfer analysis
103  else if( page == m_pgDC )
104  {
105  // At least one source has to be enabled
106  if( !m_dcEnable1->IsChecked() && !m_dcEnable2->IsChecked() )
107  {
108  DisplayError( this, _( "You need to enable at least one source" ) );
109  return false;
110  }
111 
112  wxString simCmd = wxString( ".dc " );
113 
114  if( m_dcEnable1->IsChecked() )
115  {
116  if( empty( m_dcSource1 ) )
117  {
118  DisplayError( this, _( "You need to select DC source (sweep 1)" ) );
119  return false;
120  }
121 
123  // hence try..catch below
124  if( !m_dcStart1->Validate() || !m_dcStop1->Validate() || !m_dcIncr1->Validate() )
125  return false;
126 
127  try
128  {
129  wxString dcSource = m_exporter->GetSpiceDevice( m_dcSource1->GetValue() );
130 
131  simCmd += wxString::Format( "%s %s %s %s",
132  dcSource,
133  SPICE_VALUE( m_dcStart1->GetValue() ).ToSpiceString(),
134  SPICE_VALUE( m_dcStop1->GetValue() ).ToSpiceString(),
135  SPICE_VALUE( m_dcIncr1->GetValue() ).ToSpiceString() );
136  }
137  catch( std::exception& e )
138  {
139  DisplayError( this, e.what() );
140  return false;
141  }
142  catch( const KI_PARAM_ERROR& e )
143  {
144  DisplayError( this, e.What() );
145  return false;
146  }
147  catch( ... )
148  {
149  return false;
150  }
151  }
152 
153  if( m_dcEnable2->IsChecked() )
154  {
155  if( empty( m_dcSource2 ) )
156  {
157  DisplayError( this, _( "You need to select DC source (sweep 2)" ) );
158  return false;
159  }
160 
161  if( m_dcEnable1->IsChecked() && m_dcSource1->GetValue() == m_dcSource2->GetValue() )
162  {
163  DisplayError( this, _( "Source 1 and Source 2 must be different" ) );
164  return false;
165  }
166 
168  // hence try..catch below
169  if( !m_dcStart2->Validate() || !m_dcStop2->Validate() || !m_dcIncr2->Validate() )
170  return false;
171 
172  try
173  {
174  wxString dcSource = m_exporter->GetSpiceDevice( m_dcSource2->GetValue() );
175 
176  if( m_dcEnable1->IsChecked() )
177  simCmd += " ";
178 
179  simCmd += wxString::Format( "%s %s %s %s",
180  dcSource,
181  SPICE_VALUE( m_dcStart2->GetValue() ).ToSpiceString(),
182  SPICE_VALUE( m_dcStop2->GetValue() ).ToSpiceString(),
183  SPICE_VALUE( m_dcIncr2->GetValue() ).ToSpiceString() );
184  }
185  catch( std::exception& e )
186  {
187  DisplayError( this, e.what() );
188  return false;
189  }
190  catch( const KI_PARAM_ERROR& e )
191  {
192  DisplayError( this, e.What() );
193  return false;
194  }
195  catch( ... )
196  {
197  return false;
198  }
199 
200  }
201 
202  m_simCommand = simCmd;
203  }
204 
205 
206  // Noise analysis
207  else if( page == m_pgNoise )
208  {
210 
213  return false;
214 
215  wxString ref = empty( m_noiseRef )
216  ? wxString() : wxString::Format( ", %d", netMap.at( m_noiseRef->GetValue() ) );
217 
218  wxString noiseSource = m_exporter->GetSpiceDevice( m_noiseSrc->GetValue() );
219 
220  // Add voltage source prefix if needed
221  if( noiseSource[0] != 'v' && noiseSource[0] != 'V' )
222  noiseSource += 'v' + noiseSource;
223 
224  m_simCommand = wxString::Format( ".noise v(%d%s) %s %s %s %s %s",
225  netMap.at( m_noiseMeas->GetValue() ), ref,
226  noiseSource, scaleToString( m_noiseScale->GetSelection() ),
227  m_noisePointsNumber->GetValue(),
228  SPICE_VALUE( m_noiseFreqStart->GetValue() ).ToSpiceString(),
229  SPICE_VALUE( m_noiseFreqStop->GetValue() ).ToSpiceString() );
230  }
231 
232 
233  // DC operating point analysis
234  else if( page == m_pgOP )
235  {
236  m_simCommand = wxString( ".op" );
237  }
238 
239 
240  // Transient analysis
241  else if( page == m_pgTransient )
242  {
243  if( !m_pgTransient->Validate() )
244  return false;
245 
246  wxString initial = empty( m_transInitial )
247  ? "" : SPICE_VALUE( m_transInitial->GetValue() ).ToSpiceString();
248 
249  m_simCommand = wxString::Format( ".tran %s %s %s",
250  SPICE_VALUE( m_transStep->GetValue() ).ToSpiceString(),
251  SPICE_VALUE( m_transFinal->GetValue() ).ToSpiceString(),
252  initial );
253  }
254 
255 
256  // Custom directives
257  else if( page == m_pgCustom )
258  {
259  m_simCommand = m_customTxt->GetValue();
260  }
261 
262  else
263  {
264  return false;
265  }
266 
267  m_simCommand.Trim();
269 
270  return true;
271 }
272 
273 
275 {
277  if( empty( m_customTxt ) )
278  loadDirectives();
279 
280  if( m_simCommand.IsEmpty() && !empty( m_customTxt ) )
281  return parseCommand( m_customTxt->GetValue() );
282 
283  return true;
284 }
285 
286 
288 {
289  // Fill out comboboxes that allows one to select nets
290  // Map comoboxes to their current values
291  std::map<wxComboBox*, wxString> cmbNet = {
292  { m_noiseMeas, m_noiseMeas->GetStringSelection() },
293  { m_noiseRef, m_noiseRef->GetStringSelection() }
294  };
295 
296  for( auto c : cmbNet )
297  c.first->Clear();
298 
299  for( auto net : m_exporter->GetNetIndexMap() )
300  {
301  for( auto c : cmbNet )
302  c.first->Append( net.first );
303  }
304 
305  // Try to restore the previous selection, if possible
306  for( auto c : cmbNet )
307  {
308  int idx = c.first->FindString( c.second );
309 
310  if( idx != wxNOT_FOUND )
311  c.first->SetSelection( idx );
312  }
313 
314 
315  // Fill out comboboxes that allows one to select power sources
316  std::map<wxComboBox*, wxString> cmbSrc = {
317  { m_dcSource1, m_dcSource1->GetStringSelection() },
318  { m_dcSource2, m_dcSource2->GetStringSelection() },
319  { m_noiseSrc, m_noiseSrc->GetStringSelection() },
320  };
321 
322  for( auto c : cmbSrc )
323  c.first->Clear();
324 
325  for( auto item : m_exporter->GetSpiceItems() )
326  {
327  if( item.m_primitive == 'V' )
328  {
329  for( auto c : cmbSrc )
330  c.first->Append( item.m_refName );
331  }
332  }
333 
334  // Try to restore the previous selection, if possible
335  for( auto c : cmbSrc )
336  {
337  int idx = c.first->FindString( c.second );
338 
339  if( idx != wxNOT_FOUND )
340  c.first->SetSelection( idx );
341  }
342 
343  return DIALOG_SIM_SETTINGS_BASE::ShowModal();
344 }
345 
346 
347 bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand )
348 {
349  if( aCommand.IsEmpty() )
350  return false;
351 
352  wxStringTokenizer tokenizer( aCommand, " " );
353  wxString tkn = tokenizer.GetNextToken().Lower();
354 
355  try {
356  if( tkn == ".ac" )
357  {
358  m_simPages->SetSelection( m_simPages->FindPage( m_pgAC ) );
359 
360  tkn = tokenizer.GetNextToken().Lower();
361 
362  if( tkn == "dec" )
363  m_acScale->SetSelection( 0 );
364  if( tkn == "oct" )
365  m_acScale->SetSelection( 1 );
366  if( tkn == "lin" )
367  m_acScale->SetSelection( 2 );
368  else
369  return false;
370 
371  // If the fields below are empty, it will be caught by the exception handler
372  m_acPointsNumber->SetValue( tokenizer.GetNextToken() );
373  m_acFreqStart->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
374  m_acFreqStop->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
375  }
376 
377  else if( tkn == ".dc" )
378  {
379  m_simPages->SetSelection( m_simPages->FindPage( m_pgDC ) );
380 
381  tkn = tokenizer.GetNextToken();
382 
383  if( !tkn.IsEmpty() )
384  {
385  m_dcSource1->SetValue( tkn );
386  m_dcStart1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
387  m_dcStop1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
388  m_dcIncr1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
389 
390  // Check the 'Enabled' field, if all values are filled
391  m_dcEnable1->SetValue( !empty( m_dcSource1 ) && !empty( m_dcStart1 )
392  && !empty( m_dcStop1 ) && !empty( m_dcIncr1 ) );
393  }
394 
395  tkn = tokenizer.GetNextToken();
396 
397  if( !tkn.IsEmpty() )
398  {
399  m_dcSource2->SetValue( tkn );
400  m_dcStart2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
401  m_dcStop2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
402  m_dcIncr2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
403 
404  // Check the 'Enabled' field, if all values are filled
405  m_dcEnable2->SetValue( !empty( m_dcSource2 ) && !empty( m_dcStart2 )
406  && !empty( m_dcStop2 ) && !empty( m_dcIncr2 ) );
407  }
408 
409  // Check if the directive is complete
410  if( !m_dcEnable1->IsChecked() || !m_dcEnable2->IsChecked() )
411  return false;
412  }
413 
414  else if( tkn == ".tran" )
415  {
416  m_simPages->SetSelection( m_simPages->FindPage( m_pgTransient ) );
417 
418  // If the fields below are empty, it will be caught by the exception handler
419  m_transStep->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
420  m_transFinal->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
421 
422  // Initial time is an optional field
423  tkn = tokenizer.GetNextToken();
424 
425  if( !tkn.IsEmpty() )
426  m_transInitial->SetValue( SPICE_VALUE( tkn ).ToSpiceString() );
427  }
428 
429  // Custom directives
430  else if( !empty( m_customTxt ) )
431  {
432  m_simPages->SetSelection( m_simPages->FindPage( m_pgCustom ) );
433  }
434  }
435  catch( ... )
436  {
437  // Nothing really bad has happened
438  return false;
439  }
440 
441  return true;
442 }
443 
444 
446 {
447  if( m_exporter )
448  m_customTxt->SetValue( m_exporter->GetSheetSimCommand() );
449 }
450 
451 
453 {
455 
456  if( !m_fixPassiveVals->IsChecked() )
458 
459  if( !m_fixIncludePaths->IsChecked() )
461 }
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.
#define _(s)
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:205
DIALOG_SIM_SETTINGS(wxWindow *aParent)
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.
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:243
class KI_PARAM_ERROR is a class used to hold a translatable error message and may be used when throwi...
Definition: ki_exception.h:45