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  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.TXT for contributors.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 3
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * https://www.gnu.org/licenses/gpl-3.0.html
22  * or you may search the http://www.gnu.org website for the version 3 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
28 #include <kicad_string.h>
29 #include <macros.h> // for TO_UTF8 def
30 #include <wx/regex.h>
31 #include <wx/tokenzr.h>
32 
34  const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam ) const
35 {
36  wxString res;
37 
38  // Some of the flags should exclude mutually
39  wxASSERT( ( ( aType & SPT_VOLTAGE ) == 0 ) != ( ( aType & SPT_CURRENT ) == 0 ) );
40  wxASSERT( ( ( aType & SPT_AC_PHASE ) == 0 ) || ( ( aType & SPT_AC_MAG ) == 0 ) );
41 
42  if( aType & SPT_VOLTAGE )
43  {
44  // netnames are escaped (can contain "{slash}" for '/') Unscape them:
45  wxString spicenet = UnescapeString( aName );
46 
47  // Spice netlist netnames does not accept some chars, which are replaced
48  // by eeschema netlist generator.
49  // Replace these forbidden chars to find the actual spice net name
51 
52  return wxString::Format( "V(%s)", spicenet );
53  }
54 
55  else if( aType & SPT_CURRENT )
56  {
57  wxString device = GetSpiceDevice( aName ).Lower();
58  wxString param = aParam.Lower();
59 
60  if( device.length() > 0 && device[0] == 'x' )
61  {
62  return "current probe of .subckt not yet implemented";
63  }
64  else
65  {
66  return wxString::Format( "@%s[%s]",
67  device,
68  param.IsEmpty() ? "i" : param );
69  }
70  }
71 
72  return res;
73 }
74 
75 
77  const std::string& aVector, wxString& aSignal ) const
78 {
79  using namespace std;
80 
81  // See ngspice manual chapt. 31.1 "Accessing internal device parameters"
82  wxRegEx internalDevParameter( "^@(\\w*[\\.\\w+]*)\\[(\\w*)\\]$", wxRE_ADVANCED );
83  wxString vector( aVector );
84 
85  if( !internalDevParameter.Matches( vector ) )
86  {
87  // any text is a node name, which returns voltage
88  aSignal = "V(" + aVector + ")";
89  return SPT_VOLTAGE;
90  }
91  else
92  {
93  wxString paramType = internalDevParameter.GetMatch( vector, 2 );
94 
95  if( paramType.Lower()[0] == 'i' )
96  {
97  // this is a branch current
98  paramType[0] = 'I';
99  aSignal = paramType + "(";
100  aSignal += internalDevParameter.GetMatch( vector, 1 ).Upper() + ")";
101  return SPT_CURRENT;
102  }
103  else
104  {
105  return SPT_UNKNOWN;
106  }
107  }
108 }
109 
110 
111 const std::vector<wxString>& NETLIST_EXPORTER_PSPICE_SIM::GetCurrents( SPICE_PRIMITIVE aPrimitive )
112 {
113  static const std::vector<wxString> passive = { "I" };
114  static const std::vector<wxString> diode = { "Id" };
115  static const std::vector<wxString> bjt = { "Ib", "Ic", "Ie" };
116  static const std::vector<wxString> mos = { "Ig", "Id", "Is" };
117  static const std::vector<wxString> empty;
118 
119  switch( aPrimitive )
120  {
121  case SP_RESISTOR:
122  case SP_CAPACITOR:
123  case SP_INDUCTOR:
124  case SP_VSOURCE:
125  return passive;
126 
127  case SP_DIODE:
128  return diode;
129 
130  case SP_BJT:
131  return bjt;
132 
133  case SP_MOSFET:
134  return mos;
135 
136  default:
137  return empty;
138  }
139 }
140 
141 
143 {
144  wxString simCmd;
145 
147 
148  for( const auto& dir : GetDirectives() )
149  {
150  if( IsSimCommand( dir ) )
151  simCmd += wxString::Format( "%s\r\n", dir );
152  }
153 
154  return simCmd;
155 }
156 
157 
159 {
160  return m_simCommand.IsEmpty() ? GetSheetSimCommand() : m_simCommand;
161 }
162 
163 
165 {
167 }
168 
169 
171 {
172  const std::vector<std::pair<wxString, SIM_TYPE>> simCmds = {
173  { "^.ac\\M.*", ST_AC },
174  { "^.dc\\M.*", ST_DC },
175  { "^.tran\\M.*", ST_TRANSIENT },
176  { "^.op\\M.*", ST_OP },
177  { "^.disto\\M.*", ST_DISTORTION },
178  { "^.noise\\M.*", ST_NOISE },
179  { "^.pz\\M.*", ST_POLE_ZERO },
180  { "^.sens\\M.*", ST_SENSITIVITY },
181  { "^.tf\\M.*", ST_TRANS_FUNC } };
182  wxRegEx simCmd;
183 
184  for( const auto& c : simCmds )
185  {
186  simCmd.Compile( c.first, wxRE_ADVANCED | wxRE_NOSUB | wxRE_ICASE );
187 
188  if( simCmd.Matches( aCmd ) )
189  return c.second;
190  }
191 
192  return ST_UNKNOWN;
193 }
194 
195 
196 bool NETLIST_EXPORTER_PSPICE_SIM::ParseDCCommand( const wxString& aCmd, SPICE_DC_PARAMS* aSource1,
197  SPICE_DC_PARAMS* aSource2 )
198 {
199  if( !aCmd.Lower().StartsWith( ".dc" ) )
200  return false;
201 
202  wxString cmd = aCmd.Mid( 3 ).Trim().Trim( false );
203 
204  wxStringTokenizer tokens( cmd );
205 
206  size_t num = tokens.CountTokens();
207 
208  if( num != 4 && num != 8 )
209  return false;
210 
211  aSource1->m_source = tokens.GetNextToken();
212  aSource1->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
213  aSource1->m_vend = SPICE_VALUE( tokens.GetNextToken() );
214  aSource1->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
215 
216  if( num == 8 )
217  {
218  aSource2->m_source = tokens.GetNextToken();
219  aSource2->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
220  aSource2->m_vend = SPICE_VALUE( tokens.GetNextToken() );
221  aSource2->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
222  }
223 
224  return true;
225 }
226 
227 
228 void NETLIST_EXPORTER_PSPICE_SIM::writeDirectives( OUTPUTFORMATTER* aFormatter, unsigned aCtl ) const
229 {
230  // Add a directive to obtain currents
231  //aFormatter->Print( 0, ".options savecurrents\n" ); // does not work :(
232 
233  for( const auto& item : GetSpiceItems() )
234  {
235  for( const auto& current :
237  {
238  if( !item.m_enabled )
239  continue;
240 
242  aFormatter->Print( 0, ".save %s\n",
243  TO_UTF8( ComponentToVector( item.m_refName, SPT_CURRENT, current ) ) );
244  }
245  }
246 
247  // If we print out .save directives for currents, then it needs to be done for voltages as well
248  for( const auto& netMap : GetNetIndexMap() )
249  {
250  // the "0" and the "GND" nets are automatically saved internally by ngspice.
251  // Skip them
252  wxString netname = ComponentToVector( netMap.first, SPT_VOLTAGE );
253 
254  if( netname == "V(0)" || netname == "V(GND)" )
255  continue;
256 
257  aFormatter->Print( 0, ".save %s\n", TO_UTF8( netname ) );
258  }
259 
260  if( m_simCommand.IsEmpty() )
261  {
262  // Fallback to the default behavior and just write all directives
263  NETLIST_EXPORTER_PSPICE::writeDirectives( aFormatter, aCtl );
264  }
265  else
266  {
267  // Dump all directives but simulation commands
268  for( const auto& dir : GetDirectives() )
269  {
270  if( !IsSimCommand( dir ) )
271  aFormatter->Print( 0, "%s\n", TO_UTF8( dir ) );
272  }
273 
274  // Finish with our custom simulation command
275  aFormatter->Print( 0, "%s\n", TO_UTF8( m_simCommand ) );
276  }
277 }
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:64
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:309
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.
This file contains miscellaneous commonly used macros and functions.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
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.
wxString GetSpiceDevice(const wxString &aSymbol) const
Return name of Spice device corresponding to a schematic symbol.
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 symbols.
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:222
static bool empty(const wxTextEntryBase *aCtrl)
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:426
SIM_PLOT_TYPE
Definition: sim_types.h:46
SIM_TYPE GetSimType()
Return simulation type basing on the simulation command directives.