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