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 <wx/tokenzr.h>
27 
28 wxString NETLIST_EXPORTER_PSPICE_SIM::GetSpiceVector( const wxString& aName, SIM_PLOT_TYPE aType,
29  const wxString& aParam ) const
30 {
31  wxString res;
32 
33  // Some of the flags should exclude mutually
34  assert( ( ( aType & SPT_VOLTAGE ) == 0 ) != ( ( aType & SPT_CURRENT ) == 0 ) );
35  assert( ( ( aType & SPT_AC_PHASE ) == 0 ) || ( ( aType & SPT_AC_MAG ) == 0 ) );
36 
37  if( aType & SPT_VOLTAGE )
38  {
39  // netnames are escaped (can contain "{slash}" for '/') Unscape them:
40  wxString spicenet = UnescapeString( aName );
41 
42  // Spice netlist netnames does not accept some chars, whicyh are replaced
43  // by eeschema netlist generator.
44  // Replace these forbidden chars to find the actual spice net name
46 
47  return wxString::Format( "V(%s)", spicenet );
48  }
49 
50  else if( aType & SPT_CURRENT )
51  {
52  return wxString::Format( "@%s[%s]", GetSpiceDevice( aName ).Lower(),
53  aParam.IsEmpty() ? "i" : aParam.Lower() );
54  }
55 
56  return res;
57 }
58 
59 
60 const std::vector<wxString>& NETLIST_EXPORTER_PSPICE_SIM::GetCurrents( SPICE_PRIMITIVE aPrimitive )
61 {
62  static const std::vector<wxString> passive = { "I" };
63  static const std::vector<wxString> diode = { "Id" };
64  static const std::vector<wxString> bjt = { "Ib", "Ic", "Ie" };
65  static const std::vector<wxString> mos = { "Ig", "Id", "Is" };
66  static const std::vector<wxString> empty;
67 
68  switch( aPrimitive )
69  {
70  case SP_RESISTOR:
71  case SP_CAPACITOR:
72  case SP_INDUCTOR:
73  case SP_VSOURCE:
74  return passive;
75 
76  case SP_DIODE:
77  return diode;
78 
79  case SP_BJT:
80  return bjt;
81 
82  case SP_MOSFET:
83  return mos;
84 
85  default:
86  return empty;
87  }
88 }
89 
90 
92 {
93  wxString simCmd;
94 
96 
97  for( const auto& dir : GetDirectives() )
98  {
99  if( IsSimCommand( dir ) )
100  simCmd += wxString::Format( "%s\r\n", dir );
101  }
102 
103  return simCmd;
104 }
105 
106 
108 {
109  return m_simCommand.IsEmpty() ? GetSheetSimCommand() : m_simCommand;
110 }
111 
112 
114 {
116 }
117 
118 
120 {
121  const std::map<wxString, SIM_TYPE> simCmds = {
122  { ".ac", ST_AC }, { ".dc", ST_DC }, { ".disto", ST_DISTORTION }, { ".noise", ST_NOISE },
123  { ".op", ST_OP }, { ".pz", ST_POLE_ZERO }, { ".sens", ST_SENSITIVITY }, { ".tf", ST_TRANS_FUNC },
124  { ".tran", ST_TRANSIENT }
125  };
126  wxString lcaseCmd = aCmd.Lower();
127 
128  for( const auto& c : simCmds )
129  {
130  if( lcaseCmd.StartsWith( c.first ) )
131  return c.second;
132  }
133 
134  return ST_UNKNOWN;
135 }
136 
137 
138 bool NETLIST_EXPORTER_PSPICE_SIM::ParseDCCommand( const wxString& aCmd, SPICE_DC_PARAMS* aSource1,
139  SPICE_DC_PARAMS* aSource2 )
140 {
141  if( !aCmd.Lower().StartsWith( ".dc" ) )
142  return false;
143 
144  wxString cmd = aCmd.Mid( 3 ).Trim().Trim( false );
145 
146  wxStringTokenizer tokens( cmd );
147 
148  size_t num = tokens.CountTokens();
149 
150  if( num != 4 && num != 8 )
151  return false;
152 
153  aSource1->m_source = tokens.GetNextToken();
154  aSource1->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
155  aSource1->m_vend = SPICE_VALUE( tokens.GetNextToken() );
156  aSource1->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
157 
158  if( num == 8 )
159  {
160  aSource2->m_source = tokens.GetNextToken();
161  aSource2->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
162  aSource2->m_vend = SPICE_VALUE( tokens.GetNextToken() );
163  aSource2->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
164  }
165 
166  return true;
167 }
168 
169 
170 void NETLIST_EXPORTER_PSPICE_SIM::writeDirectives( OUTPUTFORMATTER* aFormatter, unsigned aCtl ) const
171 {
172  // Add a directive to obtain currents
173  //aFormatter->Print( 0, ".options savecurrents\n" ); // does not work :(
174 
175  for( const auto& item : GetSpiceItems() )
176  {
177  for( const auto& current :
179  {
180  if( !item.m_enabled )
181  continue;
182 
184  aFormatter->Print( 0, ".save %s\n",
185  (const char*) GetSpiceVector( item.m_refName, SPT_CURRENT, current ).c_str() );
186  }
187  }
188 
189  // If we print out .save directives for currents, then it needs to be done for voltages as well
190  for( const auto& netMap : GetNetIndexMap() )
191  {
192  // the "0" and the "GND" nets are automaticallly saved internally by ngspice.
193  // Skip them
194  wxString netname = GetSpiceVector( netMap.first, SPT_VOLTAGE );
195 
196  if( netname == "V(0)" || netname == "V(GND)" )
197  continue;
198 
199  aFormatter->Print( 0, ".save %s\n", (const char*) netname.c_str() );
200  }
201 
202  if( m_simCommand.IsEmpty() )
203  {
204  // Fallback to the default behavior and just write all directives
205  NETLIST_EXPORTER_PSPICE::writeDirectives( aFormatter, aCtl );
206  }
207  else
208  {
209  // Dump all directives but simulation commands
210  for( const auto& dir : GetDirectives() )
211  {
212  if( !IsSimCommand( dir ) )
213  aFormatter->Print( 0, "%s\n", (const char*) dir.c_str() );
214  }
215 
216  // Finish with our custom simulation command
217  aFormatter->Print( 0, "%s\n", (const char*) m_simCommand.c_str() );
218  }
219 }
wxString m_simCommand
Custom simulation command (has priority over the schematic sheet simulation commands)
const SPICE_ITEM_LIST & GetSpiceItems() const
Returns list of items representing schematic components in the Spice world.
wxString GetUsedSimCommand()
Returns the command directive that is in use (either from the sheet or from m_simCommand.
const std::vector< wxString > GetDirectives() const
Returnss a vector of Spice directives found in the schematics.
Class OUTPUTFORMATTER is an important interface (abstract class) used to output 8 bit text in a conve...
Definition: richio.h:327
static bool IsSimCommand(const wxString &aCmd)
Determines if a directive is a simulation command.
static SIM_TYPE CommandToSimType(const wxString &aCmd)
Returns simulation type basing on a simulation command directive.
void writeDirectives(OUTPUTFORMATTER *aFormatter, unsigned aCtl) const override
Saves the Spice directives.
static const std::vector< wxString > & GetCurrents(SPICE_PRIMITIVE aPrimitive)
Returns a list of currents that can be probed in a Spice primitive.
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
SIM_TYPE
Possible simulation types
Definition: sim_types.h:29
virtual void writeDirectives(OUTPUTFORMATTER *aFormatter, unsigned aCtl) const
Saves the Spice directives.
void UpdateDirectives(unsigned aCtl)
Updates the vector of Spice directives placed in the schematics.
wxString GetSpiceVector(const wxString &aName, SIM_PLOT_TYPE aType, const wxString &aParam=wxEmptyString) const
Returns name of Spice dataset for a specific plot.
const NET_INDEX_MAP & GetNetIndexMap() const
Returns a map of circuit nodes to net names.
static void ReplaceForbiddenChars(wxString &aNetName)
some chars are not accepted in netnames in spice netlists.
bool ParseDCCommand(const wxString &aCmd, SPICE_DC_PARAMS *aSource1, SPICE_DC_PARAMS *aSource2)
Parses a two-source .dc command directive into its components.
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
SPICE_PRIMITIVE
Basic Spice component primitives
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:123
static bool empty(const wxTextEntryBase *aCtrl)
wxString GetSpiceDevice(const wxString &aComponent) const
Returns name of Spice device corresponding to a schematic component.
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Function Print formats and writes text to the output stream.
Definition: richio.cpp:404
SIM_PLOT_TYPE
Possible plot types
Definition: sim_types.h:35
SIM_TYPE GetSimType()
Returns simulation type basing on the simulation command directives.