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