Branch data Line data Source code
1 : : /*
2 : : * spsolver.cpp - S-parameter solver class implementation
3 : : *
4 : : * Copyright (C) 2003-2008 Stefan Jahn <stefan@lkcc.org>
5 : : *
6 : : * This is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU General Public License as published by
8 : : * the Free Software Foundation; either version 2, or (at your option)
9 : : * any later version.
10 : : *
11 : : * This software is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : : * GNU General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU General Public License
17 : : * along with this package; see the file COPYING. If not, write to
18 : : * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
19 : : * Boston, MA 02110-1301, USA.
20 : : *
21 : : * $Id$
22 : : *
23 : : */
24 : :
25 : : #if HAVE_CONFIG_H
26 : : # include <config.h>
27 : : #endif
28 : :
29 : : #include <stdio.h>
30 : : #include <stdlib.h>
31 : : #include <string.h>
32 : :
33 : : #include "logging.h"
34 : : #include "complex.h"
35 : : #include "object.h"
36 : : #include "node.h"
37 : : #include "circuit.h"
38 : : #include "strlist.h"
39 : : #include "vector.h"
40 : : #include "matvec.h"
41 : : #include "dataset.h"
42 : : #include "net.h"
43 : : #include "analysis.h"
44 : : #include "sweep.h"
45 : : #include "nodelist.h"
46 : : #include "netdefs.h"
47 : : #include "characteristic.h"
48 : : #include "spsolver.h"
49 : : #include "constants.h"
50 : : #include "components/component_id.h"
51 : : #include "components/tee.h"
52 : : #include "components/open.h"
53 : : #include "components/itrafo.h"
54 : : #include "components/cross.h"
55 : : #include "components/ground.h"
56 : :
57 : : /* Evolved optimization flags. */
58 : : #define USE_GROUNDS 1 // use extra grounds ?
59 : : #define USE_CROSSES 1 // use additional cross connectors ?
60 : : #define SORTED_LIST 1 // use sorted node list?
61 : :
62 : : #define TINYS (NR_TINY * 1.235) // 'tiny' value for singularities
63 : :
64 : : namespace qucs {
65 : :
66 : : // Constructor creates an unnamed instance of the spsolver class.
67 : 18 : spsolver::spsolver () : analysis () {
68 : 18 : type = ANALYSIS_SPARAMETER;
69 : 18 : swp = NULL;
70 : 18 : saveCVs = 0;
71 : 18 : noise = 0;
72 : 18 : nlist = NULL;
73 : 18 : tees = crosses = opens = grounds = 0;
74 : 18 : gnd = NULL;
75 : 18 : }
76 : :
77 : : // Constructor creates a named instance of the spsolver class.
78 : 0 : spsolver::spsolver (char * n) : analysis (n) {
79 : 0 : type = ANALYSIS_SPARAMETER;
80 : 0 : swp = NULL;
81 : 0 : saveCVs = 0;
82 : 0 : noise = 0;
83 : 0 : nlist = NULL;
84 : 0 : tees = crosses = opens = grounds = 0;
85 : 0 : gnd = NULL;
86 : 0 : }
87 : :
88 : : // Destructor deletes the spsolver class object.
89 : 18 : spsolver::~spsolver () {
90 [ + - ][ + - ]: 18 : if (swp) delete swp;
[ + - ][ # # ]
[ # # ][ # # ]
91 [ - + ][ # # ]: 18 : if (nlist) delete nlist;
[ # # ][ # # ]
[ # # ][ # # ]
92 [ - + ][ # # ]: 36 : }
93 : :
94 : : /* The copy constructor creates a new instance of the spsolver class
95 : : based on the given spsolver object. */
96 : 0 : spsolver::spsolver (spsolver & n) : analysis (n) {
97 : 0 : tees = n.tees;
98 : 0 : crosses = n.crosses;
99 : 0 : opens = n.opens;
100 : 0 : grounds = n.grounds;
101 : 0 : noise = n.noise;
102 : 0 : saveCVs = n.saveCVs;
103 [ # # ][ # # ]: 0 : swp = n.swp ? new sweep (*n.swp) : NULL;
[ # # ][ # # ]
[ # # ][ # # ]
104 [ # # ][ # # ]: 0 : nlist = n.nlist ? new nodelist (*n.nlist) : NULL;
[ # # ][ # # ]
[ # # ][ # # ]
105 : 0 : gnd = n.gnd;
106 : 0 : }
107 : :
108 : : /* This function joins two nodes of a single circuit (interconnected
109 : : nodes) and returns the resulting circuit. */
110 : 13105 : circuit * spsolver::interconnectJoin (node * n1, node * n2) {
111 : :
112 : 13105 : circuit * s = n1->getCircuit ();
113 [ + - ][ + - ]: 13105 : circuit * result = new circuit (s->getSize () - 2);
114 : 13105 : nr_complex_t p;
115 : :
116 : : // allocate S-parameter and noise corellation matrices
117 [ + - ][ + + ]: 13105 : result->initSP (); if (noise) result->initNoiseSP ();
[ + - ]
118 : :
119 : : // interconnected port numbers
120 : 13105 : int k = n1->getPort (), l = n2->getPort ();
121 : :
122 : : // denominator needs to be calculated only once
123 [ + - ][ + - ]: 13105 : nr_complex_t d = (1.0 - s->getS (k, l)) * (1.0 - s->getS (l, k)) -
124 [ + - ][ + - ]: 26210 : s->getS (k, k) * s->getS (l, l);
[ + - + - ]
[ + - ]
125 : :
126 : : // avoid singularity when two full reflective ports are interconnected
127 [ + - ][ - + ]: 13105 : nr_double_t tiny1 = (d == 0) ? 1.0 - TINYS : 1.0;
128 : 13105 : nr_double_t tiny2 = tiny1 * tiny1;
129 : 13105 : nr_double_t tiny3 = tiny1 * tiny2;
130 [ + - ][ + - ]: 13105 : d = (1.0 - s->getS (k, l) * tiny1) * (1.0 - s->getS (l, k) * tiny1) -
131 [ + - ][ + - ]: 26210 : s->getS (k, k) * s->getS (l, l) * tiny2;
[ + - ][ + - ]
[ + - ]
132 : :
133 : : int j2; // column index for resulting matrix
134 : : int i2; // row index for resulting matrix
135 : : int j1; // column index for S matrix
136 : : int i1; // row index for S matrix
137 : :
138 : : // handle single S block only
139 : 13105 : i2 = j2 = 0;
140 [ + + ]: 80405 : for (j1 = 0; j1 < s->getSize (); j1++) {
141 : :
142 : : // skip connected node
143 [ + + ][ + + ]: 67300 : if (j1 == k || j1 == l) continue;
144 : :
145 : : // assign node name of resulting circuit
146 [ + - ][ + - ]: 41090 : result->setNode (j2, s->getNode(j1)->getName ());
147 : :
148 : : // inside S only
149 [ + + ]: 272722 : for (i1 = 0; i1 < s->getSize (); i1++) {
150 : :
151 : : // skip connected node
152 [ + + ][ + + ]: 231632 : if (i1 == k || i1 == l) continue;
153 : :
154 : : // compute S'ij
155 [ + - ]: 149452 : p = s->getS (i1, j1);
156 : : p +=
157 [ + - ][ + - ]: 149452 : (s->getS (k, j1) * s->getS (i1, l) * (1.0 - s->getS (l, k)) * tiny3 +
[ + - ][ + - ]
[ + - ]
158 [ + - ][ + - ]: 298904 : s->getS (l, j1) * s->getS (i1, k) * (1.0 - s->getS (k, l)) * tiny3 +
[ + - ][ + - ]
[ + - ]
159 [ + - ][ + - ]: 298904 : s->getS (k, j1) * s->getS (l, l) * s->getS (i1, k) * tiny3 +
[ + - ][ + - ]
[ + - ][ + - ]
160 [ + - ][ + - ]: 298904 : s->getS (l, j1) * s->getS (k, k) * s->getS (i1, l) * tiny3) / d;
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
161 [ + - ]: 149452 : result->setS (i2++, j2, p);
162 : : }
163 : :
164 : : // next column
165 : 41090 : j2++; i2 = 0;
166 : : }
167 : 13105 : return result;
168 : : }
169 : :
170 : : /* This function joins two nodes of two different circuits (connected
171 : : nodes) and returns the resulting circuit. */
172 : 111570 : circuit * spsolver::connectedJoin (node * n1, node * n2) {
173 : :
174 : 111570 : circuit * s = n1->getCircuit ();
175 : 111570 : circuit * t = n2->getCircuit ();
176 [ + - ][ + - ]: 111570 : circuit * result = new circuit (s->getSize () + t->getSize () - 2);
177 : 111570 : nr_complex_t p;
178 : :
179 : : // allocate S-parameter and noise corellation matrices
180 [ + - ][ + + ]: 111570 : result->initSP (); if (noise) result->initNoiseSP ();
[ + - ]
181 : :
182 : : // connected port numbers
183 : 111570 : int k = n1->getPort (), l = n2->getPort ();
184 : :
185 : : // denominator needs to be calculated only once
186 [ + - ][ + - ]: 111570 : nr_complex_t d = 1.0 - s->getS (k, k) * t->getS (l, l);
[ + - ]
187 : :
188 : : // avoid singularity when two full reflective ports are connected
189 [ + - ][ - + ]: 111570 : nr_double_t tiny1 = (d == 0) ? 1.0 - TINYS : 1.0;
190 : 111570 : nr_double_t tiny2 = tiny1 * tiny1;
191 : 111570 : nr_double_t tiny3 = tiny1 * tiny2;
192 [ + - ][ + - ]: 111570 : d = 1.0 - s->getS (k, k) * t->getS (l, l) * tiny2;
[ + - ]
193 : :
194 : : int j2; // column index for resulting matrix
195 : : int i2; // row index for resulting matrix
196 : : int j1; // column index for S matrix
197 : : int i1; // row index for S matrix
198 : :
199 : : // handle S block
200 : 111570 : i2 = j2 = 0;
201 [ + + ]: 374868 : for (j1 = 0; j1 < s->getSize (); j1++) {
202 : :
203 : : // skip connected node
204 [ + + ]: 263298 : if (j1 == k) continue;
205 : :
206 : : // assign node name of resulting circuit
207 [ + - ][ + - ]: 151728 : result->setNode (j2, s->getNode(j1)->getName());
208 : :
209 : : // inside S
210 [ + + ]: 620110 : for (i1 = 0; i1 < s->getSize (); i1++) {
211 : :
212 : : // skip connected node
213 [ + + ]: 468382 : if (i1 == k) continue;
214 : :
215 : : // compute S'ij
216 [ + - ]: 316654 : p = s->getS (i1, j1);
217 [ + - ][ + - ]: 316654 : p += s->getS (k, j1) * t->getS (l, l) * s->getS (i1, k) * tiny3 / d;
[ + - ][ + - ]
[ + - ][ + - ]
218 [ + - ]: 316654 : result->setS (i2++, j2, p);
219 : : }
220 : :
221 : : // across S and T
222 [ + + ]: 475805 : for (i1 = 0; i1 < t->getSize (); i1++) {
223 : :
224 : : // skip connected node
225 [ + + ]: 324077 : if (i1 == l) continue;
226 : :
227 : : // compute S'mj
228 [ + - ][ + - ]: 172349 : p = s->getS (k, j1) * t->getS (i1, l) * tiny2 / d;
[ + - ][ + - ]
229 [ + - ]: 172349 : result->setS (i2++, j2, p);
230 : : }
231 : : // next column
232 : 151728 : j2++; i2 = 0;
233 : : }
234 : :
235 : : // handle T block
236 [ + + ]: 375936 : for (j1 = 0; j1 < t->getSize (); j1++) {
237 : :
238 : : // skip connected node
239 [ + + ]: 264366 : if (j1 == l) continue;
240 : :
241 : : // assign node name of resulting circuit
242 [ + - ][ + - ]: 152796 : result->setNode (j2, t->getNode(j1)->getName ());
243 : :
244 : : // across T and S
245 [ + + ]: 477941 : for (i1 = 0; i1 < s->getSize (); i1++) {
246 : :
247 : : // skip connected node
248 [ + + ]: 325145 : if (i1 == k) continue;
249 : :
250 : : // compute S'mj
251 [ + - ][ + - ]: 172349 : p = t->getS (l, j1) * s->getS (i1, k) * tiny2 / d;
[ + - ][ + - ]
252 [ + - ]: 172349 : result->setS (i2++, j2, p);
253 : : }
254 : :
255 : : // inside T
256 [ + + ]: 671972 : for (i1 = 0; i1 < t->getSize (); i1++) {
257 : :
258 : : // skip connected node
259 [ + + ]: 519176 : if (i1 == l) continue;
260 : :
261 : : // compute S'ij
262 [ + - ]: 366380 : p = t->getS (i1, j1);
263 [ + - ][ + - ]: 366380 : p += t->getS (l, j1) * s->getS (k, k) * t->getS (i1, l) * tiny3 / d;
[ + - ][ + - ]
[ + - ][ + - ]
264 [ + - ]: 366380 : result->setS (i2++, j2, p);
265 : : }
266 : :
267 : : // next column
268 : 152796 : j2++; i2 = 0;
269 : : }
270 : :
271 : 111570 : return result;
272 : : }
273 : :
274 : : /* This function joins the two given nodes of a single circuit
275 : : (interconnected nodes) and modifies the resulting circuit
276 : : appropriately. */
277 : 1173 : void spsolver::noiseInterconnect (circuit * result, node * n1, node * n2) {
278 : :
279 : 1173 : circuit * c = n1->getCircuit ();
280 : 1173 : nr_complex_t p, k1, k2, k3, k4;
281 : :
282 : : // interconnected port numbers
283 : 1173 : int k = n1->getPort (), l = n2->getPort ();
284 : :
285 : : // denominator needs to be calculated only once
286 [ + - ][ + - ]: 1173 : nr_complex_t t = (1.0 - c->getS (k, l)) * (1.0 - c->getS (l, k)) -
287 [ + - ][ + - ]: 2346 : c->getS (k, k) * c->getS (l, l);
[ + - + - ]
[ + - ]
288 : :
289 : : // avoid singularity when two full reflective ports are interconnected
290 [ + - ][ - + ]: 1173 : nr_double_t tiny1 = (t == 0) ? 1.0 - TINYS : 1.0;
291 : 1173 : nr_double_t tiny2 = tiny1 * tiny1;
292 [ + - ][ + - ]: 1173 : t = (1.0 - c->getS (k, l) * tiny1) * (1.0 - c->getS (l, k) * tiny1) -
293 [ + - ][ + - ]: 2346 : c->getS (k, k) * c->getS (l, l) * tiny2;
[ + - ][ + - ]
[ + - ]
294 : :
295 : : int j2; // column index for resulting matrix
296 : : int i2; // row index for resulting matrix
297 : : int j1; // column index for S matrix
298 : : int i1; // row index for S matrix
299 : :
300 : : // handle single C block only
301 : 1173 : i2 = j2 = 0;
302 [ + + ]: 10017 : for (j1 = 0; j1 < c->getSize (); j1++) {
303 : :
304 : : // skip connected node
305 [ + + ][ + + ]: 8844 : if (j1 == k || j1 == l) continue;
306 : :
307 : : // inside C only
308 [ + - ]: 31594 : for (i1 = 0; i1 < c->getSize (); i1++) {
309 : :
310 : : // skip connected node
311 [ + + ][ + + ]: 31594 : if (i1 == k || i1 == l) continue;
312 : :
313 [ + - ][ + - ]: 24780 : k1 = (c->getS (i1, l) * (1.0 - c->getS (l, k)) +
314 [ + - ][ + - ]: 49560 : c->getS (l, l) * c->getS (i1, k)) * tiny2 / t;
[ + - ][ + - ]
[ + - ][ + - ]
315 [ + - ][ + - ]: 24780 : k2 = (c->getS (i1, k) * (1.0 - c->getS (k, l)) +
316 [ + - ][ + - ]: 49560 : c->getS (k, k) * c->getS (i1, l)) * tiny2 / t;
[ + - ][ + - ]
[ + - ][ + - ]
317 [ + - ][ + - ]: 24780 : k3 = (c->getS (j1, l) * (1.0 - c->getS (l, k)) +
318 [ + - ][ + - ]: 49560 : c->getS (l, l) * c->getS (j1, k)) * tiny2 / t;
[ + - ][ + - ]
[ + - ][ + - ]
319 [ + - ][ + - ]: 24780 : k4 = (c->getS (j1, k) * (1.0 - c->getS (k, l)) +
320 [ + - ][ + - ]: 49560 : c->getS (k, k) * c->getS (j1, l)) * tiny2 / t;
[ + - ][ + - ]
[ + - ][ + - ]
321 : :
322 : : p =
323 [ + - ][ + - ]: 24780 : c->getN (i1, j1) + c->getN (k, j1) * k1 + c->getN (l, j1) * k2 +
[ + - ][ + - ]
[ + - ][ + - ]
324 [ + - ][ + - ]: 24780 : conj (k3) * (c->getN (i1, k) + c->getN (k, k) * k1 +
[ + - ]
325 [ + - ][ + - ]: 74340 : c->getN (l, k) * k2) +
[ + - ][ + - ]
[ + - ][ + - ]
326 [ + - ][ + - ]: 24780 : conj (k4) * (c->getN (i1, l) + c->getN (k, l) * k1 +
[ + - ]
327 [ + - ][ + - ]: 74340 : c->getN (l, l) * k2);
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
328 [ + - ]: 24780 : result->setN (i2, j2, p);
329 : :
330 [ + + ]: 24780 : if (i2 >= j2) break; // the other half need not be computed
331 [ + - ]: 18282 : result->setN (j2, i2, conj (p));
332 : 18282 : i2++;
333 : : }
334 : :
335 : : // next column
336 : 6498 : j2++; i2 = 0;
337 : : }
338 : 1173 : }
339 : :
340 : :
341 : : /* The following function joins two nodes of two different circuits
342 : : and saves the noise wave correlation matrix in the resulting
343 : : circuit. */
344 : 7898 : void spsolver::noiseConnect (circuit * result, node * n1, node * n2) {
345 : 7898 : circuit * c = n1->getCircuit ();
346 : 7898 : circuit * d = n2->getCircuit ();
347 : 7898 : nr_complex_t p;
348 : :
349 : : // connected port numbers
350 : 7898 : int k = n1->getPort (), l = n2->getPort ();
351 : :
352 : : // denominator needs to be calculated only once
353 [ + - ][ + - ]: 7898 : nr_complex_t t = 1.0 - c->getS (k, k) * d->getS (l, l);
[ + - ]
354 : :
355 : : // avoid singularity when two full reflective ports are connected
356 [ + - ][ - + ]: 7898 : nr_double_t tiny1 = (t == 0) ? 1.0 - TINYS : 1.0;
357 : 7898 : nr_double_t tiny2 = tiny1 * tiny1;
358 : 7898 : nr_double_t tiny3 = tiny1 * tiny2;
359 : 7898 : nr_double_t tiny4 = tiny1 * tiny3;
360 [ + - ][ + - ]: 7898 : t = 1.0 - c->getS (k, k) * d->getS (l, l) * tiny2;
[ + - ]
361 : :
362 : : int j2; // column index for resulting matrix
363 : : int i2; // row index for resulting matrix
364 : : int j1; // column index for S matrix
365 : : int i1; // row index for S matrix
366 : :
367 : : // handle C block
368 : 7898 : i2 = j2 = 0;
369 [ + + ]: 27925 : for (j1 = 0; j1 < c->getSize (); j1++) {
370 : :
371 : : // skip connected node
372 [ + + ]: 20027 : if (j1 == k) continue;
373 : :
374 : : // inside C
375 [ + - ]: 27274 : for (i1 = 0; i1 < c->getSize (); i1++) {
376 : :
377 : : // skip connected node
378 [ + + ]: 27274 : if (i1 == k) continue;
379 : :
380 : : // compute C'ij
381 : : p = c->getN (i1, j1) +
382 [ + - ][ + - ]: 21432 : c->getN (k, j1) * d->getS (l, l) * c->getS (i1, k) * tiny2 / t +
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
383 [ + - ][ + - ]: 42864 : c->getN (i1, k) * conj (d->getS (l, l) * c->getS (j1, k) * tiny2 / t) +
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
384 [ + - ][ + - ]: 21432 : (c->getN (k, k) * norm (d->getS (l, l)) + d->getN (l, l)) *
[ + - ][ + - ]
385 [ + - ][ + - ]: 64296 : c->getS (i1, k) * conj (c->getS (j1, k)) * tiny4 / norm (t);
[ + - + - ]
[ + - ][ + - ]
[ + - ][ + - ]
386 : :
387 [ + - ]: 21432 : result->setN (i2, j2, p);
388 [ + + ]: 21432 : if (i2 >= j2) break; // the other half need not be computed
389 [ + - ]: 9303 : result->setN (j2, i2, conj (p));
390 : 9303 : i2++;
391 : : }
392 : :
393 : : /* The formulas "across C and D" are calculated elsewhere by the
394 : : other half of the matrix (conjugate complex). Therefore, they
395 : : are missing here. */
396 : :
397 : : // next column
398 : 12129 : j2++; i2 = 0;
399 : : }
400 : :
401 : : // handle D block
402 [ + + ]: 29559 : for (j1 = 0; j1 < d->getSize (); j1++) {
403 : :
404 : : // skip connected node
405 [ + + ]: 21661 : if (j1 == l) continue;
406 : :
407 : : // across D and C
408 [ + + ]: 49896 : for (i1 = 0; i1 < c->getSize (); i1++) {
409 : :
410 : : // skip connected node
411 [ + + ]: 36133 : if (i1 == k) continue;
412 : :
413 : : // compute C'ij
414 [ + - ][ + - ]: 22370 : p = (c->getN (k, k) * d->getS (l, l) +
415 [ + - ][ + - ]: 44740 : d->getN (l, l) * conj (c->getS (k, k))) *
[ + - ][ + - ]
416 [ + - ][ + - ]: 44740 : c->getS (i1, k) * conj (d->getS (j1, l)) * tiny3 / norm (t) +
[ + - ][ + - ]
[ + - ][ + - ]
417 [ + - ][ + - ]: 44740 : d->getN (l, j1) * c->getS (i1, k) * tiny1 / t +
[ + - ][ + - ]
418 [ + - ][ + - ]: 44740 : c->getN (i1, k) * conj (d->getS (j1, l) * tiny1 / t);
[ + - ]
[ + - + - ]
[ + - ]
419 [ + - ]: 22370 : result->setN (i2, j2, p);
420 [ + - ]: 22370 : result->setN (j2, i2, conj (p));
421 : 22370 : i2++;
422 : : }
423 : :
424 : : // inside D
425 [ + - ]: 32720 : for (i1 = 0; i1 < d->getSize (); i1++) {
426 : :
427 : : // skip connected node
428 [ + + ]: 32720 : if (i1 == l) continue;
429 : :
430 : : // compute C'ij
431 : : p = d->getN (i1, j1) +
432 [ + - ][ + - ]: 30481 : (d->getN (l, l) * norm (c->getS (k, k)) + c->getN (k, k)) *
[ + - ][ + - ]
433 [ + - ][ + - ]: 60962 : d->getS (i1, l) * conj (d->getS (j1, l)) * tiny4 / norm (t) +
[ + - + - ]
[ + - ][ + - ]
[ + - ]
434 [ + - ][ + - ]: 60962 : d->getN (i1, l) * conj (c->getS (k, k) * d->getS (j1, l) * tiny2 / t) +
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
435 [ + - ][ + - ]: 60962 : d->getN (l, j1) * c->getS (k, k) * d->getS (i1, l) * tiny2 / t;
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
436 [ + - ]: 30481 : result->setN (i2, j2, p);
437 [ + + ]: 30481 : if (i2 >= j2) break; // the other half need not be computed
438 [ + - ]: 16718 : result->setN (j2, i2, conj (p));
439 : 16718 : i2++;
440 : : }
441 : :
442 : : // next column
443 : 13763 : j2++; i2 = 0;
444 : : }
445 : 7898 : }
446 : :
447 : : /* Goes through the list of circuit objects and runs its frequency
448 : : dependent calcSP() function. */
449 : 5783 : void spsolver::calc (nr_double_t freq) {
450 : 5783 : circuit * root = subnet->getRoot ();
451 [ + + ]: 135144 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
452 : 129361 : c->calcSP (freq);
453 [ + + ]: 129361 : if (noise) c->calcNoiseSP (freq);
454 : : }
455 : 5783 : }
456 : :
457 : : /* Go through each registered circuit object in the list and find the
458 : : connection which results in a new subnetwork with the smallest
459 : : number of s-parameters to calculate. */
460 : 124675 : void spsolver::reduce (void) {
461 : :
462 : : #if SORTED_LIST
463 : : node * n1, * n2;
464 : : circuit * result, * cand1, * cand2;
465 : :
466 [ + - ]: 124675 : nlist->sortedNodes (&n1, &n2);
467 : 124675 : cand1 = n1->getCircuit ();
468 : 124675 : cand2 = n2->getCircuit ();
469 : : #else /* !SORTED_LIST */
470 : : node * n1, * n2, * cand;
471 : : circuit * result, * c1, * c2, * cand1, * cand2;
472 : : int ports;
473 : : circuit * root = subnet->getRoot ();
474 : :
475 : : // initialize local variables
476 : : result = c1 = c2 = cand1 = cand2 = NULL;
477 : : n1 = n2 = cand = NULL;
478 : : ports = 10000; // huge
479 : :
480 : : // go through the circuit list
481 : : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
482 : :
483 : : // skip signal ports
484 : : if (c->getPort ()) continue;
485 : :
486 : : // and each node in the circuit
487 : : for (int i = 0; i < c->getSize (); i++) {
488 : :
489 : : // find duplicate node
490 : : if ((cand = subnet->findConnectedCircuitNode (c->getNode (i))) != NULL) {
491 : :
492 : : // save both candidates
493 : : c1 = c; c2 = cand->getCircuit ();
494 : : // connected
495 : : if (c1 != c2) {
496 : : if (c1->getSize () + c2->getSize () - 2 < ports) {
497 : : ports = c1->getSize () + c2->getSize () - 2;
498 : : cand1 = c1; cand2 = c2; n1 = c1->getNode (i); n2 = cand;
499 : : }
500 : : }
501 : : // interconnect
502 : : else {
503 : : if (c1->getSize () - 2 < ports) {
504 : : ports = c1->getSize () - 2;
505 : : cand1 = c1; cand2 = c2; n1 = c1->getNode (i); n2 = cand;
506 : : }
507 : : }
508 : : }
509 : : }
510 : : }
511 : : #endif /* !SORTED_LIST */
512 : :
513 : : // found a connection ?
514 [ + - ][ + - ]: 124675 : if (cand1 != NULL && cand2 != NULL) {
515 : : // connected
516 [ + + ]: 124675 : if (cand1 != cand2) {
517 : : #if DEBUG && 0
518 : : logprint (LOG_STATUS, "DEBUG: connected node (%s): %s - %s\n",
519 : : n1->getName (), cand1->getName (), cand2->getName ());
520 : : #endif /* DEBUG */
521 [ + - ]: 111570 : result = connectedJoin (n1, n2);
522 [ + + ][ + - ]: 111570 : if (noise) noiseConnect (result, n1, n2);
523 [ + - ]: 111570 : subnet->reducedCircuit (result);
524 : : #if SORTED_LIST
525 [ + - ]: 111570 : nlist->remove (cand1);
526 [ + - ]: 111570 : nlist->remove (cand2);
527 [ + - ]: 111570 : nlist->insert (result);
528 : : #endif /* SORTED_LIST */
529 [ + - ]: 111570 : subnet->removeCircuit (cand1);
530 [ + - ]: 111570 : subnet->removeCircuit (cand2);
531 [ + - ]: 111570 : subnet->insertCircuit (result);
532 : 111570 : result->setOriginal (0);
533 : : }
534 : : // interconnect
535 : : else {
536 : : #if DEBUG && 0
537 : : logprint (LOG_STATUS, "DEBUG: interconnected node (%s): %s\n",
538 : : n1->getName (), cand1->getName ());
539 : : #endif
540 [ + - ]: 13105 : result = interconnectJoin (n1, n2);
541 [ + + ][ + - ]: 13105 : if (noise) noiseInterconnect (result, n1, n2);
542 [ + - ]: 13105 : subnet->reducedCircuit (result);
543 : : #if SORTED_LIST
544 [ + - ]: 13105 : nlist->remove (cand1);
545 [ + - ]: 13105 : nlist->insert (result);
546 : : #endif /* SORTED_LIST */
547 [ + - ]: 13105 : subnet->removeCircuit (cand1);
548 [ + - ]: 13105 : subnet->insertCircuit (result);
549 : 13105 : result->setOriginal (0);
550 : : }
551 : : }
552 : 124675 : }
553 : :
554 : : /* Goes through the list of circuit objects and runs initializing
555 : : functions if necessary. */
556 : 18 : void spsolver::init (void) {
557 : 18 : circuit * root = subnet->getRoot ();
558 [ + + ]: 233 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
559 [ + + ]: 215 : if (c->isNonLinear ()) c->calcOperatingPoints ();
560 : 215 : c->initSP ();
561 [ + + ]: 215 : if (noise) c->initNoiseSP ();
562 : : }
563 : 18 : }
564 : :
565 : : /* This is the netlist solver. It prepares the circuit list for each
566 : : requested frequency and solves it then. */
567 : 18 : int spsolver::solve (void) {
568 : : nr_double_t freq;
569 : : int ports;
570 : 18 : runs++;
571 : :
572 : : // fetch simulation properties
573 [ - + ]: 18 : saveCVs |= !strcmp (getPropertyString ("saveCVs"), "yes") ? SAVE_CVS : 0;
574 [ - + ]: 18 : saveCVs |= !strcmp (getPropertyString ("saveAll"), "yes") ? SAVE_ALL : 0;
575 : :
576 : : // run additional noise analysis ?
577 [ + + ]: 18 : noise = !strcmp (getPropertyString ("Noise"), "yes") ? 1 : 0;
578 : :
579 : : // create frequency sweep if necessary
580 [ + - ]: 18 : if (swp == NULL) {
581 : 18 : swp = createSweep ("frequency");
582 : : }
583 : :
584 : 18 : init ();
585 : 18 : insertConnections ();
586 : :
587 : : #if SORTED_LIST
588 : : #if DEBUG
589 : : logprint (LOG_STATUS, "NOTIFY: %s: creating sorted nodelist for "
590 : 18 : "SP analysis\n", getName ());
591 : : #endif
592 [ + - ]: 18 : nlist = new nodelist (subnet);
593 : 18 : nlist->sort ();
594 : : #endif /* SORTED_LIST */
595 : :
596 : : #if DEBUG
597 : 18 : logprint (LOG_STATUS, "NOTIFY: %s: solving SP netlist\n", getName ());
598 : : #endif
599 : :
600 : 18 : swp->reset ();
601 [ + + ]: 5801 : for (int i = 0; i < swp->getSize (); i++) {
602 : 5783 : freq = swp->next ();
603 [ + - ]: 5783 : if (progress) logprogressbar (i, swp->getSize (), 40);
604 : :
605 : 5783 : ports = subnet->countNodes ();
606 : 5783 : subnet->setReduced (0);
607 : 5783 : calc (freq);
608 : :
609 : : #if DEBUG && 0
610 : : logprint (LOG_STATUS, "NOTIFY: %s: solving netlist for f = %e\n",
611 : : getName (), (double) freq);
612 : : #endif
613 : :
614 [ + + ]: 130458 : while (ports > subnet->getPorts ()) {
615 : 124675 : reduce ();
616 : 124675 : ports -= 2;
617 : : }
618 : :
619 : 5783 : saveResults (freq);
620 : 5783 : subnet->getDroppedCircuits (nlist);
621 : 5783 : subnet->deleteUnusedCircuits (nlist);
622 [ - + ]: 5783 : if (saveCVs & SAVE_CVS) saveCharacteristics (freq);
623 : : }
624 [ + - ]: 18 : if (progress) logprogressclear (40);
625 : 18 : dropConnections ();
626 : : #if SORTED_LIST
627 [ + - ]: 18 : delete nlist; nlist = NULL;
628 : : #endif
629 : 18 : return 0;
630 : : }
631 : :
632 : : /* The function goes through the list of circuit objects and creates
633 : : tee and cross circuits if necessary. It looks for nodes in the
634 : : circuit list connected to the given node. */
635 : 440 : void spsolver::insertConnectors (node * n) {
636 : :
637 : 440 : int count = 0;
638 : : node * nodes[4], * _node;
639 : 440 : char * _name = n->getName ();
640 : 440 : circuit * root = subnet->getRoot ();
641 : :
642 : : #if USE_GROUNDS
643 [ + + ]: 880 : if (!strcmp (_name, "gnd")) return;
644 : : #endif /* USE_GROUNDS */
645 : :
646 : 350 : nodes[0] = n;
647 : :
648 : : // go through list of circuit objects
649 [ + + ]: 6472 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
650 : : // and each node in a circuit
651 [ + + ]: 20935 : for (int i = 0; i < c->getSize (); i++) {
652 [ + - ]: 14813 : _node = c->getNode (i);
653 [ + + ]: 14813 : if (!strcmp (_node->getName (), _name)) {
654 [ + + ]: 780 : if (_node != n) {
655 : :
656 : : // found a connected node
657 : 430 : nodes[++count] = _node;
658 : : #if USE_CROSSES
659 [ + + ]: 430 : if (count == 3) {
660 : : // create an additional cross and assign its nodes
661 [ + - ]: 18 : insertCross (nodes, _name);
662 : 18 : count = 1;
663 : : }
664 : : #else /* !USE_CROSSES */
665 : : if (count == 2) {
666 : : // create an additional tee and assign its nodes
667 : : insertTee (nodes, _name);
668 : : count = 1;
669 : : }
670 : : #endif /* !USE_CROSSES */
671 : : }
672 : : }
673 : : }
674 : : }
675 : : #if USE_CROSSES
676 : : /* if using crosses there can be a tee left here */
677 [ + + ]: 350 : if (count == 2) {
678 [ + - ]: 440 : insertTee (nodes, _name);
679 : : }
680 : : #endif /* USE_CROSSES */
681 : : }
682 : :
683 : : /* The following function creates a tee circuit with the given nodes
684 : : and the node name. The tee's node names are adjusted to be
685 : : internal nodes. */
686 : 50 : void spsolver::insertTee (node ** nodes, char * name) {
687 : : circuit * result;
688 : : // create a tee and assign its node names
689 [ + - ]: 50 : result = new tee ();
690 : 50 : subnet->insertedCircuit (result);
691 : 50 : result->setNode (0, name);
692 : 50 : subnet->insertedNode (result->getNode (1));
693 : 50 : subnet->insertedNode (result->getNode (2));
694 : : // rename the nodes connected to the tee
695 : 50 : nodes[1]->setName (result->getNode(1)->getName ());
696 : 50 : nodes[2]->setName (result->getNode(2)->getName ());
697 : : // complete the nodes of the tee
698 : 50 : result->getNode(1)->setCircuit (result);
699 : 50 : result->getNode(2)->setCircuit (result);
700 : 50 : result->getNode(1)->setPort (1);
701 : 50 : result->getNode(2)->setPort (2);
702 : : // put the tee into the circuit list and initialize it
703 : 50 : subnet->insertCircuit (result);
704 [ + + ]: 50 : result->initSP (); if (noise) result->initNoiseSP ();
705 : : // put the tee's first node into the node collection
706 : 50 : nodes[1] = result->getNode (0);
707 : 50 : tees++;
708 : 50 : }
709 : :
710 : : /* The following function creates a cross circuit with the given nodes
711 : : and the node name. The cross's node names are adjusted to be
712 : : internal nodes. */
713 : 18 : void spsolver::insertCross (node ** nodes, char * name) {
714 : : circuit * result;
715 : : // create a cross and assign its node names
716 [ + - ]: 18 : result = new cross ();
717 : 18 : subnet->insertedCircuit (result);
718 : 18 : result->setNode (0, name);
719 : 18 : subnet->insertedNode (result->getNode (1));
720 : 18 : subnet->insertedNode (result->getNode (2));
721 : 18 : subnet->insertedNode (result->getNode (3));
722 : : // rename the nodes connected to the cross
723 : 18 : nodes[1]->setName (result->getNode(1)->getName ());
724 : 18 : nodes[2]->setName (result->getNode(2)->getName ());
725 : 18 : nodes[3]->setName (result->getNode(3)->getName ());
726 : : // complete the nodes of the cross
727 : 18 : result->getNode(1)->setCircuit (result);
728 : 18 : result->getNode(2)->setCircuit (result);
729 : 18 : result->getNode(3)->setCircuit (result);
730 : 18 : result->getNode(1)->setPort (1);
731 : 18 : result->getNode(2)->setPort (2);
732 : 18 : result->getNode(3)->setPort (3);
733 : : // put the cross into the circuit list and initialize it
734 : 18 : subnet->insertCircuit (result);
735 [ + + ]: 18 : result->initSP (); if (noise) result->initNoiseSP ();
736 : : // put the cross's first node into the node collection
737 : 18 : nodes[1] = result->getNode (0);
738 : 18 : crosses++;
739 : 18 : }
740 : :
741 : : /* This function removes an inserted tee from the netlist and restores
742 : : the original node names. */
743 : 50 : void spsolver::dropTee (circuit * c) {
744 : : node * n;
745 [ + - ]: 50 : if (c->getType () == CIR_TEE) {
746 : 50 : char * name = c->getNode(0)->getName ();
747 : 50 : n = subnet->findConnectedNode (c->getNode (1)); n->setName (name);
748 : 50 : n = subnet->findConnectedNode (c->getNode (2)); n->setName (name);
749 : 50 : c->setOriginal (0);
750 : 50 : subnet->removeCircuit (c);
751 : : }
752 : 50 : }
753 : :
754 : : /* This function removes an inserted cross from the netlist and restores
755 : : the original node names. */
756 : 18 : void spsolver::dropCross (circuit * c) {
757 : : node * n;
758 [ + - ]: 18 : if (c->getType () == CIR_CROSS) {
759 : 18 : char * name = c->getNode(0)->getName ();
760 : 18 : n = subnet->findConnectedNode (c->getNode (1)); n->setName (name);
761 : 18 : n = subnet->findConnectedNode (c->getNode (2)); n->setName (name);
762 : 18 : n = subnet->findConnectedNode (c->getNode (3)); n->setName (name);
763 : 18 : c->setOriginal (0);
764 : 18 : subnet->removeCircuit (c);
765 : : }
766 : 18 : }
767 : :
768 : : /* The function adds an open to the circuit list if the given node is
769 : : unconnected. */
770 : 440 : void spsolver::insertOpen (node * n) {
771 [ + + + + ]: 790 : if (strcmp (n->getName (), "gnd") &&
[ + + ]
772 : 350 : subnet->findConnectedNode (n) == NULL) {
773 [ + - ]: 6 : circuit * result = new open ();
774 : 6 : subnet->insertedCircuit (result);
775 : 6 : result->setNode (0, n->getName ());
776 : 6 : subnet->insertCircuit (result);
777 [ + - ]: 6 : result->initSP (); if (noise) result->initNoiseSP ();
778 : 6 : opens++;
779 : : }
780 : 440 : }
781 : :
782 : : // This function removes an inserted open from the netlist.
783 : 6 : void spsolver::dropOpen (circuit * c) {
784 [ + - ]: 6 : if (c->getType () == CIR_OPEN) {
785 : 6 : c->setOriginal (0);
786 : 6 : subnet->removeCircuit (c);
787 : : }
788 : 6 : }
789 : :
790 : : /* The function adds a ground circuit to the circuit list if the given
791 : : node is a ground connection. */
792 : 788 : void spsolver::insertGround (node * n) {
793 [ + + ]: 878 : if (!strcmp (n->getName (), "gnd") && !n->getCircuit()->getPort () &&
[ + - + - ]
[ + + ]
794 : 90 : n->getCircuit()->getType () != CIR_GROUND) {
795 [ + - ]: 90 : circuit * result = new ground ();
796 : 90 : subnet->insertedCircuit (result);
797 : 90 : subnet->insertedNode (result->getNode (0));
798 : 90 : result->getNode(0)->setCircuit (result);
799 : 90 : result->getNode(0)->setPort (0);
800 : 90 : n->setName (result->getNode(0)->getName ());
801 : 90 : subnet->insertCircuit (result);
802 [ + + ]: 90 : result->initSP (); if (noise) result->initNoiseSP ();
803 : 90 : grounds++;
804 : : }
805 : 788 : }
806 : :
807 : : // This function removes an inserted ground from the netlist.
808 : 90 : void spsolver::dropGround (circuit * c) {
809 [ + - ]: 90 : if (c->getType () == CIR_GROUND) {
810 : 90 : node * n = subnet->findConnectedNode (c->getNode (0));
811 : 90 : n->setName ("gnd");
812 : 90 : c->setOriginal (0);
813 : 90 : subnet->removeCircuit (c);
814 : : }
815 : 90 : }
816 : :
817 : : /* This function prepares the circuit list by adding Ts and opens to
818 : : the circuit list. With this adjustments the solver is able to
819 : : solve the circuit. */
820 : 18 : void spsolver::insertConnections (void) {
821 : :
822 : : circuit * root, * c;
823 : : #if DEBUG
824 : : logprint (LOG_STATUS, "NOTIFY: %s: preparing circuit for analysis\n",
825 : 18 : getName ());
826 : : #endif /* DEBUG */
827 : :
828 : : #if USE_GROUNDS
829 : : // remove original ground circuit from netlist
830 : 18 : root = subnet->getRoot ();
831 [ + - ]: 21 : for (c = root; c != NULL; c = (circuit *) c->getNext ()) {
832 [ + + ]: 21 : if (c->getType () == CIR_GROUND) {
833 : 18 : gnd = c;
834 : 18 : subnet->removeCircuit (c, 0);
835 : 18 : break;
836 : : }
837 : : }
838 : : #endif /* USE_GROUNDS */
839 : :
840 : : // insert opens, tee and crosses where necessary
841 : 18 : tees = crosses = opens = grounds = 0;
842 : 18 : root = subnet->getRoot ();
843 [ + + ]: 218 : for (c = root; c != NULL; c = (circuit *) c->getNext ()) {
844 [ + + ]: 640 : for (int i = 0; i < c->getSize (); i++) {
845 : 440 : insertConnectors (c->getNode (i));
846 : 440 : insertOpen (c->getNode (i));
847 : : }
848 : : }
849 : :
850 : : // insert S-parameter port transformers
851 : 18 : insertDifferentialPorts ();
852 : :
853 : : #if USE_GROUNDS
854 : : // insert grounds where necessary
855 : 18 : root = subnet->getRoot ();
856 [ + + ]: 332 : for (c = root; c != NULL; c = (circuit *) c->getNext ()) {
857 [ + + ]: 1102 : for (int i = 0; i < c->getSize (); i++) {
858 : 788 : insertGround (c->getNode (i));
859 : : }
860 : : }
861 : : #endif /* USE_GROUNDS */
862 : :
863 : : #if DEBUG
864 : : logprint (LOG_STATUS, "NOTIFY: %s: inserted %d tees, %d crosses, %d opens "
865 : : "and %d grounds\n",
866 : 18 : getName (), tees, crosses, opens, grounds);
867 : : #endif /* DEBUG */
868 : 18 : }
869 : :
870 : : /* The function is the counterpart of insertConnections(). It removes
871 : : all additional circuits from the netlist which were necessary to
872 : : run the analysis algorithm. */
873 : 222 : void spsolver::dropConnections (void) {
874 : : circuit * next, * cand;
875 : : int inserted;
876 : :
877 : : // drop all additional inserted circuits in correct order
878 [ + + ]: 222 : do {
879 : : // find last inserted circuit
880 : 222 : inserted = -1;
881 : 222 : cand = NULL;
882 [ + + ]: 4202 : for (circuit * c = subnet->getRoot (); c != NULL; c = next) {
883 : 3980 : next = (circuit *) c->getNext ();
884 [ + + ]: 3980 : if (c->getInserted () > inserted) {
885 : 458 : inserted = c->getInserted ();
886 : 458 : cand = c;
887 : : }
888 : : }
889 : : // if found, then drop that circuit
890 [ + + ]: 222 : if (cand != NULL) {
891 [ + + + + : 204 : switch (cand->getType ()) {
+ - ]
892 : : case CIR_OPEN:
893 : 6 : dropOpen (cand);
894 : 6 : break;
895 : : case CIR_TEE:
896 : 50 : dropTee (cand);
897 : 50 : break;
898 : : case CIR_CROSS:
899 : 18 : dropCross (cand);
900 : 18 : break;
901 : : case CIR_GROUND:
902 : 90 : dropGround (cand);
903 : 90 : break;
904 : : case CIR_ITRAFO:
905 : 40 : dropDifferentialPort (cand);
906 : 204 : break;
907 : : }
908 : : }
909 : : } while (cand != NULL);
910 : :
911 : : #if USE_GROUNDS
912 : : // attach the original ground to the netlist
913 : 18 : subnet->insertCircuit (gnd);
914 : : #endif /* USE_GROUNDS */
915 : 18 : }
916 : :
917 : : /* This function inserts an ideal transformer before an AC power
918 : : source in order to allow differential S parameter ports. */
919 : 18 : void spsolver::insertDifferentialPorts (void) {
920 : 18 : circuit * root = subnet->getRoot ();
921 [ + + ]: 292 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
922 [ + + ]: 274 : if (c->getPort ()) {
923 : :
924 : : // create an ideal transformer and assign its node names
925 [ + - ]: 40 : circuit * result = new itrafo ();
926 : 40 : subnet->insertedCircuit (result);
927 : 40 : subnet->insertedNode (result->getNode (0));
928 : 40 : result->setNode (1, c->getNode(0)->getName ());
929 : 40 : result->setNode (2, c->getNode(1)->getName ());
930 : :
931 : : // rename the nodes connected to the trafo
932 : 40 : c->getNode(0)->setName (result->getNode(0)->getName ());
933 : 40 : c->getNode(1)->setName ("PacGround");
934 : :
935 : : // complete the nodes of the trafo
936 : 40 : result->getNode(0)->setCircuit (result);
937 : 40 : result->getNode(0)->setPort (0);
938 : :
939 : : // pass the port impedance to the ideal trafo
940 : 40 : result->addProperty ("Z", c->getPropertyDouble ("Z"));
941 : :
942 : : // put the trafo in the circuit list
943 : 40 : subnet->insertCircuit (result);
944 : :
945 : : // allocate S-parameter and noise correlation matrices
946 [ + + ]: 40 : result->initSP (); if (noise) result->initNoiseSP ();
947 : : }
948 : : }
949 : 18 : }
950 : :
951 : : /* This function removes an ideal transformer which was necessary to
952 : : be placed in front of a s-parameter port in order to allow
953 : : differential s-parameters. It also restores the original node
954 : : names. */
955 : 40 : void spsolver::dropDifferentialPort (circuit * c) {
956 : : circuit * pac;
957 : : node * n;
958 [ + - ]: 40 : if (c->getType () == CIR_ITRAFO) {
959 : 40 : n = subnet->findConnectedNode (c->getNode (0));
960 : 40 : pac = n->getCircuit ();
961 : 40 : pac->getNode(0)->setName (c->getNode(1)->getName ());
962 : 40 : pac->getNode(1)->setName (c->getNode(2)->getName ());
963 : 40 : c->setOriginal (0);
964 : 40 : subnet->removeCircuit (c);
965 : : }
966 : 40 : }
967 : :
968 : : /* This function saves the results of a single solve() functionality
969 : : (for the given frequency) into the output dataset. */
970 : 5783 : void spsolver::saveResults (nr_double_t freq) {
971 : :
972 : : vector * f;
973 : : node * sig_i, * sig_j;
974 : : char * n;
975 : : int res_i, res_j;
976 : 5783 : circuit * root = subnet->getRoot ();
977 : :
978 : : // temporary noise matrices and input port impedance
979 [ + + ][ + + ]: 52047 : nr_complex_t noise_c[4], noise_s[4];
980 : 5783 : nr_double_t z0 = circuit::z0;
981 : :
982 : : // add current frequency to the dependency of the output dataset
983 [ + - ][ + + ]: 5783 : if ((f = data->findDependency ("frequency")) == NULL) {
984 [ + - ][ + - ]: 18 : f = new vector ("frequency");
985 [ + - ]: 18 : data->addDependency (f);
986 : : }
987 [ + - ][ + - ]: 5783 : if (runs == 1) f->add (freq);
988 : :
989 : : // go through the list of remaining circuits
990 [ + + ]: 23574 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
991 : : // skip signals
992 [ + + ]: 17791 : if (!c->getPort ()) {
993 : : // handle each s-parameter
994 [ + + ]: 17791 : for (int i = 0; i < c->getSize (); i++) {
995 [ + + ]: 36744 : for (int j = 0; j < c->getSize (); j++) {
996 : :
997 : : // generate the appropriate variable name
998 [ + - ][ + - ]: 24837 : sig_i = subnet->findConnectedNode (c->getNode (i));
999 [ + - ][ + - ]: 24837 : sig_j = subnet->findConnectedNode (c->getNode (j));
1000 [ + - ]: 24837 : res_i = sig_i->getCircuit()->getPropertyInteger ("Num");
1001 [ + - ]: 24837 : res_j = sig_j->getCircuit()->getPropertyInteger ("Num");
1002 [ + - ]: 24837 : n = createSP (res_i, res_j);
1003 : :
1004 : : // add variable data item to dataset
1005 [ + - ][ + - ]: 24837 : saveVariable (n, c->getS (i, j), f);
1006 : :
1007 : : // if noise analysis is requested
1008 [ + + ]: 24837 : if (noise) {
1009 : : int ro, co;
1010 [ + - ]: 1036 : int ni = getPropertyInteger ("NoiseIP");
1011 [ + - ]: 1036 : int no = getPropertyInteger ("NoiseOP");
1012 [ + + ][ + - ]: 1036 : if ((res_i == ni || res_i == no) && (res_j == ni || res_j == no)) {
[ + + ][ + - ]
1013 [ + + ]: 1036 : if (ni == res_i) {
1014 : : // assign input port impedance
1015 [ + - ]: 518 : z0 = sig_i->getCircuit()->getPropertyDouble ("Z");
1016 : : }
1017 : 1036 : ro = (res_i == ni) ? 0 : 1;
1018 : 1036 : co = (res_j == ni) ? 0 : 1;
1019 : : // save results in temporary data items
1020 [ + - ]: 1036 : noise_c[co + ro * 2] = c->getN (i, j);
1021 [ + - ]: 1036 : noise_s[co + ro * 2] = c->getS (i, j);
1022 : : }
1023 : : }
1024 : : }
1025 : : }
1026 : : }
1027 : : }
1028 : :
1029 : : // finally compute and save noise parameters
1030 [ + + ]: 5783 : if (noise) {
1031 [ + - ]: 259 : saveNoiseResults (noise_s, noise_c, z0, f);
1032 : : }
1033 : 5783 : }
1034 : :
1035 : : /* This function takes the s-parameter matrix and noise wave
1036 : : correlation matrix and computes the noise parameters based upon
1037 : : these values. Then it save the results into the dataset. */
1038 : 259 : void spsolver::saveNoiseResults (nr_complex_t s[4], nr_complex_t c[4],
1039 : : nr_double_t z0, vector * f) {
1040 : 259 : nr_complex_t c22 = c[3], c11 = c[0], c12 = c[1];
1041 : 259 : nr_complex_t s11 = s[0], s21 = s[2];
1042 : 259 : nr_complex_t n1, n2, F, Sopt, Fmin, Rn;
1043 : :
1044 : : // linear noise figure
1045 [ + - ]: 259 : F = real (1.0 + c22 / norm (s21));
1046 : : n1 =
1047 [ + - ][ + - ]: 259 : c11 * norm (s21) - 2.0 * real (c12 * s21 * conj (s11)) +
[ + - ]
1048 [ + - ][ + - ]: 518 : c22 * norm (s11);
1049 [ + - ][ + - ]: 259 : n2 = 2.0 * (c22 * s11 - c12 * s21) / (c22 + n1);
[ + - ][ + - ]
[ + - ]
1050 : :
1051 : : // optimal source reflection coefficient
1052 [ + - ]: 259 : Sopt = 1.0 - norm (n2);
1053 [ - + ]: 259 : if (real (Sopt) < 0.0)
1054 [ # # ][ # # ]: 0 : Sopt = (1.0 + sqrt (Sopt)) / n2; // avoid a negative radicant
1055 : : else
1056 [ + - ][ + - ]: 259 : Sopt = (1.0 - sqrt (Sopt)) / n2;
1057 : :
1058 : : // minimum noise figure
1059 [ + - ]: 259 : Fmin = real (1.0 + (c22 - n1 * norm (Sopt)) /
1060 [ + - ]: 518 : norm (s21) / (1.0 + norm (Sopt)));
[ + - + - ]
1061 : :
1062 : : // equivalent noise resistance
1063 [ + - ][ + - ]: 259 : Rn = real ((c11 - 2.0 * real (c12 * conj ((1.0 + s11) / s21)) +
1064 [ + - ][ + - ]: 518 : c22 * norm ((1.0 + s11) / s21)) / 4.0);
[ + - ]
1065 : 259 : Rn = Rn * z0;
1066 : :
1067 : : // add variable data items to dataset
1068 [ + - ]: 259 : saveVariable ("F", F, f);
1069 [ + - ]: 259 : saveVariable ("Sopt", Sopt, f);
1070 [ + - ]: 259 : saveVariable ("Fmin", Fmin, f);
1071 [ + - ]: 259 : saveVariable ("Rn", Rn, f);
1072 : 259 : }
1073 : :
1074 : : // Create an appropriate variable name.
1075 : 24837 : char * spsolver::createSP (int i, int j) {
1076 : 24837 : return matvec::createMatrixString ("S", i - 1, j - 1);
1077 : : }
1078 : :
1079 : : /* Create an appropriate variable name for characteristic values. The
1080 : : caller is responsible to free() the returned string. */
1081 : 0 : const char * spsolver::createCV (const std::string &c, const std::string &n) {
1082 [ # # ]: 0 : return (c+"."+n).c_str();
1083 : : }
1084 : :
1085 : : /* Goes through the list of circuit objects and runs its
1086 : : saveCharacteristics() function. Then puts these values into the
1087 : : dataset. */
1088 : 0 : void spsolver::saveCharacteristics (nr_double_t freq) {
1089 : 0 : circuit * root = subnet->getRoot ();
1090 : : const char * n;
1091 : 0 : vector * f = data->findDependency ("frequency");
1092 [ # # ]: 0 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
1093 [ # # ]: 0 : c->saveCharacteristics (freq);
1094 [ # # ][ # # ]: 0 : if (c->getSubcircuit () && !(saveCVs & SAVE_ALL)) continue;
[ # # ]
1095 [ # # ]: 0 : c->calcCharacteristics (freq);
1096 [ # # ]: 0 : valuelistiterator<characteristic> it (c->getCharacteristics ());
1097 [ # # ][ # # ]: 0 : for (; *it; ++it) {
1098 : 0 : characteristic * p = it.currentVal ();
1099 [ # # ][ # # ]: 0 : n = createCV (c->getName (), p->getName ());
[ # # ]
1100 [ # # ]: 0 : saveVariable (n, p->getValue (), f);
1101 : : }
1102 : 0 : }
1103 : 0 : }
1104 : :
1105 : : // properties
1106 : : PROP_REQ [] = {
1107 : : { "Type", PROP_STR, { PROP_NO_VAL, "lin" }, PROP_RNG_TYP },
1108 : : PROP_NO_PROP };
1109 : : PROP_OPT [] = {
1110 : : { "Noise", PROP_STR, { PROP_NO_VAL, "no" }, PROP_RNG_YESNO },
1111 : : { "NoiseIP", PROP_INT, { 1, PROP_NO_STR }, PROP_RNGII (1, MAX_PORTS) },
1112 : : { "NoiseOP", PROP_INT, { 2, PROP_NO_STR }, PROP_RNGII (1, MAX_PORTS) },
1113 : : { "Start", PROP_REAL, { 1e9, PROP_NO_STR }, PROP_POS_RANGE },
1114 : : { "Stop", PROP_REAL, { 10e9, PROP_NO_STR }, PROP_POS_RANGE },
1115 : : { "Points", PROP_INT, { 10, PROP_NO_STR }, PROP_MIN_VAL (2) },
1116 : : { "Values", PROP_LIST, { 10, PROP_NO_STR }, PROP_POS_RANGE },
1117 : : { "saveCVs", PROP_STR, { PROP_NO_VAL, "no" }, PROP_RNG_YESNO },
1118 : : { "saveAll", PROP_STR, { PROP_NO_VAL, "no" }, PROP_RNG_YESNO },
1119 : : PROP_NO_PROP };
1120 : : struct define_t spsolver::anadef =
1121 : : { "SP", 0, PROP_ACTION, PROP_NO_SUBSTRATE, PROP_LINEAR, PROP_DEF };
1122 : :
1123 : : } // namespace qucs
|