Qucs-core
0.0.19
|
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 }