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 
162  // hence try..catch below
163  if( !m_dcStart2->Validate() || !m_dcStop2->Validate() || !m_dcIncr2->Validate() )
164  return false;
165 
166  try
167  {
168  wxString dcSource = m_exporter->GetSpiceDevice( m_dcSource2->GetValue() );
169 
170  if( m_dcEnable1->IsChecked() )
171  simCmd += " ";
172 
173  simCmd += wxString::Format( "%s %s %s %s",
174  dcSource,
175  SPICE_VALUE( m_dcStart2->GetValue() ).ToSpiceString(),
176  SPICE_VALUE( m_dcStop2->GetValue() ).ToSpiceString(),
177  SPICE_VALUE( m_dcIncr2->GetValue() ).ToSpiceString() );
178  }
179  catch( std::exception& e )
180  {
181  DisplayError( this, e.what() );
182  return false;
183  }
184  catch( const KI_PARAM_ERROR& e )
185  {
186  DisplayError( this, e.What() );
187  return false;
188  }
189  catch( ... )
190  {
191  return false;
192  }
193 
194  }
195 
196  m_simCommand = simCmd;
197  }
198 
199 
200  // Noise analysis
201  else if( page == m_pgNoise )
202  {
204 
207  return false;
208 
209  wxString ref = empty( m_noiseRef )
210  ? wxString() : wxString::Format( ", %d", netMap.at( m_noiseRef->GetValue() ) );
211 
212  wxString noiseSource = m_exporter->GetSpiceDevice( m_noiseSrc->GetValue() );
213 
214  // Add voltage source prefix if needed
215  if( noiseSource[0] != 'v' && noiseSource[0] != 'V' )
216  noiseSource += 'v' + noiseSource;
217 
218  m_simCommand = wxString::Format( ".noise v(%d%s) %s %s %s %s %s",
219  netMap.at( m_noiseMeas->GetValue() ), ref,
220  noiseSource, scaleToString( m_noiseScale->GetSelection() ),
221  m_noisePointsNumber->GetValue(),
222  SPICE_VALUE( m_noiseFreqStart->GetValue() ).ToSpiceString(),
223  SPICE_VALUE( m_noiseFreqStop->GetValue() ).ToSpiceString() );
224  }
225 
226 
227  // DC operating point analysis
228  else if( page == m_pgOP )
229  {
230  m_simCommand = wxString( ".op" );
231  }
232 
233 
234  // Transient analysis
235  else if( page == m_pgTransient )
236  {
237  if( !m_pgTransient->Validate() )
238  return false;
239 
240  wxString initial = empty( m_transInitial )
241  ? "" : SPICE_VALUE( m_transInitial->GetValue() ).ToSpiceString();
242 
243  m_simCommand = wxString::Format( ".tran %s %s %s",
244  SPICE_VALUE( m_transStep->GetValue() ).ToSpiceString(),
245  SPICE_VALUE( m_transFinal->GetValue() ).ToSpiceString(),
246  initial );
247  }
248 
249 
250  // Custom directives
251  else if( page == m_pgCustom )
252  {
253  m_simCommand = m_customTxt->GetValue();
254  }
255 
256  else
257  {
258  return false;
259  }
260 
261  m_simCommand.Trim();
263 
264  return true;
265 }
266 
267 
269 {
271  if( empty( m_customTxt ) )
272  loadDirectives();
273 
274  if( m_simCommand.IsEmpty() && !empty( m_customTxt ) )
275  return parseCommand( m_customTxt->GetValue() );
276 
277  return true;
278 }
279 
280 
282 {
283  // Fill out comboboxes that allows one to select nets
284  // Map comoboxes to their current values
285  std::map<wxComboBox*, wxString> cmbNet = {
286  { m_noiseMeas, m_noiseMeas->GetStringSelection() },
287  { m_noiseRef, m_noiseRef->GetStringSelection() }
288  };
289 
290  for( auto c : cmbNet )
291  c.first->Clear();
292 
293  for( auto net : m_exporter->GetNetIndexMap() )
294  {
295  for( auto c : cmbNet )
296  c.first->Append( net.first );
297  }
298 
299  // Try to restore the previous selection, if possible
300  for( auto c : cmbNet )
301  {
302  int idx = c.first->FindString( c.second );
303 
304  if( idx != wxNOT_FOUND )
305  c.first->SetSelection( idx );
306  }
307 
308 
309  // Fill out comboboxes that allows one to select power sources
310  std::map<wxComboBox*, wxString> cmbSrc = {
311  { m_dcSource1, m_dcSource1->GetStringSelection() },
312  { m_dcSource2, m_dcSource2->GetStringSelection() },
313  { m_noiseSrc, m_noiseSrc->GetStringSelection() },
314  };
315 
316  for( auto c : cmbSrc )
317  c.first->Clear();
318 
319  for( auto item : m_exporter->GetSpiceItems() )
320  {
321  if( item.m_primitive == 'V' )
322  {
323  for( auto c : cmbSrc )
324  c.first->Append( item.m_refName );
325  }
326  }
327 
328  // Try to restore the previous selection, if possible
329  for( auto c : cmbSrc )
330  {
331  int idx = c.first->FindString( c.second );
332 
333  if( idx != wxNOT_FOUND )
334  c.first->SetSelection( idx );
335  }
336 
337  return DIALOG_SIM_SETTINGS_BASE::ShowModal();
338 }
339 
340 
341 bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand )
342 {
343  if( aCommand.IsEmpty() )
344  return false;
345 
346  wxStringTokenizer tokenizer( aCommand, " " );
347  wxString tkn = tokenizer.GetNextToken().Lower();
348 
349  try {
350  if( tkn == ".ac" )
351  {
352  m_simPages->SetSelection( m_simPages->FindPage( m_pgAC ) );
353 
354  tkn = tokenizer.GetNextToken().Lower();
355 
356  if( tkn == "dec" )
357  m_acScale->SetSelection( 0 );
358  if( tkn == "oct" )
359  m_acScale->SetSelection( 1 );
360  if( tkn == "lin" )
361  m_acScale->SetSelection( 2 );
362  else
363  return false;
364 
365  // If the fields below are empty, it will be caught by the exception handler
366  m_acPointsNumber->SetValue( tokenizer.GetNextToken() );
367  m_acFreqStart->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
368  m_acFreqStop->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
369  }
370 
371  else if( tkn == ".dc" )
372  {
373  m_simPages->SetSelection( m_simPages->FindPage( m_pgDC ) );
374 
375  tkn = tokenizer.GetNextToken();
376 
377  if( !tkn.IsEmpty() )
378  {
379  m_dcSource1->SetValue( tkn );
380  m_dcStart1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
381  m_dcStop1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
382  m_dcIncr1->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
383 
384  // Check the 'Enabled' field, if all values are filled
385  m_dcEnable1->SetValue( !empty( m_dcSource1 ) && !empty( m_dcStart1 )
386  && !empty( m_dcStop1 ) && !empty( m_dcIncr1 ) );
387  }
388 
389  tkn = tokenizer.GetNextToken();
390 
391  if( !tkn.IsEmpty() )
392  {
393  m_dcSource2->SetValue( tkn );
394  m_dcStart2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
395  m_dcStop2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
396  m_dcIncr2->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
397 
398  // Check the 'Enabled' field, if all values are filled
399  m_dcEnable2->SetValue( !empty( m_dcSource2 ) && !empty( m_dcStart2 )
400  && !empty( m_dcStop2 ) && !empty( m_dcIncr2 ) );
401  }
402 
403  // Check if the directive is complete
404  if( !m_dcEnable1->IsChecked() || !m_dcEnable2->IsChecked() )
405  return false;
406  }
407 
408  else if( tkn == ".tran" )
409  {
410  m_simPages->SetSelection( m_simPages->FindPage( m_pgTransient ) );
411 
412  // If the fields below are empty, it will be caught by the exception handler
413  m_transStep->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
414  m_transFinal->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() );
415 
416  // Initial time is an optional field
417  tkn = tokenizer.GetNextToken();
418 
419  if( !tkn.IsEmpty() )
420  m_transInitial->SetValue( SPICE_VALUE( tkn ).ToSpiceString() );
421  }
422 
423  // Custom directives
424  else if( !empty( m_customTxt ) )
425  {
426  m_simPages->SetSelection( m_simPages->FindPage( m_pgCustom ) );
427  }
428  }
429  catch( ... )
430  {
431  // Nothing really bad has happened
432  return false;
433  }
434 
435  return true;
436 }
437 
438 
440 {
441  if( m_exporter )
442  m_customTxt->SetValue( m_exporter->GetSheetSimCommand() );
443 }
444 
445 
447 {
449 
450  if( !m_fixPassiveVals->IsChecked() )
452 
453  if( !m_fixIncludePaths->IsChecked() )
455 }
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: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:244
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