Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

io.cc

Go to the documentation of this file.
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 doxygen1.2.7