KiCad PCB EDA Suite
dialog_spice_model.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_spice_model.h"
26 
28 #include <sim/spice_value.h>
29 #include <confirm.h>
30 #include <project.h>
31 
32 #include <wx/tokenzr.h>
33 
34 // Helper function to shorten conditions
35 static bool empty( const wxTextCtrl* aCtrl )
36 {
37  return aCtrl->GetValue().IsEmpty();
38 }
39 
40 
41 // Function to sort PWL values list
42 static int wxCALLBACK comparePwlValues( wxIntPtr aItem1, wxIntPtr aItem2, wxIntPtr WXUNUSED( aSortData ) )
43 {
44  float* t1 = reinterpret_cast<float*>( &aItem1 );
45  float* t2 = reinterpret_cast<float*>( &aItem2 );
46 
47  if( *t1 > *t2 )
48  return 1;
49 
50  if( *t1 < *t2 )
51  return -1;
52 
53  return 0;
54 }
55 
56 
57 DIALOG_SPICE_MODEL::DIALOG_SPICE_MODEL( wxWindow* aParent, SCH_COMPONENT& aComponent, SCH_FIELDS& aFields )
58  : DIALOG_SPICE_MODEL_BASE( aParent ), m_component( aComponent ), m_fields( aFields ),
59  m_spiceEmptyValidator( true ), m_notEmptyValidator( wxFILTER_EMPTY )
60 {
61  m_pasValue->SetValidator( m_spiceValidator );
62 
63  m_semiType->SetValidator( m_notEmptyValidator );
64  m_semiModel->SetValidator( m_notEmptyValidator );
65 
66  m_icModel->SetValidator( m_notEmptyValidator );
67 
68  m_genDc->SetValidator( m_spiceEmptyValidator );
69  m_genAcMag->SetValidator( m_spiceEmptyValidator );
70  m_genAcPhase->SetValidator( m_spiceEmptyValidator );
71 
72  m_pulseInit->SetValidator( m_spiceEmptyValidator );
73  m_pulseNominal->SetValidator( m_spiceEmptyValidator );
74  m_pulseDelay->SetValidator( m_spiceEmptyValidator );
75  m_pulseRise->SetValidator( m_spiceEmptyValidator );
76  m_pulseFall->SetValidator( m_spiceEmptyValidator );
77  m_pulseWidth->SetValidator( m_spiceEmptyValidator );
78  m_pulsePeriod->SetValidator( m_spiceEmptyValidator );
79 
80  m_sinOffset->SetValidator( m_spiceEmptyValidator );
81  m_sinAmplitude->SetValidator( m_spiceEmptyValidator );
82  m_sinFreq->SetValidator( m_spiceEmptyValidator );
83  m_sinDelay->SetValidator( m_spiceEmptyValidator );
84  m_sinDampFactor->SetValidator( m_spiceEmptyValidator );
85 
86  m_expInit->SetValidator( m_spiceEmptyValidator );
87  m_expPulsed->SetValidator( m_spiceEmptyValidator );
88  m_expRiseDelay->SetValidator( m_spiceEmptyValidator );
89  m_expRiseConst->SetValidator( m_spiceEmptyValidator );
90  m_expFallDelay->SetValidator( m_spiceEmptyValidator );
91  m_expFallConst->SetValidator( m_spiceEmptyValidator );
92 
93  m_pwlTimeCol = m_pwlValList->AppendColumn( "Time [s]", wxLIST_FORMAT_LEFT, 100 );
94  m_pwlValueCol = m_pwlValList->AppendColumn( "Value [V/A]", wxLIST_FORMAT_LEFT, 100 );
95 
96  m_sdbSizerOK->SetDefault();
97 
98 }
99 
100 
102 {
103  if( !DIALOG_SPICE_MODEL_BASE::TransferDataFromWindow() )
104  return false;
105 
106  wxWindow* page = m_notebook->GetCurrentPage();
107 
108  // Passive
109  if( page == m_passive )
110  {
111  if( !m_passive->Validate() )
112  return false;
113 
114  switch( m_pasType->GetSelection() )
115  {
116  case 0: m_fieldsTmp[SF_PRIMITIVE] = (char) SP_RESISTOR; break;
117  case 1: m_fieldsTmp[SF_PRIMITIVE] = (char) SP_CAPACITOR; break;
118  case 2: m_fieldsTmp[SF_PRIMITIVE] = (char) SP_INDUCTOR; break;
119 
120  default:
121  wxASSERT_MSG( false, "Unhandled passive type" );
122  return false;
123  break;
124  }
125 
126  m_fieldsTmp[SF_MODEL] = m_pasValue->GetValue();
127  }
128 
129 
130  // Semiconductor
131  else if( page == m_semiconductor )
132  {
133  if( !m_semiconductor->Validate() )
134  return false;
135 
136  switch( m_semiType->GetSelection() )
137  {
138  case 0: m_fieldsTmp[SF_PRIMITIVE] = (char) SP_DIODE; break;
139  case 1: m_fieldsTmp[SF_PRIMITIVE] = (char) SP_BJT; break;
140  case 2: m_fieldsTmp[SF_PRIMITIVE] = (char) SP_MOSFET; break;
141 
142  default:
143  wxASSERT_MSG( false, "Unhandled semiconductor type" );
144  return false;
145  break;
146  }
147 
148  m_fieldsTmp[SF_MODEL] = m_semiModel->GetValue();
149 
150  if( !empty( m_semiLib ) )
151  m_fieldsTmp[SF_LIB_FILE] = m_semiLib->GetValue();
152  }
153 
154 
155  // Integrated circuit
156  else if( page == m_ic )
157  {
158  if( !m_ic->Validate() )
159  return false;
160 
162  m_fieldsTmp[SF_MODEL] = m_icModel->GetValue();
163 
164  if( !empty( m_icLib ) )
165  m_fieldsTmp[SF_LIB_FILE] = m_icLib->GetValue();
166  }
167 
168 
169  // Power source
170  else if( page == m_power )
171  {
172  wxString model;
173 
174  if( !generatePowerSource( model ) )
175  return false;
176 
177  m_fieldsTmp[SF_PRIMITIVE] = (char)( m_pwrType->GetSelection() ? SP_ISOURCE : SP_VSOURCE );
178  m_fieldsTmp[SF_MODEL] = model;
179  }
180 
181 
182  else
183  {
184  wxASSERT_MSG( false, "Unhandled model type" );
185  return false;
186  }
187 
188  m_fieldsTmp[SF_ENABLED] = !m_disabled->GetValue() ? "Y" : "N"; // note bool inversion
189  m_fieldsTmp[SF_NODE_SEQUENCE] = m_nodeSeqCheck->IsChecked() ? m_nodeSeqVal->GetValue() : "";
190 
191  // Apply the settings
192  for( int i = 0; i < SF_END; ++i )
193  {
194  if( m_fieldsTmp.count( (SPICE_FIELD) i ) > 0 && !m_fieldsTmp.at( i ).IsEmpty() )
195  {
196  getField( i ).SetText( m_fieldsTmp[i] );
197  }
198  else
199  {
200  // Erase empty fields (having empty fields causes a warning in the properties dialog)
201  const wxString& spiceField = NETLIST_EXPORTER_PSPICE::GetSpiceFieldName( (SPICE_FIELD) i );
202 
203  auto fieldIt = std::find_if( m_fields.begin(), m_fields.end(), [&]( const SCH_FIELD& f ) {
204  return f.GetName() == spiceField;
205  } );
206 
207  if( fieldIt != m_fields.end() )
208  m_fields.erase( fieldIt );
209  }
210  }
211 
212  return true;
213 }
214 
215 
217 {
218  const auto& spiceFields = NETLIST_EXPORTER_PSPICE::GetSpiceFields();
219 
220  // Fill out the working buffer
221  for( unsigned int idx = 0; idx < spiceFields.size(); ++idx )
222  {
223  const wxString& spiceField = spiceFields[idx];
224 
225  auto fieldIt = std::find_if( m_fields.begin(), m_fields.end(), [&]( const SCH_FIELD& f ) {
226  return f.GetName() == spiceField;
227  } );
228 
229  // Do not modify the existing value, just add missing fields with default values
230  if( fieldIt != m_fields.end() && !fieldIt->GetText().IsEmpty() )
231  m_fieldsTmp[idx] = fieldIt->GetText();
232  else
235  }
236 
237  // Analyze the component fields to fill out the dialog
238  char primitive = toupper( m_fieldsTmp[SF_PRIMITIVE][0] );
239 
240  switch( primitive )
241  {
242  case SP_RESISTOR:
243  case SP_CAPACITOR:
244  case SP_INDUCTOR:
245  m_notebook->SetSelection( m_notebook->FindPage( m_passive ) );
246  m_pasType->SetSelection( primitive == SP_RESISTOR ? 0
247  : primitive == SP_CAPACITOR ? 1
248  : primitive == SP_INDUCTOR ? 2
249  : -1 );
250  m_pasValue->SetValue( m_fieldsTmp[SF_MODEL] );
251  break;
252 
253  case SP_DIODE:
254  case SP_BJT:
255  case SP_MOSFET:
256  m_notebook->SetSelection( m_notebook->FindPage( m_semiconductor ) );
257  m_semiType->SetSelection( primitive == SP_DIODE ? 0
258  : primitive == SP_BJT ? 1
259  : primitive == SP_MOSFET ? 2
260  : -1 );
261  m_semiModel->SetValue( m_fieldsTmp[SF_MODEL] );
262  m_semiLib->SetValue( m_fieldsTmp[SF_LIB_FILE] );
263 
264  if( !empty( m_semiLib ) )
265  {
266  const wxString& libFile = m_semiLib->GetValue();
267  m_fieldsTmp[SF_LIB_FILE] = libFile;
268  updateFromFile( m_semiModel, libFile, ".model" );
269  }
270  break;
271 
272  case SP_SUBCKT:
273  m_notebook->SetSelection( m_notebook->FindPage( m_ic ) );
274  m_icModel->SetValue( m_fieldsTmp[SF_MODEL] );
275  m_icLib->SetValue( m_fieldsTmp[SF_LIB_FILE] );
276 
277  if( !empty( m_icLib ) )
278  {
279  const wxString& libFile = m_icLib->GetValue();
280  m_fieldsTmp[SF_LIB_FILE] = libFile;
281  updateFromFile( m_icModel, libFile, ".subckt" );
282  }
283  break;
284 
285  case SP_VSOURCE:
286  case SP_ISOURCE:
287  if( !parsePowerSource( m_fieldsTmp[SF_MODEL] ) )
288  return false;
289 
290  m_notebook->SetSelection( m_notebook->FindPage( m_power ) );
291  m_pwrType->SetSelection( primitive == SP_ISOURCE ? 1 : 0 );
292  break;
293 
294  default:
295  //wxASSERT_MSG( false, "Unhandled Spice primitive type" );
296  break;
297  }
298 
300 
301  // Check if node sequence is different than the default one
304  {
305  m_nodeSeqCheck->SetValue( true );
307  }
308 
309  return DIALOG_SPICE_MODEL_BASE::TransferDataToWindow();
310 }
311 
312 
313 bool DIALOG_SPICE_MODEL::parsePowerSource( const wxString& aModel )
314 {
315  if( aModel.IsEmpty() )
316  return false;
317 
318  wxStringTokenizer tokenizer( aModel, " ()" );
319  wxString tkn = tokenizer.GetNextToken().Lower();
320 
321  while( tokenizer.HasMoreTokens() )
322  {
323  // Variables used for generic values processing (filling out wxTextCtrls in sequence)
324  bool genericProcessing = false;
325  unsigned int genericReqParamsCount = 0;
326  std::vector<wxTextCtrl*> genericControls;
327 
328  if( tkn == "dc" )
329  {
330  // There might be an optional "dc" or "trans" directive, skip it
331  if( tkn == "dc" || tkn == "trans" )
332  tkn = tokenizer.GetNextToken().Lower();
333 
334  // DC value
335  try
336  {
337  m_genDc->SetValue( SPICE_VALUE( tkn ).ToSpiceString() );
338  }
339  catch( ... )
340  {
341  return false;
342  }
343  }
344 
345 
346  else if( tkn == "ac" )
347  {
348  // AC magnitude
349  try
350  {
351  tkn = tokenizer.GetNextToken().Lower();
352  m_genAcMag->SetValue( SPICE_VALUE( tkn ).ToSpiceString() );
353  }
354  catch( ... )
355  {
356  return false;
357  }
358 
359  // AC phase (optional)
360  try
361  {
362  tkn = tokenizer.GetNextToken().Lower();
363  m_genAcPhase->SetValue( SPICE_VALUE( tkn ).ToSpiceString() );
364  }
365  catch( ... )
366  {
367  continue; // perhaps another directive
368  }
369  }
370 
371 
372  else if( tkn == "pulse" )
373  {
374  m_powerNotebook->SetSelection( m_powerNotebook->FindPage( m_pwrPulse ) );
375 
376  genericProcessing = true;
377  genericReqParamsCount = 2;
378  genericControls = { m_pulseInit, m_pulseNominal, m_pulseDelay,
380  }
381 
382 
383  else if( tkn == "sin" )
384  {
385  m_powerNotebook->SetSelection( m_powerNotebook->FindPage( m_pwrSin ) );
386 
387  genericProcessing = true;
388  genericReqParamsCount = 2;
390  }
391 
392 
393  else if( tkn == "exp" )
394  {
395  m_powerNotebook->SetSelection( m_powerNotebook->FindPage( m_pwrExp ) );
396 
397  genericProcessing = true;
398  genericReqParamsCount = 2;
399  genericControls = { m_expInit, m_expPulsed,
401  }
402 
403 
404  else if( tkn == "pwl" )
405  {
406  m_powerNotebook->SetSelection( m_powerNotebook->FindPage( m_pwrPwl ) );
407 
408  try
409  {
410  while( tokenizer.HasMoreTokens() )
411  {
412  tkn = tokenizer.GetNextToken();
413  SPICE_VALUE time( tkn );
414 
415  tkn = tokenizer.GetNextToken();
416  SPICE_VALUE value( tkn );
417 
418  addPwlValue( time.ToSpiceString(), value.ToSpiceString() );
419  }
420  }
421  catch( ... )
422  {
423  return false;
424  }
425  }
426 
427 
428  else
429  {
430  // Unhandled power source type
431  wxASSERT_MSG( false, "Unhandled power source type" );
432  return false;
433  }
434 
435 
436  if( genericProcessing )
437  {
438  try
439  {
440  for( unsigned int i = 0; i < genericControls.size(); ++i )
441  {
442  // If there are no more tokens, let's check if we got at least required fields
443  if( !tokenizer.HasMoreTokens() )
444  return ( i >= genericReqParamsCount );
445 
446  tkn = tokenizer.GetNextToken().Lower();
447  genericControls[i]->SetValue( SPICE_VALUE( tkn ).ToSpiceString() );
448  }
449  }
450  catch( ... )
451  {
452  return false;
453  }
454  }
455 
456  // Get the next token now, so if any of the branches catches an expection, try to
457  // process it in another branch
458  tkn = tokenizer.GetNextToken().Lower();
459  }
460 
461  return true;
462 }
463 
464 
465 bool DIALOG_SPICE_MODEL::generatePowerSource( wxString& aTarget ) const
466 {
467  wxString acdc, trans;
468  wxWindow* page = m_powerNotebook->GetCurrentPage();
469  bool useTrans = true; // shall we use the transient command part?
470 
471  // Variables for generic processing
472  bool genericProcessing = false;
473  unsigned int genericReqParamsCount = 0;
474  std::vector<wxTextCtrl*> genericControls;
475 
477  // If SPICE_VALUE can be properly constructed, then it is a valid value
478  try
479  {
480  if( !empty( m_genDc ) )
481  acdc += wxString::Format( "dc %s ", SPICE_VALUE( m_genDc->GetValue() ).ToSpiceString() );
482  }
483  catch( ... )
484  {
485  DisplayError( NULL, wxT( "Invalid DC value" ) );
486  return false;
487  }
488 
489  try
490  {
491  if( !empty( m_genAcMag ) )
492  {
493  acdc += wxString::Format( "ac %s ", SPICE_VALUE( m_genAcMag->GetValue() ).ToSpiceString() );
494 
495  if( !empty( m_genAcPhase ) )
496  acdc += wxString::Format( "%s ", SPICE_VALUE( m_genAcPhase->GetValue() ).ToSpiceString() );
497  }
498  }
499  catch( ... )
500  {
501  DisplayError( NULL, wxT( "Invalid AC magnitude or phase" ) );
502  return false;
503  }
504 
506  if( page == m_pwrPulse )
507  {
508  if( !m_pwrPulse->Validate() )
509  return false;
510 
511  genericProcessing = true;
512  trans += "pulse";
513  genericReqParamsCount = 2;
514  genericControls = { m_pulseInit, m_pulseNominal, m_pulseDelay,
516  }
517 
518 
519  else if( page == m_pwrSin )
520  {
521  if( !m_pwrSin->Validate() )
522  return false;
523 
524  genericProcessing = true;
525  trans += "sin";
526  genericReqParamsCount = 2;
528  }
529 
530 
531  else if( page == m_pwrExp )
532  {
533  if( !m_pwrExp->Validate() )
534  return false;
535 
536  genericProcessing = true;
537  trans += "exp";
538  genericReqParamsCount = 2;
539  genericControls = { m_expInit, m_expPulsed,
541  }
542 
543 
544  else if( page == m_pwrPwl )
545  {
546  if( m_pwlValList->GetItemCount() > 0 )
547  {
548  trans += "pwl(";
549 
550  for( int i = 0; i < m_pwlValList->GetItemCount(); ++i )
551  {
552  trans += wxString::Format( "%s %s ", m_pwlValList->GetItemText( i, m_pwlTimeCol ),
553  m_pwlValList->GetItemText( i, m_pwlValueCol ) );
554  }
555 
556  trans.Trim();
557  trans += ")";
558  }
559  }
560 
561  if( genericProcessing )
562  {
563  bool finished = false;
564  unsigned int paramCounter = 0;
565 
566  trans += "(";
567 
568  for( auto textCtrl : genericControls )
569  {
570  if( empty( textCtrl ) )
571  {
572  finished = true;
573 
574  if( paramCounter < genericReqParamsCount )
575  {
576  if( paramCounter == 0 )
577  {
578  // It is fine, no parameters were entered
579  useTrans = false;
580  break;
581  }
582 
583  DisplayError( NULL,
584  wxString::Format( wxT( "You need to specify at least the "
585  "first %d parameters for the transient source" ),
586  genericReqParamsCount ) );
587 
588  return false;
589  }
590  }
591  else if( finished )
592  {
593  DisplayError( NULL, wxT( "You cannot leave interleaved blank "
594  "spaces for the transient source" ) );
595  return false;
596  }
597 
598  trans += wxString::Format( "%s ", textCtrl->GetValue() );
599  ++paramCounter;
600  }
601 
602  trans.Trim();
603  trans += ")";
604  }
605 
606  aTarget = acdc;
607 
608  if( useTrans )
609  aTarget += trans;
610 
611  aTarget.Trim( false );
612  aTarget.Trim( true );
613 
614  return true;
615 }
616 
617 
618 void DIALOG_SPICE_MODEL::updateFromFile( wxComboBox* aComboBox,
619  const wxString& aFilePath, const wxString& aKeyword )
620 {
621  wxString curValue = aComboBox->GetValue();
622  const wxString keyword( aKeyword.Lower() );
623  wxFileName filePath( aFilePath );
624 
625  if( !filePath.Exists() )
626  {
627  // Look for the file in the project path
628  filePath.SetPath( Prj().GetProjectPath() + filePath.GetPath() );
629  }
630 
631  wxTextFile file;
632 
633  if( !file.Open( filePath.GetFullPath() ) )
634  return;
635 
636  aComboBox->Clear();
637 
638  // Process the file, looking for components
639  for( wxString line = file.GetFirstLine().Lower(); !file.Eof(); line = file.GetNextLine() )
640  {
641  wxStringTokenizer tokenizer( line, " " );
642 
643  while( tokenizer.HasMoreTokens() )
644  {
645  wxString token = tokenizer.GetNextToken().Lower();
646 
647  if( token == keyword )
648  {
649  token = tokenizer.GetNextToken();
650 
651  if( !token.IsEmpty() )
652  aComboBox->Append( token );
653  }
654  }
655  }
656 
657  // Restore the previous value or if there is none - pick the first one from the loaded library
658  if( !curValue.IsEmpty() )
659  aComboBox->SetValue( curValue );
660  else if( aComboBox->GetCount() > 0 )
661  aComboBox->SetSelection( 0 );
662 }
663 
664 
666 {
667  const wxString& spiceField = NETLIST_EXPORTER_PSPICE::GetSpiceFieldName( (SPICE_FIELD) aFieldType );
668 
669  auto fieldIt = std::find_if( m_fields.begin(), m_fields.end(), [&]( const SCH_FIELD& f ) {
670  return f.GetName() == spiceField;
671  } );
672 
673  // Found one, so return it
674  if( fieldIt != m_fields.end() )
675  return *fieldIt;
676 
677  // Create a new field with requested name
678  m_fields.emplace_back( wxPoint(), m_fields.size(), &m_component, spiceField );
679  return m_fields.back();
680 }
681 
682 
683 bool DIALOG_SPICE_MODEL::addPwlValue( const wxString& aTime, const wxString& aValue )
684 {
685  // TODO execute validators
686  if( aTime.IsEmpty() || aValue.IsEmpty() )
687  return false;
688 
689  long idx = m_pwlValList->InsertItem( m_pwlTimeCol, aTime );
690  m_pwlValList->SetItem( idx, m_pwlValueCol, aValue );
691 
692  // There is no wxString::ToFloat, but we need to guarantee it fits in 4 bytes
693  double timeD;
694  float timeF;
695  m_pwlTime->GetValue().ToDouble( &timeD );
696  timeF = timeD;
697 
698  // Store the time value, so the entries can be sorted
699  m_pwlValList->SetItemData( idx, *reinterpret_cast<long*>( &timeF ) );
700 
701  // Sort items by timestamp
702  m_pwlValList->SortItems( comparePwlValues, -1 );
703 
704  return true;
705 }
706 
707 
708 void DIALOG_SPICE_MODEL::onSemiSelectLib( wxCommandEvent& event )
709 {
710  wxString searchPath = wxFileName( m_semiLib->GetValue() ).GetPath();
711 
712  if( searchPath.IsEmpty() )
713  searchPath = Prj().GetProjectPath();
714 
715  wxFileDialog openDlg( this, wxT( "Select library" ), searchPath, "",
716  "Spice library file (*.lib)|*.lib;*.LIB|Any file|*",
717  wxFD_OPEN | wxFD_FILE_MUST_EXIST );
718 
719  if( openDlg.ShowModal() == wxID_CANCEL )
720  return;
721 
722  wxFileName libPath( openDlg.GetPath() );
723 
724  // Try to convert the path to relative to project
725  if( libPath.MakeRelativeTo( Prj().GetProjectPath() ) && !libPath.GetFullPath().StartsWith( ".." ) )
726  m_semiLib->SetValue( libPath.GetFullPath() );
727  else
728  m_semiLib->SetValue( openDlg.GetPath() );
729 
730  updateFromFile( m_semiModel, openDlg.GetPath(), ".model" );
731  m_semiModel->Popup();
732 }
733 
734 
735 void DIALOG_SPICE_MODEL::onSelectIcLib( wxCommandEvent& event )
736 {
737  wxString searchPath = wxFileName( m_icLib->GetValue() ).GetPath();
738 
739  if( searchPath.IsEmpty() )
740  searchPath = Prj().GetProjectPath();
741 
742  wxFileDialog openDlg( this, wxT( "Select library" ), searchPath, "",
743  "Spice library file (*.lib)|*.lib;*.LIB|Any file|*",
744  wxFD_OPEN | wxFD_FILE_MUST_EXIST );
745 
746  if( openDlg.ShowModal() == wxID_CANCEL )
747  return;
748 
749  wxFileName libPath( openDlg.GetPath() );
750 
751  // Try to convert the path to relative to project
752  if( libPath.MakeRelativeTo( Prj().GetProjectPath() ) && !libPath.GetFullPath().StartsWith( ".." ) )
753  m_icLib->SetValue( libPath.GetFullPath() );
754  else
755  m_icLib->SetValue( openDlg.GetPath() );
756 
757  updateFromFile( m_icModel, openDlg.GetPath(), ".subckt" );
758  m_icModel->Popup();
759 }
760 
761 
762 void DIALOG_SPICE_MODEL::onPwlAdd( wxCommandEvent& event )
763 {
764  addPwlValue( m_pwlTime->GetValue(), m_pwlValue->GetValue() );
765 }
766 
767 
768 void DIALOG_SPICE_MODEL::onPwlRemove( wxCommandEvent& event )
769 {
770  long idx = m_pwlValList->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
771  m_pwlValList->DeleteItem( idx );
772 }
Class SCH_FIELD instances are attached to a component and provide a place for the component's value...
Definition: sch_field.h:56
SCH_COMPONENT & m_component
Edited component
SPICE_VALIDATOR m_spiceValidator
wxString ToSpiceString() const
Returns string value in Spice format (e.g.
This file is part of the common library.
SCH_FIELD & getField(int aFieldType)
Returns or creates a field in the edited schematic fields vector.
virtual bool TransferDataFromWindow() override
void onSelectIcLib(wxCommandEvent &event) override
static const wxString & GetSpiceFieldName(SPICE_FIELD aField)
Returns a string used for a particular component field related to Spice simulation.
bool addPwlValue(const wxString &aTime, const wxString &aValue)
Adds a value to the PWL values list.
long m_pwlTimeCol
Column identifiers for PWL power source value list
void onSemiSelectLib(wxCommandEvent &event) override
bool generatePowerSource(wxString &aTarget) const
Generates a string to describe power source parameters, basing on the current selection.
static bool StringToBool(const wxString &aStr)
Convertes typical boolean string values (no/yes, true/false, 1/0) to a boolean value.
bool parsePowerSource(const wxString &aModel)
Parse a string describing a power source, so appropriate settings are checked in the dialog...
static wxString GetSpiceFieldDefVal(SPICE_FIELD aField, SCH_COMPONENT *aComponent, unsigned aCtl)
Retrieves the default value for a given field.
static bool empty(const wxTextCtrl *aCtrl)
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
Helper class to handle Spice way of expressing values (e.g. 10.5 Meg)
Definition: spice_value.h:32
std::vector< SCH_FIELD > SCH_FIELDS
A container for several SCH_FIELD items.
Definition: sch_component.h:53
DIALOG_SPICE_MODEL(wxWindow *aParent, SCH_COMPONENT &aComponent, SCH_FIELDS &aSchFields)
wxTextValidator m_notEmptyValidator
std::map< int, wxString > m_fieldsTmp
Temporary field values
void onPwlRemove(wxCommandEvent &event) override
SCH_FIELDS & m_fields
Fields from the component properties dialog
void onPwlAdd(wxCommandEvent &event) override
Class DIALOG_SPICE_MODEL_BASE.
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
void updateFromFile(wxComboBox *aComboBox, const wxString &aFilePath, const wxString &aKeyword)
Loads a list of components from a file and adds them to a combo box.
Class SCH_COMPONENT describes a real schematic component.
Definition: sch_component.h:69
static int wxCALLBACK comparePwlValues(wxIntPtr aItem1, wxIntPtr aItem2, wxIntPtr WXUNUSED(aSortData))
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:102
static const std::vector< wxString > & GetSpiceFields()
Returns a vector of component field names related to Spice simulation.
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:69
SPICE_VALIDATOR m_spiceEmptyValidator
virtual bool TransferDataToWindow() override
virtual void SetText(const wxString &aText)
Definition: eda_text.h:141