KiCad PCB EDA Suite
Loading...
Searching...
No Matches
spice_circuit_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-2022 CERN
5 * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.TXT for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * https://www.gnu.org/licenses/gpl-3.0.html
21 * or you may search the http://www.gnu.org website for the version 3 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "spice_circuit_model.h"
27#include <string_utils.h>
28#include <wx/regex.h>
29#include <wx/tokenzr.h>
30#include <locale_io.h>
31
32
34 wxString& aSignal ) const
35{
36 static wxString BRANCH( wxS( "#branch" ) );
37 static wxString POWER( wxS( ":power" ) );
38
39 // See ngspice manual chapt. 31.1 "Accessing internal device parameters"
40 static wxRegEx internalDevParameter( wxS( "^@(\\w*[\\.\\w+]*)\\[(\\w*)\\]$" ), wxRE_ADVANCED );
41
42 wxString vector( aVector );
43
44 if( !internalDevParameter.Matches( vector ) )
45 {
46 if( vector.EndsWith( BRANCH ) )
47 {
48 aSignal = wxT( "I(" ) + vector.Left( vector.Length() - BRANCH.Length() ) + wxT( ")" );
49 return SPT_CURRENT;
50 }
51 else if( vector.EndsWith( POWER ) )
52 {
53 aSignal = wxT( "P(" ) + vector.Left( vector.Length() - POWER.Length() ) + wxT( ")" );
54 return SPT_POWER;
55 }
56 else
57 {
58 aSignal = wxT( "V(" ) + vector + wxT( ")" );
59 return SPT_VOLTAGE;
60 }
61 }
62 else
63 {
64 wxString paramType = internalDevParameter.GetMatch( vector, 2 );
65
66 if( paramType.Lower()[0] == 'i' )
67 {
68 // this is a branch current
69 paramType[0] = 'I';
70 aSignal = paramType + wxT( "(" );
71 aSignal += internalDevParameter.GetMatch( vector, 1 ).Upper() + wxT( ")" );
72 return SPT_CURRENT;
73 }
74 else
75 {
76 return SPT_UNKNOWN;
77 }
78 }
79}
80
81
83{
84 wxString simCmd;
85
86 ReadDirectives( 0 );
87
88 for( const wxString& directive : GetDirectives() )
89 {
90 if( IsSimCommand( directive ) )
91 simCmd += wxString::Format( wxT( "%s\r\n" ), directive );
92 }
93
94 return simCmd.Trim();
95}
96
97
99{
100 wxString cmd = aCmd.Lower().Trim();
101
102 if( cmd == wxT( ".op" ) ) return ST_OP;
103 else if( cmd.StartsWith( wxT( ".ac" ) ) ) return ST_AC;
104 else if( cmd.StartsWith( wxT( ".dc" ) ) ) return ST_DC;
105 else if( cmd.StartsWith( wxT( ".tran" ) ) ) return ST_TRAN;
106 else if( cmd.StartsWith( wxT( ".disto" ) ) ) return ST_DISTO;
107 else if( cmd.StartsWith( wxT( ".noise" ) ) ) return ST_NOISE;
108 else if( cmd.StartsWith( wxT( ".pz" ) ) ) return ST_PZ;
109 else if( cmd.StartsWith( wxT( ".sens" ) ) ) return ST_SENS;
110 else if( cmd.StartsWith( wxT( ".sp" ) ) ) return ST_SP;
111 else if( cmd.StartsWith( wxT( ".tf" ) ) ) return ST_TF;
112
113 else if( cmd.StartsWith( wxT( "fft" ) ) || cmd.Contains( wxT( "\nfft" ) ) )
114 return ST_FFT;
115 else
116 return ST_UNKNOWN;
117}
118
119
120bool SPICE_CIRCUIT_MODEL::ParseDCCommand( const wxString& aCmd, SPICE_DC_PARAMS* aSource1,
121 SPICE_DC_PARAMS* aSource2 )
122{
123 if( !aCmd.Lower().StartsWith( ".dc" ) )
124 return false;
125
126 wxString cmd = aCmd.Mid( 3 );
127 wxStringTokenizer tokens( cmd, wxS( " \t" ), wxTOKEN_STRTOK );
128
129 aSource1->m_source = tokens.GetNextToken();
130 aSource1->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
131 aSource1->m_vend = SPICE_VALUE( tokens.GetNextToken() );
132 aSource1->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
133
134 if( tokens.HasMoreTokens() )
135 {
136 aSource2->m_source = tokens.GetNextToken();
137 aSource2->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
138 aSource2->m_vend = SPICE_VALUE( tokens.GetNextToken() );
139 aSource2->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
140 }
141
142 return true;
143}
144
145
146bool SPICE_CIRCUIT_MODEL::ParsePZCommand( const wxString& aCmd, wxString* transferFunction,
147 wxString* input, wxString* inputRef, wxString* output,
148 wxString* outputRef, SPICE_PZ_ANALYSES* analyses )
149{
150 if( !aCmd.Lower().StartsWith( wxS( ".pz" ) ) )
151 return false;
152
153 *transferFunction = "vol";
154 analyses->m_Poles = true;
155 analyses->m_Zeros = true;
156
157 wxStringTokenizer tokens( aCmd.Mid( 3 ), wxS( " \t" ), wxTOKEN_STRTOK );
158
159 if( tokens.HasMoreTokens() )
160 *input = tokens.GetNextToken();
161
162 if( tokens.HasMoreTokens() )
163 *inputRef = tokens.GetNextToken();
164
165 if( tokens.HasMoreTokens() )
166 *output = tokens.GetNextToken();
167
168 if( tokens.HasMoreTokens() )
169 *outputRef = tokens.GetNextToken();
170
171 if( tokens.HasMoreTokens() )
172 *transferFunction = tokens.GetNextToken();
173
174 if( tokens.HasMoreTokens() )
175 {
176 wxString token = tokens.GetNextToken().Lower();
177
178 if( token == wxS( "pol" ) )
179 analyses->m_Zeros = false;
180 else if( token == wxS( "zer" ) )
181 analyses->m_Poles = false;
182 }
183
184 return true;
185}
186
187bool SPICE_CIRCUIT_MODEL::ParseNoiseCommand( const wxString& aCmd, wxString* aOutput,
188 wxString* aRef, wxString* aSource, wxString* aScale,
189 SPICE_VALUE* aPts, SPICE_VALUE* aFStart,
190 SPICE_VALUE* aFStop, bool* aSaveAll )
191{
192 if( !aCmd.Lower().StartsWith( wxS( ".noise" ) ) )
193 return false;
194
195 wxString cmd = aCmd.Mid( 6 );
196
197 cmd.Trim( false );
198
199 if( !cmd.Lower().StartsWith( wxS( "v(" ) ) )
200 return false;
201
202 cmd = cmd.Mid( 2 );
203
204 wxString function = cmd.Before( ')' );
205 wxString params = cmd.After( ')' );
206
207 wxStringTokenizer func_tokens( function, wxS( " ,\t" ), wxTOKEN_STRTOK );
208
209 *aOutput = func_tokens.GetNextToken();
210 *aRef = func_tokens.GetNextToken();
211
212 wxStringTokenizer tokens( params, wxS( " \t" ), wxTOKEN_STRTOK );
213 wxString token = tokens.GetNextToken();
214
215 if( !token.IsEmpty() )
216 {
217 *aSource = token;
218 token = tokens.GetNextToken();
219 }
220
221 if( token.Lower() == "dec" || token.Lower() == "oct" || token.Lower() == "lin" )
222 {
223 *aScale = token;
224 token = tokens.GetNextToken();
225 }
226
227 if( !token.IsEmpty() )
228 {
229 *aPts = token;
230 token = tokens.GetNextToken();
231 }
232
233 if( !token.IsEmpty() )
234 {
235 *aFStart = SPICE_VALUE( token );
236 token = tokens.GetNextToken();
237 }
238
239 if( !token.IsEmpty() )
240 {
241 *aFStop = SPICE_VALUE( token );
242 token = tokens.GetNextToken();
243 }
244
245 if( !token.IsEmpty() )
246 *aSaveAll = true;
247
248 return true;
249}
250
251
252void SPICE_CIRCUIT_MODEL::WriteDirectives( const wxString& aSimCommand, unsigned aSimOptions,
253 OUTPUTFORMATTER& aFormatter ) const
254{
255 if( aSimCommand.IsEmpty() )
256 aSimOptions |= OPTION_SIM_COMMAND;
257
258 NETLIST_EXPORTER_SPICE::WriteDirectives( aSimCommand, aSimOptions, aFormatter );
259
260 if( !aSimCommand.IsEmpty() )
261 aFormatter.Print( 0, "%s\n", TO_UTF8( aSimCommand ) );
262}
@ POWER
Power pin.
void ReadDirectives(unsigned aNetlistOptions)
const std::vector< wxString > & GetDirectives()
virtual void WriteDirectives(const wxString &aSimCommand, unsigned aSimOptions, OUTPUTFORMATTER &candidate) const
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:322
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:458
bool ParsePZCommand(const wxString &aCmd, wxString *transferFunction, wxString *input, wxString *inputRef, wxString *output, wxString *outputRef, SPICE_PZ_ANALYSES *analyses)
static bool IsSimCommand(const wxString &aCmd)
Determine if a directive is a simulation command.
bool ParseDCCommand(const wxString &aCmd, SPICE_DC_PARAMS *aSource1, SPICE_DC_PARAMS *aSource2)
Parse a two-source .dc command directive into its symbols.
static SIM_TYPE CommandToSimType(const wxString &aCmd)
Return simulation type basing on a simulation command directive.
bool ParseNoiseCommand(const wxString &aCmd, wxString *aOutput, wxString *aRef, wxString *aSource, wxString *aScale, SPICE_VALUE *aPts, SPICE_VALUE *aFStart, SPICE_VALUE *aFStop, bool *aSaveAll)
void WriteDirectives(const wxString &aSimCommand, unsigned aSimOptions, OUTPUTFORMATTER &aFormatter) const override
wxString GetSchTextSimCommand()
Return simulation command directives placed in schematic sheets (if any).
SIM_TRACE_TYPE VectorToSignal(const std::string &aVector, wxString &aSignal) const
Return name of Spice dataset for a specific trace.
Helper class to recognize Spice formatted values.
Definition: spice_value.h:56
SIM_TRACE_TYPE
Definition: sim_types.h:50
@ SPT_UNKNOWN
Definition: sim_types.h:67
@ SPT_VOLTAGE
Definition: sim_types.h:52
@ SPT_POWER
Definition: sim_types.h:56
@ SPT_CURRENT
Definition: sim_types.h:53
SIM_TYPE
< Possible simulation types
Definition: sim_types.h:32
@ ST_SP
Definition: sim_types.h:43
@ ST_TRAN
Definition: sim_types.h:42
@ ST_UNKNOWN
Definition: sim_types.h:33
@ ST_NOISE
Definition: sim_types.h:37
@ ST_AC
Definition: sim_types.h:34
@ ST_DISTO
Definition: sim_types.h:36
@ ST_TF
Definition: sim_types.h:41
@ ST_SENS
Definition: sim_types.h:40
@ ST_DC
Definition: sim_types.h:35
@ ST_OP
Definition: sim_types.h:38
@ ST_FFT
Definition: sim_types.h:44
@ ST_PZ
Definition: sim_types.h:39
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:391
SPICE_VALUE m_vincrement