My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
qf_cauer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qf_cauer.cpp
3  ----------------
4  begin : Mon Jan 02 2006
5  copyright : (C) 2006 by Vincent Habchi, F5RCS
6  email : 10.50@free.fr
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 // Elliptic (Cauer) filters, odd order
19 
20 #include <math.h>
21 #include <stdlib.h>
22 #include <iostream>
23 #include <string>
24 
25 #ifndef M_1_PI
26 #define M_1_PI 0.3183098861837906715377675267450287
27 #endif
28 #ifndef M_LN2
29 #define M_LN2 0.6931471805599453094172321214581766
30 #endif
31 #ifndef M_PI
32 #define M_PI 3.1415926535897932384626433832795029
33 #endif
34 
35 #undef _QF_CAUER_DEBUG
36 //#define _QF_CAUER_DEBUG 1
37 
38 #include "qf_poly.h"
39 #include "qf_filter.h"
40 #include "qf_cauer.h"
41 
43  qf_filter (CAUER, LOWPASS, 1, M_1_PI / 2, 0), rho (r) {
44  ord = n;
45  th = sin (t);
46  a = new qf_double_t[ord + 1];
47  xfer ();
48  values ();
49  synth (LOWPASS);
50 }
51 
53  qf_double_t fs, qf_double_t r = 1, qf_double_t b = 0,
54  qft type = LOWPASS) :
55  qf_filter (CAUER, type, r, fc, b), a (NULL) {
56  if (amin > amax)
57  return;
58 
59  if (amin > 3 || amax < 3)
60  return;
61 
62  if ((fc > fs && type == LOWPASS) || (fc < fs && type == HIGHPASS))
63  return;
64 
65  if ((type == BANDPASS || type == BANDSTOP) &&
66  fabs (fs - (fc * fc) / fs) < bw)
67  return;
68 
69  normalize (amin, amax, fs, type);
70  xfer ();
71  values ();
72  synth (type);
73 }
74 
76  if (a != NULL)
77  delete[] a;
78 }
79 
80 static qf_double_t FMAX (qf_double_t x, qf_double_t y) {
81  return ((x > y) ? x : y);
82 }
83 
84 // This is extracted from "Handbook of mathematical functions"
85 // Edited by M. Abramowitz & I. A. Stegun
86 // U.S. National Bureau of Standards, June '64 / Dec. '72
87 
88 // K by the arithmetic/geometric mean (AGM) method
90  qf_double_t a = 1, b = sqrt (1 - k * k);
91  while (fabs (a - b) > K_ERR) {
92  qf_double_t t = a;
93  a = 0.5 * (a + b);
94  b = sqrt (t * b);
95  }
96  return M_PI / (2 * a);
97 }
98 
99 // sn (u, m) by descending Landen transforms
100 // m = k^2
102  if (m < SK_ERR) {
103  // Final approx.
104  return sin (u) - 0.25 * m * cos (u) * (u - 0.5 * sin (2 * u));
105  } else {
106  qf_double_t kp = sqrt (1 - m);
107  qf_double_t smu = (1 - kp) / (1 + kp);
108  qf_double_t v = u / (1 + smu);
109  // Recurse
110  qf_double_t sn1 = sn (v, smu * smu);
111  return (1 + smu) * sn1 / (1 + smu * sn1 * sn1);
112  }
113 }
114 
115 // Computes elliptic jacobi functions
116 // Adapted from: Numerical receipes in C, pp. 264 et seq.
117 
118 // Computes Carlson's elliptic integral of the first kind
120  qf_double_t alamb, ave, delx, dely, delz, e2, e3;
121  qf_double_t sqrtx, sqrty, sqrtz, xt, yt, zt;
122 
123  // Constants
124  const qf_double_t THIRD = 1.0 / 3.0;
125  const qf_double_t C1 = 1.0 / 24.0;
126  const qf_double_t C2 = 0.1;
127  const qf_double_t C3 = 3.0 / 44.0;
128  const qf_double_t C4 = 1.0 / 14.0;
129 
130  xt = x;
131  yt = y;
132  zt = z;
133  do {
134  sqrtx = sqrt (xt);
135  sqrty = sqrt (yt);
136  sqrtz = sqrt (zt);
137  alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz;
138  xt = 0.25 * (xt + alamb);
139  yt = 0.25 * (yt + alamb);
140  zt = 0.25 * (zt + alamb);
141  ave = THIRD * (xt + yt + zt);
142  delx = (ave - xt) / ave;
143  dely = (ave - yt) / ave;
144  delz = (ave - zt) / ave;
145  }
146  while (FMAX (FMAX (fabs (delx), fabs (dely)), fabs (delz)) > K_ERR1);
147 
148  e2 = delx * dely - delz * delz;
149  e3 = delx * dely * delz;
150  return (1 + (C1 * e2 - C2 - C3 * e3) * e2 + C4 * e3) / sqrt (ave);
151 }
152 
153 // K(k) = RF(0, 1 - k^2, 1) -> complete elliptic intergral of the 1st kind
155  return ellip_RF (0, 1 - k * k, 1);
156 }
157 
158 // K'(k) = K(sqrt(1 - k^2)), even for small k's
160  qf_double_t Kp;
161  qf_double_t f1 = 1, f2, w = 1;
162  qf_double_t kb = 1;
163 
164  Kp = f2 = 2 * M_LN2 - log (k); // K' = ln (4 / k')
165  while (kb > K_ERR2) {
166  kb *= k * k;
167  f1 *= (w / (w + 1));
168  f2 -= 2 / (w * (w + 1));
169  Kp += f1 * f2 * kb;
170  w += 2;
171  }
172  return Kp;
173 }
174 
175 // Compute the Jacobian elliptic functions sn(u,k), cn(u,k) and dn(u,k).
178  qf_double_t& sn, qf_double_t& cn, qf_double_t& dn) {
179  qf_double_t a, b, c, d, emc, u;
180  qf_double_t em[14], en[14];
181  int i, ii, l;
182  bool bo;
183 
184  emc = emmc;
185  d = 1 - emc;
186  u = uu;
187 
188  if (emc) {
189  bo = (emc < 0);
190  if (bo) {
191  emc /= -1 / d;
192  u *= (d = sqrt (d));
193  }
194  a = 1;
195  dn = 1;
196 
197  for (i = 1; i < 14; i++) {
198  l = i;
199  em[i] = a;
200  en[i] = (emc = sqrt (emc));
201  c = 0.5 * (a + emc);
202  if (fabs (a - emc) <= SN_ACC * a)
203  break;
204  emc *= a;
205  a = c;
206  }
207 
208  u *= c;
209  sn = sin (u);
210  cn = cos (u);
211 
212  if (sn) {
213  a = cn / sn;
214  c *= a;
215  for (ii = l; ii > 0; ii--) {
216  b = em[ii];
217  a *= c;
218  c *= dn;
219  dn = (en[ii] + a) / (b + a);
220  a = c / b;
221  }
222  a = 1 / sqrt (c * c + 1);
223  sn = (sn >= 0 ? a : -a);
224  cn = sn * c;
225  }
226 
227  if (bo) {
228  a = dn;
229  dn = cn;
230  cn = a;
231  sn /= d;
232  }
233  } else {
234  cn = 1 / cosh (u);
235  dn = cn;
236  sn = tanh (u);
237  }
238  return sn;
239 }
240 
242  qf_double_t sn, cn, dn;
243  return ellip_sncndn (x, 1 - k * k, sn, cn, dn);
244 }
245 
246 // Arc sin in degrees
247 static qf_double_t ASIND (qf_double_t ang) {
248  return (180 * asin (ang) / M_PI);
249 }
250 
251 // Normalize the filter parameters to Z = 1 O and w = 1 rad/s
252 // and computes order
254  qf_double_t fs, qft type) {
255  qf_double_t Amax = pow (10, -amin / 10);
256  qf_double_t Amin = pow (10, -amax / 10);
257  qf_double_t Aemax = 1 - Amin;
258  qf_double_t Aemin = 1 - Amax;
259  qf_double_t sAmin = -10 * log10 (Aemax) + amin;
260  qf_double_t sAmax = -10 * log10 (Aemin) + amax;
261  qf_double_t sdiff = sAmax - sAmin;
262 
263 #ifdef _QF_CAUER_DEBUG
264  std::cout << "amin + aemin = " << sAmin << " dB\n";
265  std::cout << "amax + aemax = " << sAmax << " dB\n";
266  std::cout << "D(a) = " << sdiff << " dB\n";
267 #endif
268 
269  qf_double_t kA = pow (10, -sdiff / 20);
270  qf_double_t KA;
271 
272  if (kA < 0.001)
273  KA = Kp (kA) / K (kA);
274  else
275  KA = K (sqrt (1 - kA * kA)) / K (kA);
276 
277  rho = sqrt (Aemin);
278 
279  switch (type) {
280  case LOWPASS:
281  th = fc / fs;
282  break;
283  case HIGHPASS:
284  th = fs / fc;
285  break;
286  case BANDPASS:
287  th = bw / fabs (fs - (fc * fc) / fs);
288  break;
289  case BANDSTOP :
290  th = fabs (fs * bw / (fs * fs - fc * fc));
291  bw = fabs (fs - (fc * fc) / fs);
292  break;
293  }
294 
295  // Calculate order
296  qf_double_t Kth = K (th) / K (sqrt (1 - th * th));
297  ord = (unsigned) ceil (Kth * KA);
298  if ((ord % 2) == 0)
299  ord++;
300 
301 #ifdef _QF_CAUER_DEBUG
302  std::cout << "K'/K = " << Kth << ", K1/K'1 = " << KA << '\n';
303  std::cout << "Order = " << ord << '\n';
304 #endif
305 
306  a = new qf_double_t[ord + 1];
307 }
308 
309 // A Cauer (or elliptic) filter has a symetric D(O)
310 // D(O) = F (O) / P (O) = K * O * Prod {(O^2 + a^2(i)) / (a^2(i) * O^2 + 1)}
311 // So that it is Chebichev in the passband and in the stopband
312 void qf_cauer::xfer (void) {
313  int m = (ord - 1) / 2;
314  qf_double_t Ws = a[ord] = sqrt (th);
315  qf_double_t k = K (th);
316  int u;
317 
318 #ifdef _QF_CAUER_DEBUG
319  std::cerr << "Computing filter of order " << ord << " with ";
320  std::cerr << "rho = " << rho << " and theta = " << ASIND (th) << "°\n";
321  std::cerr << "k = " << k << '\n';
322 #endif
323 
324  for (unsigned i = 1; i < ord; i++) {
326  a[i] = Ws * sn (j * k, th);
327 #ifdef _QF_CAUER_DEBUG
328  std::cerr << "a(" << i << ") = " << a[i] << '\n';
329 #endif
330  }
331 
332 #ifdef _QF_CAUER_DEBUG
333  std::cerr << "Norm. puls. (Ws) = " << Ws << '\n';
334 #endif
335 
336  qf_double_t delta = 1;
337  for (u = 1; u < m + 2; u++)
338  delta *= a[2 * u - 1] * a[2 * u - 1];
339  delta /= Ws;
340  qf_double_t c = delta * sqrt (1 / (rho * rho) - 1);
341 
342 #ifdef _QF_CAUER_DEBUG
343  std::cerr << "D = " << delta << '\n';
344  std::cerr << "c = " << c << '\n';
345 #endif
346 
347  // Computes D
348  F = qf_poly (1, 0, 0, 1); // F(X) = X
349  P = qf_poly (c, 0, 0, 0); // P(X) = c
350 
351  for (u = 1; u < m + 1; u++) {
352  qf_poly MF (1, 0, a[2 * u] * a[2 * u], 2);
353  qf_poly MP (a[2 * u] * a[2 * u], 0, 1, 2);
354 
355  MF.disp ("MF");
356  MP.disp ("MP");
357  F *= MF;
358  P *= MP;
359  }
360 
361  F.disp ("F");
362  P.disp ("P");
363 
364  // E(x) * E(-x) = F(x) * F(-x) + P(x) * P(-x)
365  E = F.hsq () + P.hsq ();
366 
367  E.disp ("E");
368  E.hurw ();
369  E.disp ("E");
370 
371  BN = E.odd () + F.odd ();
372  BD = E.even () - F.even ();
373 }
374 
375 void qf_cauer::values (void) {
376 
377  ncomp = (3 * ord) / 2;
378  Comp = (qfc *) malloc (sizeof (qfc) * ncomp);
379 
380  // For each zero of transmission, we apply the method as in
381  // Saal & Ulbrich p. 288 or Zverev pp. 129 et seq.
382  qf_double_t Ws = sqrt (th);
383  for (unsigned k = 0, l = 2; k < (ncomp - 1); k += 3) {
384 #ifdef _QF_CAUER_DEBUG
385  std::cerr << "Pole (" << l << ") = " << (1 / (a[l] * Ws)) << "\n";
386 #endif
387  extract_pole_pCsLC (1 / a[l], &Comp[k], Ws);
388 
389  // Zeros mangeling
390  l = ord - l + 1;
391  if (l < (ord + 1) / 2)
392  l += 2;
393  }
394 
395  // Final removal of inifite pole
396  Comp[ncomp - 1].val = Ws * BN.eval (1) / BD.eval (1);
397 }
398 
399 void qf_cauer::synth (qft type) {
400  qf_double_t cnrm = 1 / (2 * M_PI * fc * imp);
401  qf_double_t lnrm = imp / (2 * M_PI * fc);
402  unsigned i, node;
403 
404  switch (type) {
405  case LOWPASS:
406  for (i = 0, node = 1;;) {
407  Comp[i].comp = CAP; // Parallel capa first
408  Comp[i].val *= cnrm; // and last !
409  Comp[i].node1 = node;
410  Comp[i].node2 = 0;
411  i++;
412  if (i == ncomp)
413  break;
414  Comp[i].comp = CAP; // Resonator cap
415  Comp[i].val *= cnrm;
416  Comp[i].node1 = node;
417  Comp[i].node2 = node + 1;
418  i++;
419  Comp[i].comp = IND;
420  Comp[i].val *= lnrm;
421  Comp[i].node1 = node;
422  Comp[i].node2 = ++node;
423  i++;
424  }
425  break;
426  case HIGHPASS:
427  // First comes a serial cap
428  Comp[0].comp = CAP;
429  Comp[0].node1 = 1;
430  Comp[0].node2 = 2;
431  Comp[0].val = cnrm / Comp[0].val;
432  for (i = 1, node = 2; i < ncomp;) {
433  // Then a serial resonant circuit
434  Comp[i].comp = CAP;
435  Comp[i].node1 = node;
436  Comp[i].node2 = node + 1;
437  Comp[i].val = cnrm / Comp[i].val;
438  i++;
439  Comp[i].comp = IND;
440  Comp[i].node1 = node + 1;
441  Comp[i].node2 = 0;
442  Comp[i].val = lnrm / Comp[i].val;
443  i++;
444  // Then another serial cap
445  Comp[i].comp = CAP;
446  Comp[i].node1 = node;
447  node += 2;
448  Comp[i].node2 = node;
449  Comp[i].val = cnrm / Comp[i].val;
450  i++;
451  }
452  break;
453  case BANDPASS: {
454  // We double the number of components
455  ncomp = 3 * ord - 1;
456  qfc * Comp2 = (qfc *) malloc (sizeof (qfc) * ncomp);
457  qf_double_t q = fc / bw;
458 
459  for (unsigned i = 0, j = 0, node = 1;;) {
460  Comp2[j].comp = CAP;
461  Comp2[j].node1 = node;
462  Comp2[j].node2 = 0;
463  Comp2[j++].val = Comp[i].val * q * cnrm;
464 
465  Comp2[j].comp = IND;
466  Comp2[j].node1 = node;
467  Comp2[j].node2 = 0;
468  Comp2[j++].val = lnrm / (q * Comp[i++].val);
469  if (j == ncomp)
470  break;
471 
472  qf_double_t c = Comp[i].val;
473  qf_double_t l = Comp[i + 1].val;
474  qf_double_t iw2 = l * c;
475 #ifdef _QF_CAUER_DEBUG
476  std::cout << "O(inf) = " << sqrt (1 / iw2) << '\n';
477 #endif
478  qf_double_t b = sqrt (1 + 4 * q * q * iw2);
479 
480 #ifdef _QF_CAUER_DEBUG
481  std::cout << "b = " << b << '\n';
482 #endif
483 
484  Comp2[j + 1].comp = IND;
485  Comp2[j + 1].node1 = node;
486  Comp2[j + 1].node2 = node + 1;
487  Comp2[j + 1].val = (b - 1) / (q * c * 2 * b);
488 
489  Comp2[j + 3].comp = IND;
490  Comp2[j + 3].node1 = node + 1;
491  Comp2[j + 3].node2 = node + 2;
492  Comp2[j + 3].val = (b + 1) / (q * c * 2 * b);
493 
494  Comp2[j].comp = CAP;
495  Comp2[j].node1 = node;
496  Comp2[j].node2 = node + 1;
497  Comp2[j].val = 1 / Comp2[j + 3].val;
498 
499  Comp2[j + 2].comp = CAP;
500  Comp2[j + 2].node1 = node + 1;
501  Comp2[j + 2].node2 = node + 2;
502  Comp2[j + 2].val = 1 / Comp2[j + 1].val;
503  Comp2[j].val *= cnrm;
504  Comp2[j + 2].val *= cnrm;
505  Comp2[j + 1].val *= lnrm;
506  Comp2[j + 3].val *= lnrm;
507  j += 4;
508  i += 2;
509  node += 2;
510  }
511 
512  delete[] Comp;
513  Comp = Comp2;
514  break;
515  }
516  case BANDSTOP: {
517  ncomp = 3 * ord - 1;
518  qfc * Comp2 = (qfc *) malloc (sizeof (qfc) * ncomp);
519  qf_double_t q = fc / bw;
520 
521  for (unsigned i = 0, j = 0, node = 1;;) {
522  Comp2[j].comp = CAP;
523  Comp2[j].node1 = node;
524  Comp2[j].node2 = node + 1;
525  Comp2[j++].val = cnrm * Comp[i].val / q;
526 
527  Comp2[j].comp = IND;
528  Comp2[j].node1 = node + 1;
529  Comp2[j].node2 = 0;
530  Comp2[j++].val = lnrm * q / Comp[i++].val;
531  if (j == ncomp)
532  break;
533 
534  qf_double_t c = Comp[i].val;
535  qf_double_t l = Comp[i + 1].val;
536  qf_double_t w2 = 1 / (l * c);
537 #ifdef _QF_CAUER_DEBUG
538  std::cout << "O(inf) = " << sqrt (w2) << "; q = " << q << '\n';
539 #endif
540  qf_double_t b = sqrt (1 + 4 * q * q * w2);
541 
542 #ifdef _QF_CAUER_DEBUG
543  std::cout << "b = " << b << '\n';
544 #endif
545  Comp2[j + 1].comp = IND;
546  Comp2[j + 1].node1 = node;
547  Comp2[j + 1].node2 = node + 2;
548  Comp2[j + 1].val = l * (b - 1) / (2 * b * q);
549 
550  Comp2[j + 3].comp = IND;
551  Comp2[j + 3].node1 = node + 2;
552  Comp2[j + 3].node2 = node + 3;
553  Comp2[j + 3].val = l * (b + 1) / (2 * b * q);
554 
555  Comp2[j].comp = CAP;
556  Comp2[j].node1 = node;
557  Comp2[j].node2 = node + 2;
558  Comp2[j].val = 1 / Comp2[j + 3].val;
559 
560  Comp2[j + 2].comp = CAP;
561  Comp2[j + 2].node1 = node + 2;
562  Comp2[j + 2].node2 = node + 3;
563  Comp2[j + 2].val = 1 / Comp2[j + 1].val;
564  Comp2[j].val *= cnrm;
565  Comp2[j + 2].val *= cnrm;
566  Comp2[j + 1].val *= lnrm;
567  Comp2[j + 3].val *= lnrm;
568  j += 4;
569  i += 2;
570  node += 3;
571  }
572 
573  delete[] Comp;
574  Comp = Comp2;
575  break;
576  }
577  }
578 }
579 
580 void qf_cauer::dump () {
581  if (Comp == NULL)
582  return; // Not initialized
583 
584  std::cout << "qf_cauer::dump ()\n";
585 
586  switch (type) {
587  case LOWPASS:
588  std::cout << "Lowpass ";
589  break;
590  case HIGHPASS:
591  std::cout << "Highpass ";
592  break;
593  case BANDPASS:
594  std::cout << "Bandpass ";
595  break;
596  case BANDSTOP:
597  std::cout << "Bandstop ";
598  break;
599  }
600  std::cout << "of order " << ord << ", theta = "
601  << ASIND (th) << "°, rho = " << rho << '\n';
602  dump_cout ();
603 }
604 
605 void CC (void) {
606  unsigned o;
607  qf_double_t t, r;
608 
609  do {
610  std::cout << "Order : ";
611  std::cin >> o;
612  if (o == 0)
613  return;
614  std::cout << "Reflexion (%) : ";
615  std::cin >> r;
616  r /= 100.0;
617  std::cout << "Angle (°) : ";
618  std::cin >> t;
619  t = M_PI * t / 180.0;
620  qf_cauer F (o, r, t);
621  F.dump ();
622  }
623  while (true);
624 }
625 
626 #if TEST
627 int main (void) {
628  qf_double_t amin, amax, fc, fs, bw, r;
629 
630  while (true) {
631  std::cout << "Enter cutoff (Hz) [0 to end]: ";
632  std::cin >> fc;
633  if (fc == 0)
634  return 0;
635  std::cout << "Enter ripple in passband (dB): ";
636  std::cin >> amin;
637  std::cout << "Enter stopband frequency (Hz): ";
638  std::cin >> fs;
639  std::cout << "Enter minimal attenuation in stopband (dB): ";
640  std::cin >> amax;
641  std::cout << "Enter bandwith (0 for high- or low-pass) (Hz): ";
642  std::cin >> bw;
643  std::cout << "Enter reference impedance (Ohm): ";
644  std::cin >> r;
645 
646  if (bw == 0) {
647  if (fs > fc) {
648  qf_cauer F (amin, amax, fc, fs, r, 0, LOWPASS);
649  F.dump ();
650  }
651  else {
652  qf_cauer F (amin, amax, fc, fs, r, 0, HIGHPASS);
653  F.dump ();
654  }
655  }
656 
657  else {
658  if (amin < amax) {
659  qf_cauer F (amin, amax, fc, fs, r, bw, BANDPASS);
660  F.dump ();
661  }
662  else {
663  qf_cauer F (amax, amin, fc, fs, r, bw, BANDSTOP);
664  F.dump ();
665  }
666  }
667  }
668 }
669 #endif /* TEST */