My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
lc_filter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  lc_filter.cpp
3  ---------------
4  begin : Wed Mar 02 2005
5  copyright : (C) 2005 by Toyoyuki Ishikawa
6  (C) 2005 by Vincent Habchi, F5RCS
7  (C) 2005 by Michael Margraf
8  ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 
23 #include "lc_filter.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 
28 #include <qstring.h>
29 
30 #ifndef M_PI
31 #define M_PI 3.14159265358979323846
32 #endif
33 
35 {
36 }
37 
39 {
40 }
41 
42 // #########################################################################
43 QString LC_Filter::num2str(double Num)
44 {
45  char c = 0;
46  double cal = fabs(Num);
47  if(cal > 1e-20) {
48  cal = log10(cal) / 3.0;
49  if(cal < -0.2) cal -= 0.98;
50  int Expo = int(cal);
51 
52  if(Expo >= -5) if(Expo <= 4)
53  switch(Expo) {
54  case -5: c = 'f'; break;
55  case -4: c = 'p'; break;
56  case -3: c = 'n'; break;
57  case -2: c = 'u'; break;
58  case -1: c = 'm'; break;
59  case 1: c = 'k'; break;
60  case 2: c = 'M'; break;
61  case 3: c = 'G'; break;
62  case 4: c = 'T'; break;
63  }
64 
65  if(c) Num /= pow(10.0, double(3*Expo));
66  }
67 
68  QString Str = QString::number(Num, 'g', 4);
69  if(c) Str += c;
70 
71  return Str;
72 }
73 
74 
75 double BesselCoef[18][23] = {
76 
77 /* 2 */ {0.57550275, 2.1478055, 0.0, 0.0, 0.0,
78  0.0, 0.0, 0.0, 0.0, 0.0,
79  0.0, 0.0, 0.0, 0.0, 0.0,
80  0.0, 0.0, 0.0, 0.0,},
81 
82 /* 3 */ {0.33742149, 0.97051182, 2.2034114, 0.0, 0.0,
83  0.0, 0.0, 0.0, 0.0, 0.0,
84  0.0, 0.0, 0.0, 0.0, 0.0,
85  0.0, 0.0, 0.0, 0.0,},
86 
87 /* 4 */ {0.2334158, 0.67252481, 1.0815161, 2.2403786, 0.0,
88  0.0, 0.0, 0.0, 0.0, 0.0,
89  0.0, 0.0, 0.0, 0.0, 0.0,
90  0.0, 0.0, 0.0, 0.0,},
91 
92 /* 5 */ {0.17431938, 0.50724063, 0.80401117, 1.1110332, 2.2582171,
93  0.0, 0.0, 0.0, 0.0, 0.0,
94  0.0, 0.0, 0.0, 0.0, 0.0,
95  0.0, 0.0, 0.0, 0.0,},
96 
97 /* 6 */ {0.13649238, 0.40018984, 0.6391554, 0.85378587, 1.112643,
98  2.2645236, 0.0, 0.0, 0.0, 0.0,
99  0.0, 0.0, 0.0, 0.0, 0.0,
100  0.0, 0.0, 0.0, 0.0,},
101 
102 /* 7 */ {0.11056245, 0.32588813, 0.52489273, 0.70200915, 0.86902684,
103  1.1051644, 2.2659006, 0.0, 0.0, 0.0,
104  0.0, 0.0, 0.0, 0.0, 0.0,
105  0.0, 0.0, 0.0, 0.0,},
106 
107 /* 8 */ {0.091905558, 0.27191069, 0.44092213, 0.59357268, 0.73025665,
108  0.86950037, 1.0955593, 2.2656071, 0.0, 0.0,
109  0.0, 0.0, 0.0, 0.0, 0.0,
110  0.0, 0.0, 0.0, 0.0,},
111 
112 /* 9 */ {0.077965506, 0.23129119, 0.37698651, 0.5107787, 0.63059516,
113  0.74073299, 0.86387345, 1.0862838, 2.2648789, 0.0,
114  0.0, 0.0, 0.0, 0.0, 0.0,
115  0.0, 0.0, 0.0, 0.0,},
116 
117 /* 10 */ {0.067229245, 0.19984023, 0.32699699, 0.44543381, 0.55281473,
118  0.64933545, 0.74201735, 0.85607238, 1.0780948, 2.2641262,
119  0.0, 0.0, 0.0, 0.0, 0.0,
120  0.0, 0.0, 0.0, 0.0,},
121 
122 /* 11 */ {0.058753264, 0.1749102, 0.28706331, 0.39272513, 0.48983467,
123  0.57742576, 0.6573719, 0.73864992, 0.84789472, 1.0711184,
124  2.2634675, 0.0, 0.0, 0.0, 0.0,
125  0.0, 0.0, 0.0, 0.0,},
126 
127 /* 12 */ {0.051923, 0.15475798, 0.25458412, 0.34949045, 0.43777572,
128  0.51824604, 0.59097358, 0.65913453, 0.73309605, 0.84013853,
129  1.0652583, 2.2629233, 0.0, 0.0, 0.0,
130  0.0, 0.0, 0.0, 0.0,},
131 
132 /* 13 */ {0.046323098, 0.13819538, 0.22775963, 0.31352943, 0.39412779,
133  0.46842754, 0.53589822, 0.59744624, 0.65727966, 0.72670805,
134  0.83311934, 1.0603538, 2.2624829, 0.0, 0.0,
135  0.0, 0.0, 0.0, 0.0,},
136 
137 /* 14 */ {0.041663913, 0.12438811, 0.20530976, 0.28325704, 0.35711858,
138  0.42591257, 0.48895292, 0.54624778, 0.59939971, 0.6534283,
139  0.72022065, 0.82691896, 1.0562406, 2.262128, 0.0,
140  0.0, 0.0, 0.0, 0.0,},
141 
142 /* 15 */ {0.037738195, 0.11273464, 0.186303, 0.25750279, 0.32543754,
143  0.38927393, 0.44833655, 0.5022966, 0.55162099, 0.59850238,
144  0.64857809, 0.71401822, 0.8215094, 1.0527729, 2.2618408,
145  0.0, 0.0, 0.0, 0.0},
146 
147 /* 16 */ {0.03439122, 0.102799, 0.1700389, 0.23539257, 0.29808411,
148  0.35745153, 0.41285998, 0.4638916, 0.51051757, 0.55360981,
149  0.59585789, 0.64334149, 0.70828423, 0.81681581, 1.0498286,
150  2.2616066, 0.0, 0.0, 0.0},
151 
152 /* 17 */ {0.031523496, 0.094214373, 0.1560301, 0.21622781, 0.27429733,
153  0.32962199, 0.38165374, 0.42998671, 0.47437364, 0.51507678,
154  0.55331904, 0.59220181, 0.63808843, 0.70308744, 0.81274832,
155  1.0473087, 2.2614134, 0.0, 0.0},
156 
157 /* 18 */ {0.028450351, 0.088289337, 0.14199897, 0.20112875, 0.25237658,
158  0.30571616, 0.35380272, 0.39992872, 0.44218287, 0.48110224,
159  0.5170226, 0.55152405, 0.58802463, 0.63303427, 0.69843256,
160  0.80921764, 1.0451338, 2.2612522, 0.0},
161 
162 /* 19 */ {0.027944243, 0.077389235, 0.13716781, 0.18087554, 0.23831545,
163  0.28142035, 0.33055916, 0.37249664, 0.41348365, 0.45066049,
164  0.48503552, 0.51711998, 0.54877223, 0.58365096, 0.62829555,
165  0.69429034, 0.8061422, 1.043241, 2.2611162}
166 };
167 
168 // Calculate normalized component value for Bessel filter
169 double LC_Filter::BesselValue(int No, int Order)
170 {
171  return BesselCoef [Order - 2] [No];
172 }
173 
174 
175 // Calculate normalized component value for Butterworth filter
176 double LC_Filter::ButterworthValue(int No, int Order)
177 {
178  return 2.0 * sin(double(2*No + 1) / double(2*Order) * M_PI);
179 }
180 
181 
182 // Calculate normalized component value for Chebyshev filter
183 // "Ripple" is in dB !
184 double LC_Filter::ChebyshevValue(int No, int Order, double Ripple)
185 {
186  // We must define static variables since g(k+1) is tied to g(k)
187  static double ak, gk ;
188  double a, b;
189 
190  Ripple = sqrt(pow(10.0, (Ripple / 10.0)) - 1.0);
191  Ripple = sinh(asinh(1.0 / Ripple) / double(Order));
192 
193  a = sin(double(2 * No + 1) / double(2 * Order) * M_PI);
194  if (No == 0)
195  gk = a / Ripple;
196  else {
197  b = sin(double(No) * M_PI / double(Order));
198  gk *= Ripple * Ripple + b * b;
199  gk = ak * a / gk;
200  }
201  ak = a;
202 
203  return 2.0 * gk;
204 }
205 
206 
207 // ====================================================================
208 // This is the main function. It creates an LC filter.
209 // Input parameters:
210 // Class - CLASS_LOWPASS
211 // CLASS_HIGHPASS
212 // CLASS_BANDPASS
213 // CLASS_BANDSTOP
214 // Impedance - input and output reference impedance in ohms
215 // Order - order of the filter
216 // Frequency - corner frequency (lowpass and highpass) or
217 // band start frequency (bandpass and bandstop)
218 // Frequency2 - band stop frequency (only for bandpass and bandstop)
220 {
221  double Value, Value2, Omega, Bandwidth;
222  if((Filter->Class == CLASS_BANDPASS) || (Filter->Class == CLASS_BANDSTOP))
223  Omega = (Filter->Frequency2 + Filter->Frequency) / 2.0;
224  else {
225  Filter->Frequency2 = 0.0;
226  Omega = Filter->Frequency;
227  }
228 
229  Bandwidth = fabs(Filter->Frequency2 - Filter->Frequency) / Omega;
230  Omega *= 2.0*M_PI; // angular frequency
231 
232  // create the Qucs schematic
233  QString *s = new QString("<Qucs Schematic ");
234  *s += PACKAGE_VERSION;
235  *s += ">\n";
236 
237  int i, x, yc=320, yl;
238  x = 20;
239  if(Filter->Class != CLASS_BANDPASS) x += 40;
240  *s += "<Components>\n";
241  *s += QString("<Pac P1 1 %1 320 18 -26 0 1 \"1\" 1 \"%2 Ohm\" 1 \"0 dBm\" 0 \"1 GHz\" 0>\n").arg(x).arg(Filter->Impedance);
242  *s += QString("<GND * 1 %1 350 0 0 0 0>\n").arg(x);
243 
244  for(i = 0; i < Filter->Order; i++) {
245  x = 100 +((i+1) * 70);
246  yc = 320;
247  yl = 240;
248 
249  switch(Filter->Type) {
250  case TYPE_BESSEL:
251  Value = BesselValue(i, Filter->Order);
252  break;
253  case TYPE_BUTTERWORTH:
254  Value = ButterworthValue(i, Filter->Order);
255  break;
256  case TYPE_CHEBYSHEV:
257  Value = ChebyshevValue(i, Filter->Order, Filter->Ripple);
258  break;
259  default:
260  Value = 0.0;
261  *s = QString("");
262  return s;
263  }
264 
265  // de-normalize
266  if(i & 1) Value *= Filter->Impedance / Omega;
267  else Value /= Filter->Impedance * Omega;
268 
269 
270  switch(Filter->Class) {
271 
272  case CLASS_LOWPASS:
273  if(i & 1)
274  *s += QString("<L L%1 1 %2 %3 -26 10 0 0 \"%4H\" 1>\n").arg(i+1).arg(x).arg(yl).arg(num2str(Value));
275  else
276  *s += QString("<C C%1 1 %2 %3 17 -26 0 1 \"%4F\" 1>\n").arg(i+1).arg(x).arg(yc).arg(num2str(Value));
277  break;
278 
279 
280  case CLASS_HIGHPASS:
281  Value = 1.0 / Omega / Omega / Value; // transform to highpass
282  if(i & 1)
283  *s += QString("<C C%1 1 %2 %3 -27 10 0 0 \"%4F\" 1>\n").arg(i+1).arg(x).arg(yl).arg(num2str(Value));
284  else
285  *s += QString("<L L%1 1 %2 %3 17 -26 0 1 \"%4H\" 1>\n").arg(i+1).arg(x).arg(yc).arg(num2str(Value));
286  break;
287 
288 
289  case CLASS_BANDPASS:
290  Value /= Bandwidth; // transform to bandpass
291  Value2 = 0.25 / Filter->Frequency / Filter->Frequency2 / M_PI / M_PI / Value;
292  if(i & 1) {
293  *s += QString("<L L%1 1 %2 %3 -26 -44 0 0 \"%4H\" 1>\n").arg(i+1).arg(x+40).arg(yl).arg(num2str(Value));
294  *s += QString("<C C%1 1 %2 %3 -26 10 0 0 \"%4F\" 1>\n").arg(i+1).arg(x-20).arg(yl).arg(num2str(Value2));
295  }
296  else {
297  *s += QString("<L L%1 1 %2 %3 8 -26 0 1 \"%4H\" 1>\n").arg(i+1).arg(x).arg(yc).arg(num2str(Value2));
298  *s += QString("<C C%1 1 %2 %3 -8 46 0 1 \"%4F\" 1>\n").arg(i+1).arg(x-30).arg(yc).arg(num2str(Value));
299  }
300  break;
301 
302 
303  case CLASS_BANDSTOP:
304  Value2 = 1.0 / Omega / Omega / Bandwidth / Value; // transform to bandstop
305  Value *= 0.5 * fabs(Filter->Frequency2/Filter->Frequency - Filter->Frequency/Filter->Frequency2);
306  if(i & 1) {
307  *s += QString("<L L%1 1 %2 %3 -26 -44 0 0 \"%4H\" 1>\n").arg(i+1).arg(x).arg(yl-35).arg(num2str(Value));
308  *s += QString("<C C%1 1 %2 %3 -26 10 0 0 \"%4F\" 1>\n").arg(i+1).arg(x).arg(yl).arg(num2str(Value2));
309  }
310  else {
311  *s += QString("<L L%1 1 %2 %3 17 -26 0 1 \"%4H\" 1>\n").arg(i+1).arg(x).arg(yc).arg(num2str(Value2));
312  *s += QString("<C C%1 1 %2 %3 17 -26 0 1 \"%4F\" 1>\n").arg(i+1).arg(x).arg(yc+60).arg(num2str(Value));
313  }
314  yc += 60;
315  break;
316 
317  }
318 
319  if((i & 1) == 0)
320  *s += QString("<GND * 1 %1 %2 0 0 0 0>\n").arg(x).arg(yc + 30);
321 
322  }
323 
324 
325  if(Filter->Order & 1) x += 110;
326  else x += 70;
327  *s += QString("<Pac P2 1 %1 320 18 -26 0 1 \"2\" 1 \"%2 Ohm\" 1 \"0 dBm\" 0 \"1 GHz\" 0>\n").arg(x).arg(Filter->Impedance);
328  *s += QString("<GND * 1 %1 350 0 0 0 0>\n").arg(x);
329 
330  yc += 100;
331  Value = Filter->Frequency / 10.0;
332  if((Filter->Class == CLASS_BANDPASS) || (Filter->Class == CLASS_BANDSTOP))
333  Value2 = 10.0 * Filter->Frequency2;
334  else
335  Value2 = 10.0 * Filter->Frequency;
336  *s += QString("<.SP SP1 1 70 %1 0 50 0 0 \"log\" 1 \"%2Hz\" 1 \"%3Hz\" 1 \"200\" 1 \"no\" 0 \"1\" 0 \"2\" 0>\n").arg(yc).arg(num2str(Value)).arg(num2str(Value2));
337  *s += QString("<Eqn Eqn1 1 260 %1 -28 15 0 0 \"dBS21=dB(S[2,1])\" 1 \"dBS11=dB(S[1,1])\" 1 \"yes\" 0>\n").arg(yc+10);
338  *s += "</Components>\n";
339 
340  *s += "<Wires>\n";
341 
342  // connect left source
343  x = 20;
344  if(Filter->Class != CLASS_BANDPASS) x += 40;
345  *s += QString("<%1 240 %2 290 \"\" 0 0 0>\n").arg(x).arg(x);
346  *s += QString("<%1 240 170 240 \"\" 0 0 0>\n").arg(x);
347 
348  // wires down to shunt components
349  for(i = 0; i < (Filter->Order / 2) + 1; i++) {
350  x = 170 + (i * 140);
351  *s += QString("<%1 240 %2 290 \"\" 0 0 0>\n").arg(x).arg(x);
352  }
353 
354  // horizontal wires for series components
355  if(Filter->Class == CLASS_BANDPASS) {
356  for(i = 0; i < (Filter->Order / 2); i++) {
357  x = 170 + (i * 140);
358  *s += QString("<%1 240 %2 240 \"\" 0 0 0>\n").arg(x).arg(x+20);
359  *s += QString("<%1 290 %2 290 \"\" 0 0 0>\n").arg(x-30).arg(x);
360  *s += QString("<%1 350 %2 350 \"\" 0 0 0>\n").arg(x-30).arg(x);
361  }
362  if(Filter->Order & 1) {
363  *s += QString("<%1 290 %2 290 \"\" 0 0 0>\n").arg(x+110).arg(x+140);
364  *s += QString("<%1 350 %2 350 \"\" 0 0 0>\n").arg(x+110).arg(x+140);
365  }
366  }
367  else
368  for(i = 0; i < (Filter->Order / 2); i++) {
369  x = 170 + (i * 140);
370  *s += QString("<%1 240 %2 240 \"\" 0 0 0>\n").arg(x).arg(x+40);
371  *s += QString("<%1 240 %2 240 \"\" 0 0 0>\n").arg(x+100).arg(x+140);
372  if(Filter->Class == CLASS_BANDSTOP) {
373  *s += QString("<%1 240 %2 205 \"\" 0 0 0>\n").arg(x+40).arg(x+40);
374  *s += QString("<%1 240 %2 205 \"\" 0 0 0>\n").arg(x+100).arg(x+100);
375  }
376  }
377 
378  // connect right source
379  if(Filter->Order & 1) {
380  x += 140 + 110;
381  *s += QString("<%1 240 %2 290 \"\" 0 0 0>\n").arg(x).arg(x);
382  *s += QString("<%1 240 %2 240 \"\" 0 0 0>\n").arg(x-110).arg(x);
383  }
384  *s += "</Wires>\n";
385 
386  *s += "<Diagrams>\n";
387  *s += "</Diagrams>\n";
388 
389  *s += "<Paintings>\n";
390 
391  *s += QString("<Text 400 %1 12 #000000 0 \"").arg(yc+10);
392  switch(Filter->Type) {
393  case TYPE_BESSEL: *s += QString("Bessel "); break;
394  case TYPE_BUTTERWORTH: *s += QString("Butterworth "); break;
395  case TYPE_CHEBYSHEV: *s += QString("Chebyshev "); break;
396  }
397 
398  switch(Filter->Class) {
399  case CLASS_LOWPASS:
400  *s += QString("low-pass filter\\n%1Hz cutoff").arg(num2str(Filter->Frequency)); break;
401  case CLASS_HIGHPASS:
402  *s += QString("high-pass filter\\n%1Hz cutoff").arg(num2str(Filter->Frequency)); break;
403  case CLASS_BANDPASS:
404  *s += QString("band-pass filter\\n%1Hz...%2Hz").arg(num2str(Filter->Frequency)).arg(num2str(Filter->Frequency2)); break;
405  case CLASS_BANDSTOP:
406  *s += QString("band-reject filter\\n%1Hz...%2Hz").arg(num2str(Filter->Frequency)).arg(num2str(Filter->Frequency2)); break;
407  }
408  *s += QString(", PI-type,\\nimpedance matching %3 Ohm\">\n").arg(Filter->Impedance);
409  *s += "</Paintings>\n";
410 
411  return s;
412 }