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 aSource2->m_source = tokens.GetNextToken();
135 aSource2->m_vstart = SPICE_VALUE( tokens.GetNextToken() );
136 aSource2->m_vend = SPICE_VALUE( tokens.GetNextToken() );
137 aSource2->m_vincrement = SPICE_VALUE( tokens.GetNextToken() );
138
139 return true;
140}
141
142
143bool SPICE_CIRCUIT_MODEL::ParsePZCommand( const wxString& aCmd, wxString* transferFunction,
144 wxString* input, wxString* inputRef, wxString* output,
145 wxString* outputRef, SPICE_PZ_ANALYSES* analyses )
146{
147 if( !aCmd.Lower().StartsWith( wxS( ".pz" ) ) )
148 return false;
149
150 *transferFunction = "vol";
151 analyses->m_Poles = true;
152 analyses->m_Zeros = true;
153
154 wxStringTokenizer tokens( aCmd.Mid( 3 ), wxS( " \t" ), wxTOKEN_STRTOK );
155
156 if( tokens.HasMoreTokens() )
157 *input = tokens.GetNextToken();
158
159 if( tokens.HasMoreTokens() )
160 *inputRef = tokens.GetNextToken();
161
162 if( tokens.HasMoreTokens() )
163 *output = tokens.GetNextToken();
164
165 if( tokens.HasMoreTokens() )
166 *outputRef = tokens.GetNextToken();
167
168 if( tokens.HasMoreTokens() )
169 *transferFunction = tokens.GetNextToken();
170
171 if( tokens.HasMoreTokens() )
172 {
173 wxString token = tokens.GetNextToken().Lower();
174
175 if( token == wxS( "pol" ) )
176 analyses->m_Zeros = false;
177 else if( token == wxS( "zer" ) )
178 analyses->m_Poles = false;
179 }
180
181 return true;
182}
183
184bool SPICE_CIRCUIT_MODEL::ParseNoiseCommand( const wxString& aCmd, wxString* aOutput,
185 wxString* aRef, wxString* aSource, wxString* aScale,
186 SPICE_VALUE* aPts, SPICE_VALUE* aFStart,
187 SPICE_VALUE* aFStop, bool* aSaveAll )
188{
189 if( !aCmd.Lower().StartsWith( wxS( ".noise" ) ) )
190 return false;
191
192 wxString cmd = aCmd.Mid( 6 );
193
194 cmd.Trim( false );
195
196 if( !cmd.Lower().StartsWith( wxS( "v(" ) ) )
197 return false;
198
199 cmd = cmd.Mid( 2 );
200
201 wxString function = cmd.Before( ')' );
202 wxString params = cmd.After( ')' );
203
204 wxStringTokenizer func_tokens( function, wxS( " ,\t" ), wxTOKEN_STRTOK );
205
206 *aOutput = func_tokens.GetNextToken();
207 *aRef = func_tokens.GetNextToken();
208
209 wxStringTokenizer tokens( params, wxS( " \t" ), wxTOKEN_STRTOK );
210 wxString token = tokens.GetNextToken();
211
212 if( !token.IsEmpty() )
213 {
214 *aSource = token;
215 token = tokens.GetNextToken();
216 }
217
218 if( token.Lower() == "dec" || token.Lower() == "oct" || token.Lower() == "lin" )
219 {
220 *aScale = token;
221 token = tokens.GetNextToken();
222 }
223
224 if( !token.IsEmpty() )
225 {
226 *aPts = token;
227 token = tokens.GetNextToken();
228 }
229
230 if( !token.IsEmpty() )
231 {
232 *aFStart = SPICE_VALUE( token );
233 token = tokens.GetNextToken();
234 }
235
236 if( !token.IsEmpty() )
237 {
238 *aFStop = SPICE_VALUE( token );
239 token = tokens.GetNextToken();
240 }
241
242 if( !token.IsEmpty() )
243 *aSaveAll = true;
244
245 return true;
246}
247
248
249void SPICE_CIRCUIT_MODEL::WriteDirectives( const wxString& aSimCommand, unsigned aSimOptions,
250 OUTPUTFORMATTER& aFormatter ) const
251{
252 if( aSimCommand.IsEmpty() )
253 aSimOptions |= OPTION_SIM_COMMAND;
254
255 NETLIST_EXPORTER_SPICE::WriteDirectives( aSimCommand, aSimOptions, aFormatter );
256
257 if( !aSimCommand.IsEmpty() )
258 aFormatter.Print( 0, "%s\n", TO_UTF8( aSimCommand ) );
259}
@ 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