KiCad PCB EDA Suite
netlist_exporter_pspice_sim.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 
26 #include <kicad_string.h>
27 #include <wx/regex.h>
28 #include <wx/tokenzr.h>
29 
31  const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam ) const
32 {
33  wxString res;
34 
35  // Some of the flags should exclude mutually
36  assert( ( ( aType & SPT_VOLTAGE ) == 0 ) != ( ( aType & SPT_CURRENT ) == 0 ) );
37  assert( ( ( aType & SPT_AC_PHASE ) == 0 ) || ( ( aType & SPT_AC_MAG ) == 0 ) );
38 
39  if( aType & SPT_VOLTAGE )
40  {
41  // netnames are escaped (can contain "{slash}" for '/') Unscape them:
42  wxString spicenet = UnescapeString( aName );
43 
44  // Spice netlist netnames does not accept some chars, which are replaced
45  // by eeschema netlist generator.
46  // Replace these forbidden chars to find the actual spice net name
48 
49  return wxString::Format( "V(%s)", spicenet );
50  }
51 
52  else if( aType & SPT_CURRENT )
53  {
54  wxString device = GetSpiceDevice( aName ).Lower();
55  wxString param = aParam.Lower();
56 
57  if( device.length() > 0 && device[0] == 'x' )
58  {
59  return "current probe of .subckt not yet implemented";
60  }
61  else
62  {
63  return wxString::Format( "@%s[%s]",
64  device,
65  param.IsEmpty() ? "i" : param );
66  }
67  }
68 
69  return res;
70 }
71 
72 
74  const std::string& aVector, wxString& aSignal ) const
75 {
76  using namespace std;
77 
78  // See ngspice manual chapt. 31.1 "Accessing internal device parameters"
79  wxRegEx internalDevParameter( "^@(\\w*[\\.\\w+]*)\\[(\\w*)\\]$", wxRE_ADVANCED );
80  wxString vector( aVector );
81 
82  if( !internalDevParameter.Matches( vector ) )
83  {
84  // any text is a node name, which returns voltage
85  aSignal = "V(" + aVector + ")";
86  return SPT_VOLTAGE;
87  }
88  else
89  {
90  wxString paramType = internalDevParameter.GetMatch( vector, 2 );
91 
92  if( paramType.Lower()[0] == 'i' )
93  {
94  // this is a branch current
95  paramType[0] = 'I';
96  aSignal = paramType + "(";
97  aSignal += internalDevParameter.GetMatch( vector, 1 ).Upper() + ")";
98  return SPT_CURRENT;
99  }
100  else
101  {
102  return SPT_UNKNOWN;
103  }
104  }
105 }
106 
107 
108 const std::vector<wxString>& NETLIST_EXPORTER_PSPICE_SIM::GetCurrents( SPICE_PRIMITIVE aPrimitive )
109 {
110  static const std::vector<wxString> passive = { "I" };
111  static const std::vector<wxString> diode = { "Id" };
112  static const std::vector<wxString> bjt = { "Ib", "Ic", "Ie" };
113  static const std::vector<wxString> mos = { "Ig", "Id", "Is" };
114  static const std::vector<wxString> empty;
115 
116  switch( aPrimitive )
117  {
118  case SP_RESISTOR:
119  case SP_CAPACITOR:
120  case SP_INDUCTOR:
121  case SP_VSOURCE:
122  return passive;
123 
124  case SP_DIODE:
125  return diode;
126 
127  case SP_BJT:
128  return bjt;
129 
130  case SP_MOSFET:
131  return mos;
132 
133  default:
134  return empty;
135  }
136 }
137 
138 
140 {
141  wxString simCmd;
142 
144 
145  for( const auto& dir : GetDirectives() )
146  {
147  if( IsSimCommand( dir ) )
148  simCmd += wxString::Format( "%s\r\n", dir );
149  }
150 
151  return simCmd;
152 }
153 
154 
156 {
157  return m_simCommand.IsEmpty() ? GetSheetSimCommand() : m_simCommand;
158 }
159 
160 
162 {
164 }
165 
166 
168 {
169  const std::vector<std::pair<wxString, SIM_TYPE>> simCmds = {
170  { "^.ac\\M.*", ST_AC },
171  { "^.dc\\M.*", ST_DC },
172  { "^.tran\\M.*", ST_TRANSIENT },
173  { "^.op\\M.*", ST_OP },
174  { "^.disto\\M.*", ST_DISTORTION },
175  { "^.noise\\M.*", ST_NOISE },
176  { "^.pz\\M.*", ST_POLE_ZERO },
177  { "^.sens\\M.*", ST_SENSITIVITY },
178  { "^.tf\\M.*", ST_TRANS_FUNC } };
179  wxRegEx simCmd;
180 
181  for( const auto& c : simCmds )
182  {
183  simCmd.Compile( c.first, wxRE_ADVANCED | wxRE_NOSUB | wxRE_ICASE );
184 
185  if( simCmd.Matches( aCmd ) )
186  return c.second;
187  }
188 
189  return ST_UNKNOWN;
190 }
191 
192 
193 bool NETLIST_EXPORTER_PSPICE_SIM::ParseDCCommand( const wxString& aCmd, SPICE_DC_PARAMS* aSource1,
194  SPICE_DC_PARAMS* aSource2 )
195 {
196  if( !aCmd.Lower().StartsWith( ".dc" ) )
197  return false;
198 
199  wxString cmd = aCmd.Mid( 3 ).Trim().Trim( false );
200 
201  wxStringTokenizer tokens( cmd );
202 
203  size_t num = tokens.CountTokens();
204 
205  if( num != 4 && num != 8 )
206  return false;
207 
208  aSource1->m_source = tokens.GetNextToken();
209  aSource1->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
210  aSource1->m_vend = SPICE_VALUE( tokens.GetNextToken() );
211  aSource1->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
212 
213  if( num == 8 )
214  {
215  aSource2->m_source = tokens.GetNextToken();
216  aSource2->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
217  aSource2->m_vend = SPICE_VALUE( tokens.GetNextToken() );
218  aSource2->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
219  }
220 
221  return true;
222 }
223 
224 
225 void NETLIST_EXPORTER_PSPICE_SIM::writeDirectives( OUTPUTFORMATTER* aFormatter, unsigned aCtl ) const
226 {
227  // Add a directive to obtain currents
228  //aFormatter->Print( 0, ".options savecurrents\n" ); // does not work :(
229 
230  for( const auto& item : GetSpiceItems() )
231  {
232  for( const auto& current :
234  {
235  if( !item.m_enabled )
236  continue;
237 
239  aFormatter->Print( 0, ".save %s\n",
240  (const char*) ComponentToVector( item.m_refName, SPT_CURRENT, current )
241  .c_str() );
242  }
243  }
244 
245  // If we print out .save directives for currents, then it needs to be done for voltages as well
246  for( const auto& netMap : GetNetIndexMap() )
247  {
248  // the "0" and the "GND" nets are automaticallly saved internally by ngspice.
249  // Skip them
250  wxString netname = ComponentToVector( netMap.first, SPT_VOLTAGE );
251 
252  if( netname == "V(0)" || netname == "V(GND)" )
253  continue;
254 
255  aFormatter->Print( 0, ".save %s\n", (const char*) netname.c_str() );
256  }
257 
258  if( m_simCommand.IsEmpty() )
259  {
260  // Fallback to the default behavior and just write all directives
261  NETLIST_EXPORTER_PSPICE::writeDirectives( aFormatter, aCtl );
262  }
263  else
264  {
265  // Dump all directives but simulation commands
266  for( const auto& dir : GetDirectives() )
267  {
268  if( !IsSimCommand( dir ) )
269  aFormatter->Print( 0, "%s\n", (const char*) dir.c_str() );
270  }
271 
272  // Finish with our custom simulation command
273  aFormatter->Print( 0, "%s\n", (const char*) m_simCommand.c_str() );
274  }
275 }
wxString m_simCommand
< Custom simulation command (has priority over the schematic sheet simulation commands)
wxString ComponentToVector(const wxString &aName, SIM_PLOT_TYPE aType, const wxString &aParam=wxEmptyString) const
Return name of Spice dataset for a specific plot.
const SPICE_ITEM_LIST & GetSpiceItems() const
Return list of items representing schematic components in the Spice world.
wxString GetUsedSimCommand()
Return the command directive that is in use (either from the sheet or from m_simCommand.
const std::vector< wxString > GetDirectives() const
Return a vector of Spice directives found in the schematics.
Definition: bitmap.cpp:58
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:306
SIM_PLOT_TYPE VectorToSignal(const std::string &aVector, wxString &aSignal) const
Return name of Spice dataset for a specific plot.
static bool IsSimCommand(const wxString &aCmd)
Determine if a directive is a simulation command.
static SIM_TYPE CommandToSimType(const wxString &aCmd)
Return simulation type basing on a simulation command directive.
void writeDirectives(OUTPUTFORMATTER *aFormatter, unsigned aCtl) const override
Save the Spice directives.
static const std::vector< wxString > & GetCurrents(SPICE_PRIMITIVE aPrimitive)
Return a list of currents that can be probed in a Spice primitive.
wxString GetSheetSimCommand()
Return simulation command directives placed in schematic sheets (if any).
< Helper class to handle Spice way of expressing values (e.g. 10.5 Meg) Helper class to recognize Spi...
Definition: spice_value.h:34
SIM_TYPE
< Possible simulation types
Definition: sim_types.h:31
virtual void writeDirectives(OUTPUTFORMATTER *aFormatter, unsigned aCtl) const
Save the Spice directives.
void UpdateDirectives(unsigned aCtl)
Update the vector of Spice directives placed in the schematics.
const NET_INDEX_MAP & GetNetIndexMap() const
Return a map of circuit nodes to net names.
static void ReplaceForbiddenChars(wxString &aNetName)
Replace illegal spice net name characters with an underscore.
bool ParseDCCommand(const wxString &aCmd, SPICE_DC_PARAMS *aSource1, SPICE_DC_PARAMS *aSource2)
Parse a two-source .dc command directive into its components.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:150
static bool empty(const wxTextEntryBase *aCtrl)
wxString GetSpiceDevice(const wxString &aComponent) const
Return name of Spice device corresponding to a schematic component.
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:408
SIM_PLOT_TYPE
Definition: sim_types.h:46
SIM_TYPE GetSimType()
Return simulation type basing on the simulation command directives.