KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_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) 2022 Mikolaj Wielgus
5 * Copyright (C) 2022 CERN
6 * Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors.
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#include <lib_symbol.h>
26#include <sch_symbol.h>
27#include <string_utils.h>
28#include <wx/regex.h>
29
30#include <sim/sim_model.h>
32#include <sim/sim_model_ideal.h>
35#include <sim/sim_model_r_pot.h>
36#include <sim/sim_model_kibis.h>
41#include <sim/sim_model_tline.h>
43#include <sim/sim_lib_mgr.h>
45
46#include <boost/algorithm/string.hpp>
47#include <fmt/core.h>
48#include <pegtl/contrib/parse_tree.hpp>
49
50#include <iterator>
52
53using TYPE = SIM_MODEL::TYPE;
54
55
56SIM_MODEL::DEVICE_INFO SIM_MODEL::DeviceInfo( DEVICE_T aDeviceType )
57{
58 switch( aDeviceType )
59 {
60 // | fieldValue | description | showInMenu |
61 // -------------------------------------------------------
62 //
63 case DEVICE_T::NONE: return { "", "", true };
64 case DEVICE_T::R: return { "R", "Resistor", true };
65 case DEVICE_T::C: return { "C", "Capacitor", true };
66 case DEVICE_T::L: return { "L", "Inductor", true };
67 case DEVICE_T::K: return { "K", "Mutual Inductance Statement", true };
68 case DEVICE_T::TLINE: return { "TLINE", "Transmission Line", true };
69 case DEVICE_T::SW: return { "SW", "Switch", true };
70
71 case DEVICE_T::D: return { "D", "Diode", true };
72 case DEVICE_T::NPN: return { "NPN", "NPN BJT", true };
73 case DEVICE_T::PNP: return { "PNP", "PNP BJT", true };
74
75 case DEVICE_T::NJFET: return { "NJFET", "N-channel JFET", true };
76 case DEVICE_T::PJFET: return { "PJFET", "P-channel JFET", true };
77
78 case DEVICE_T::NMOS: return { "NMOS", "N-channel MOSFET", true };
79 case DEVICE_T::PMOS: return { "PMOS", "P-channel MOSFET", true };
80 case DEVICE_T::NMES: return { "NMES", "N-channel MESFET", true };
81 case DEVICE_T::PMES: return { "PMES", "P-channel MESFET", true };
82
83 case DEVICE_T::V: return { "V", "Voltage Source", true };
84 case DEVICE_T::I: return { "I", "Current Source", true };
85 case DEVICE_T::E: return { "E", "Voltage Source", false };
86 case DEVICE_T::F: return { "F", "Current Source", false };
87 case DEVICE_T::G: return { "G", "Current Source", false };
88 case DEVICE_T::H: return { "H", "Voltage Source", false };
89
90 case DEVICE_T::KIBIS: return { "IBIS", "IBIS Model", false };
91
92 case DEVICE_T::SUBCKT: return { "SUBCKT", "Subcircuit", false };
93 case DEVICE_T::XSPICE: return { "XSPICE", "XSPICE Code Model", true };
94 case DEVICE_T::SPICE: return { "SPICE", "Raw Spice Element", true };
95
96 default: wxFAIL; return {};
97 }
98}
99
100
101SIM_MODEL::INFO SIM_MODEL::TypeInfo( TYPE aType )
102{
103 switch( aType )
104 {
105 // | deviceType | fieldValue | description |
106 // ---------------------------------------------------------------------
107 //
108 case TYPE::NONE: return { DEVICE_T::NONE, "", "" };
109
110 case TYPE::R: return { DEVICE_T::R, "", "Ideal" };
111 case TYPE::R_POT: return { DEVICE_T::R, "POT", "Potentiometer" };
112 case TYPE::R_BEHAVIORAL: return { DEVICE_T::R, "=", "Behavioral" };
113
114 case TYPE::C: return { DEVICE_T::C, "", "Ideal" };
115 case TYPE::C_BEHAVIORAL: return { DEVICE_T::C, "=", "Behavioral" };
116
117 case TYPE::L: return { DEVICE_T::L, "", "Ideal" };
118 case TYPE::L_BEHAVIORAL: return { DEVICE_T::L, "=", "Behavioral" };
119
120 case TYPE::K: return { DEVICE_T::K, "", "Mutual Inductance Statement" };
121
122 case TYPE::TLINE_Z0: return { DEVICE_T::TLINE, "", "Characteristic impedance" };
123 case TYPE::TLINE_RLGC: return { DEVICE_T::TLINE, "RLGC", "RLGC" };
124
125 case TYPE::SW_V: return { DEVICE_T::SW, "V", "Voltage-controlled" };
126 case TYPE::SW_I: return { DEVICE_T::SW, "I", "Current-controlled" };
127
128 case TYPE::D: return { DEVICE_T::D, "", "" };
129
130 case TYPE::NPN_VBIC: return { DEVICE_T::NPN, "VBIC", "VBIC" };
131 case TYPE::PNP_VBIC: return { DEVICE_T::PNP, "VBIC", "VBIC" };
132 case TYPE::NPN_GUMMELPOON: return { DEVICE_T::NPN, "GUMMELPOON", "Gummel-Poon" };
133 case TYPE::PNP_GUMMELPOON: return { DEVICE_T::PNP, "GUMMELPOON", "Gummel-Poon" };
134 //case TYPE::BJT_MEXTRAM: return {};
135 case TYPE::NPN_HICUM2: return { DEVICE_T::NPN, "HICUML2", "HICUM level 2" };
136 case TYPE::PNP_HICUM2: return { DEVICE_T::PNP, "HICUML2", "HICUM level 2" };
137 //case TYPE::BJT_HICUM_L0: return {};
138
139 case TYPE::NJFET_SHICHMANHODGES: return { DEVICE_T::NJFET, "SHICHMANHODGES", "Shichman-Hodges" };
140 case TYPE::PJFET_SHICHMANHODGES: return { DEVICE_T::PJFET, "SHICHMANHODGES", "Shichman-Hodges" };
141 case TYPE::NJFET_PARKERSKELLERN: return { DEVICE_T::NJFET, "PARKERSKELLERN", "Parker-Skellern" };
142 case TYPE::PJFET_PARKERSKELLERN: return { DEVICE_T::PJFET, "PARKERSKELLERN", "Parker-Skellern" };
143
144 case TYPE::NMES_STATZ: return { DEVICE_T::NMES, "STATZ", "Statz" };
145 case TYPE::PMES_STATZ: return { DEVICE_T::PMES, "STATZ", "Statz" };
146 case TYPE::NMES_YTTERDAL: return { DEVICE_T::NMES, "YTTERDAL", "Ytterdal" };
147 case TYPE::PMES_YTTERDAL: return { DEVICE_T::PMES, "YTTERDAL", "Ytterdal" };
148 case TYPE::NMES_HFET1: return { DEVICE_T::NMES, "HFET1", "HFET1" };
149 case TYPE::PMES_HFET1: return { DEVICE_T::PMES, "HFET1", "HFET1" };
150 case TYPE::NMES_HFET2: return { DEVICE_T::NMES, "HFET2", "HFET2" };
151 case TYPE::PMES_HFET2: return { DEVICE_T::PMES, "HFET2", "HFET2" };
152
153 case TYPE::NMOS_VDMOS: return { DEVICE_T::NMOS, "VDMOS", "VDMOS" };
154 case TYPE::PMOS_VDMOS: return { DEVICE_T::PMOS, "VDMOS", "VDMOS" };
155 case TYPE::NMOS_MOS1: return { DEVICE_T::NMOS, "MOS1", "Classical quadratic (MOS1)" };
156 case TYPE::PMOS_MOS1: return { DEVICE_T::PMOS, "MOS1", "Classical quadratic (MOS1)" };
157 case TYPE::NMOS_MOS2: return { DEVICE_T::NMOS, "MOS2", "Grove-Frohman (MOS2)" };
158 case TYPE::PMOS_MOS2: return { DEVICE_T::PMOS, "MOS2", "Grove-Frohman (MOS2)" };
159 case TYPE::NMOS_MOS3: return { DEVICE_T::NMOS, "MOS3", "MOS3" };
160 case TYPE::PMOS_MOS3: return { DEVICE_T::PMOS, "MOS3", "MOS3" };
161 case TYPE::NMOS_BSIM1: return { DEVICE_T::NMOS, "BSIM1", "BSIM1" };
162 case TYPE::PMOS_BSIM1: return { DEVICE_T::PMOS, "BSIM1", "BSIM1" };
163 case TYPE::NMOS_BSIM2: return { DEVICE_T::NMOS, "BSIM2", "BSIM2" };
164 case TYPE::PMOS_BSIM2: return { DEVICE_T::PMOS, "BSIM2", "BSIM2" };
165 case TYPE::NMOS_MOS6: return { DEVICE_T::NMOS, "MOS6", "MOS6" };
166 case TYPE::PMOS_MOS6: return { DEVICE_T::PMOS, "MOS6", "MOS6" };
167 case TYPE::NMOS_BSIM3: return { DEVICE_T::NMOS, "BSIM3", "BSIM3" };
168 case TYPE::PMOS_BSIM3: return { DEVICE_T::PMOS, "BSIM3", "BSIM3" };
169 case TYPE::NMOS_MOS9: return { DEVICE_T::NMOS, "MOS9", "MOS9" };
170 case TYPE::PMOS_MOS9: return { DEVICE_T::PMOS, "MOS9", "MOS9" };
171 case TYPE::NMOS_B4SOI: return { DEVICE_T::NMOS, "B4SOI", "BSIM4 SOI (B4SOI)" };
172 case TYPE::PMOS_B4SOI: return { DEVICE_T::PMOS, "B4SOI", "BSIM4 SOI (B4SOI)" };
173 case TYPE::NMOS_BSIM4: return { DEVICE_T::NMOS, "BSIM4", "BSIM4" };
174 case TYPE::PMOS_BSIM4: return { DEVICE_T::PMOS, "BSIM4", "BSIM4" };
175 //case TYPE::NMOS_EKV2_6: return {};
176 //case TYPE::PMOS_EKV2_6: return {};
177 //case TYPE::NMOS_PSP: return {};
178 //case TYPE::PMOS_PSP: return {};
179 case TYPE::NMOS_B3SOIFD: return { DEVICE_T::NMOS, "B3SOIFD", "B3SOIFD (BSIM3 FD-SOI)" };
180 case TYPE::PMOS_B3SOIFD: return { DEVICE_T::PMOS, "B3SOIFD", "B3SOIFD (BSIM3 FD-SOI)" };
181 case TYPE::NMOS_B3SOIDD: return { DEVICE_T::NMOS, "B3SOIDD", "B3SOIDD (BSIM3 SOI)" };
182 case TYPE::PMOS_B3SOIDD: return { DEVICE_T::PMOS, "B3SOIDD", "B3SOIDD (BSIM3 SOI)" };
183 case TYPE::NMOS_B3SOIPD: return { DEVICE_T::NMOS, "B3SOIPD", "B3SOIPD (BSIM3 PD-SOI)" };
184 case TYPE::PMOS_B3SOIPD: return { DEVICE_T::PMOS, "B3SOIPD", "B3SOIPD (BSIM3 PD-SOI)" };
185 //case TYPE::NMOS_STAG: return {};
186 //case TYPE::PMOS_STAG: return {};
187 case TYPE::NMOS_HISIM2: return { DEVICE_T::NMOS, "HISIM2", "HiSIM2" };
188 case TYPE::PMOS_HISIM2: return { DEVICE_T::PMOS, "HISIM2", "HiSIM2" };
189 case TYPE::NMOS_HISIMHV1: return { DEVICE_T::NMOS, "HISIMHV1", "HiSIM_HV1" };
190 case TYPE::PMOS_HISIMHV1: return { DEVICE_T::PMOS, "HISIMHV1", "HiSIM_HV1" };
191 case TYPE::NMOS_HISIMHV2: return { DEVICE_T::NMOS, "HISIMHV2", "HiSIM_HV2" };
192 case TYPE::PMOS_HISIMHV2: return { DEVICE_T::PMOS, "HISIMHV2", "HiSIM_HV2" };
193
194 case TYPE::V: return { DEVICE_T::V, "DC", "DC", };
195 case TYPE::V_SIN: return { DEVICE_T::V, "SIN", "Sine" };
196 case TYPE::V_PULSE: return { DEVICE_T::V, "PULSE", "Pulse" };
197 case TYPE::V_EXP: return { DEVICE_T::V, "EXP", "Exponential" };
198 case TYPE::V_AM: return { DEVICE_T::V, "AM", "Amplitude modulated" };
199 case TYPE::V_SFFM: return { DEVICE_T::V, "SFFM", "Single-frequency FM" };
200 case TYPE::V_VCL: return { DEVICE_T::E, "", "Voltage-controlled" };
201 case TYPE::V_CCL: return { DEVICE_T::H, "", "Current-controlled" };
202 case TYPE::V_PWL: return { DEVICE_T::V, "PWL", "Piecewise linear" };
203 case TYPE::V_WHITENOISE: return { DEVICE_T::V, "WHITENOISE", "White noise" };
204 case TYPE::V_PINKNOISE: return { DEVICE_T::V, "PINKNOISE", "Pink noise (1/f)" };
205 case TYPE::V_BURSTNOISE: return { DEVICE_T::V, "BURSTNOISE", "Burst noise" };
206 case TYPE::V_RANDUNIFORM: return { DEVICE_T::V, "RANDUNIFORM", "Random uniform" };
207 case TYPE::V_RANDGAUSSIAN: return { DEVICE_T::V, "RANDGAUSSIAN", "Random Gaussian" };
208 case TYPE::V_RANDEXP: return { DEVICE_T::V, "RANDEXP", "Random exponential" };
209 case TYPE::V_RANDPOISSON: return { DEVICE_T::V, "RANDPOISSON", "Random Poisson" };
210 case TYPE::V_BEHAVIORAL: return { DEVICE_T::V, "=", "Behavioral" };
211
212 case TYPE::I: return { DEVICE_T::I, "DC", "DC", };
213 case TYPE::I_SIN: return { DEVICE_T::I, "SIN", "Sine" };
214 case TYPE::I_PULSE: return { DEVICE_T::I, "PULSE", "Pulse" };
215 case TYPE::I_EXP: return { DEVICE_T::I, "EXP", "Exponential" };
216 case TYPE::I_AM: return { DEVICE_T::I, "AM", "Amplitude modulated" };
217 case TYPE::I_SFFM: return { DEVICE_T::I, "SFFM", "Single-frequency FM" };
218 case TYPE::I_VCL: return { DEVICE_T::G, "", "Voltage-controlled" };
219 case TYPE::I_CCL: return { DEVICE_T::F, "", "Current-controlled" };
220 case TYPE::I_PWL: return { DEVICE_T::I, "PWL", "Piecewise linear" };
221 case TYPE::I_WHITENOISE: return { DEVICE_T::I, "WHITENOISE", "White noise" };
222 case TYPE::I_PINKNOISE: return { DEVICE_T::I, "PINKNOISE", "Pink noise (1/f)" };
223 case TYPE::I_BURSTNOISE: return { DEVICE_T::I, "BURSTNOISE", "Burst noise" };
224 case TYPE::I_RANDUNIFORM: return { DEVICE_T::I, "RANDUNIFORM", "Random uniform" };
225 case TYPE::I_RANDGAUSSIAN: return { DEVICE_T::I, "RANDGAUSSIAN", "Random Gaussian" };
226 case TYPE::I_RANDEXP: return { DEVICE_T::I, "RANDEXP", "Random exponential" };
227 case TYPE::I_RANDPOISSON: return { DEVICE_T::I, "RANDPOISSON", "Random Poisson" };
228 case TYPE::I_BEHAVIORAL: return { DEVICE_T::I, "=", "Behavioral" };
229
230 case TYPE::SUBCKT: return { DEVICE_T::SUBCKT, "", "Subcircuit" };
231 case TYPE::XSPICE: return { DEVICE_T::XSPICE, "", "" };
232
233 case TYPE::KIBIS_DEVICE: return { DEVICE_T::KIBIS, "DEVICE", "Device" };
234 case TYPE::KIBIS_DRIVER_DC: return { DEVICE_T::KIBIS, "DCDRIVER", "DC driver" };
235 case TYPE::KIBIS_DRIVER_RECT: return { DEVICE_T::KIBIS, "RECTDRIVER", "Rectangular wave driver" };
236 case TYPE::KIBIS_DRIVER_PRBS: return { DEVICE_T::KIBIS, "PRBSDRIVER", "PRBS driver" };
237
238 case TYPE::RAWSPICE: return { DEVICE_T::SPICE, "", "" };
239
240 default: wxFAIL; return {};
241 }
242}
243
244
246{
247 switch( aType )
248 {
249 // | itemType | modelType | fnName | level |isDefaultLvl|hasExpr|version|
250 // -------------------------------------------------------------------------
251 case TYPE::R: return { "R", "" };
252 case TYPE::R_POT: return { "A", "" };
253 case TYPE::R_BEHAVIORAL: return { "R", "", "", "0", false, true };
254
255 case TYPE::C: return { "C", "" };
256 case TYPE::C_BEHAVIORAL: return { "C", "", "", "0", false, true };
257
258 case TYPE::L: return { "L", "" };
259 case TYPE::L_BEHAVIORAL: return { "L", "", "", "0", false, true };
260
261 case TYPE::K: return { "K", "" };
262
263 //case TYPE::TLINE_Z0: return { "T" };
264 case TYPE::TLINE_Z0: return { "O", "LTRA" };
265 case TYPE::TLINE_RLGC: return { "O", "LTRA" };
266
267 case TYPE::SW_V: return { "S", "SW" };
268 case TYPE::SW_I: return { "W", "CSW" };
269
270 case TYPE::D: return { "D", "D" };
271
272 case TYPE::NPN_VBIC: return { "Q", "NPN", "", "4" };
273 case TYPE::PNP_VBIC: return { "Q", "PNP", "", "4" };
274 case TYPE::NPN_GUMMELPOON: return { "Q", "NPN", "", "1", true };
275 case TYPE::PNP_GUMMELPOON: return { "Q", "PNP", "", "1", true };
276 case TYPE::NPN_HICUM2: return { "Q", "NPN", "", "8" };
277 case TYPE::PNP_HICUM2: return { "Q", "PNP", "", "8" };
278
279 case TYPE::NJFET_SHICHMANHODGES: return { "J", "NJF", "", "1", true };
280 case TYPE::PJFET_SHICHMANHODGES: return { "J", "PJF", "", "1", true };
281 case TYPE::NJFET_PARKERSKELLERN: return { "J", "NJF", "", "2" };
282 case TYPE::PJFET_PARKERSKELLERN: return { "J", "PJF", "", "2" };
283
284 case TYPE::NMES_STATZ: return { "Z", "NMF", "", "1", true };
285 case TYPE::PMES_STATZ: return { "Z", "PMF", "", "1", true };
286 case TYPE::NMES_YTTERDAL: return { "Z", "NMF", "", "2" };
287 case TYPE::PMES_YTTERDAL: return { "Z", "PMF", "", "2" };
288 case TYPE::NMES_HFET1: return { "Z", "NMF", "", "5" };
289 case TYPE::PMES_HFET1: return { "Z", "PMF", "", "5" };
290 case TYPE::NMES_HFET2: return { "Z", "NMF", "", "6" };
291 case TYPE::PMES_HFET2: return { "Z", "PMF", "", "6" };
292
293 case TYPE::NMOS_VDMOS: return { "M", "VDMOS NCHAN" };
294 case TYPE::PMOS_VDMOS: return { "M", "VDMOS PCHAN" };
295 case TYPE::NMOS_MOS1: return { "M", "NMOS", "", "1", true };
296 case TYPE::PMOS_MOS1: return { "M", "PMOS", "", "1", true };
297 case TYPE::NMOS_MOS2: return { "M", "NMOS", "", "2" };
298 case TYPE::PMOS_MOS2: return { "M", "PMOS", "", "2" };
299 case TYPE::NMOS_MOS3: return { "M", "NMOS", "", "3" };
300 case TYPE::PMOS_MOS3: return { "M", "PMOS", "", "3" };
301 case TYPE::NMOS_BSIM1: return { "M", "NMOS", "", "4" };
302 case TYPE::PMOS_BSIM1: return { "M", "PMOS", "", "4" };
303 case TYPE::NMOS_BSIM2: return { "M", "NMOS", "", "5" };
304 case TYPE::PMOS_BSIM2: return { "M", "PMOS", "", "5" };
305 case TYPE::NMOS_MOS6: return { "M", "NMOS", "", "6" };
306 case TYPE::PMOS_MOS6: return { "M", "PMOS", "", "6" };
307 case TYPE::NMOS_BSIM3: return { "M", "NMOS", "", "8" };
308 case TYPE::PMOS_BSIM3: return { "M", "PMOS", "", "8" };
309 case TYPE::NMOS_MOS9: return { "M", "NMOS", "", "9" };
310 case TYPE::PMOS_MOS9: return { "M", "PMOS", "", "9" };
311 case TYPE::NMOS_B4SOI: return { "M", "NMOS", "", "10" };
312 case TYPE::PMOS_B4SOI: return { "M", "PMOS", "", "10" };
313 case TYPE::NMOS_BSIM4: return { "M", "NMOS", "", "14" };
314 case TYPE::PMOS_BSIM4: return { "M", "PMOS", "", "14" };
315 //case TYPE::NMOS_EKV2_6: return {};
316 //case TYPE::PMOS_EKV2_6: return {};
317 //case TYPE::NMOS_PSP: return {};
318 //case TYPE::PMOS_PSP: return {};
319 case TYPE::NMOS_B3SOIFD: return { "M", "NMOS", "", "55" };
320 case TYPE::PMOS_B3SOIFD: return { "M", "PMOS", "", "55" };
321 case TYPE::NMOS_B3SOIDD: return { "M", "NMOS", "", "56" };
322 case TYPE::PMOS_B3SOIDD: return { "M", "PMOS", "", "56" };
323 case TYPE::NMOS_B3SOIPD: return { "M", "NMOS", "", "57" };
324 case TYPE::PMOS_B3SOIPD: return { "M", "PMOS", "", "57" };
325 //case TYPE::NMOS_STAG: return {};
326 //case TYPE::PMOS_STAG: return {};
327 case TYPE::NMOS_HISIM2: return { "M", "NMOS", "", "68" };
328 case TYPE::PMOS_HISIM2: return { "M", "PMOS", "", "68" };
329 case TYPE::NMOS_HISIMHV1: return { "M", "NMOS", "", "73", false, false, "1.2.4" };
330 case TYPE::PMOS_HISIMHV1: return { "M", "PMOS", "", "73", false, false, "1.2.4" };
331 case TYPE::NMOS_HISIMHV2: return { "M", "NMOS", "", "73", false, false, "2.2.0" };
332 case TYPE::PMOS_HISIMHV2: return { "M", "PMOS", "", "73", false, false, "2.2.0" };
333
334 case TYPE::V: return { "V", "", "DC" };
335 case TYPE::V_SIN: return { "V", "", "SIN" };
336 case TYPE::V_PULSE: return { "V", "", "PULSE" };
337 case TYPE::V_EXP: return { "V", "", "EXP" };
338 case TYPE::V_AM: return { "V", "", "AM" };
339 case TYPE::V_SFFM: return { "V", "", "SFFM" };
340 case TYPE::V_VCL: return { "E", "", "" };
341 case TYPE::V_CCL: return { "H", "", "" };
342 case TYPE::V_PWL: return { "V", "", "PWL" };
343 case TYPE::V_WHITENOISE: return { "V", "", "TRNOISE" };
344 case TYPE::V_PINKNOISE: return { "V", "", "TRNOISE" };
345 case TYPE::V_BURSTNOISE: return { "V", "", "TRNOISE" };
346 case TYPE::V_RANDUNIFORM: return { "V", "", "TRRANDOM" };
347 case TYPE::V_RANDGAUSSIAN: return { "V", "", "TRRANDOM" };
348 case TYPE::V_RANDEXP: return { "V", "", "TRRANDOM" };
349 case TYPE::V_RANDPOISSON: return { "V", "", "TRRANDOM" };
350 case TYPE::V_BEHAVIORAL: return { "B" };
351
352 case TYPE::I: return { "I", "", "DC" };
353 case TYPE::I_PULSE: return { "I", "", "PULSE" };
354 case TYPE::I_SIN: return { "I", "", "SIN" };
355 case TYPE::I_EXP: return { "I", "", "EXP" };
356 case TYPE::I_AM: return { "V", "", "AM" };
357 case TYPE::I_SFFM: return { "V", "", "SFFM" };
358 case TYPE::I_VCL: return { "G", "", "" };
359 case TYPE::I_CCL: return { "F", "", "" };
360 case TYPE::I_PWL: return { "I", "", "PWL" };
361 case TYPE::I_WHITENOISE: return { "I", "", "TRNOISE" };
362 case TYPE::I_PINKNOISE: return { "I", "", "TRNOISE" };
363 case TYPE::I_BURSTNOISE: return { "I", "", "TRNOISE" };
364 case TYPE::I_RANDUNIFORM: return { "I", "", "TRRANDOM" };
365 case TYPE::I_RANDGAUSSIAN: return { "I", "", "TRRANDOM" };
366 case TYPE::I_RANDEXP: return { "I", "", "TRRANDOM" };
367 case TYPE::I_RANDPOISSON: return { "I", "", "TRRANDOM" };
368 case TYPE::I_BEHAVIORAL: return { "B" };
369
370 case TYPE::SUBCKT: return { "X" };
371 case TYPE::XSPICE: return { "A" };
372
373 case TYPE::KIBIS_DEVICE: return { "X" };
374 case TYPE::KIBIS_DRIVER_DC: return { "X" };
375 case TYPE::KIBIS_DRIVER_RECT: return { "X" };
376 case TYPE::KIBIS_DRIVER_PRBS: return { "X" };
377
378 case TYPE::NONE:
379 case TYPE::RAWSPICE: return {};
380
381 default: wxFAIL; return {};
382 }
383}
384
385
386TYPE SIM_MODEL::ReadTypeFromFields( const std::vector<SCH_FIELD>& aFields, REPORTER& aReporter )
387{
388 std::string deviceTypeFieldValue = GetFieldValue( &aFields, SIM_DEVICE_FIELD );
389 std::string typeFieldValue = GetFieldValue( &aFields, SIM_DEVICE_SUBTYPE_FIELD );
390
391 if( deviceTypeFieldValue != "" )
392 {
393 for( TYPE type : TYPE_ITERATOR() )
394 {
395 if( typeFieldValue == TypeInfo( type ).fieldValue )
396 {
397 if( deviceTypeFieldValue == DeviceInfo( TypeInfo( type ).deviceType ).fieldValue )
398 return type;
399 }
400 }
401 }
402
403 if( typeFieldValue != "" )
404 return TYPE::NONE;
405
406 if( aFields.size() > REFERENCE_FIELD )
407 {
408 aReporter.Report( wxString::Format( _( "No simulation model definition found for "
409 "symbol '%s'." ),
410 aFields[REFERENCE_FIELD].GetText() ),
412 }
413 else
414 {
415 aReporter.Report( _( "No simulation model definition found." ),
417 }
418
419 return TYPE::NONE;
420}
421
422
423void SIM_MODEL::ReadDataFields( const std::vector<SCH_FIELD>* aFields,
424 const std::vector<SCH_PIN*>& aPins )
425{
426 bool diffMode = GetFieldValue( aFields, SIM_LIBRARY_KIBIS::DIFF_FIELD ) == "1";
427 SwitchSingleEndedDiff( diffMode );
428
429 m_serializer->ParseEnable( GetFieldValue( aFields, SIM_LEGACY_ENABLE_FIELD_V7 ) );
430
431 createPins( aPins );
432 m_serializer->ParsePins( GetFieldValue( aFields, SIM_PINS_FIELD ) );
433
434 std::string paramsField = GetFieldValue( aFields, SIM_PARAMS_FIELD );
435
436 if( !m_serializer->ParseParams( paramsField ) )
437 m_serializer->ParseValue( GetFieldValue( aFields, SIM_VALUE_FIELD ) );
438}
439
440
441void SIM_MODEL::WriteFields( std::vector<SCH_FIELD>& aFields ) const
442{
443 // Remove duplicate fields: they are at the end of list
444 for( size_t ii = aFields.size() - 1; ii > 0; ii-- )
445 {
446 wxString currFieldName = aFields[ii].GetName();
447
448 auto end_candidate_list = aFields.begin() + ii - 1;
449
450 auto fieldIt = std::find_if( aFields.begin(), end_candidate_list,
451 [&]( const SCH_FIELD& f )
452 {
453 return f.GetName() == currFieldName;
454 } );
455
456 // If duplicate field found: remove current checked item
457 if( fieldIt != end_candidate_list )
458 aFields.erase( aFields.begin() + ii );
459 }
460
461 SetFieldValue( aFields, SIM_DEVICE_FIELD, m_serializer->GenerateDevice() );
462 SetFieldValue( aFields, SIM_DEVICE_SUBTYPE_FIELD, m_serializer->GenerateDeviceSubtype() );
463
464 SetFieldValue( aFields, SIM_LEGACY_ENABLE_FIELD_V7, m_serializer->GenerateEnable() );
465 SetFieldValue( aFields, SIM_PINS_FIELD, m_serializer->GeneratePins() );
466
467 SetFieldValue( aFields, SIM_PARAMS_FIELD, m_serializer->GenerateParams() );
468
469 if( IsStoredInValue() )
470 SetFieldValue( aFields, SIM_VALUE_FIELD, m_serializer->GenerateValue() );
471
472 // New fields have a ID = -1 (undefined). so replace the undefined ID
473 // by a degined ID
474 int lastFreeId = MANDATORY_FIELDS;
475
476 // Search for the first available value:
477 for( auto& fld : aFields )
478 {
479 if( fld.GetId() >= lastFreeId )
480 lastFreeId = fld.GetId() + 1;
481 }
482
483 // Set undefined IDs to a better value
484 for( auto& fld : aFields )
485 {
486 if( fld.GetId() < 0 )
487 fld.SetId( lastFreeId++ );
488 }
489}
490
491
492std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType, const std::vector<SCH_PIN*>& aPins,
493 REPORTER& aReporter )
494{
495 std::unique_ptr<SIM_MODEL> model = Create( aType );
496
497 try
498 {
499 // Passing nullptr to ReadDataFields will make it act as if all fields were empty.
500 model->ReadDataFields( static_cast<const std::vector<SCH_FIELD>*>( nullptr ), aPins );
501 }
502 catch( IO_ERROR& )
503 {
504 wxFAIL_MSG( "Shouldn't throw reading empty fields!" );
505 }
506
507 return model;
508}
509
510
511std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( const SIM_MODEL* aBaseModel,
512 const std::vector<SCH_PIN*>& aPins,
513 REPORTER& aReporter )
514{
515 std::unique_ptr<SIM_MODEL> model;
516
517 if( aBaseModel )
518 {
519 TYPE type = aBaseModel->GetType();
520
521 if( dynamic_cast<const SIM_MODEL_SPICE_FALLBACK*>( aBaseModel ) )
522 model = std::make_unique<SIM_MODEL_SPICE_FALLBACK>( type );
523 else if( dynamic_cast< const SIM_MODEL_RAW_SPICE*>( aBaseModel ) )
524 model = std::make_unique<SIM_MODEL_RAW_SPICE>();
525 else
526 model = Create( type );
527
528 model->SetBaseModel( *aBaseModel );
529 }
530 else // No base model means the model wasn't found in the library, so create a fallback
531 {
532 model = std::make_unique<SIM_MODEL_SPICE_FALLBACK>( TYPE::NONE );
533 }
534
535 try
536 {
537 model->ReadDataFields( static_cast<const std::vector<SCH_FIELD>*>( nullptr ), aPins );
538 }
539 catch( IO_ERROR& )
540 {
541 wxFAIL_MSG( "Shouldn't throw reading empty fields!" );
542 }
543
544 return model;
545}
546
547
548std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( const SIM_MODEL* aBaseModel,
549 const std::vector<SCH_PIN*>& aPins,
550 const std::vector<SCH_FIELD>& aFields,
551 REPORTER& aReporter )
552{
553 std::unique_ptr<SIM_MODEL> model;
554
555 if( aBaseModel )
556 {
557 NULL_REPORTER devnull;
558 TYPE type = aBaseModel->GetType();
559
560 // No REPORTER here; we're just checking to see if we have an override
561 if( ReadTypeFromFields( aFields, devnull ) != TYPE::NONE )
562 type = ReadTypeFromFields( aFields, devnull );
563
564 if( dynamic_cast<const SIM_MODEL_SPICE_FALLBACK*>( aBaseModel ) )
565 model = std::make_unique<SIM_MODEL_SPICE_FALLBACK>( type );
566 else if( dynamic_cast< const SIM_MODEL_RAW_SPICE*>( aBaseModel ) )
567 model = std::make_unique<SIM_MODEL_RAW_SPICE>();
568 else
569 model = Create( type );
570
571 model->SetBaseModel( *aBaseModel );
572 }
573 else // No base model means the model wasn't found in the library, so create a fallback
574 {
575 TYPE type = ReadTypeFromFields( aFields, aReporter );
576 model = std::make_unique<SIM_MODEL_SPICE_FALLBACK>( type );
577 }
578
579 try
580 {
581 model->ReadDataFields( &aFields, aPins );
582 }
583 catch( IO_ERROR& err )
584 {
585 aReporter.Report( wxString::Format( _( "Error reading simulation model from "
586 "symbol '%s':\n%s" ),
587 aFields[REFERENCE_FIELD].GetText(),
588 err.Problem() ),
590 }
591
592 return model;
593}
594
595
596std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( const std::vector<SCH_FIELD>& aFields,
597 const std::vector<SCH_PIN*>& aPins,
598 bool aResolved, REPORTER& aReporter )
599{
600 TYPE type = ReadTypeFromFields( aFields, aReporter );
601 std::unique_ptr<SIM_MODEL> model = SIM_MODEL::Create( type );
602
603 try
604 {
605 model->ReadDataFields( &aFields, aPins );
606 }
607 catch( const IO_ERROR& parse_err )
608 {
609 if( !aResolved )
610 {
611 aReporter.Report( parse_err.What(), RPT_SEVERITY_ERROR );
612 return model;
613 }
614
615 // Just because we can't parse it doesn't mean that a SPICE interpreter can't. Fall
616 // back to a raw spice code model.
617
618 std::string modelData = GetFieldValue( &aFields, SIM_PARAMS_FIELD );
619
620 if( modelData.empty() )
621 modelData = GetFieldValue( &aFields, SIM_VALUE_FIELD );
622
623 model = std::make_unique<SIM_MODEL_RAW_SPICE>( modelData );
624
625 try
626 {
627 model->createPins( aPins );
628 model->m_serializer->ParsePins( GetFieldValue( &aFields, SIM_PINS_FIELD ) );
629 }
630 catch( const IO_ERROR& err )
631 {
632 // We own the pin syntax, so if we can't parse it then there's an error.
633 aReporter.Report( wxString::Format( _( "Error reading simulation model from "
634 "symbol '%s':\n%s" ),
635 aFields[REFERENCE_FIELD].GetText(),
636 err.Problem() ),
638 }
639 }
640
641 return model;
642}
643
644
645std::string SIM_MODEL::GetFieldValue( const std::vector<SCH_FIELD>* aFields,
646 const wxString& aFieldName, bool aResolve )
647{
648 if( !aFields )
649 return ""; // Should not happen, T=void specialization will be called instead.
650
651 for( const SCH_FIELD& field : *aFields )
652 {
653 if( field.GetName() == aFieldName )
654 {
655 return aResolve ? field.GetShownText( false ).ToStdString()
656 : field.GetText().ToStdString();
657 }
658 }
659
660 return "";
661}
662
663
664void SIM_MODEL::SetFieldValue( std::vector<SCH_FIELD>& aFields, const wxString& aFieldName,
665 const std::string& aValue )
666{
667 auto fieldIt = std::find_if( aFields.begin(), aFields.end(),
668 [&]( const SCH_FIELD& f )
669 {
670 return f.GetName() == aFieldName;
671 } );
672
673 if( fieldIt != aFields.end() )
674 {
675 if( aValue == "" )
676 aFields.erase( fieldIt );
677 else
678 fieldIt->SetText( aValue );
679
680 return;
681 }
682
683 if( aValue == "" )
684 return;
685
686 SCH_ITEM* parent = static_cast<SCH_ITEM*>( aFields.at( 0 ).GetParent() );
687 aFields.emplace_back( VECTOR2I(), aFields.size(), parent, aFieldName );
688
689 aFields.back().SetText( aValue );
690}
691
692
693SIM_MODEL::~SIM_MODEL() = default;
694
695
696void SIM_MODEL::AddPin( const PIN& aPin )
697{
698 m_pins.push_back( aPin );
699}
700
701
703{
704 m_pins.clear();
705}
706
707
708int SIM_MODEL::FindModelPinIndex( const std::string& aSymbolPinNumber )
709{
710 for( int modelPinIndex = 0; modelPinIndex < GetPinCount(); ++modelPinIndex )
711 {
712 if( GetPin( modelPinIndex ).symbolPinNumber == aSymbolPinNumber )
713 return modelPinIndex;
714 }
715
716 return PIN::NOT_CONNECTED;
717}
718
719
721{
722 m_params.emplace_back( aInfo );
723
724 // Enums are initialized with their default values.
725 if( aInfo.enumValues.size() >= 1 )
726 m_params.back().value = aInfo.defaultValue;
727}
728
729
730void SIM_MODEL::SetBaseModel( const SIM_MODEL& aBaseModel )
731{
732 wxASSERT_MSG( GetType() == aBaseModel.GetType(),
733 wxS( "Simulation model type must be the same as its base class!" ) );
734
735 m_baseModel = &aBaseModel;
736}
737
738
739std::vector<std::reference_wrapper<const SIM_MODEL::PIN>> SIM_MODEL::GetPins() const
740{
741 std::vector<std::reference_wrapper<const PIN>> pins;
742
743 for( int modelPinIndex = 0; modelPinIndex < GetPinCount(); ++modelPinIndex )
744 pins.emplace_back( GetPin( modelPinIndex ) );
745
746 return pins;
747}
748
749void SIM_MODEL::SetPinSymbolPinNumber( int aPinIndex, const std::string& aSymbolPinNumber )
750{
751 if( aPinIndex >= 0 && aPinIndex < (int) m_pins.size() )
752 m_pins.at( aPinIndex ).symbolPinNumber = aSymbolPinNumber;
753}
754
755
756void SIM_MODEL::SetPinSymbolPinNumber( const std::string& aPinName,
757 const std::string& aSymbolPinNumber )
758{
759 for( PIN& pin : m_pins )
760 {
761 if( pin.name == aPinName )
762 {
763 pin.symbolPinNumber = aSymbolPinNumber;
764 return;
765 }
766 }
767
768 // If aPinName wasn't in fact a name, see if it's a raw (1-based) index. This is required
769 // for legacy files which didn't use pin names.
770 int aPinIndex = (int) strtol( aPinName.c_str(), nullptr, 10 );
771
772 if( aPinIndex < 1 || aPinIndex > (int) m_pins.size() )
773 THROW_IO_ERROR( wxString::Format( _( "Unknown simulation model pin '%s'" ), aPinName ) );
774
775 m_pins[ --aPinIndex /* convert to 0-based */ ].symbolPinNumber = aSymbolPinNumber;
776}
777
778
779const SIM_MODEL::PARAM& SIM_MODEL::GetParam( unsigned aParamIndex ) const
780{
781 if( m_baseModel && m_params.at( aParamIndex ).value == "" )
782 return m_baseModel->GetParam( aParamIndex );
783 else
784 return m_params.at( aParamIndex );
785}
786
787
788bool SIM_MODEL::PARAM::INFO::Matches( const std::string& aParamName ) const
789{
790 return boost::iequals( name, aParamName );
791}
792
793
794int SIM_MODEL::doFindParam( const std::string& aParamName ) const
795{
796 std::vector<std::reference_wrapper<const PARAM>> params = GetParams();
797
798 for( int ii = 0; ii < (int) params.size(); ++ii )
799 {
800 if( params[ii].get().Matches( aParamName ) )
801 return ii;
802 }
803
804 return -1;
805}
806
807
808const SIM_MODEL::PARAM* SIM_MODEL::FindParam( const std::string& aParamName ) const
809{
810 int idx = doFindParam( aParamName );
811
812 return idx >= 0 ? &GetParam( idx ) : nullptr;
813}
814
815
816std::vector<std::reference_wrapper<const SIM_MODEL::PARAM>> SIM_MODEL::GetParams() const
817{
818 std::vector<std::reference_wrapper<const PARAM>> params;
819
820 for( int i = 0; i < GetParamCount(); ++i )
821 params.emplace_back( GetParam( i ) );
822
823 return params;
824}
825
826
827const SIM_MODEL::PARAM& SIM_MODEL::GetParamOverride( unsigned aParamIndex ) const
828{
829 return m_params.at( aParamIndex );
830}
831
832
833const SIM_MODEL::PARAM& SIM_MODEL::GetBaseParam( unsigned aParamIndex ) const
834{
835 if( m_baseModel )
836 return m_baseModel->GetParam( aParamIndex );
837 else
838 return m_params.at( aParamIndex );
839}
840
841
842void SIM_MODEL::doSetParamValue( int aParamIndex, const std::string& aValue )
843{
844 m_params.at( aParamIndex ).value = aValue;
845}
846
847
848void SIM_MODEL::SetParamValue( int aParamIndex, const std::string& aValue,
849 SIM_VALUE::NOTATION aNotation )
850{
851 std::string value = aValue;
852
853 if( aNotation != SIM_VALUE::NOTATION::SI || aValue.find( ',' ) != std::string::npos )
854 value = SIM_VALUE::ConvertNotation( value, aNotation, SIM_VALUE::NOTATION::SI );
855
856 doSetParamValue( aParamIndex, value );
857}
858
859
860void SIM_MODEL::SetParamValue( const std::string& aParamName, const std::string& aValue,
861 SIM_VALUE::NOTATION aNotation )
862{
863 int idx = doFindParam( aParamName );
864
865 if( idx < 0 )
866 THROW_IO_ERROR( wxString::Format( "Unknown simulation model parameter '%s'", aParamName ) );
867
868 SetParamValue( idx, aValue, aNotation );
869}
870
871
872std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType )
873{
874 switch( aType )
875 {
876 case TYPE::R:
877 case TYPE::C:
878 case TYPE::L:
879 return std::make_unique<SIM_MODEL_IDEAL>( aType );
880
881 case TYPE::R_POT:
882 return std::make_unique<SIM_MODEL_R_POT>();
883
884 case TYPE::K:
885 return std::make_unique<SIM_MODEL_L_MUTUAL>();
886
887 case TYPE::R_BEHAVIORAL:
888 case TYPE::C_BEHAVIORAL:
889 case TYPE::L_BEHAVIORAL:
890 case TYPE::V_BEHAVIORAL:
891 case TYPE::I_BEHAVIORAL:
892 return std::make_unique<SIM_MODEL_BEHAVIORAL>( aType );
893
894 case TYPE::TLINE_Z0:
895 case TYPE::TLINE_RLGC:
896 return std::make_unique<SIM_MODEL_TLINE>( aType );
897
898 case TYPE::SW_V:
899 case TYPE::SW_I:
900 return std::make_unique<SIM_MODEL_SWITCH>( aType );
901
902 case TYPE::V:
903 case TYPE::I:
904 case TYPE::V_SIN:
905 case TYPE::I_SIN:
906 case TYPE::V_PULSE:
907 case TYPE::I_PULSE:
908 case TYPE::V_EXP:
909 case TYPE::I_EXP:
910 case TYPE::V_AM:
911 case TYPE::I_AM:
912 case TYPE::V_SFFM:
913 case TYPE::I_SFFM:
914 case TYPE::V_VCL:
915 case TYPE::V_CCL:
916 case TYPE::V_PWL:
917 case TYPE::I_VCL:
918 case TYPE::I_CCL:
919 case TYPE::I_PWL:
920 case TYPE::V_WHITENOISE:
921 case TYPE::I_WHITENOISE:
922 case TYPE::V_PINKNOISE:
923 case TYPE::I_PINKNOISE:
924 case TYPE::V_BURSTNOISE:
925 case TYPE::I_BURSTNOISE:
926 case TYPE::V_RANDUNIFORM:
927 case TYPE::I_RANDUNIFORM:
928 case TYPE::V_RANDGAUSSIAN:
929 case TYPE::I_RANDGAUSSIAN:
930 case TYPE::V_RANDEXP:
931 case TYPE::I_RANDEXP:
932 case TYPE::V_RANDPOISSON:
933 case TYPE::I_RANDPOISSON:
934 return std::make_unique<SIM_MODEL_SOURCE>( aType );
935
936 case TYPE::SUBCKT:
937 return std::make_unique<SIM_MODEL_SUBCKT>();
938
939 case TYPE::XSPICE:
940 return std::make_unique<SIM_MODEL_XSPICE>( aType );
941
942 case TYPE::KIBIS_DEVICE:
943 case TYPE::KIBIS_DRIVER_DC:
944 case TYPE::KIBIS_DRIVER_RECT:
945 case TYPE::KIBIS_DRIVER_PRBS:
946 return std::make_unique<SIM_MODEL_KIBIS>( aType );
947
948 case TYPE::RAWSPICE:
949 return std::make_unique<SIM_MODEL_RAW_SPICE>();
950
951 default:
952 return std::make_unique<SIM_MODEL_NGSPICE>( aType );
953 }
954}
955
956
958 SIM_MODEL( aType, std::make_unique<SPICE_GENERATOR>( *this ),
959 std::make_unique<SIM_MODEL_SERIALIZER>( *this ) )
960{
961}
962
963
964SIM_MODEL::SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator ) :
965 SIM_MODEL( aType, std::move( aSpiceGenerator ),
966 std::make_unique<SIM_MODEL_SERIALIZER>( *this ) )
967{
968}
969
970
971SIM_MODEL::SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
972 std::unique_ptr<SIM_MODEL_SERIALIZER> aSerializer ) :
973 m_baseModel( nullptr ),
974 m_serializer( std::move( aSerializer ) ),
975 m_spiceGenerator( std::move( aSpiceGenerator ) ),
976 m_type( aType ),
977 m_isEnabled( true ),
978 m_isStoredInValue( false )
979{
980}
981
982
983void SIM_MODEL::createPins( const std::vector<SCH_PIN*>& aSymbolPins )
984{
985 // Default pin sequence: model pins are the same as symbol pins.
986 // Excess model pins are set as Not Connected.
987 // Note that intentionally nothing is added if `GetPinNames()` returns an empty vector.
988
989 // SIM_MODEL pins must be ordered by symbol pin numbers -- this is assumed by the code that
990 // accesses them.
991
992 std::vector<std::string> pinNames = GetPinNames();
993
994 for( unsigned modelPinIndex = 0; modelPinIndex < pinNames.size(); ++modelPinIndex )
995 {
996 wxString pinName = pinNames[ modelPinIndex ];
997 bool optional = false;
998
999 if( pinName.StartsWith( '<' ) && pinName.EndsWith( '>' ) )
1000 {
1001 pinName = pinName.Mid( 1, pinName.Length() - 2 );
1002 optional = true;
1003 }
1004
1005 if( modelPinIndex < aSymbolPins.size() )
1006 {
1007 AddPin( { pinNames.at( modelPinIndex ),
1008 aSymbolPins[ modelPinIndex ]->GetNumber().ToStdString() } );
1009 }
1010 else if( !optional )
1011 {
1012 AddPin( { pinNames.at( modelPinIndex ), "" } );
1013 }
1014 }
1015}
1016
1017
1019{
1020 // SUBCKTs are a single level; there's never a baseModel.
1021 if( m_type == TYPE::SUBCKT )
1022 return false;
1023
1024 // Model must be written if there's no base model or the base model is an internal model
1025 if( !m_baseModel || aItem.baseModelName == "" )
1026 return true;
1027
1028 for( int ii = 0; ii < GetParamCount(); ++ii )
1029 {
1030 const PARAM& param = m_params[ii];
1031
1032 // Instance parameters are written in item lines
1033 if( param.info.isSpiceInstanceParam )
1034 continue;
1035
1036 // Empty parameters are interpreted as default-value
1037 if ( param.value == "" )
1038 continue;
1039
1040 const SIM_MODEL* baseModel = dynamic_cast<const SIM_MODEL*>( m_baseModel );
1041
1042 wxCHECK( baseModel, false );
1043
1044 std::string baseValue = baseModel->m_params[ii].value;
1045
1046 if( param.value == baseValue )
1047 continue;
1048
1049 // One more check for equivalence, mostly for early 7.0 files which wrote all parameters
1050 // to the Sim.Params field in normalized format
1051 if( param.value == SIM_VALUE::Normalize( SIM_VALUE::ToDouble( baseValue ) ) )
1052 continue;
1053
1054 // Overrides must be written
1055 return true;
1056 }
1057
1058 return false;
1059}
1060
1061
1062template <class T>
1063bool SIM_MODEL::InferSimModel( T& aSymbol, std::vector<SCH_FIELD>* aFields, bool aResolve,
1064 SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString* aDeviceType,
1065 wxString* aModelType, wxString* aModelParams, wxString* aPinMap )
1066{
1067 // SPICE notation is case-insensitive and locale-insensitve. This means it uses "Meg" for
1068 // mega (as both 'M' and 'm' must mean milli), and "." (always) for a decimal separator.
1069 //
1070 // KiCad's GUI uses the SI-standard 'M' for mega and 'm' for milli, and a locale-dependent
1071 // decimal separator.
1072 //
1073 // KiCad's Sim.* fields are in-between, using SI notation but a fixed decimal separator.
1074 //
1075 // So where does that leave inferred value fields? Behavioural models must be passed in
1076 // straight, because we don't (at present) know how to parse them.
1077 //
1078 // However, behavioural models _look_ like SPICE code, so it's not a stretch to expect them
1079 // to _be_ SPICE code. A passive capacitor model on the other hand, just looks like a
1080 // capacitance. Some users might expect 3,3u to work, while others might expect 3,300uF to
1081 // work.
1082 //
1083 // Checking the locale isn't reliable because it assumes the current computer's locale is
1084 // the same as the locale the schematic was authored in -- something that isn't true, for
1085 // instance, when sharing designs over DIYAudio.com.
1086 //
1087 // However, even the E192 series of preferred values uses only 3 significant digits, so a ','
1088 // or '.' followed by 3 digits _could_ reasonably-reliably be interpreted as a thousands
1089 // separator.
1090 //
1091 // Or we could just say inferred values are locale-independent, with "." used as a decimal
1092 // separator and "," used as a thousands separator. 3,300uF works, but 3,3 does not.
1093
1094 auto convertNotation =
1095 [&]( const wxString& units ) -> wxString
1096 {
1099 if( units == wxS( "µ" ) || units == wxS( "μ" ) )
1100 return wxS( "u" );
1101
1102 if( aNotation == SIM_VALUE_GRAMMAR::NOTATION::SPICE )
1103 {
1104 if( units == wxT( "M" ) )
1105 return wxT( "Meg" );
1106 }
1107 else if( aNotation == SIM_VALUE_GRAMMAR::NOTATION::SI )
1108 {
1109 if( units.Capitalize() == wxT( "Meg" ) )
1110 return wxT( "M" );
1111 }
1112
1113 return units;
1114 };
1115
1116 auto convertSeparators =
1117 []( wxString* mantissa )
1118 {
1119 mantissa->Replace( wxS( " " ), wxEmptyString );
1120
1121 wxChar ambiguousSeparator = '?';
1122 wxChar thousandsSeparator = '?';
1123 bool thousandsSeparatorFound = false;
1124 wxChar decimalSeparator = '?';
1125 bool decimalSeparatorFound = false;
1126 int digits = 0;
1127
1128 for( int ii = (int) mantissa->length() - 1; ii >= 0; --ii )
1129 {
1130 wxChar c = mantissa->GetChar( ii );
1131
1132 if( c >= '0' && c <= '9' )
1133 {
1134 digits += 1;
1135 }
1136 else if( c == '.' || c == ',' )
1137 {
1138 if( decimalSeparator != '?' || thousandsSeparator != '?' )
1139 {
1140 // We've previously found a non-ambiguous separator...
1141
1142 if( c == decimalSeparator )
1143 {
1144 if( thousandsSeparatorFound )
1145 return false; // decimal before thousands
1146 else if( decimalSeparatorFound )
1147 return false; // more than one decimal
1148 else
1149 decimalSeparatorFound = true;
1150 }
1151 else if( c == thousandsSeparator )
1152 {
1153 if( digits != 3 )
1154 return false; // thousands not followed by 3 digits
1155 else
1156 thousandsSeparatorFound = true;
1157 }
1158 }
1159 else if( ambiguousSeparator != '?' )
1160 {
1161 // We've previously found a separator, but we don't know for sure
1162 // which...
1163
1164 if( c == ambiguousSeparator )
1165 {
1166 // They both must be thousands separators
1167 thousandsSeparator = ambiguousSeparator;
1168 thousandsSeparatorFound = true;
1169 decimalSeparator = c == '.' ? ',' : '.';
1170 }
1171 else
1172 {
1173 // The first must have been a decimal, and this must be a
1174 // thousands.
1175 decimalSeparator = ambiguousSeparator;
1176 decimalSeparatorFound = true;
1177 thousandsSeparator = c;
1178 thousandsSeparatorFound = true;
1179 }
1180 }
1181 else
1182 {
1183 // This is the first separator...
1184
1185 // If it's preceeded by a '0' (only), or if it's followed by some
1186 // number of digits not equal to 3, then it -must- be a decimal
1187 // separator.
1188 //
1189 // In all other cases we don't really know what it is yet.
1190
1191 if( ( ii == 1 && mantissa->GetChar( 0 ) == '0' ) || digits != 3 )
1192 {
1193 decimalSeparator = c;
1194 decimalSeparatorFound = true;
1195 thousandsSeparator = c == '.' ? ',' : '.';
1196 }
1197 else
1198 {
1199 ambiguousSeparator = c;
1200 }
1201 }
1202
1203 digits = 0;
1204 }
1205 else
1206 {
1207 digits = 0;
1208 }
1209 }
1210
1211 // If we found nothing difinitive then we have to assume SPICE-native syntax
1212 if( decimalSeparator == '?' && thousandsSeparator == '?' )
1213 {
1214 decimalSeparator = '.';
1215 thousandsSeparator = ',';
1216 }
1217
1218 mantissa->Replace( thousandsSeparator, wxEmptyString );
1219 mantissa->Replace( decimalSeparator, '.' );
1220
1221 return true;
1222 };
1223
1224 wxString prefix = aSymbol.GetPrefix();
1225 wxString library = GetFieldValue( aFields, SIM_LIBRARY_FIELD, aResolve );
1226 wxString modelName = GetFieldValue( aFields, SIM_NAME_FIELD, aResolve );
1227 wxString value = GetFieldValue( aFields, SIM_VALUE_FIELD, aResolve );
1228 std::vector<SCH_PIN*> pins = aSymbol.GetAllLibPins();
1229
1230 *aDeviceType = GetFieldValue( aFields, SIM_DEVICE_FIELD, aResolve );
1231 *aModelType = GetFieldValue( aFields, SIM_DEVICE_SUBTYPE_FIELD, aResolve );
1232 *aModelParams = GetFieldValue( aFields, SIM_PARAMS_FIELD, aResolve );
1233 *aPinMap = GetFieldValue( aFields, SIM_PINS_FIELD, aResolve );
1234
1235 if( pins.size() != 2 )
1236 return false;
1237
1238 if( ( ( *aDeviceType == "R" || *aDeviceType == "L" || *aDeviceType == "C" )
1239 && aModelType->IsEmpty() )
1240 ||
1241 ( library.IsEmpty() && modelName.IsEmpty()
1242 && aDeviceType->IsEmpty()
1243 && aModelType->IsEmpty()
1244 && !value.IsEmpty()
1245 && ( prefix.StartsWith( "R" ) || prefix.StartsWith( "L" ) || prefix.StartsWith( "C" ) ) ) )
1246 {
1247 if( aModelParams->IsEmpty() )
1248 {
1249 wxRegEx idealVal( wxT( "^"
1250 "([0-9\\,\\. ]+)"
1251 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1252 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1253 "([-1-9 ]*)"
1254 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1255 "$" ) );
1256
1257 if( idealVal.Matches( value ) ) // Ideal
1258 {
1259 wxString valueMantissa( idealVal.GetMatch( value, 1 ) );
1260 wxString valueExponent( idealVal.GetMatch( value, 2 ) );
1261 wxString valueFraction( idealVal.GetMatch( value, 6 ) );
1262
1263 if( !convertSeparators( &valueMantissa ) )
1264 return false;
1265
1266 if( valueMantissa.Contains( wxT( "." ) ) || valueFraction.IsEmpty() )
1267 {
1268 aModelParams->Printf( wxT( "%s=\"%s%s\"" ),
1269 prefix.Left(1).Lower(),
1270 valueMantissa,
1271 convertNotation( valueExponent ) );
1272 }
1273 else
1274 {
1275 aModelParams->Printf( wxT( "%s=\"%s.%s%s\"" ),
1276 prefix.Left(1).Lower(),
1277 valueMantissa,
1278 valueFraction,
1279 convertNotation( valueExponent ) );
1280 }
1281 }
1282 else // Behavioral
1283 {
1284 *aModelType = wxT( "=" );
1285 aModelParams->Printf( wxT( "%s=\"%s\"" ), prefix.Left(1).Lower(), value );
1286 }
1287 }
1288
1289 if( aDeviceType->IsEmpty() )
1290 *aDeviceType = prefix.Left( 1 );
1291
1292 if( aPinMap->IsEmpty() )
1293 aPinMap->Printf( wxT( "%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1294
1295 return true;
1296 }
1297
1298 if( ( ( *aDeviceType == wxT( "V" ) || *aDeviceType == wxT( "I" ) )
1299 && ( aModelType->IsEmpty() || *aModelType == wxT( "DC" ) ) )
1300 ||
1301 ( aDeviceType->IsEmpty()
1302 && aModelType->IsEmpty()
1303 && !value.IsEmpty()
1304 && ( prefix.StartsWith( "V" ) || prefix.StartsWith( "I" ) ) ) )
1305 {
1306 if( !value.IsEmpty() )
1307 {
1308 wxString param = "dc";
1309
1310 if( value.StartsWith( wxT( "DC " ) ) )
1311 {
1312 value = value.Right( value.Length() - 3 );
1313 }
1314 else if( value.StartsWith( wxT( "AC " ) ) )
1315 {
1316 value = value.Right( value.Length() - 3 );
1317 param = "ac";
1318 }
1319
1320 wxRegEx sourceVal( wxT( "^"
1321 "([0-9\\,\\. ]+)"
1322 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1323 "([vVaA])?"
1324 "([-1-9 ]*)"
1325 "([vVaA])?"
1326 "$" ) );
1327
1328 if( sourceVal.Matches( value ) )
1329 {
1330 wxString valueMantissa( sourceVal.GetMatch( value, 1 ) );
1331 wxString valueExponent( sourceVal.GetMatch( value, 2 ) );
1332 wxString valueFraction( sourceVal.GetMatch( value, 6 ) );
1333
1334 if( !convertSeparators( &valueMantissa ) )
1335 return false;
1336
1337 if( valueMantissa.Contains( wxT( "." ) ) || valueFraction.IsEmpty() )
1338 {
1339 aModelParams->Printf( wxT( "%s=\"%s%s\" %s" ),
1340 param,
1341 valueMantissa,
1342 convertNotation( valueExponent ),
1343 *aModelParams );
1344 }
1345 else
1346 {
1347 aModelParams->Printf( wxT( "%s=\"%s.%s%s\" %s" ),
1348 param,
1349 valueMantissa,
1350 valueFraction,
1351 convertNotation( valueExponent ),
1352 *aModelParams );
1353 }
1354 }
1355 else
1356 {
1357 aModelParams->Printf( wxT( "%s=\"%s\" %s" ),
1358 param,
1359 value,
1360 *aModelParams );
1361 }
1362 }
1363
1364 if( aDeviceType->IsEmpty() )
1365 *aDeviceType = prefix.Left( 1 );
1366
1367 if( aModelType->IsEmpty() )
1368 *aModelType = wxT( "DC" );
1369
1370 if( aPinMap->IsEmpty() )
1371 aPinMap->Printf( wxT( "%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1372
1373 return true;
1374 }
1375
1376 return false;
1377}
1378
1379
1380template bool SIM_MODEL::InferSimModel<SCH_SYMBOL>( SCH_SYMBOL& aSymbol,
1381 std::vector<SCH_FIELD>* aFields, bool aResolve,
1383 wxString* aDeviceType, wxString* aModelType,
1384 wxString* aModelParams, wxString* aPinMap );
1385template bool SIM_MODEL::InferSimModel<LIB_SYMBOL>( LIB_SYMBOL& aSymbol,
1386 std::vector<SCH_FIELD>* aFields, bool aResolve,
1388 wxString* aDeviceType, wxString* aModelType,
1389 wxString* aModelParams, wxString* aPinMap );
1390
1391
1392template <typename T>
1393void SIM_MODEL::MigrateSimModel( T& aSymbol, const PROJECT* aProject )
1394{
1395 class FIELD_INFO
1396 {
1397 public:
1398 FIELD_INFO()
1399 {
1400 m_Attributes.m_Visible = false;
1401 m_Attributes.m_Size = VECTOR2I( DEFAULT_SIZE_TEXT * schIUScale.IU_PER_MILS,
1403 };
1404
1405 FIELD_INFO( const wxString& aText, SCH_FIELD* aField ) :
1406 m_Text( aText ),
1407 m_Attributes( aField->GetAttributes() ),
1408 m_Pos( aField->GetPosition() )
1409 {}
1410
1411 bool IsEmpty() const { return m_Text.IsEmpty(); }
1412
1413 SCH_FIELD CreateField( T* aSymbol, const wxString& aFieldName )
1414 {
1415 SCH_FIELD field( aSymbol, -1, aFieldName );
1416
1417 field.SetText( m_Text );
1418 field.SetAttributes( m_Attributes );
1419 field.SetPosition( m_Pos );
1420
1421 return field;
1422 }
1423
1424 public:
1425 wxString m_Text;
1426 TEXT_ATTRIBUTES m_Attributes;
1427 VECTOR2I m_Pos;
1428 };
1429
1430 SCH_FIELD* existing_deviceField = aSymbol.FindField( SIM_DEVICE_FIELD );
1431 SCH_FIELD* existing_deviceSubtypeField = aSymbol.FindField( SIM_DEVICE_SUBTYPE_FIELD );
1432 SCH_FIELD* existing_pinsField = aSymbol.FindField( SIM_PINS_FIELD );
1433 SCH_FIELD* existing_paramsField = aSymbol.FindField( SIM_PARAMS_FIELD );
1434
1435 wxString existing_deviceSubtype;
1436
1437 if( existing_deviceSubtypeField )
1438 existing_deviceSubtype = existing_deviceSubtypeField->GetShownText( false ).Upper();
1439
1440 if( existing_deviceField
1441 || existing_deviceSubtypeField
1442 || existing_pinsField
1443 || existing_paramsField )
1444 {
1445 // Has a current (V7+) model field.
1446
1447 // Up until 7.0RC2 we used '+' and '-' for potentiometer pins, which doesn't match
1448 // SPICE. Here we remap them to 'r0' and 'r1'.
1449 if( existing_deviceSubtype == wxS( "POT" ) )
1450 {
1451 if( existing_pinsField )
1452 {
1453 wxString pinMap = existing_pinsField->GetText();
1454 pinMap.Replace( wxS( "=+" ), wxS( "=r1" ) );
1455 pinMap.Replace( wxS( "=-" ), wxS( "=r0" ) );
1456 existing_pinsField->SetText( pinMap );
1457 }
1458 }
1459
1460 // Up until 8.0RC1 random voltage/current sources were a bit of a mess.
1461 if( existing_deviceSubtype.StartsWith( wxS( "RAND" ) ) )
1462 {
1463 // Re-fetch value without resolving references. If it's an indirect value then we
1464 // can't migrate it.
1465 existing_deviceSubtype = existing_deviceSubtypeField->GetText().Upper();
1466
1467 if( existing_deviceSubtype.Replace( wxS( "NORMAL" ), wxS( "GAUSSIAN" ) ) )
1468 existing_deviceSubtypeField->SetText( existing_deviceSubtype );
1469
1470 if( existing_paramsField )
1471 {
1472 wxString params = existing_paramsField->GetText().Lower();
1473 size_t count = 0;
1474
1475 // We used to support 'min' and 'max' instead of 'range' and 'offset', but we
1476 // wrote all 4 to the netlist which would cause ngspice to barf, so no one has
1477 // working documents with min and max specified. Just delete them if they're
1478 // uninitialized.
1479 count += params.Replace( wxS( "min=0 " ), wxEmptyString );
1480 count += params.Replace( wxS( "max=0 " ), wxEmptyString );
1481
1482 // We used to use 'dt', but the correct ngspice name is 'ts'.
1483 count += params.Replace( wxS( "dt=" ), wxS( "ts=" ) );
1484
1485 if( count )
1486 existing_paramsField->SetText( params );
1487 }
1488 }
1489
1490 // Up until 8.0.1 we treated a mutual inductance statement as a type of inductor --
1491 // which is confusing because it doesn't represent a device at all.
1492 if( existing_deviceSubtype == wxS( "MUTUAL" ) )
1493 {
1494 if( existing_deviceSubtypeField ) // Can't be null, but Coverity doesn't know that
1495 aSymbol.RemoveField( existing_deviceSubtypeField );
1496
1497 if( existing_deviceField )
1498 {
1499 existing_deviceField->SetText( wxS( "K" ) );
1500 }
1501 else
1502 {
1503 FIELD_INFO deviceFieldInfo;
1504 deviceFieldInfo.m_Text = wxS( "K" );
1505
1506 SCH_FIELD deviceField = deviceFieldInfo.CreateField( &aSymbol, SIM_DEVICE_FIELD );
1507 aSymbol.AddField( deviceField );
1508 }
1509 }
1510
1511 return;
1512 }
1513
1514 auto getSIValue =
1515 []( SCH_FIELD* aField )
1516 {
1517 if( !aField ) // no, not really, but it keeps Coverity happy
1518 return wxString( wxEmptyString );
1519
1520 wxRegEx regex( wxT( "([^a-z])(M)(e|E)(g|G)($|[^a-z])" ) );
1521 wxString value = aField->GetText();
1522
1523 // Keep prefix, M, and suffix, but drop e|E and g|G
1524 regex.ReplaceAll( &value, wxT( "\\1\\2\\5" ) );
1525
1526 return value;
1527 };
1528
1529 auto generateDefaultPinMapFromSymbol =
1530 []( const std::vector<SCH_PIN*>& sourcePins )
1531 {
1532 wxString pinMap;
1533
1534 // If we're creating the pinMap from the symbol it means we don't know what the
1535 // SIM_MODEL's pin names are, so just use indexes.
1536
1537 for( unsigned ii = 0; ii < sourcePins.size(); ++ii )
1538 {
1539 if( ii > 0 )
1540 pinMap.Append( wxS( " " ) );
1541
1542 pinMap.Append( wxString::Format( wxT( "%s=%u" ),
1543 sourcePins[ii]->GetNumber(),
1544 ii + 1 ) );
1545 }
1546
1547 return pinMap;
1548 };
1549
1550 wxString prefix = aSymbol.GetPrefix();
1551 SCH_FIELD* valueField = aSymbol.FindField( wxT( "Value" ) );
1552 std::vector<SCH_PIN*> sourcePins = aSymbol.GetAllLibPins();
1553 bool sourcePinsSorted = false;
1554
1555 auto lazySortSourcePins =
1556 [&sourcePins, &sourcePinsSorted]()
1557 {
1558 if( !sourcePinsSorted )
1559 {
1560 std::sort( sourcePins.begin(), sourcePins.end(),
1561 []( const SCH_PIN* lhs, const SCH_PIN* rhs )
1562 {
1563 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
1564 } );
1565 }
1566
1567 sourcePinsSorted = true;
1568 };
1569
1570 FIELD_INFO deviceInfo;
1571 FIELD_INFO modelInfo;
1572 FIELD_INFO deviceSubtypeInfo;
1573 FIELD_INFO libInfo;
1574 FIELD_INFO spiceParamsInfo;
1575 FIELD_INFO pinMapInfo;
1576 bool modelFromValueField = false;
1577
1578 if( aSymbol.FindField( SIM_LEGACY_PRIMITIVE_FIELD )
1579 || aSymbol.FindField( SIM_LEGACY_PINS_FIELD )
1580 || aSymbol.FindField( SIM_LEGACY_MODEL_FIELD )
1581 || aSymbol.FindField( SIM_LEGACY_ENABLE_FIELD )
1582 || aSymbol.FindField( SIM_LEGACY_LIBRARY_FIELD ) )
1583 {
1584 if( SCH_FIELD* primitiveField = aSymbol.FindField( SIM_LEGACY_PRIMITIVE_FIELD ) )
1585 {
1586 deviceInfo = FIELD_INFO( primitiveField->GetText(), primitiveField );
1587 aSymbol.RemoveField( primitiveField );
1588 }
1589
1590 if( SCH_FIELD* nodeSequenceField = aSymbol.FindField( SIM_LEGACY_PINS_FIELD ) )
1591 {
1592 const wxString delimiters( "{:,; }" );
1593 const wxString& nodeSequence = nodeSequenceField->GetText();
1594 wxString pinMap;
1595
1596 if( nodeSequence != "" )
1597 {
1598 wxStringTokenizer tkz( nodeSequence, delimiters );
1599
1600 for( long modelPinNumber = 1; tkz.HasMoreTokens(); ++modelPinNumber )
1601 {
1602 long symbolPinNumber = 1;
1603 tkz.GetNextToken().ToLong( &symbolPinNumber );
1604
1605 if( modelPinNumber != 1 )
1606 pinMap.Append( " " );
1607
1608 pinMap.Append( wxString::Format( "%ld=%ld", symbolPinNumber, modelPinNumber ) );
1609 }
1610 }
1611
1612 pinMapInfo = FIELD_INFO( pinMap, nodeSequenceField );
1613 aSymbol.RemoveField( nodeSequenceField );
1614 }
1615
1616 if( SCH_FIELD* modelField = aSymbol.FindField( SIM_LEGACY_MODEL_FIELD ) )
1617 {
1618 modelInfo = FIELD_INFO( getSIValue( modelField ), modelField );
1619 aSymbol.RemoveField( modelField );
1620 }
1621 else if( valueField )
1622 {
1623 modelInfo = FIELD_INFO( getSIValue( valueField ), valueField );
1624 modelFromValueField = true;
1625 }
1626
1627 if( SCH_FIELD* libFileField = aSymbol.FindField( SIM_LEGACY_LIBRARY_FIELD ) )
1628 {
1629 libInfo = FIELD_INFO( libFileField->GetText(), libFileField );
1630 aSymbol.RemoveField( libFileField );
1631 }
1632 }
1633 else
1634 {
1635 // Auto convert some legacy fields used in the middle of 7.0 development...
1636
1637 if( SCH_FIELD* legacyType = aSymbol.FindField( wxT( "Sim_Type" ) ) )
1638 {
1639 legacyType->SetName( SIM_DEVICE_SUBTYPE_FIELD );
1640 }
1641
1642 if( SCH_FIELD* legacyDevice = aSymbol.FindField( wxT( "Sim_Device" ) ) )
1643 {
1644 legacyDevice->SetName( SIM_DEVICE_FIELD );
1645 }
1646
1647 if( SCH_FIELD* legacyPins = aSymbol.FindField( wxT( "Sim_Pins" ) ) )
1648 {
1649 bool isPassive = prefix.StartsWith( wxT( "R" ) )
1650 || prefix.StartsWith( wxT( "L" ) )
1651 || prefix.StartsWith( wxT( "C" ) );
1652
1653 // Migrate pins from array of indexes to name-value-pairs
1654 wxString pinMap;
1655 wxArrayString pinIndexes;
1656
1657 wxStringSplit( legacyPins->GetText(), pinIndexes, ' ' );
1658
1659 lazySortSourcePins();
1660
1661 if( isPassive && pinIndexes.size() == 2 && sourcePins.size() == 2 )
1662 {
1663 if( pinIndexes[0] == wxT( "2" ) )
1664 {
1665 pinMap.Printf( wxT( "%s=- %s=+" ),
1666 sourcePins[0]->GetNumber(),
1667 sourcePins[1]->GetNumber() );
1668 }
1669 else
1670 {
1671 pinMap.Printf( wxT( "%s=+ %s=-" ),
1672 sourcePins[0]->GetNumber(),
1673 sourcePins[1]->GetNumber() );
1674 }
1675 }
1676 else
1677 {
1678 for( unsigned ii = 0; ii < pinIndexes.size() && ii < sourcePins.size(); ++ii )
1679 {
1680 if( ii > 0 )
1681 pinMap.Append( wxS( " " ) );
1682
1683 pinMap.Append( wxString::Format( wxT( "%s=%s" ),
1684 sourcePins[ii]->GetNumber(),
1685 pinIndexes[ ii ] ) );
1686 }
1687 }
1688
1689 legacyPins->SetName( SIM_PINS_FIELD );
1690 legacyPins->SetText( pinMap );
1691 }
1692
1693 if( SCH_FIELD* legacyParams = aSymbol.FindField( wxT( "Sim_Params" ) ) )
1694 {
1695 legacyParams->SetName( SIM_PARAMS_FIELD );
1696 }
1697
1698 return;
1699 }
1700
1701 wxString device = deviceInfo.m_Text.Trim( true ).Trim( false );
1702 wxString lib = libInfo.m_Text.Trim( true ).Trim( false );
1703 wxString model = modelInfo.m_Text.Trim( true ).Trim( false );
1704 wxString modelLineParams;
1705
1706 bool libraryModel = false;
1707 bool inferredModel = false;
1708 bool internalModel = false;
1709
1710 if( !lib.IsEmpty() )
1711 {
1712 wxString msg;
1713 WX_STRING_REPORTER reporter( &msg );
1714 SIM_LIB_MGR libMgr( aProject );
1715 std::vector<SCH_FIELD> emptyFields;
1716
1717 // Pull out any following parameters from model name
1718 model = model.BeforeFirst( ' ', &modelLineParams );
1719 modelInfo.m_Text = model;
1720
1721 lazySortSourcePins();
1722
1723 SIM_LIBRARY::MODEL simModel = libMgr.CreateModel( lib, model.ToStdString(),
1724 emptyFields, sourcePins, reporter );
1725
1726 if( reporter.HasMessage() )
1727 libraryModel = false; // Fall back to raw spice model
1728 else
1729 libraryModel = true;
1730
1731 if( pinMapInfo.IsEmpty() )
1732 {
1733 // Try to generate a default pin map from the SIM_MODEL's pins; if that fails,
1734 // generate one from the symbol's pins
1735 pinMapInfo.m_Text = wxString( simModel.model.Serializer().GeneratePins() );
1736
1737 if( pinMapInfo.IsEmpty() )
1738 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1739 }
1740 }
1741 else if( ( device == wxS( "R" )
1742 || device == wxS( "L" )
1743 || device == wxS( "C" )
1744 || device == wxS( "V" )
1745 || device == wxS( "I" ) )
1746 && prefix.StartsWith( device )
1747 && modelFromValueField )
1748 {
1749 inferredModel = true;
1750 }
1751 else if( device == wxS( "V" ) || device == wxS( "I" ) )
1752 {
1753 // See if we have a SPICE time-dependent function such as "sin(0 1 60)" or "sin 0 1 60"
1754 // that can be handled by a built-in SIM_MODEL_SOURCE.
1755
1756 wxStringTokenizer tokenizer( model, wxT( "() " ), wxTOKEN_STRTOK );
1757
1758 if( tokenizer.HasMoreTokens() )
1759 {
1760 deviceSubtypeInfo.m_Text = tokenizer.GetNextToken();
1761 deviceSubtypeInfo.m_Text.MakeUpper();
1762
1763 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1764 {
1765 if( device == SIM_MODEL::SpiceInfo( type ).itemType
1766 && deviceSubtypeInfo.m_Text == SIM_MODEL::SpiceInfo( type ).functionName )
1767 {
1768 try
1769 {
1770 std::unique_ptr<SIM_MODEL> simModel = SIM_MODEL::Create( type );
1771
1772 if( deviceSubtypeInfo.m_Text == wxT( "DC" ) && tokenizer.CountTokens() == 1 )
1773 {
1774 wxCHECK( valueField, /* void */ );
1775 valueField->SetText( tokenizer.GetNextToken() );
1776 modelFromValueField = false;
1777 }
1778 else
1779 {
1780 for( int ii = 0; tokenizer.HasMoreTokens(); ++ii )
1781 {
1782 simModel->SetParamValue( ii, tokenizer.GetNextToken().ToStdString(),
1784 }
1785
1786 deviceSubtypeInfo.m_Text = SIM_MODEL::TypeInfo( type ).fieldValue;
1787
1788 spiceParamsInfo = modelInfo;
1789 spiceParamsInfo.m_Text = wxString( simModel->Serializer().GenerateParams() );
1790 }
1791
1792 internalModel = true;
1793
1794 if( pinMapInfo.IsEmpty() )
1795 {
1796 lazySortSourcePins();
1797
1798 // Generate a default pin map from the SIM_MODEL's pins
1799 simModel->createPins( sourcePins );
1800 pinMapInfo.m_Text = wxString( simModel->Serializer().GeneratePins() );
1801 }
1802 }
1803 catch( ... )
1804 {
1805 // Fall back to raw spice model
1806 }
1807
1808 break;
1809 }
1810 }
1811 }
1812 }
1813
1814 if( libraryModel )
1815 {
1816 SCH_FIELD libField = libInfo.CreateField( &aSymbol, SIM_LIBRARY_FIELD );
1817 aSymbol.AddField( libField );
1818
1819 SCH_FIELD nameField = modelInfo.CreateField( &aSymbol, SIM_NAME_FIELD );
1820 aSymbol.AddField( nameField );
1821
1822 if( !modelLineParams.IsEmpty() )
1823 {
1824 spiceParamsInfo = modelInfo;
1825 spiceParamsInfo.m_Pos.x += nameField.GetBoundingBox().GetWidth();
1826 spiceParamsInfo.m_Text = modelLineParams;
1827
1828 BOX2I nameBBox = nameField.GetBoundingBox();
1829 int nameWidth = nameBBox.GetWidth();
1830
1831 // Add space between model name and additional parameters
1832 nameWidth += KiROUND( nameBBox.GetHeight() * 1.25 );
1833
1834 if( nameField.GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
1835 spiceParamsInfo.m_Pos.x -= nameWidth;
1836 else
1837 spiceParamsInfo.m_Pos.x += nameWidth;
1838
1839 SCH_FIELD paramsField = spiceParamsInfo.CreateField( &aSymbol, SIM_PARAMS_FIELD );
1840 aSymbol.AddField( paramsField );
1841 }
1842
1843 if( modelFromValueField )
1844 valueField->SetText( wxT( "${SIM.NAME}" ) );
1845 }
1846 else if( inferredModel )
1847 {
1848 // DeviceType is left in the reference designator and Model is left in the value field,
1849 // so there's nothing to do here....
1850 }
1851 else if( internalModel )
1852 {
1853 SCH_FIELD deviceField = deviceInfo.CreateField( &aSymbol, SIM_DEVICE_FIELD );
1854 aSymbol.AddField( deviceField );
1855
1856 if( !deviceSubtypeInfo.m_Text.IsEmpty() )
1857 {
1858 SCH_FIELD subtypeField = deviceSubtypeInfo.CreateField( &aSymbol, SIM_DEVICE_SUBTYPE_FIELD );
1859 aSymbol.AddField( subtypeField );
1860 }
1861
1862 if( !spiceParamsInfo.IsEmpty() )
1863 {
1864 SCH_FIELD paramsField = spiceParamsInfo.CreateField( &aSymbol, SIM_PARAMS_FIELD );
1865 aSymbol.AddField( paramsField );
1866 }
1867
1868 if( modelFromValueField )
1869 valueField->SetText( wxT( "${SIM.PARAMS}" ) );
1870 }
1871 else // Insert a raw spice model as a substitute.
1872 {
1873 if( device.IsEmpty() && lib.IsEmpty() )
1874 {
1875 spiceParamsInfo = modelInfo;
1876 }
1877 else
1878 {
1879 spiceParamsInfo.m_Text.Printf( wxT( "type=\"%s\" model=\"%s\" lib=\"%s\"" ), device,
1880 model, lib );
1881 }
1882
1883 deviceInfo.m_Text = SIM_MODEL::DeviceInfo( SIM_MODEL::DEVICE_T::SPICE ).fieldValue;
1884
1885 SCH_FIELD deviceField = deviceInfo.CreateField( &aSymbol, SIM_DEVICE_FIELD );
1886 aSymbol.AddField( deviceField );
1887
1888 SCH_FIELD paramsField = spiceParamsInfo.CreateField( &aSymbol, SIM_PARAMS_FIELD );
1889 aSymbol.AddField( paramsField );
1890
1891 if( modelFromValueField )
1892 {
1893 // Get the current Value field, after previous changes.
1894 valueField = aSymbol.FindField( wxT( "Value" ) );
1895
1896 if( valueField )
1897 valueField->SetText( wxT( "${SIM.PARAMS}" ) );
1898 }
1899
1900 // We know nothing about the SPICE model here, so we've got no choice but to generate
1901 // the default pin map from the symbol's pins.
1902
1903 if( pinMapInfo.IsEmpty() )
1904 {
1905 lazySortSourcePins();
1906 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1907 }
1908 }
1909
1910 if( !pinMapInfo.IsEmpty() )
1911 {
1912 SCH_FIELD pinsField = pinMapInfo.CreateField( &aSymbol, SIM_PINS_FIELD );
1913 aSymbol.AddField( pinsField );
1914 }
1915}
1916
1917
1918template void SIM_MODEL::MigrateSimModel<SCH_SYMBOL>( SCH_SYMBOL& aSymbol,
1919 const PROJECT* aProject );
1920template void SIM_MODEL::MigrateSimModel<LIB_SYMBOL>( LIB_SYMBOL& aSymbol,
1921 const PROJECT* aProject );
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:110
size_type GetHeight() const
Definition: box2.h:205
size_type GetWidth() const
Definition: box2.h:204
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:98
void SetAttributes(const EDA_TEXT &aSrc, bool aSetPosition=true)
Set the text attributes from another instance.
Definition: eda_text.cpp:292
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition: eda_text.h:164
const TEXT_ATTRIBUTES & GetAttributes() const
Definition: eda_text.h:195
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
virtual const wxString Problem() const
what was the problem?
Definition: exceptions.cpp:46
Define a library symbol object.
Definition: lib_symbol.h:77
A singleton reporter that reports to nowhere.
Definition: reporter.h:223
Container for project specific data.
Definition: project.h:62
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:51
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: sch_field.cpp:586
VECTOR2I GetPosition() const override
Definition: sch_field.cpp:1407
void SetPosition(const VECTOR2I &aPosition) override
Definition: sch_field.cpp:1387
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
Definition: sch_field.cpp:197
void SetText(const wxString &aText) override
Definition: sch_field.cpp:1138
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:172
Schematic symbol object.
Definition: sch_symbol.h:108
static constexpr auto DIFF_FIELD
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
Serializes/deserializes a SIM_MODEL for storage in LIB_FIELDs/SCH_FIELDs.
std::string GeneratePins() const
int FindModelPinIndex(const std::string &aSymbolPinNumber)
Definition: sim_model.cpp:708
static void MigrateSimModel(T &aSymbol, const PROJECT *aProject)
Definition: sim_model.cpp:1393
const PARAM & GetBaseParam(unsigned aParamIndex) const
Definition: sim_model.cpp:833
void AddParam(const PARAM::INFO &aInfo)
Definition: sim_model.cpp:720
bool IsStoredInValue() const
Definition: sim_model.h:512
virtual std::vector< std::string > GetPinNames() const
Definition: sim_model.h:470
virtual bool requiresSpiceModelLine(const SPICE_ITEM &aItem) const
Definition: sim_model.cpp:1018
void ClearPins()
Definition: sim_model.cpp:702
std::vector< PIN > m_pins
Definition: sim_model.h:542
static TYPE ReadTypeFromFields(const std::vector< SCH_FIELD > &aFields, REPORTER &aReporter)
Definition: sim_model.cpp:386
static INFO TypeInfo(TYPE aType)
Definition: sim_model.cpp:101
static void SetFieldValue(std::vector< SCH_FIELD > &aFields, const wxString &aFieldName, const std::string &aValue)
Definition: sim_model.cpp:664
int GetPinCount() const
Definition: sim_model.h:472
std::vector< std::reference_wrapper< const PIN > > GetPins() const
Definition: sim_model.cpp:739
static SPICE_INFO SpiceInfo(TYPE aType)
Definition: sim_model.cpp:245
const SIM_MODEL_SERIALIZER & Serializer() const
Definition: sim_model.h:437
void ReadDataFields(const std::vector< SCH_FIELD > *aFields, const std::vector< SCH_PIN * > &aPins)
Definition: sim_model.cpp:423
virtual const PARAM & GetParam(unsigned aParamIndex) const
Definition: sim_model.cpp:779
static bool InferSimModel(T &aSymbol, std::vector< SCH_FIELD > *aFields, bool aResolve, SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString *aDeviceType, wxString *aModelType, wxString *aModelParams, wxString *aPinMap)
Definition: sim_model.cpp:1063
void createPins(const std::vector< SCH_PIN * > &aSymbolPins)
Definition: sim_model.cpp:983
void WriteFields(std::vector< SCH_FIELD > &aFields) const
Definition: sim_model.cpp:441
virtual void SetBaseModel(const SIM_MODEL &aBaseModel)
Definition: sim_model.cpp:730
SIM_MODEL()=delete
static std::string GetFieldValue(const std::vector< SCH_FIELD > *aFields, const wxString &aFieldName, bool aResolve=true)
Definition: sim_model.cpp:645
int GetParamCount() const
Definition: sim_model.h:482
void SetPinSymbolPinNumber(int aPinIndex, const std::string &aSymbolPinNumber)
Definition: sim_model.cpp:749
void AddPin(const PIN &aPin)
Definition: sim_model.cpp:696
static DEVICE_INFO DeviceInfo(DEVICE_T aDeviceType)
Definition: sim_model.cpp:56
const PIN & GetPin(unsigned aIndex) const
Definition: sim_model.h:473
const PARAM * FindParam(const std::string &aParamName) const
Definition: sim_model.cpp:808
virtual void doSetParamValue(int aParamIndex, const std::string &aValue)
Definition: sim_model.cpp:842
std::vector< PARAM > m_params
Definition: sim_model.h:541
const PARAM & GetParamOverride(unsigned aParamIndex) const
Definition: sim_model.cpp:827
virtual ~SIM_MODEL()
static std::unique_ptr< SIM_MODEL > Create(TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
Definition: sim_model.cpp:492
void SetParamValue(int aParamIndex, const std::string &aValue, SIM_VALUE::NOTATION aNotation=SIM_VALUE::NOTATION::SI)
Definition: sim_model.cpp:848
virtual void SwitchSingleEndedDiff(bool aDiff)
Definition: sim_model.h:514
std::vector< std::reference_wrapper< const PARAM > > GetParams() const
Definition: sim_model.cpp:816
std::unique_ptr< SIM_MODEL_SERIALIZER > m_serializer
Definition: sim_model.h:544
virtual int doFindParam(const std::string &aParamName) const
Definition: sim_model.cpp:794
TYPE GetType() const
Definition: sim_model.h:465
const TYPE m_type
Definition: sim_model.h:549
const SIM_MODEL * m_baseModel
Definition: sim_model.h:543
static std::string Normalize(double aValue)
Definition: sim_value.cpp:405
static std::string ConvertNotation(const std::string &aString, NOTATION aFromNotation, NOTATION aToNotation)
Definition: sim_value.cpp:369
static double ToDouble(const std::string &aString, double aDefault=NAN)
Definition: sim_value.cpp:418
A wrapper for reporting to a wxString object.
Definition: reporter.h:164
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:71
#define _(s)
#define DEFAULT_SIZE_TEXT
This is the "default-of-the-default" hardcoded text size; individual application define their own def...
Definition: eda_text.h:73
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
STL namespace.
@ RPT_SEVERITY_ERROR
SIM_MODEL::TYPE TYPE
Definition: sim_model.cpp:53
#define SIM_PINS_FIELD
Definition: sim_model.h:54
#define SIM_DEVICE_FIELD
Definition: sim_model.h:52
#define SIM_NAME_FIELD
Definition: sim_model.h:57
#define SIM_LIBRARY_FIELD
Definition: sim_model.h:56
#define SIM_LEGACY_ENABLE_FIELD
Definition: sim_model.h:64
#define SIM_LEGACY_ENABLE_FIELD_V7
Definition: sim_model.h:60
#define SIM_LEGACY_MODEL_FIELD
Definition: sim_model.h:62
#define SIM_LEGACY_PINS_FIELD
Definition: sim_model.h:63
#define SIM_LEGACY_LIBRARY_FIELD
Definition: sim_model.h:65
#define SIM_PARAMS_FIELD
Definition: sim_model.h:55
#define SIM_VALUE_FIELD
Definition: sim_model.h:50
#define SIM_LEGACY_PRIMITIVE_FIELD
Definition: sim_model.h:61
#define SIM_DEVICE_SUBTYPE_FIELD
Definition: sim_model.h:53
bool convertSeparators(wxString *value)
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
const double IU_PER_MILS
Definition: base_units.h:77
SIM_MODEL & model
Definition: sim_library.h:41
std::vector< std::string > enumValues
Definition: sim_model.h:389
std::string defaultValue
Definition: sim_model.h:383
bool Matches(const std::string &aName) const
Definition: sim_model.h:396
std::string value
Definition: sim_model.h:401
const INFO & info
Definition: sim_model.h:402
std::string symbolPinNumber
Definition: sim_model.h:309
static constexpr auto NOT_CONNECTED
Definition: sim_model.h:311
std::string functionName
Definition: sim_model.h:298
std::string baseModelName
@ MANDATORY_FIELDS
The first 5 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ GR_TEXT_H_ALIGN_RIGHT
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:118
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588