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
28#include <wx/regex.h>
29#include <wx/tokenzr.h>
30
31#include <locale_io.h>
32#include <richio.h>
33#include <string_utils.h>
34
36 wxString& aSignal ) const
37{
38 static wxString BRANCH( wxS( "#branch" ) );
39 static wxString POWER( wxS( ":power" ) );
40
41 // See ngspice manual chapt. 31.1 "Accessing internal device parameters"
42 static wxRegEx internalDevParameter( wxS( "^@(\\w*[\\.\\w+]*)\\[(\\w*)\\]$" ), wxRE_ADVANCED );
43
44 wxString vector( aVector );
45
46 if( !internalDevParameter.Matches( vector ) )
47 {
48 if( vector.EndsWith( BRANCH ) )
49 {
50 aSignal = wxT( "I(" ) + vector.Left( vector.Length() - BRANCH.Length() ) + wxT( ")" );
51 return SPT_CURRENT;
52 }
53 else if( vector.EndsWith( POWER ) )
54 {
55 aSignal = wxT( "P(" ) + vector.Left( vector.Length() - POWER.Length() ) + wxT( ")" );
56 return SPT_POWER;
57 }
58 else
59 {
60 aSignal = wxT( "V(" ) + vector + wxT( ")" );
61 return SPT_VOLTAGE;
62 }
63 }
64 else
65 {
66 wxString paramType = internalDevParameter.GetMatch( vector, 2 );
67
68 if( paramType.Lower()[0] == 'i' )
69 {
70 // this is a branch current
71 paramType[0] = 'I';
72 aSignal = paramType + wxT( "(" );
73 aSignal += internalDevParameter.GetMatch( vector, 1 ).Upper() + wxT( ")" );
74 return SPT_CURRENT;
75 }
76 else
77 {
78 return SPT_UNKNOWN;
79 }
80 }
81}
82
83
85{
86 wxString simCmd;
87
88 ReadDirectives( 0 );
89
90 for( const wxString& directive : GetDirectives() )
91 {
92 if( IsSimCommand( directive ) )
93 simCmd += wxString::Format( wxT( "%s\r\n" ), directive );
94 }
95
96 return simCmd.Trim();
97}
98
99
101{
102 wxString cmd = aCmd.Lower().Trim();
103
104 if( cmd == wxT( ".op" ) ) return ST_OP;
105 else if( cmd.StartsWith( wxT( ".ac" ) ) ) return ST_AC;
106 else if( cmd.StartsWith( wxT( ".dc" ) ) ) return ST_DC;
107 else if( cmd.StartsWith( wxT( ".tran" ) ) ) return ST_TRAN;
108 else if( cmd.StartsWith( wxT( ".disto" ) ) ) return ST_DISTO;
109 else if( cmd.StartsWith( wxT( ".noise" ) ) ) return ST_NOISE;
110 else if( cmd.StartsWith( wxT( ".pz" ) ) ) return ST_PZ;
111 else if( cmd.StartsWith( wxT( ".sens" ) ) ) return ST_SENS;
112 else if( cmd.StartsWith( wxT( ".sp" ) ) ) return ST_SP;
113 else if( cmd.StartsWith( wxT( ".tf" ) ) ) return ST_TF;
114
115 else if( cmd.StartsWith( wxT( "fft" ) ) || cmd.Contains( wxT( "\nfft" ) ) )
116 return ST_FFT;
117 else
118 return ST_UNKNOWN;
119}
120
121
122bool SPICE_CIRCUIT_MODEL::ParseDCCommand( const wxString& aCmd, SPICE_DC_PARAMS* aSource1,
123 SPICE_DC_PARAMS* aSource2 )
124{
125 if( !aCmd.Lower().StartsWith( ".dc" ) )
126 return false;
127
128 wxString cmd = aCmd.Mid( 3 );
129 wxStringTokenizer tokens( cmd, wxS( " \t" ), wxTOKEN_STRTOK );
130
131 aSource1->m_source = tokens.GetNextToken();
132 aSource1->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
133 aSource1->m_vend = SPICE_VALUE( tokens.GetNextToken() );
134 aSource1->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
135
136 if( tokens.HasMoreTokens() )
137 {
138 aSource2->m_source = tokens.GetNextToken();
139 aSource2->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
140 aSource2->m_vend = SPICE_VALUE( tokens.GetNextToken() );
141 aSource2->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
142 }
143
144 return true;
145}
146
147
148bool SPICE_CIRCUIT_MODEL::ParsePZCommand( const wxString& aCmd, wxString* transferFunction,
149 wxString* input, wxString* inputRef, wxString* output,
150 wxString* outputRef, SPICE_PZ_ANALYSES* analyses )
151{
152 if( !aCmd.Lower().StartsWith( wxS( ".pz" ) ) )
153 return false;
154
155 *transferFunction = "vol";
156 analyses->m_Poles = true;
157 analyses->m_Zeros = true;
158
159 wxStringTokenizer tokens( aCmd.Mid( 3 ), wxS( " \t" ), wxTOKEN_STRTOK );
160
161 if( tokens.HasMoreTokens() )
162 *input = tokens.GetNextToken();
163
164 if( tokens.HasMoreTokens() )
165 *inputRef = tokens.GetNextToken();
166
167 if( tokens.HasMoreTokens() )
168 *output = tokens.GetNextToken();
169
170 if( tokens.HasMoreTokens() )
171 *outputRef = tokens.GetNextToken();
172
173 if( tokens.HasMoreTokens() )
174 *transferFunction = tokens.GetNextToken();
175
176 if( tokens.HasMoreTokens() )
177 {
178 wxString token = tokens.GetNextToken().Lower();
179
180 if( token == wxS( "pol" ) )
181 analyses->m_Zeros = false;
182 else if( token == wxS( "zer" ) )
183 analyses->m_Poles = false;
184 }
185
186 return true;
187}
188
189bool SPICE_CIRCUIT_MODEL::ParseNoiseCommand( const wxString& aCmd, wxString* aOutput,
190 wxString* aRef, wxString* aSource, wxString* aScale,
191 SPICE_VALUE* aPts, SPICE_VALUE* aFStart,
192 SPICE_VALUE* aFStop, bool* aSaveAll )
193{
194 if( !aCmd.Lower().StartsWith( wxS( ".noise" ) ) )
195 return false;
196
197 wxString cmd = aCmd.Mid( 6 );
198
199 cmd.Trim( false );
200
201 if( !cmd.Lower().StartsWith( wxS( "v(" ) ) )
202 return false;
203
204 cmd = cmd.Mid( 2 );
205
206 wxString function = cmd.Before( ')' );
207 wxString params = cmd.After( ')' );
208
209 wxStringTokenizer func_tokens( function, wxS( " ,\t" ), wxTOKEN_STRTOK );
210
211 *aOutput = func_tokens.GetNextToken();
212 *aRef = func_tokens.GetNextToken();
213
214 wxStringTokenizer tokens( params, wxS( " \t" ), wxTOKEN_STRTOK );
215 wxString token = tokens.GetNextToken();
216
217 if( !token.IsEmpty() )
218 {
219 *aSource = token;
220 token = tokens.GetNextToken();
221 }
222
223 if( token.Lower() == "dec" || token.Lower() == "oct" || token.Lower() == "lin" )
224 {
225 *aScale = token;
226 token = tokens.GetNextToken();
227 }
228
229 if( !token.IsEmpty() )
230 {
231 *aPts = token;
232 token = tokens.GetNextToken();
233 }
234
235 if( !token.IsEmpty() )
236 {
237 *aFStart = SPICE_VALUE( token );
238 token = tokens.GetNextToken();
239 }
240
241 if( !token.IsEmpty() )
242 {
243 *aFStop = SPICE_VALUE( token );
244 token = tokens.GetNextToken();
245 }
246
247 if( !token.IsEmpty() )
248 *aSaveAll = true;
249
250 return true;
251}
252
253
254void SPICE_CIRCUIT_MODEL::WriteDirectives( const wxString& aSimCommand, unsigned aSimOptions,
255 OUTPUTFORMATTER& aFormatter ) const
256{
257 if( aSimCommand.IsEmpty() )
258 aSimOptions |= OPTION_SIM_COMMAND;
259
260 NETLIST_EXPORTER_SPICE::WriteDirectives( aSimCommand, aSimOptions, aFormatter );
261
262 if( !aSimCommand.IsEmpty() )
263 aFormatter.Print( 0, "%s\n", TO_UTF8( aSimCommand ) );
264}
@ 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_N 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:398
SPICE_VALUE m_vincrement