My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
cpwline.cpp
Go to the documentation of this file.
1 /*
2  * cpwline.cpp - coplanar waveguide line class implementation
3  *
4  * Copyright (C) 2004, 2005 Vincent Habchi, F5RCS <10.50@free.fr>
5  * Copyright (C) 2004, 2005, 2006, 2008 Stefan Jahn <stefan@lkcc.org>
6  *
7  * This is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this package; see the file COPYING. If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * $Id: cpwline.cpp 1825 2011-03-11 20:42:14Z ela $
23  *
24  */
25 
26 #if HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29 
30 #include "component.h"
31 #include "substrate.h"
32 #include "cpwline.h"
33 
34 cpwline::cpwline () : circuit (2) {
35  Zl = Er = 0;
36  type = CIR_CPWLINE;
37 }
38 
39 /* The function computes the complete elliptic integral of first kind
40  K() and the second kind E() using the arithmetic-geometric mean
41  algorithm (AGM) by Abramowitz and Stegun. */
42 void cpwline::ellipke (nr_double_t arg, nr_double_t &k, nr_double_t &e) {
43  int iMax = 16;
44  if (arg == 1.0) {
45  k = NR_INF; // infinite
46  e = 0;
47  }
48  else if (isinf (arg) && arg < 0) {
49  k = 0;
50  e = NR_INF; // infinite
51  }
52  else {
53  nr_double_t a, b, c, f, s, fk = 1, fe = 1, t, da = arg;
54  int i;
55  if (arg < 0) {
56  fk = 1 / sqrt (1 - arg);
57  fe = sqrt (1 - arg);
58  da = -arg / (1 - arg);
59  }
60  a = 1;
61  b = sqrt (1 - da);
62  c = sqrt (da);
63  f = 0.5;
64  s = f * c * c;
65  for (i = 0; i < iMax; i++) {
66  t = (a + b) / 2;
67  c = (a - b) / 2;
68  b = sqrt (a * b);
69  a = t;
70  f *= 2;
71  s += f * c * c;
72  if (c / a < NR_EPSI) break;
73  }
74  if (i >= iMax) {
75  k = 0; e = 0;
76  }
77  else {
78  k = M_PI_2 / a;
79  e = M_PI_2 * (1 - s) / a;
80  if (arg < 0) {
81  k *= fk;
82  e *= fe;
83  }
84  }
85  }
86 }
87 
88 /* We need to know only K(k), and if possible KISS. */
89 nr_double_t cpwline::ellipk (nr_double_t k) {
90  nr_double_t r, lost;
91  ellipke (k, r, lost);
92  return r;
93 }
94 
95 /* More or less accurate approximation of K(k)/K'(k). Suggested by
96  publications dealing with coplanar components. */
97 nr_double_t cpwline::ellipa (nr_double_t k) {
98  nr_double_t r, kp;
99  if (k < M_SQRT1_2) {
100  kp = sqrt (1 - k * k);
101  r = M_PI / log (2 * (1 + sqrt (kp)) / (1 - sqrt (kp)));
102  }
103  else {
104  r = log (2 * (1 + sqrt (k)) / (1 - sqrt (k))) / M_PI;
105  }
106  return r;
107 }
108 
109 void cpwline::initSP (void) {
110  // allocate S-parameter matrix
111  allocMatrixS ();
112  // pre-compute propagation factors
113  initPropagation ();
114 }
115 
116 void cpwline::initPropagation (void) {
117  // get properties of substrate and coplanar line
118  nr_double_t W = getPropertyDouble ("W");
119  nr_double_t s = getPropertyDouble ("S");
120  substrate * subst = getSubstrate ();
121  nr_double_t er = subst->getPropertyDouble ("er");
122  nr_double_t h = subst->getPropertyDouble ("h");
123  nr_double_t t = subst->getPropertyDouble ("t");
124  int backMetal = !strcmp (getPropertyString ("Backside"), "Metal");
125  int approx = !strcmp (getPropertyString ("Approx"), "yes");
126 
127  tand = subst->getPropertyDouble ("tand");
128  rho = subst->getPropertyDouble ("rho");
129  len = getPropertyDouble ("L");
130 
131  // other local variables (quasi-static constants)
132  nr_double_t k1, kk1, kpk1, k2, k3, q1, q2, q3 = 0, qz, er0 = 0;
133 
134  // compute the necessary quasi-static approx. (K1, K3, er(0) and Z(0))
135  k1 = W / (W + s + s);
136  kk1 = ellipk (k1);
137  kpk1 = ellipk (sqrt (1 - k1 * k1));
138  if (approx) {
139  q1 = ellipa (k1);
140  } else {
141  q1 = kk1 / kpk1;
142  }
143 
144  // backside is metal
145  if (backMetal) {
146  k3 = tanh ((M_PI / 4) * (W / h)) / tanh ((M_PI / 4) * (W + s + s) / h);
147  if (approx) {
148  q3 = ellipa (k3);
149  } else {
150  q3 = ellipk (k3) / ellipk (sqrt (1 - k3 * k3));
151  }
152  qz = 1 / (q1 + q3);
153  er0 = 1 + q3 * qz * (er - 1);
154  zl_factor = Z0 / 2 * qz;
155  }
156  // backside is air
157  else {
158  k2 = sinh ((M_PI / 4) * (W / h)) / sinh ((M_PI / 4) * (W + s + s) / h);
159  if (approx) {
160  q2 = ellipa (k2);
161  } else {
162  q2 = ellipk (k2) / ellipk (sqrt (1 - k2 * k2));
163  }
164  er0 = 1 + (er - 1) / 2 * q2 / q1;
165  zl_factor = Z0 / 4 / q1;
166  }
167 
168  // adds effect of strip thickness
169  if (t > 0) {
170  nr_double_t d, se, We, ke, qe;
171  d = (t * 1.25 / M_PI) * (1 + log (4 * M_PI * W / t));
172  se = s - d;
173  We = W + d;
174 
175  // modifies k1 accordingly (k1 = ke)
176  ke = We / (We + se + se); // ke = k1 + (1 - k1 * k1) * d / 2 / s;
177  if (approx) {
178  qe = ellipa (ke);
179  } else {
180  qe = ellipk (ke) / ellipk (sqrt (1 - ke * ke));
181  }
182  // backside is metal
183  if (backMetal) {
184  qz = 1 / (qe + q3);
185  er0 = 1 + q3 * qz * (er - 1);
186  zl_factor = Z0 / 2 * qz;
187  }
188  // backside is air
189  else {
190  zl_factor = Z0 / 4 / qe;
191  }
192 
193  // modifies er0 as well
194  er0 = er0 - (0.7 * (er0 - 1) * t / s) / (q1 + (0.7 * t / s));
195  }
196 
197  // pre-compute square roots
198  sr_er = sqrt (er);
199  sr_er0 = sqrt (er0);
200 
201  // cut-off frequency of the TE0 mode
202  fte = (C0 / 4) / (h * sqrt (er - 1));
203 
204  // dispersion factor G
205  nr_double_t p = log (W / h);
206  nr_double_t u = 0.54 - (0.64 - 0.015 * p) * p;
207  nr_double_t v = 0.43 - (0.86 - 0.54 * p) * p;
208  G = exp (u * log (W / s) + v);
209 
210  // loss constant factors (computed only once for efficency sake)
211  nr_double_t ac = 0;
212  if (t > 0) {
213  // equations by GHIONE
214  nr_double_t n = (1 - k1) * 8 * M_PI / (t * (1 + k1));
215  nr_double_t a = W / 2;
216  nr_double_t b = a + s;
217  ac = (M_PI + log (n * a)) / a + (M_PI + log (n * b)) / b;
218  }
219  ac_factor = ac / (4 * Z0 * kk1 * kpk1 * (1 - k1 * k1));
220  ac_factor *= sqrt (M_PI * MU0 * rho); // Rs factor
221  ad_factor = (er / (er - 1)) * tand * M_PI / C0;
222 
223  bt_factor = 2 * M_PI / C0;
224 }
225 
226 void cpwline::calcAB (nr_double_t f, nr_double_t& zl, nr_double_t& al,
227  nr_double_t& bt) {
228  nr_double_t sr_er_f = sr_er0;
229  nr_double_t ac = ac_factor;
230  nr_double_t ad = ad_factor;
231 
232  // by initializing as much as possible outside this function, the
233  // overhead is minimal
234 
235  // add the dispersive effects to er0
236  sr_er_f += (sr_er - sr_er0) / (1 + G * pow (f / fte, -1.8));
237 
238  // computes impedance
239  zl /= sr_er_f;
240 
241  // for now, the loss are limited to strip losses (no radiation
242  // losses yet) losses in neper/length
243  ad *= f * (sr_er_f - 1 / sr_er_f);
244  ac *= sqrt (f) * sr_er0;
245 
246  al = ac + ad;
247  bt *= sr_er_f * f;
248 
249  Er = sqr (sr_er_f);
250  Zl = zl;
251 }
252 
253 void cpwline::saveCharacteristics (nr_double_t) {
254  setCharacteristic ("Zl", Zl);
255  setCharacteristic ("Er", Er);
256 }
257 
258 void cpwline::calcSP (nr_double_t frequency) {
259 
260  nr_double_t zl = zl_factor;
261  nr_double_t beta = bt_factor;
262  nr_double_t alpha;
263 
264  calcAB (frequency, zl, alpha, beta);
265 
266  // calculate and set S-parameters
267  nr_double_t z = zl / z0;
268  nr_double_t y = 1 / z;
269  nr_complex_t g = rect (alpha, beta);
270  nr_complex_t n = 2.0 * cosh (g * len) + (z + y) * sinh (g * len);
271  nr_complex_t s11 = (z - y) * sinh (g * len) / n;
272  nr_complex_t s21 = 2.0 / n;
273 
274  setS (NODE_1, NODE_1, s11); setS (NODE_2, NODE_2, s11);
275  setS (NODE_1, NODE_2, s21); setS (NODE_2, NODE_1, s21);
276 }
277 
278 /* The function calculates the quasi-static impedance of a coplanar
279  waveguide line and the value of the effective dielectric constant
280  for the given coplanar line and substrate properties. */
281 void cpwline::analyseQuasiStatic (nr_double_t W, nr_double_t s, nr_double_t h,
282  nr_double_t t, nr_double_t er, int backMetal,
283  nr_double_t& ZlEff, nr_double_t& ErEff) {
284 
285  // local variables (quasi-static constants)
286  nr_double_t k1, k2, k3, q1, q2, q3 = 0, qz;
287 
288  ErEff = er;
289  ZlEff = 0;
290 
291  // compute the necessary quasi-static approx. (K1, K3, er(0) and Z(0))
292  k1 = W / (W + s + s);
293  q1 = ellipk (k1) / ellipk (sqrt (1 - k1 * k1));
294 
295  // backside is metal
296  if (backMetal) {
297  k3 = tanh ((M_PI / 4) * (W / h)) / tanh ((M_PI / 4) * (W + s + s) / h);
298  q3 = ellipk (k3) / ellipk (sqrt (1 - k3 * k3));
299  qz = 1 / (q1 + q3);
300  ErEff = 1 + q3 * qz * (er - 1);
301  ZlEff = Z0 / 2 * qz;
302  }
303  // backside is air
304  else {
305  k2 = sinh ((M_PI / 4) * (W / h)) / sinh ((M_PI / 4) * (W + s + s) / h);
306  q2 = ellipk (k2) / ellipk (sqrt (1 - k2 * k2));
307  ErEff = 1 + (er - 1) / 2 * q2 / q1;
308  ZlEff = Z0 / 4 / q1;
309  }
310 
311  // adds effect of strip thickness
312  if (t > 0) {
313  nr_double_t d, se, We, ke, qe;
314  d = (t * 1.25 / M_PI) * (1 + log (4 * M_PI * W / t));
315  se = s - d;
316  We = W + d;
317 
318  // modifies k1 accordingly (k1 = ke)
319  ke = We / (We + se + se); // ke = k1 + (1 - k1 * k1) * d / 2 / s;
320  qe = ellipk (ke) / ellipk (sqrt (1 - ke * ke));
321 
322  // backside is metal
323  if (backMetal) {
324  qz = 1 / (qe + q3);
325  ErEff = 1 + q3 * qz * (er - 1);
326  ZlEff = Z0 / 2 * qz;
327  }
328  // backside is air
329  else {
330  ZlEff = Z0 / 4 / qe;
331  }
332 
333  // modifies ErEff as well
334  ErEff = ErEff - (0.7 * (ErEff - 1) * t / s) / (q1 + (0.7 * t / s));
335  }
336  ErEff = sqrt (ErEff);
337  ZlEff /= ErEff;
338 }
339 
340 /* This function calculates the frequency dependent value of the
341  effective dielectric constant and the coplanar line impedance for
342  the given frequency. */
343 void cpwline::analyseDispersion (nr_double_t W, nr_double_t s, nr_double_t h,
344  nr_double_t er, nr_double_t ZlEff,
345  nr_double_t ErEff, nr_double_t frequency,
346  nr_double_t& ZlEffFreq,
347  nr_double_t& ErEffFreq) {
348  // local variables
349  nr_double_t fte, G;
350 
351  ErEffFreq = ErEff;
352  ZlEffFreq = ZlEff * ErEff;
353 
354  // cut-off frequency of the TE0 mode
355  fte = (C0 / 4) / (h * sqrt (er - 1));
356 
357  // dispersion factor G
358  nr_double_t p = log (W / h);
359  nr_double_t u = 0.54 - (0.64 - 0.015 * p) * p;
360  nr_double_t v = 0.43 - (0.86 - 0.54 * p) * p;
361  G = exp (u * log (W / s) + v);
362 
363  // add the dispersive effects to er0
364  ErEffFreq += (sqrt (er) - ErEff) / (1 + G * pow (frequency / fte, -1.8));
365 
366  // computes impedance
367  ZlEffFreq /= ErEffFreq;
368 }
369 
370 void cpwline::calcNoiseSP (nr_double_t) {
371  // calculate noise using Bosma's theorem
372  nr_double_t T = getPropertyDouble ("Temp");
373  matrix s = getMatrixS ();
374  matrix e = eye (getSize ());
375  setMatrixN (kelvin (T) / T0 * (e - s * transpose (conj (s))));
376 }
377 
378 void cpwline::initDC (void) {
379  // a DC short (voltage source V = 0 volts)
380  setVoltageSources (1);
382  allocMatrixMNA ();
383  clearY ();
385 }
386 
387 void cpwline::initTR (void) {
388  initDC ();
389 }
390 
391 void cpwline::initAC (void) {
392  setVoltageSources (0);
393  allocMatrixMNA ();
394  initPropagation ();
395 }
396 
397 void cpwline::calcAC (nr_double_t frequency) {
398 
399  nr_double_t zl = zl_factor;
400  nr_double_t beta = bt_factor;
401  nr_double_t alpha;
402 
403  calcAB (frequency, zl, alpha, beta);
404 
405  // calculate and set Y-parameters
406  nr_complex_t g = rect (alpha, beta);
407  nr_complex_t y11 = coth (g * len) / zl;
408  nr_complex_t y21 = -cosech (g * len) / zl;
409 
410  setY (NODE_1, NODE_1, y11); setY (NODE_2, NODE_2, y11);
411  setY (NODE_1, NODE_2, y21); setY (NODE_2, NODE_1, y21);
412 }
413 
414 void cpwline::calcNoiseAC (nr_double_t) {
415  // calculate noise using Bosma's theorem
416  nr_double_t T = getPropertyDouble ("Temp");
417  setMatrixN (4 * kelvin (T) / T0 * real (getMatrixY ()));
418 }
419 
420 // properties
421 PROP_REQ [] = {
422  { "W", PROP_REAL, { 1e-3, PROP_NO_STR }, PROP_POS_RANGE },
423  { "S", PROP_REAL, { 1e-3, PROP_NO_STR }, PROP_POS_RANGE },
424  { "L", PROP_REAL, { 10e-3, PROP_NO_STR }, PROP_POS_RANGE },
425  { "Subst", PROP_STR, { PROP_NO_VAL, "Subst1" }, PROP_NO_RANGE },
426  PROP_NO_PROP };
427 PROP_OPT [] = {
428  { "Temp", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
429  { "Backside", PROP_STR, { PROP_NO_VAL, "Metal" },
430  PROP_RNG_STR2 ("Metal", "Air") },
431  { "Approx", PROP_STR, { PROP_NO_VAL, "no" }, PROP_RNG_YESNO },
432  PROP_NO_PROP };
433 struct define_t cpwline::cirdef =