My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
qf_filter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qf_filter.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 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <math.h>
23 #include <iostream>
24 #include <sstream>
25 #include <string>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "qf_poly.h"
30 #include "qf_filter.h"
31 
32 // Constructor of a filter of nth order
33 
34 // Just initialize a few things
36  type (LOWPASS), kind (UNDEF), ord (n), fc (0), bw (0), imp (1),
37  ncomp (0), Comp (NULL) {
38 }
39 
40 // Default constructor
42  type (LOWPASS), kind (UNDEF), ord (0), fc (0), bw (0), imp (1),
43  ncomp (0), Comp (NULL) {
44 }
45 
47  type (t), kind (k), ord (n), fc (0), bw (0), imp (1),
48  ncomp (0), Comp (NULL) {
49 }
50 
52  qf_double_t f = 1, qf_double_t b = 0) :
53  type (t), kind (k), fc (f), bw (b), imp (r), ncomp (0), Comp (NULL) {
54  fstart = fc - bw / 2;
55  fstop = fc + bw / 2;
56 }
57 
59  type (t), kind (k), ord (0), fc (0), bw (0), imp (1),
60  ncomp (0), Comp (NULL) {
61 }
62 
63 // Destructor of a filter
65  if (Comp != NULL)
66  free (Comp);
67 }
68 
69 // Extraction routines
70 
71 // Extract finite attenuation pole
72 // Result is a parallel cap, and a serial resonator (L // C)
74  qf_double_t Ws) {
75  BN.disp ("BN");
76  BD.disp ("BD");
77 
78  qf_double_t pl = -p * p;
79  qf_double_t bdpl = BD.evalX2 (pl);
80 
81  // Partial removal of infinite pole (first // cap)
82  // c = [B(s)/s] (s^2 = - O^2)
83  qf_double_t c = ((BN << 1).evalX2 (pl)) / bdpl;
84  pComp->val = c * Ws;
85  qf_poly cS (c, 0, 0, 1);
86  BN = BN - (BD * cS); // B = B - cs
87  BN.disp ("BN");
88  BN.div (0, p);
89 
90  // Full removal of finite pole
91  // c1 = (s B(s) / (s^2 + O^2)) @ s^2 = - O^2
92  BN.disp ("BN");
93  qf_double_t c1 = (BN >> 1).evalX2 (pl) / bdpl;
94  (pComp + 1)->val = c1;
95  (pComp + 2)->val = -Ws / (c1 * pl);
96  (pComp + 1)->val *= Ws;
97 
98  // 1/B = 1/B - (s/c1) / (s^2 + O^2)
99  BD = BD - (BN >> 1) * (1 / c1);
100  BD.div (0, p);
101 
102  BN.disp ("BN");
103  BD.disp ("BD");
104 }
105 
106 // User readable value string.
107 std::string qf_filter::num2str (qf_double_t num) {
108  char c = 0;
109  qf_double_t cal = fabs(num);
110  if(cal > 1e-20) {
111  cal = log10(cal) / 3.0;
112  if(cal < -0.2) cal -= 0.98;
113  int expo = int(cal);
114 
115  if(expo >= -5) if(expo <= 4)
116  switch(expo) {
117  case -5: c = 'f'; break;
118  case -4: c = 'p'; break;
119  case -3: c = 'n'; break;
120  case -2: c = 'u'; break;
121  case -1: c = 'm'; break;
122  case 1: c = 'k'; break;
123  case 2: c = 'M'; break;
124  case 3: c = 'G'; break;
125  case 4: c = 'T'; break;
126  }
127 
128  if(c) num /= pow(10.0, (qf_double_t)(3*expo));
129  }
130 
131  std::stringstream str;
132  str << num;
133  if(c) str << c;
134  return str.str();
135 }
136 
137 // Dumps a filter to std::cout
138 void qf_filter::dump_cout (void) {
139  std::string unit;
140 
141  for (unsigned i = 0; i < ncomp; i++) {
142  switch (Comp[i].comp) {
143  case CAP:
144  std::cout << "Cap. from node ";
145  unit = "F";
146  break;
147  case IND:
148  std::cout << "Ind. from node ";
149  unit = "H";
150  break;
151  case RES:
152  std::cout << "Res. from node ";
153  unit = "Ohm";
154  break;
155  }
156 
157  std::cout << Comp[i].node1 << " to ";
158 
159  if (Comp[i].node2 == 0)
160  std::cout << "ground, value = ";
161  else
162  std::cout << "node " << Comp[i].node2 << ", value = ";
163 
164  if (Comp[i].val > 1)
165  std::cout << Comp[i].val << " " << unit << '\n';
166  else if (Comp[i].val >= 1e-3)
167  std::cout << Comp[i].val * 1e3 << " m" << unit << '\n';
168  else if (Comp[i].val >= 1e-6)
169  std::cout << Comp[i].val * 1e6 << " u" << unit << '\n';
170  else if (Comp[i].val >= 1e-9)
171  std::cout << Comp[i].val * 1e9 << " n" << unit << '\n';
172  else if (Comp[i].val >= 1e-12)
173  std::cout << Comp[i].val * 1e12 << " p" << unit << '\n';
174  else if (Comp[i].val >= 1e-15)
175  std::cout << Comp[i].val * 1e15 << " f" << unit << '\n';
176  else
177  std::cout << "0 " << unit << '\n';
178  }
179 }
180 
181 std::string qf_filter::to_spice (void) {
182  std::ostringstream res;
183  unsigned i = 0;
184 
185  res << "* SPICE netlist\n";
186  switch(type) {
187  case LOWPASS:
188  res << "* low-pass filter " << fc << " Hz cutoff\n";
189  break;
190  case HIGHPASS:
191  res << "* high-pass filter " << fc << " Hz cutoff\n";
192  break;
193  case BANDPASS:
194  res << "* band-pass filter "
195  << fstart << " Hz ... " << fstop << " Hz\n";
196  break;
197  case BANDSTOP:
198  res << "* band-reject filter "
199  << fstart << " Hz ... " << fstop << " Hz\n";
200  break;
201  }
202  res << "* PI-type, impedance matching " << imp << " Ohm\n";
203 
204  for (unsigned ic = 1, il = 1, ir = 1; i < ncomp; i++) {
205  switch (Comp[i].comp) {
206  case CAP:
207  res << "C" << ic << "\t";
208  res << Comp[i].node1 << "\t" << Comp[i].node2;
209  res << "\t" << Comp[i].val << '\n';
210  // Insert parallel resistor
211  res << "R" << ir << "\t";
212  res << Comp[i].node1 << "\t" << Comp[i].node2;
213  res << "\t10G\n";
214  ic++;
215  ir++;
216  break;
217  case IND:
218  res << "L" << il << "\t";
219  res << Comp[i].node1 << "\t"
220  << Comp[i].node1 * 100 + Comp[i].node2 << "\t";
221  res << Comp[i].val << "\n";
222  il++;
223  // Insert serial resistor
224  res << "R" << ir << "\t";
225  res << Comp[i].node1 * 100 + Comp[i].node2
226  << "\t" << Comp[i].node2 << "\t";
227  res << "1u\n";
228  ir++;
229  break;
230  case RES:
231  res << "R" << ir << "\t";
232  ir++;
233  break;
234  }
235  }
236 
237  res << "RI\t10000\t1\t" << imp << "\n";
238  res << "RL\t";
239 
240  if (Comp[i - 1].node2 == 0)
241  res << Comp[i - 1].node1;
242  else
243  res << Comp[i - 1].node2;
244 
245  res << "\t0\t" << imp << "\n";
246  res << "VCC\t10000\t0\tDC\t1\tAC\t1\n";
247  res << ".END\n";
248  return res.str();
249 }
250 
252  std::cout << to_spice().c_str();
253 }
254 
255 std::string qf_filter::to_qucs (void) {
256  std::ostringstream res;
257  std::ostringstream wir;
258  qf_double_t Value, Value2;
259 
260  // create the Qucs schematic
261  res << "<Qucs Schematic " << PACKAGE_VERSION << ">\n";
262 
263  int x2, x = 20 + 40;
264  res << "<Components>\n";
265  res << "<Pac P1 1 " << x << " 320 18 -26 0 1 \"1\" 1 \""
266  << imp << " Ohm\" 1 \"0 dBm\" 0 \"1 GHz\" 0>\n";
267  res << "<GND * 1 " << x << " 350 0 0 0 0>\n";
268 
269  int yc = 320, yl = 240;
270  unsigned int i, j = 0, k = ord, repser = 0, gndser = 1;
271 
272  // connect left source
273  x = 20 + 40;
274  x2 = x + 110;
275  if(type == HIGHPASS) x2 -= 30;
276  wir << "<" << x << " 240 " << x << " 290 \"\" 0 0 0>\n"
277  << "<" << x << " 240 " << x2 << " 240 \"\" 0 0 0>\n";
278 
279  x = 170;
280  if (type == BANDPASS || type == BANDSTOP) k = ord * 2;
281  for(i = 0; i < k && ncomp > 0; i++) {
282  // find series components to ground
283  unsigned int a = j;
284  while (Comp[a].node2 != 0) {
285  if (a+1 < ncomp && Comp[a].node2 == Comp[a+1].node1 &&
286  Comp[a].comp != Comp[a+1].comp)
287  a++;
288  else
289  break;
290  }
291  // handle series components to ground
292  if (Comp[a].node2 == 0) {
293  // wire down to shunt components
294  wir << "<" << x << " 240 " << x << " 290 \"\" 0 0 0>\n";
295  wir << "<" << x-35 << " 240 " << x+35 << " 240 \"\" 0 0 0>\n";
296  unsigned int b, c;
297  for (c = 0, b = j; b <= a; b++, c++) {
298  // type of component
299  switch (Comp[b].comp) {
300  case CAP: res << "<C C"; break;
301  case IND: res << "<L L"; break;
302  case RES: res << "<R R"; break;
303  }
304  res << j+1 << " 1 " << x;
305  res << " " << yc+c*60 << " 17 -26 0 1";
306 
307  // value of component
308  res << " \"" << num2str(Comp[b].val).c_str();
309  switch (Comp[b].comp) {
310  case CAP: res << "F"; break;
311  case IND: res << "H"; break;
312  case RES: res << "Ohm"; break;
313  }
314  res << "\" 1>\n";
315  }
316  // place ground symbol here
317  res << "<GND * 1 " << x << " " << yc+c*60-30 << " 0 0 0 0>\n";
318  j = a + 1;
319  if (gndser < c) gndser = c;
320  repser = 0;
321  }
322  // handle parallel components in series
323  else {
324  if (repser) x += 35;
325  // connect in series
326  if (i != 0)
327  wir << "<" << x-70 << " 240 " << x-30 << " 240 \"\" 0 0 0>\n";
328  if (i != k - 1)
329  wir << "<" << x+70 << " 240 " << x+30 << " 240 \"\" 0 0 0>\n";
330  unsigned int b = j, c = 0;
331  do {
332  // type of component
333  switch (Comp[b].comp) {
334  case CAP: res << "<C C"; break;
335  case IND: res << "<L L"; break;
336  case RES: res << "<R R"; break;
337  }
338  res << j+1 << " 1 " << x;
339  res << " " << yl-c*35 << " -26 ";
340  if (c) {
341  res << "-44";
342  wir << "<" << x-30 << " " << yl
343  << " " << x-30 << " " << yl-c*35 << " \"\" 0 0 0>\n"
344  << "<" << x+30 << " " << yl
345  << " " << x+30 << " " << yl-c*35 << " \"\" 0 0 0>\n";
346  }
347  else
348  res << "10";
349  res << " 0 0";
350 
351  // value of commponent
352  res << " \"" << num2str(Comp[b].val).c_str();
353  switch (Comp[b].comp) {
354  case CAP: res << "F"; break;
355  case IND: res << "H"; break;
356  case RES: res << "Ohm"; break;
357  }
358  res << "\" 1>\n";
359  b++;
360  c++;
361  }
362  while (b < ncomp && Comp[b-1].node1 == Comp[b].node1 &&
363  Comp[b-1].node2 == Comp[b].node2);
364  j = b;
365  repser++;
366  }
367  if (j >= ncomp) break;
368  x += 70;
369  }
370 
371  if (ord & 1)
372  x += 110;
373  else
374  x += 70;
375  res << "<Pac P2 1 "<< x << " 320 18 -26 0 1 \"2\" 1 \""
376  << imp << " Ohm\" 1 \"0 dBm\" 0 \"1 GHz\" 0>\n"
377  << "<GND * 1 " << x << " 350 0 0 0 0>\n";
378 
379  yc += 20 + gndser * 60;
380  Value = fc / 10.0;
381  Value2 = 10.0 * fc;
382  res << "<.SP SP1 1 70 " << yc << " 0 50 0 0 \"log\" 1 \""
383  << num2str(Value).c_str() << "Hz\" 1 \"" << num2str(Value2).c_str()
384  << "Hz\" 1 \"200\" 1 \"no\" 0 \"1\" 0 \"2\" 0>\n"
385  << "<Eqn Eqn1 1 260 " << yc+10
386  << " -28 15 0 0 \"dBS21=dB(S[2,1])\" 1 "
387  << "\"dBS11=dB(S[1,1])\" 1 \"yes\" 0>\n"
388  << "</Components>\n";
389 
390  res << "<Wires>\n";
391 
392  // internal wires
393  res << wir.str().c_str();
394 
395  // connect right source
396  x2 = x - 110;
397  if(type == HIGHPASS) x2 += 30;
398  res << "<" << x << " 240 " << x << " 290 \"\" 0 0 0>\n"
399  << "<" << x2 << " 240 " << x << " 240 \"\" 0 0 0>\n";
400 
401  res << "</Wires>\n"
402  << "<Diagrams>\n"
403  << "</Diagrams>\n"
404  << "<Paintings>\n";
405 
406  res << "<Text 400 " << (yc+10) << " 12 #000000 0 \"";
407 
408  switch (kind) {
409  case CAUER: res << "Cauer "; break;
410  case BUTT: res << "Butterworth "; break;
411  case CHEB: res << "Chebichev "; break;
412  case ICHEB: res << "Inverse Chebichev "; break;
413  case BESS: res << "Bessel "; break;
414  case UNDEF: res << "Undefined "; break;
415  }
416 
417  switch(type) {
418  case LOWPASS:
419  res << "low-pass filter\\n"
420  << num2str(fc).c_str() << "Hz cutoff";
421  break;
422  case HIGHPASS:
423  res << "high-pass filter\\n"
424  << num2str(fc).c_str() << "Hz cutoff";
425  break;
426  case BANDPASS:
427  res << "band-pass filter\\n"
428  << num2str(fstart).c_str() << "Hz ... "
429  << num2str(fstop).c_str() << "Hz";
430  break;
431  case BANDSTOP:
432  res << "band-reject filter\\n"
433  << num2str(fstart).c_str() << "Hz ... "
434  << num2str(fstop).c_str() << "Hz";
435  break;
436  }
437  res << ", PI-type,\\nimpedance matching " << imp << " Ohm\">\n";
438 
439  res << "</Paintings>\n";
440  return res.str();
441 }
442 
443 void qf_filter::dump_qucs (void) {
444  std::cout << to_qucs().c_str();
445 }