00001 // SuperMix version 1.0 C++ source file 00002 // 00003 // Copyright (c) 1999 California Institute of Technology. 00004 // All rights reserved. 00005 // 00006 // Redistribution and use in source and binary forms for noncommercial 00007 // purposes are permitted provided that the above copyright notice and 00008 // this paragraph are duplicated in all such forms and that any 00009 // documentation and other materials related to such distribution and 00010 // use acknowledge that the software was developed by California 00011 // Institute of Technology. Redistribution and/or use in source or 00012 // binary forms is not permitted for any commercial purpose. Use of 00013 // this software does not include a permitted use of the Institute's 00014 // name or trademark for any purpose. 00015 // 00016 // DISCLAIMER: 00017 // THIS SOFTWARE AND/OR RELATED MATERIALS ARE PROVIDED "AS-IS" WITHOUT 00018 // WARRANTY OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR 00019 // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE OR PURPOSE (AS SET 00020 // FORTH IN UCC 23212-2313) OR FOR ANY PURPOSE WHATSOEVER, FOR THE 00021 // LICENSED PRODUCT, HOWEVER USED. IN NO EVENT SHALL CALTECH/JPL BE 00022 // LIABLE FOR ANY DAMAGES AND/OR COSTS, INCLUDING BUT NOT LIMITED TO 00023 // INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING ECONOMIC 00024 // DAMAGE OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF 00025 // WHETHER CALTECH/JPL SHALL BE ADVISED, HAVE REASON TO KNOW, OR IN 00026 // FACT SHALL KNOW OF THE POSSIBILITY. THE USER BEARS ALL RISK 00027 // RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND/OR RELATED 00028 // MATERIALS. 00029 // 00030 // ******************************************************************** 00031 // 00032 // Changes: 00033 // 10/2/00: Fixed touchstone_read::ports() to correctly handle more 00034 // than 3 ports. Added cabability to read HFSS-output 00035 // files with more than 4 ports. 00036 // 6/29/00: Changed touchstone_read::open() to fail if an unrecognized 00037 // spec line format is encountered. 00038 00039 #include "io.h" 00040 #include "error.h" 00041 #include "units.h" 00042 #include "nport.h" 00043 #include <ctype.h> // for isspace() 00044 #include <strings.h> // for strchr() 00045 #include <stdlib.h> // for strtod() 00046 00047 // fuctions to skip over whitespace or nonwhitespace chars 00048 // of a null-terminated string: 00049 static void skipwhite(const char * & c) 00050 { while (*c && isspace(*c)) c++ ; return ; } 00051 static void skipnonwhite(const char * & c) 00052 { while (*c && !isspace(*c)) c++ ; return ; } 00053 00054 // function to convert a string to uppercase: 00055 static void up(std::string & s) 00056 { for(unsigned i = 0; i < s.length(); ++i) s[i] = toupper(s[i]); } 00057 00058 00059 // ******************************************************************** 00060 // data_parser: 00061 00062 std::string data_parser::def_delim_ = "#!" ; 00063 00064 data_parser::data_parser() : delim_(def_delim_) { } 00065 data_parser::data_parser(istream & f) : pin(&f), delim_(def_delim_) { } 00066 00067 unsigned data_parser::parse() 00068 { 00069 string temp = "" ; 00070 if(!pin) 00071 error::warning("data_parser: no input stream assigned."); 00072 00073 else if (!(*pin) && !(pin->eof())) 00074 error::warning("data_parser: some error occurred in input stream."); 00075 00076 else if (*pin) 00077 getline(*pin, temp) ; 00078 00079 // temp contains input line to parse, if any 00080 return parse(temp) ; 00081 } 00082 00083 unsigned data_parser::skip_parse() 00084 { 00085 unsigned n = 0; 00086 while ((*pin) && 0 == (n = parse())) ; 00087 return n ; 00088 } 00089 00090 unsigned data_parser::parse(const std::string & inp) 00091 { 00092 // clear out previous line results; copy in new line 00093 comment_ = "" ; 00094 data_.erase(data_.begin(),data_.end()) ; 00095 line_ = inp ; 00096 00097 const char * c = line_.c_str(); 00098 00099 // here is the line parser for uncommented doubles: 00100 while (*c) { 00101 skipwhite(c); // skip to next token 00102 00103 // a comment char effectively terminates the line 00104 if ( strchr(delim_.c_str(), *c) ) { 00105 // *c is a comment delimiter 00106 comment_ = c ; // comment_ gets the remainder of the line 00107 break ; 00108 } 00109 00110 // try to convert this token to a double 00111 char * next ; 00112 double value = strtod(c, & next); 00113 if ( next == c ) { 00114 // then it's not a double, so skip it 00115 skipnonwhite(c); 00116 continue; 00117 } 00118 00119 // found a double, contained in value 00120 data_.push_back(value); 00121 c = next; // skip over the parsed characters 00122 } 00123 00124 return data_.size() ; 00125 } 00126 00127 unsigned data_parser::parse(const char * const s) 00128 { 00129 string temp = (!s) ? s : "" ; // use a null string if a null pointer 00130 return parse(temp) ; 00131 } 00132 00133 const std::vector<string> & data_parser::tokens() 00134 { 00135 tokens_.erase(tokens_.begin(),tokens_.end()) ; 00136 00137 // here is the line parser for more general tokens: 00138 const char * c = line_.c_str(); // beginning of line 00139 while (*c) { 00140 skipwhite(c); // skip to next token 00141 const char * next = c; 00142 skipnonwhite(next); // c and next bracket the next token, if any 00143 if(next == c) break; // no more tokens 00144 string tok(c, next-c); 00145 tokens_.push_back(tok); 00146 c = next; 00147 } 00148 00149 return tokens_ ; 00150 } 00151 00152 int data_parser::convert(real_vector & v, double u) 00153 { 00154 int m = int(data_.size()) ; 00155 if (m <= 0) return 0 ; 00156 00157 if (v.maxindex(m) < m) v.resize(m); 00158 for (int i = 0; i < m; ++i) v.get(i+1) = u * data_[unsigned(i)]; 00159 return m; 00160 } 00161 00162 int data_parser::convert(complex_vector & v, double u) 00163 { 00164 bool pushed = false; 00165 if(data_.size() % 2 == 1) { 00166 data_.push_back(0.0); // need an even number of values 00167 pushed = true; 00168 } 00169 00170 int m = int(data_.size()/2) ; 00171 if (m <= 0) return 0 ; 00172 00173 if (v.maxindex(m) < m) v.resize(m); 00174 for (int i = 0; i < m; ++i) 00175 v.get(i+1) = u * dtoz(data_[2*unsigned(i)],data_[2*unsigned(i)+1]); 00176 00177 if (pushed) data_.pop_back() ; // remove the added 0.0 00178 return m; 00179 } 00180 00181 int data_parser::convert(double & x, real_vector & v, double u1, double u2) 00182 { 00183 int m = int(data_.size()) - 1 ; 00184 if (m <= 0) return 0 ; 00185 00186 x = u1 * data_[0]; 00187 if (v.maxindex(m) < m) v.resize(m); 00188 for (int i = 1; i <= m; ++i) v.get(i) = u2 * data_[unsigned(i)]; 00189 return m; 00190 } 00191 00192 int data_parser::convert(double & x, complex_vector & v, double u1, double u2) 00193 { 00194 int m = int(data_.size()) - 1 ; 00195 if (m <= 0) return 0 ; 00196 00197 bool pushed = false; 00198 x = u1 * data_[0]; 00199 if (m % 2 == 1) { 00200 data_.push_back(0.0); // need an even number of values here 00201 ++m; 00202 pushed = true; 00203 } 00204 00205 m /= 2; 00206 if (v.maxindex(m) < m) v.resize(m); 00207 for (int i = 1; i <= m; ++i) 00208 v.get(i) = u2 * dtoz(data_[2*unsigned(i)-1],data_[2*unsigned(i)]); 00209 00210 if (pushed) data_.pop_back() ; // remove the added 0.0 00211 return m; 00212 } 00213 00214 // ******************************************************************** 00215 // touchstone_read: 00216 00217 touchstone_read::touchstone_read() 00218 : N(0), verbose_(true), 00219 f_units(GHz), z_(50*Ohm), type_(S), f_last(-1.0), mode(Complex::degree) 00220 { 00221 d.delim() = "!" ; 00222 } 00223 00224 bool touchstone_read::open(const char * const str, int ports, double f_scale) 00225 { 00226 // reset private variables 00227 if(s.is_open()) s.close(); 00228 N = 0; f_units = GHz; z_ = 50*Ohm; 00229 type_ = S; f_last = -1.0; mode = Complex::degree; 00230 end_ = false; noise_ = false; 00231 name_ = str; 00232 00233 // use the provided frequency scaling, if ok 00234 if(f_scale > 0.0) 00235 f_units = f_scale; 00236 else 00237 if(verbose_) 00238 error::warning("touchstone_read::open(): improper frequency scaling argument. Using GHz."); 00239 00240 // is the number of ports provided ok? 00241 if(ports <= 0) { 00242 if(verbose_) 00243 error::warning("touchstone_read::open(): must call with ports > 0"); 00244 return false; 00245 } 00246 00247 // can we open the file? 00248 s.open(str); 00249 if(!s) { 00250 if(verbose_) 00251 error::warning("touchstone_read::open(): could not open" , name_); 00252 return false; 00253 } 00254 00255 // ok, let's look for some data or a specification line... 00256 d.input(s); 00257 unsigned n; 00258 std::vector<string> token; 00259 00260 // parse lines until we find a number, a spec line, or reach the end: 00261 do { 00262 n = d.parse(); 00263 token = d.tokens(); 00264 } while(s && n == 0 && (token.size() == 0 || token[0] != "#")); 00265 00266 if(!s) { 00267 // file has no data! 00268 if(verbose_) 00269 error::warning("touchstone_read::open(): found no data in file", name_ ); 00270 end_ = true; 00271 return false; 00272 } 00273 00274 // The line just parsed may be the "specification line" of the file. If present, 00275 // the line should be like: "# [GHZ/MHZ/KHZ/HZ] [S/Y/Z] [MA/DB/RI] [R n]". 00276 00277 if(token[0] != "#") { 00278 // it's not a specification line, so we'll use default values already set 00279 N = ports; 00280 return true; 00281 } 00282 00283 // Found a specification line with at least one entry. Try to parse it... 00284 unsigned i = 1; 00285 00286 if(i >= token.size()) goto cleanup; // no more entries 00287 up(token[i]); 00288 00289 // first is the frequency unit, if present 00290 if(token[i] == "GHZ") { 00291 f_units = GHz; 00292 ++i; 00293 } 00294 else if(token[i] == "MHZ") { 00295 f_units = MHz; 00296 ++i; 00297 } 00298 else if(token[i] == "KHZ") { 00299 f_units = Kilo*Hertz; 00300 ++i; 00301 } 00302 else if(token[i] == "HZ") { 00303 f_units = Hertz; 00304 ++i; 00305 } 00306 00307 if(i >= token.size()) goto cleanup; // no more entries 00308 up(token[i]); 00309 00310 // next is the type of the data matrix 00311 if(token[i] == "S") { 00312 type_ = S; 00313 ++i; 00314 } 00315 else if(token[i] == "Y") { 00316 type_ = Y; 00317 ++i; 00318 } 00319 else if(token[i] == "Z") { 00320 type_ = Z; 00321 ++i; 00322 } 00323 00324 if(i >= token.size()) goto cleanup; // no more entries 00325 up(token[i]); 00326 00327 // next is the complex number format 00328 if(token[i] == "MA") { 00329 mode = complex::degree; 00330 ++i; 00331 } 00332 else if(token[i] == "DB") { 00333 mode = complex::db; 00334 ++i; 00335 } 00336 else if(token[i] == "RI") { 00337 mode = complex::cartesian; 00338 ++i; 00339 } 00340 00341 if(i >= token.size()) goto cleanup; // no more entries 00342 up(token[i]); 00343 00344 // finally the normalizing impedance 00345 if(token[i] == "R" && n == 1) { 00346 z_ = d.data()[0]*Ohm; 00347 if(z_ > 0.0) 00348 i += 2; // skip over impedance entry only if it is proper 00349 else 00350 z_ = 50*Ohm; 00351 } 00352 00353 if(i < token.size()) { 00354 // there are some unrecognized entries still in spec line 00355 if(verbose_) 00356 error::warning("touchstone_read::open(): spec line has unrecognized syntax in", name_); 00357 return false; 00358 } 00359 00360 cleanup: 00361 N = ports; 00362 d.skip_parse(); // skip to the data lines 00363 return true; 00364 } 00365 00366 00367 bool touchstone_read::value(double & freq, Matrix & M) 00368 { 00369 // On entry, d will already have parsed the next data line using d.skip_parse() 00370 00371 // ready to rumble? 00372 if((N == 0) || end_) return false; 00373 if(!s && !s.eof()) { 00374 // something very wrong 00375 if(verbose_) 00376 error::warning("touchstone_read::value(): something failed while reading", name_); 00377 N = 0; s.close(); return false; 00378 } 00379 if(!s || (d.data().size() == 0)) { 00380 end_ = true; s.close(); return false; 00381 } 00382 00383 // Have we reached "noise data"? 00384 if(d.data()[0]*f_units <= f_last) { 00385 end_ = true; noise_ = (N==2); return false; 00386 } 00387 00388 // We are ready to go... 00389 double f; // the frequency read from file 00390 Vector L; // the matrix data from a single line of file 00391 Matrix T(N, Index_1); // temporary matrix constructed from file data 00392 00393 old_mode = Complex::in_mode(mode); // set the complex format, saving previous 00394 00395 switch (N) { 00396 case 1: 00397 case 2: { 00398 // in these cases, all data is on one line 00399 if((d.convert(f,L,f_units) != N*N) || (f < 0.0)) goto bad; // something's wrong 00400 int k = 1; 00401 for(int i = 1; i <= N; ++i) for(int j = 1; j <= N; ++j) T[j][i] = L[k++]; 00402 break; 00403 } 00404 00405 case 3: 00406 case 4: { 00407 // N == 3 or N == 4, data on multiple lines, one row per line 00408 if((d.convert(f,L,f_units) != N) || (f < 0.0)) goto bad; // something's wrong 00409 // first row 00410 for(int j = 1; j <= N; ++j) T[1][j] = L[j]; 00411 // other rows 00412 for(int i = 2; i <= N; ++i) { 00413 d.skip_parse(); 00414 if(d.convert(L) != N) goto bad; 00415 for(int j = 1; j <= N; ++j) T[i][j] = L[j]; 00416 } 00417 break; 00418 } 00419 00420 default: { 00421 // N > 4, data on multiple lines, but not sure how many points/line 00422 00423 // Interpret this first line, saving the count of the S entries 00424 int P = d.convert(f,L,f_units); 00425 if((P < 4) || (P > N) || (f < 0.0)) goto bad; // something's wrong 00426 00427 // O.K., now we have the first P values of the first row of S 00428 int Pmax = P; // max entries per subsequent line 00429 00430 // Build T by reading in the rows 00431 for (int R = 1; R <= N; ++R) { 00432 // At the start of each iteration, the next input line is already parsed 00433 // Copy entries from this and subsequent lines into T to complete a row 00434 for (int C = 0; C < N; ) { 00435 // Note that we start with C = 0, not C = 1 00436 // Copy the parsed line values into T: 00437 for(int j = 1; j <= P; ++j) T[R][++C] = L[j]; 00438 if(C == N) break; // finished the row 00439 00440 // The row's not finished. Read the next line: 00441 d.skip_parse(); 00442 P = d.convert(L); 00443 // Check for an unexpected number of entries: 00444 if((P == 0) || (P > Pmax) || (C+P > N)) goto bad; 00445 } 00446 00447 // Finished this row. Is it the last? 00448 if(R == N) break; 00449 00450 // More rows to go... Get the next line 00451 d.skip_parse(); 00452 P = d.convert(L); 00453 // Check for an unexpected number of entries: 00454 if(P == 0 || P > Pmax) goto bad; 00455 } 00456 break; 00457 } 00458 } // switch 00459 00460 // normal exit, everything ok 00461 d.skip_parse(); 00462 Complex::in_mode(old_mode); 00463 freq = f_last = f; 00464 M = normalize(T); 00465 return true; 00466 00467 // abnormal exit, improper line format 00468 bad: 00469 if(verbose_) { 00470 error::warning("touchstone_read::value(): unexpected number of entries" 00471 " in a line of file", name_); 00472 error::stream() << "Number of Ports expected: " << N << endl 00473 << "Data line:" << endl << d.line() << endl; 00474 } 00475 Complex::in_mode(old_mode); 00476 s.close(); 00477 N = 0; 00478 return false; 00479 } 00480 00481 00482 bool touchstone_read::Svalue(double & freq, Matrix & M) 00483 { 00484 // if file contains S data, value() is all we need. 00485 if(type_ == S) return value(freq,M); 00486 00487 // get freq and M, if file is still belching data; if not, return false 00488 if (!value(freq,M)) return false; 00489 00490 // freq is correct, but M is a Z or Y instead of S; we must fix 00491 Matrix i = identity_matrix(N); 00492 i *= (type_ == Z) ? double(device::Z0) : 1.0/device::Z0 ; 00493 M = solve((i + M), (type_ == Z) ? (M - i) : (i - M)); 00494 return true; 00495 } 00496 00497 00498 bool touchstone_read::Yvalue(double & freq, Matrix & M) 00499 { 00500 // if file contains Y data, value() is all we need. 00501 if(type_ == Y) return value(freq,M); 00502 00503 // get freq and M, if file is still belching data; if not, return false 00504 if (!value(freq,M)) return false; 00505 00506 // freq is correct, but M is a Z or S instead of Y; we must fix 00507 Matrix i = identity_matrix(N); 00508 if (type_ == Z) 00509 M = solve(M, i); // just invert for Z -> Y 00510 else 00511 M = (1/device::Z0) * solve((i + M), (i - M)); // type_ is S 00512 return true; 00513 } 00514 00515 00516 bool touchstone_read::Zvalue(double & freq, Matrix & M) 00517 { 00518 // if file contains Z data, value() is all we need. 00519 if(type_ == Z) return value(freq,M); 00520 00521 // get freq and M, if file is still belching data; if not, return false 00522 if (!value(freq,M)) return false; 00523 00524 // freq is correct, but M is a Y or S instead of Z; we must fix 00525 Matrix i = identity_matrix(N); 00526 if (type_ == Y) 00527 M = solve(M, i); // just invert for Y -> Z 00528 else 00529 M = device::Z0 * solve((i - M), (i + M)); // type_ is S 00530 return true; 00531 } 00532 00533 00534 Matrix touchstone_read::normalize(const Matrix & T) 00535 { 00536 switch(type_) { 00537 00538 case Z: 00539 return z_ * T ; break ; 00540 00541 case Y: 00542 return (1/z_) * T ; break ; 00543 00544 default: 00545 case S: 00546 if(z_ == device::Z0) 00547 return T; 00548 else { 00549 // assumes T has size N with indexing Index_1 00550 double r = (z_ - device::Z0)/(z_ + device::Z0); 00551 Matrix i = identity_matrix(N); 00552 return solve((i + r*T), (r*i + T)); 00553 } 00554 break ; 00555 00556 } 00557 } 00558 00559 00560 bool touchstone_read::get_noise(double & f, noise & ND) 00561 { 00562 // On entry, d will already have parsed the next data line using d.skip_parse() 00563 if(!has_noise()) return false; 00564 if(!s && !s.eof()) { 00565 // something very wrong 00566 if(verbose_) 00567 error::warning("touchstone_read::get_noise(): something failed while reading", name_); 00568 N = 0; s.close(); return false; 00569 } 00570 if(!s || (d.data().size() == 0)) { 00571 noise_ = false; s.close(); return false; 00572 } 00573 00574 // Is format of noise data correct? 00575 if(d.data().size() != 5) { 00576 if(verbose_) 00577 error::warning("touchstone_read::get_noise(): couldn't interpret noise data in", name_); 00578 N = 0; s.close(); return false; 00579 } 00580 00581 // ok, fill in the data 00582 f = d.data()[0]*f_units ; 00583 ND.Fmin = d.data()[1] ; 00584 ND.Reff = d.data()[4]*z_ ; 00585 00586 // when we read Gopt, we must renormalize it to device::Z0 00587 old_mode = Complex::in_degree(); 00588 ND.Gopt = dtoz(d.data()[2],d.data()[3]); 00589 double r = (z_ - device::Z0)/(z_ + device::Z0); 00590 ND.Gopt = (r + ND.Gopt)/(1 + r*ND.Gopt); 00591 Complex::in_mode(old_mode); 00592 00593 // read the next line and exit 00594 d.skip_parse(); 00595 return true; 00596 } 00597 00598 00599 int touchstone_read::ports(const char * const name) 00600 { 00601 touchstone_read f; 00602 00603 // First check for more than 4 ports, since these may look like 4-ports 00604 // to a naive check: 00605 if (!f.open(name, 5)) // we don't actually expect 5 ports 00606 // Couldn't open or interpret the spec line; a warning will be issued. 00607 // Exit function returning 0. 00608 return 0; 00609 00610 // Now the data parser in f has moved to the first line of data 00611 double freq; 00612 Vector L; 00613 int max = int(f.d.data().size()); // the number of doubles on this line. 00614 00615 // Count the number of elements; stop if we get to the next freq point 00616 int N_sq = f.d.convert(freq,L); 00617 while (true) { 00618 int n = f.d.skip_parse(); 00619 if(n == 0 || n >= max) break; // finished this frequency 00620 N_sq += f.d.convert(L); // add the elements to the count 00621 } 00622 00623 // Now N_sq has the square of the number of ports. 00624 if (N_sq >= 5*5) { 00625 // we know we have to have at least a 5-port 00626 int N = int(sqrt(double(N_sq)) + .5); // round the sqrt to nearest int 00627 if (N*N == N_sq) 00628 return N; // success! 00629 else { 00630 error::warning("touchstone_read::ports(): number of ports could not be " 00631 "determined in file", name); 00632 return 0; 00633 } 00634 } 00635 00636 // Now we test for 4 or fewer ports 00637 for(int i = 4; i > 0; --i) { 00638 00639 // attempt to open file with <i> ports 00640 if (!f.open(name, i)) 00641 // couldn't open or interpret the spec line; a warning will be issued 00642 // exit function returning 0 00643 return 0; 00644 00645 // issue no warnings for the next check, since we don't know the number of ports 00646 f.quiet(); 00647 double freq; 00648 Matrix M; 00649 if (f.value(freq, M)) 00650 // successfully read the data for an <i>-port; exit function returning i 00651 return i; 00652 f.verbose(); 00653 } 00654 00655 // hmm... if we get here, then we don't have a clue. 00656 error::warning("touchstone_read::ports(): number of ports could not be " 00657 "determined in file", name); 00658 return 0; 00659 }
Please direct comments and corrections to
supermix@submm.caltech.edu
Go to the supermix home page
Generated by
1.2.7