Qucs-core  0.0.19
check_touchstone.cpp
Go to the documentation of this file.
00001 /*
00002  * check_touchstone.cpp - checker for Touchstone files
00003  *
00004  * Copyright (C) 2003, 2004, 2005, 2006 Stefan Jahn <stefan@lkcc.org>
00005  *
00006  * This is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2, or (at your option)
00009  * any later version.
00010  *
00011  * This software is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this package; see the file COPYING.  If not, write to
00018  * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020  *
00021  * $Id$
00022  *
00023  */
00024 
00025 #if HAVE_CONFIG_H
00026 # include <config.h>
00027 #endif
00028 
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <ctype.h>
00033 #include <cmath>
00034 
00035 #include "logging.h"
00036 #include "complex.h"
00037 #include "object.h"
00038 #include "vector.h"
00039 #include "matrix.h"
00040 #include "matvec.h"
00041 #include "dataset.h"
00042 #include "strlist.h"
00043 #include "constants.h"
00044 #include "check_touchstone.h"
00045 
00046 #define ZREF 50.0 /* reference impedance */
00047 
00048 using namespace qucs;
00049 
00050 strlist * touchstone_idents = NULL;
00051 dataset * touchstone_result = NULL;
00052 qucs::vector  * touchstone_vector = NULL;
00053 
00054 /* default touchstone options */
00055 struct touchstone_t touchstone_options = {
00056   "GHz", 'S', "MA", 50.0, 1e9, 0, 0, 0 };
00057 
00058 /* available touchstone options */
00059 static const char * touchstone_valid_options[] = {
00060   "hz", "khz", "mhz", "ghz", "s", "y", "z", "g", "h", "ma", "db", "ri", NULL };
00061 
00062 /* This subroutine is going to join vectors on multiple lines.  The
00063    input and output list of vectors of this function is the
00064    touchstone_vector variable. */
00065 static void touchstone_join (void) {
00066   qucs::vector * yroot, * xroot, * next = NULL;
00067   /* go through each vector */
00068   for (yroot = touchstone_vector; yroot != NULL; yroot = next) {
00069     /* go through each trailing vector */
00070     next = (qucs::vector *) yroot->getNext ();
00071     for (xroot = next; xroot != NULL; xroot = next) {
00072       next = (qucs::vector *) xroot->getNext ();
00073       /* append xroot vector to yroot vector (even no. of values) ? */
00074       if ((xroot->getSize () & 1) == 0) {
00075         /* yes, delete the xroot vector and adjust list */
00076         yroot->add (xroot);
00077         yroot->setNext (next);
00078         delete xroot;
00079       }
00080       else {
00081         /* no, handle next vectors */
00082         next = xroot;
00083         break;
00084       }
00085     }
00086   }
00087 }
00088 
00089 /* This subroutine checks the size and overall conformance of each
00090    touchstone matrix at a given frequency derived from the first
00091    matrix.  The function return zero on success and non-zero
00092    otherwise. */
00093 static int touchstone_vector_check (void) {
00094   qucs::vector * root = touchstone_vector, * next;
00095   int even = 0, errors = 0, size = root->getSize (), noise = 0, lines = 1;
00096   nr_double_t f = real (root->get (0));
00097 
00098   /* check size of first line */
00099   if ((size & 1) == 0) {
00100     logprint (LOG_ERROR, "checker error, first data line has %d (even) "
00101               "values\n", size);
00102     errors++;
00103     even = 1;
00104   }
00105   /* first line determines the number of expected ports */
00106   touchstone_options.ports = (int) std::sqrt ((size - 1) / 2.0);
00107 
00108   /* check first frequency value */
00109   if (f < 0.0) {
00110     logprint (LOG_ERROR, "checker error, negative data frequency "
00111               "value %g\n", f);
00112     errors++;
00113   }
00114 
00115   /* go through each vector */
00116   for (root = (qucs::vector *) root->getNext (); root != NULL; root = next) {
00117     next = (qucs::vector *) root->getNext ();
00118     nr_double_t freq = real (root->get (0));
00119 
00120     /* check increasing frequency value */
00121     if (f >= freq) {
00122       if (!noise) {
00123         /* determined start of noise parameters */
00124         noise++;
00125         size = 5;
00126         if (freq < 0.0) {
00127           logprint (LOG_ERROR, "checker error, negative noise frequency "
00128                     "value %g\n", freq);
00129           errors++;
00130         }
00131       }
00132       else {
00133         logprint (LOG_ERROR, "checker error, %s line (f = %g) has "
00134                   "decreasing frequency value\n", noise ? "noise" : "data",
00135                   freq);
00136         errors++;
00137       }
00138     }
00139     f = freq;
00140 
00141     /* check size of vector */
00142     if (!even && root->getSize () != size) {
00143       logprint (LOG_ERROR, "checker error, %s line (f = %g) has %d values, "
00144                 "%d required\n", noise ? "noise" : "data",
00145                 real (root->get (0)), root->getSize (), size);
00146       errors++;
00147     }
00148 
00149     /* count number of data lines without noise entries */
00150     if (!noise) lines++;
00151   }
00152   touchstone_options.noise = noise;
00153   touchstone_options.lines = lines;
00154   return errors;
00155 }
00156 
00157 /* The function evaluates the identifiers in the option line and fills
00158    the touchstone_options structure with appropriate values. */
00159 static void touchstone_options_eval (void) {
00160   /* go through all identifiers */
00161   for (int i = 0; i < touchstone_idents->length (); i++) {
00162     char * str = touchstone_idents->get (i);
00163     /* frequency unit */
00164     if (!strcmp (str, "hz")) {
00165       touchstone_options.factor = 1.0;
00166       touchstone_options.unit = "Hz";
00167     }
00168     else if (!strcmp (str, "khz")) {
00169       touchstone_options.factor = 1e3;
00170       touchstone_options.unit = "kHz";
00171     }
00172     else if (!strcmp (str, "mhz")) {
00173       touchstone_options.factor = 1e6;
00174       touchstone_options.unit = "MHz";
00175     }
00176     else if (!strcmp (str, "ghz")) {
00177       touchstone_options.factor = 1e9;
00178       touchstone_options.unit = "GHz";
00179     }
00180     /* parameter type */
00181     else if (!strcmp (str, "s")) {
00182       touchstone_options.parameter = 'S';
00183     }
00184     else if (!strcmp (str, "y")) {
00185       touchstone_options.parameter = 'Y';
00186     }
00187     else if (!strcmp (str, "z")) {
00188       touchstone_options.parameter = 'Z';
00189     }
00190     else if (!strcmp (str, "g")) {
00191       touchstone_options.parameter = 'G';
00192     }
00193     else if (!strcmp (str, "h")) {
00194       touchstone_options.parameter = 'H';
00195     }
00196     /* value formats */
00197     else if (!strcmp (str, "ma")) {
00198       touchstone_options.format = "MA";
00199     }
00200     else if (!strcmp (str, "db")) {
00201       touchstone_options.format = "dB";
00202     }
00203     else if (!strcmp (str, "ri")) {
00204       touchstone_options.format = "RI";
00205     }
00206   }
00207 }
00208 
00209 /* This little function returns a static string containing an
00210    appropriate variable name. */
00211 static char * touchstone_create_set (int r, int c) {
00212   char * text;
00213   text = matvec::createMatrixString (touchstone_options.parameter, r, c);
00214   return text;
00215 }
00216 
00217 /* The function actually creates the resulting dataset. */
00218 static void touchstone_create (void) {
00219   qucs::vector * f, * v, * root, * next, * nf = NULL;
00220   int ports = touchstone_options.ports, n;
00221   nr_complex_t val;
00222   strlist * s;
00223 
00224   /* create dataset and frequency vector */
00225   touchstone_result = new dataset ();
00226   f = new qucs::vector ("frequency");
00227   touchstone_result->appendDependency (f);
00228   s = new strlist ();
00229   s->add (f->getName ());
00230   /* create variable vectors for the resulting dataset */
00231   for (int r = 0; r < ports; r++) {
00232     for (int c = 0; c < ports; c++) {
00233       v = new qucs::vector ();
00234       v->setName (touchstone_create_set (r, c));
00235       v->setDependencies (new strlist (*s));
00236       touchstone_result->appendVariable (v);
00237     }
00238   }
00239   delete s;
00240 
00241   /* create noise vectors if necessary */
00242   if (touchstone_options.noise) {
00243     nf = new qucs::vector ("nfreq");
00244     touchstone_result->appendDependency (nf);
00245     s = new strlist ();
00246     s->add (nf->getName ());
00247     /* append noise parameters to dataset */
00248     v = new qucs::vector ("Fmin");
00249     v->setDependencies (new strlist (*s));
00250     touchstone_result->appendVariable (v);
00251     v = new qucs::vector ("Sopt");
00252     v->setDependencies (new strlist (*s));
00253     touchstone_result->appendVariable (v);
00254     v = new qucs::vector ("Rn");
00255     v->setDependencies (new strlist (*s));
00256     touchstone_result->appendVariable (v);
00257     delete s;
00258   }
00259 
00260   /* go through each vector */
00261   for (n = 0, root = touchstone_vector; root != NULL; root = next, n++) {
00262     next = (qucs::vector *) root->getNext ();
00263     // handle data lines
00264     if (n < touchstone_options.lines) {
00265       /* fill frequency vector */
00266       f->add (real (root->get (0)) * touchstone_options.factor);
00267       /* go through each variable vector */
00268       v = touchstone_result->getVariables ();
00269       for (int i = 0; i < ports; i++) {
00270         for (int j = 0; j < ports; j++) {
00271           int pos = 1 + j * 2 + i * 2 * ports;
00272           /* handle special case for 2-port touchstone data, '21' data
00273              precedes the '12' data */
00274           if (ports == 2 && i != j) {
00275             pos = 1 + i * 2 + j * 2 * ports;
00276           }
00277           /* depending on the touchstone data format */
00278           if (!strcmp (touchstone_options.format, "RI")) {
00279             val = nr_complex_t (real (root->get (pos + 0)),
00280                         real (root->get (pos + 1)));
00281           }
00282           else if (!strcmp (touchstone_options.format, "MA")) {
00283             val = qucs::polar (real (root->get (pos + 0)),
00284                          deg2rad (real (root->get (pos + 1))));
00285           }
00286           else if (!strcmp (touchstone_options.format, "dB")) {
00287             val = qucs::polar (std::pow (10.0, real (root->get (pos + 0)) / 20.0),
00288                          deg2rad (real (root->get (pos + 1))));
00289           }
00290           v->add (val);
00291           v = (qucs::vector *) v->getNext ();
00292         }
00293       }
00294     }
00295     // handle noise lines
00296     else if (touchstone_options.noise) {
00297       /* fill frequency vector */
00298       nf->add (real (root->get (0)) * touchstone_options.factor);
00299       /* fill minimum noise figure vector */
00300       v = touchstone_result->findVariable ("Fmin");
00301       val = std::pow (10.0, real (root->get (1)) / 10.0);
00302       v->add (val);
00303       /* fill optimal noise reflexion coefficient vector */
00304       v = touchstone_result->findVariable ("Sopt");
00305       val = qucs::polar (real (root->get (2)), deg2rad (real (root->get (3))));
00306       if (ZREF != touchstone_options.resistance) {
00307         // re-normalize reflexion coefficient if necessary
00308         nr_double_t r = (ZREF - touchstone_options.resistance) /
00309           (ZREF + touchstone_options.resistance);
00310         val = (val - r) / (1.0 - r * val);
00311       }
00312       v->add (val);
00313       /* fill equivalent noise resistance vector */
00314       v = touchstone_result->findVariable ("Rn");
00315       val = real (root->get (4)) * touchstone_options.resistance;
00316       v->add (val);
00317     }
00318   }
00319 }
00320 
00321 /* The function re-normalizes S-parameters to the internal reference
00322    impedance 50 Ohms. */
00323 static void touchstone_normalize_sp (void) {
00324   int ports = touchstone_options.ports;
00325   qucs::vector * v = touchstone_result->getVariables ();
00326   int i, j, n, len = v->getSize ();
00327   matrix s = matrix (ports);
00328 
00329   // go through each matrix entry
00330   for (n = 0; n < len; n++) {
00331     v = touchstone_result->getVariables ();
00332     // save entries in a temporary matrix
00333     for (i = 0; i < ports; i++) {
00334       for (j = 0; j < ports; j++) {
00335         s.set (i, j, v->get (n));
00336         v = (qucs::vector *) v->getNext ();
00337       }
00338     }
00339     // convert the temporary matrix
00340     s = stos (s, touchstone_options.resistance, ZREF);
00341     v = touchstone_result->getVariables ();
00342     // restore the results in the entries
00343     for (i = 0; i < ports; i++) {
00344       for (j = 0; j < ports; j++) {
00345         v->set (s.get (i, j), n);
00346         v = (qucs::vector *) v->getNext ();
00347       }
00348     }
00349   }
00350 }
00351 
00352 /* The function transforms the reference impedance given in the
00353    touchstone file to the internal reference impedance 50 Ohms. */
00354 static void touchstone_normalize (void) {
00355   qucs::vector * v = touchstone_result->getVariables ();
00356   int ports = touchstone_options.ports;
00357 
00358   // transform S-parameters if necessary
00359   if (touchstone_options.parameter == 'S') {
00360     if (touchstone_options.resistance != ZREF)
00361       touchstone_normalize_sp ();
00362     return;
00363   }
00364   // transform any other X-parameters
00365   for (int i = 1; i <= ports; i++) {
00366     for (int j = 1; j <= ports; j++) {
00367       switch (touchstone_options.parameter) {
00368       case 'Y': // Y-parameters
00369         *v /= touchstone_options.resistance;
00370         break;
00371       case 'Z': // Z-parameters
00372         *v *= touchstone_options.resistance;
00373         break;
00374       case 'G': // hybrid G-parameters
00375         if (i == 1 && j == 1)
00376           *v /= touchstone_options.resistance;
00377         else if (i == 2 && j == 2)
00378           *v *= touchstone_options.resistance;
00379         break;
00380       case 'H': // hybrid H-parameters
00381         if (i == 1 && j == 1)
00382           *v *= touchstone_options.resistance;
00383         else if (i == 2 && j == 2)
00384           *v /= touchstone_options.resistance;
00385         break;
00386       }
00387       v = (qucs::vector *) v->getNext ();
00388     }
00389   }
00390 }
00391 
00392 /* Removes temporary data items from memory if necessary. */
00393 static void touchstone_finalize (void) {
00394   qucs::vector * root, * next;
00395   for (root = touchstone_vector; root != NULL; root = next) {
00396     next = (qucs::vector *) root->getNext ();
00397     delete root;
00398   }
00399   touchstone_vector = NULL;
00400   if (touchstone_idents != NULL) {
00401     delete touchstone_idents;
00402     touchstone_idents = NULL;
00403   }
00404   touchstone_lex_destroy ();
00405   /* apply default values again */
00406   touchstone_options.unit = "GHz";
00407   touchstone_options.parameter = 'S';
00408   touchstone_options.format = "MA";
00409   touchstone_options.resistance = 50.0;
00410   touchstone_options.factor = 1e9;
00411   touchstone_options.ports = 0;
00412   touchstone_options.noise = 0;
00413   touchstone_options.lines = 0;
00414 }
00415 
00416 
00417 /* This function is the checker routine for a parsed touchstone.  It
00418    returns zero on success or non-zero if the parsed touchstone
00419    contained errors. */
00420 int touchstone_check (void) {
00421 
00422   int i, n, errors = 0;
00423 
00424   /* first checking the options */
00425   if (touchstone_idents->length () > 3) {
00426     logprint (LOG_ERROR, "checker error, found %d options\n",
00427               touchstone_idents->length ());
00428     errors++;
00429   }
00430   /* touchstone is case insensitive */
00431   for (i = 0; i < touchstone_idents->length (); i++) {
00432     for (char * p = touchstone_idents->get (i); *p != '\0'; p++)
00433       *p = tolower (*p);
00434   }
00435   /* check duplicate options */
00436   for (i = 0; i < touchstone_idents->length (); i++) {
00437     char * str = touchstone_idents->get (i);
00438     if ((n = touchstone_idents->contains (str)) != 1) {
00439       logprint (LOG_ERROR, "checker error, option `%s' occurred %dx\n",
00440                 str, n);
00441       errors++;
00442     }
00443   }
00444   /* check valid options */
00445   for (i = 0; i < touchstone_idents->length (); i++) {
00446     char * str = touchstone_idents->get (i);
00447     int valid = 0;
00448     for (int v = 0; touchstone_valid_options[v] != NULL; v++) {
00449       if (!strcmp (touchstone_valid_options[v], str))
00450         valid = 1;
00451     }
00452     if (!valid) {
00453       logprint (LOG_ERROR, "checker error, invalid option `%s'\n", str);
00454       errors++;
00455     }
00456   }
00457 
00458   /* evaluate the option line and put values into touchstone_options
00459      structure */
00460   touchstone_options_eval ();
00461 
00462   if (touchstone_vector == NULL) {
00463     logprint (LOG_ERROR, "checker error, no data in touchstone file\n");
00464     errors++;
00465   }
00466   else {
00467     /* join vectors on multiple lines */
00468     touchstone_join ();
00469 
00470     /* check each vector */
00471     errors += touchstone_vector_check ();
00472 
00473     /* check validity of ports and parameters */
00474     if ((touchstone_options.parameter == 'G' ||
00475          touchstone_options.parameter == 'H') &&
00476         touchstone_options.ports != 2) {
00477       logprint (LOG_ERROR, "checker error, %c-parameters for %d-ports not "
00478                 "defined\n", touchstone_options.parameter,
00479                 touchstone_options.ports);
00480       errors++;
00481     }
00482 
00483     /* check noise parameter compatibility */
00484     if (touchstone_options.noise && touchstone_options.ports != 2) {
00485       logprint (LOG_ERROR, "checker error, noise parameters for %d-ports not "
00486                 "defined\n", touchstone_options.ports);
00487       errors++;
00488     }
00489   }
00490 
00491   /* finally create a dataset */
00492   if (!errors) {
00493     touchstone_create ();
00494     touchstone_normalize ();
00495   }
00496 
00497 #if DEBUG
00498   /* emit little notify message on successful loading */
00499   if (!errors) {
00500     logprint (LOG_STATUS, "NOTIFY: touchstone %d-port %c-data%s loaded\n",
00501               touchstone_options.ports, touchstone_options.parameter,
00502               touchstone_options.noise ? " including noise" : "");
00503   }
00504 #endif
00505 
00506   /* free temporary memory */
00507   touchstone_finalize ();
00508 
00509   return errors ? -1 : 0;
00510 }
00511 
00512 // Destroys data used by the Touchstone file lexer, parser and checker.
00513 void touchstone_destroy (void) {
00514   if (touchstone_result != NULL) {
00515     // delete associated dataset
00516     delete touchstone_result;
00517     touchstone_result = NULL;
00518   }
00519   if (touchstone_vector != NULL) {
00520     touchstone_finalize ();
00521     touchstone_vector = NULL;
00522   }
00523 }
00524 
00525 // Initializes the Touchstone file checker.
00526 void touchstone_init (void) {
00527   touchstone_result = NULL;
00528   touchstone_vector = NULL;
00529   touchstone_idents = NULL;
00530 }