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, which 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  wxString device = GetSpiceDevice( aName ).Lower();
53  wxString param = aParam.Lower();
54 
55  if( device[0] == 'x' )
56  {
57  return "current probe of .subckt not yet implemented";
58  }
59  else
60  {
61  return wxString::Format( "@%s[%s]",
62  device,
63  param.IsEmpty() ? "i" : param );
64  }
65  }
66 
67  return res;
68 }
69 
70 
71 const std::vector<wxString>& NETLIST_EXPORTER_PSPICE_SIM::GetCurrents( SPICE_PRIMITIVE aPrimitive )
72 {
73  static const std::vector<wxString> passive = { "I" };
74  static const std::vector<wxString> diode = { "Id" };
75  static const std::vector<wxString> bjt = { "Ib", "Ic", "Ie" };
76  static const std::vector<wxString> mos = { "Ig", "Id", "Is" };
77  static const std::vector<wxString> empty;
78 
79  switch( aPrimitive )
80  {
81  case SP_RESISTOR:
82  case SP_CAPACITOR:
83  case SP_INDUCTOR:
84  case SP_VSOURCE:
85  return passive;
86 
87  case SP_DIODE:
88  return diode;
89 
90  case SP_BJT:
91  return bjt;
92 
93  case SP_MOSFET:
94  return mos;
95 
96  default:
97  return empty;
98  }
99 }
100 
101 
103 {
104  wxString simCmd;
105 
107 
108  for( const auto& dir : GetDirectives() )
109  {
110  if( IsSimCommand( dir ) )
111  simCmd += wxString::Format( "%s\r\n", dir );
112  }
113 
114  return simCmd;
115 }
116 
117 
119 {
120  return m_simCommand.IsEmpty() ? GetSheetSimCommand() : m_simCommand;
121 }
122 
123 
125 {
127 }
128 
129 
131 {
132  const std::map<wxString, SIM_TYPE> simCmds = {
133  { ".ac", ST_AC }, { ".dc", ST_DC }, { ".disto", ST_DISTORTION }, { ".noise", ST_NOISE },
134  { ".op", ST_OP }, { ".pz", ST_POLE_ZERO }, { ".sens", ST_SENSITIVITY }, { ".tf", ST_TRANS_FUNC },
135  { ".tran", ST_TRANSIENT }
136  };
137  wxString lcaseCmd = aCmd.Lower();
138 
139  for( const auto& c : simCmds )
140  {
141  if( lcaseCmd.StartsWith( c.first ) )
142  return c.second;
143  }
144 
145  return ST_UNKNOWN;
146 }
147 
148 
149 bool NETLIST_EXPORTER_PSPICE_SIM::ParseDCCommand( const wxString& aCmd, SPICE_DC_PARAMS* aSource1,
150  SPICE_DC_PARAMS* aSource2 )
151 {
152  if( !aCmd.Lower().StartsWith( ".dc" ) )
153  return false;
154 
155  wxString cmd = aCmd.Mid( 3 ).Trim().Trim( false );
156 
157  wxStringTokenizer tokens( cmd );
158 
159  size_t num = tokens.CountTokens();
160 
161  if( num != 4 && num != 8 )
162  return false;
163 
164  aSource1->m_source = tokens.GetNextToken();
165  aSource1->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
166  aSource1->m_vend = SPICE_VALUE( tokens.GetNextToken() );
167  aSource1->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
168 
169  if( num == 8 )
170  {
171  aSource2->m_source = tokens.GetNextToken();
172  aSource2->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
173  aSource2->m_vend = SPICE_VALUE( tokens.GetNextToken() );
174  aSource2->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
175  }
176 
177  return true;
178 }
179 
180 
181 void NETLIST_EXPORTER_PSPICE_SIM::writeDirectives( OUTPUTFORMATTER* aFormatter, unsigned aCtl ) const
182 {
183  // Add a directive to obtain currents
184  //aFormatter->Print( 0, ".options savecurrents\n" ); // does not work :(
185 
186  for( const auto& item : GetSpiceItems() )
187  {
188  for( const auto& current :
190  {
191  if( !item.m_enabled )
192  continue;
193 
195  aFormatter->Print( 0, ".save %s\n",
196  (const char*) GetSpiceVector( item.m_refName, SPT_CURRENT, current ).c_str() );
197  }
198  }
199 
200  // If we print out .save directives for currents, then it needs to be done for voltages as well
201  for( const auto& netMap : GetNetIndexMap() )
202  {
203  // the "0" and the "GND" nets are automaticallly saved internally by ngspice.
204  // Skip them
205  wxString netname = GetSpiceVector( netMap.first, SPT_VOLTAGE );
206 
207  if( netname == "V(0)" || netname == "V(GND)" )
208  continue;
209 
210  aFormatter->Print( 0, ".save %s\n", (const char*) netname.c_str() );
211  }
212 
213  if( m_simCommand.IsEmpty() )
214  {
215  // Fallback to the default behavior and just write all directives
216  NETLIST_EXPORTER_PSPICE::writeDirectives( aFormatter, aCtl );
217  }
218  else
219  {
220  // Dump all directives but simulation commands
221  for( const auto& dir : GetDirectives() )
222  {
223  if( !IsSimCommand( dir ) )
224  aFormatter->Print( 0, "%s\n", (const char*) dir.c_str() );
225  }
226 
227  // Finish with our custom simulation command
228  aFormatter->Print( 0, "%s\n", (const char*) m_simCommand.c_str() );
229  }
230 }
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:131
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.