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

analyze.cc

Go to the documentation of this file.
00001 // analyze.cc
00002 // SuperMix version 1.0  C++ source file
00003 //
00004 // Copyright (c) 1999 California Institute of Technology.
00005 // All rights reserved.
00006 //
00007 // Redistribution and use in source and binary forms for noncommercial
00008 // purposes are permitted provided that the above copyright notice and
00009 // this paragraph are duplicated in all such forms and that any
00010 // documentation and other materials related to such distribution and
00011 // use acknowledge that the software was developed by California
00012 // Institute of Technology. Redistribution and/or use in source or
00013 // binary forms is not permitted for any commercial purpose. Use of
00014 // this software does not include a permitted use of the Institute's
00015 // name or trademark for any purpose.
00016 //
00017 // DISCLAIMER:
00018 // THIS SOFTWARE AND/OR RELATED MATERIALS ARE PROVIDED "AS-IS" WITHOUT
00019 // WARRANTY OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR
00020 // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE OR PURPOSE (AS SET
00021 // FORTH IN UCC 23212-2313) OR FOR ANY PURPOSE WHATSOEVER, FOR THE
00022 // LICENSED PRODUCT, HOWEVER USED.  IN NO EVENT SHALL CALTECH/JPL BE
00023 // LIABLE FOR ANY DAMAGES AND/OR COSTS, INCLUDING BUT NOT LIMITED TO
00024 // INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING ECONOMIC
00025 // DAMAGE OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF
00026 // WHETHER CALTECH/JPL SHALL BE ADVISED, HAVE REASON TO KNOW, OR IN
00027 // FACT SHALL KNOW OF THE POSSIBILITY.  THE USER BEARS ALL RISK
00028 // RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND/OR RELATED
00029 // MATERIALS.
00030 // ********************************************************************
00031 // Change History:
00032 // 8/10/99:  fixed an indexing bug in operator() which caused core dumps
00033 //
00034 // ********************************************************************
00035 
00036 #include "mixer.h"
00037 #include "units.h"
00038 #include "error.h"
00039 #include "Amath.h"
00040 #include <math.h>
00041 
00042 // ********************************************************************
00043 // analyzer public functions:
00044 
00045 mixer::analyzer::analyzer(mixer & m) :
00046   result(0),
00047   mix(m),
00048   must_rebuild(1),
00049   terminate_flag(0),
00050   linear_p(0),
00051   linear_m(0),
00052   junctions(0)
00053 { }
00054 
00055 
00056 mixer::analyzer & mixer::analyzer::terminate_rf(int f)
00057 {
00058   terminate_flag = (f != 0);
00059   changed();
00060   return *this;
00061 }
00062 
00063 
00064 const sdata & mixer::analyzer::operator()()
00065 {
00066   rebuild(); fill_data(); calc_T(); calc_X(); calc_Y();
00067 
00068   // loop over all elements in the result sdata
00069   int h1, h2, n1, n2, m, h;
00070 
00071   for (h1 = h_low; h1 <= h_high; ++h1) {
00072     int n1_high = n_high(h1);  // could be different for h1 == 0
00073 
00074     for (h2 = h_low; h2 <= h_high; ++h2) {
00075       int n2_high = n_high(h2);
00076 
00077       for (n1 = n_low; n1 <= n1_high; ++n1) {
00078         for (n2 = n_low; n2 <= n2_high; ++n2) {
00079 
00080           // avoid the following index dereferences inside the inner loops
00081           complex & Sr = S(n1,n2,h1,h2);
00082           complex & Cr = C(n1,n2,h1,h2);
00083           complex * X_n1_h1 = Xrow(n1,h1);
00084           complex * X_n2_h2 = Xrow(n2,h2);
00085           complex * Y_n1_h1 = Yrow(n1,h1);
00086           complex * Y_n2_h2 = Yrow(n2,h2);
00087           
00088           Sr = (h1==h2)? S11(n1,n2,h2) : 0.0;
00089           Cr = (h1==h2)? C11(n1,n2,h2) : 0.0;
00090 
00091           for (m = m_low; m <= m_high; ++m) {
00092             complex Ytemp = Y_n1_h1[Ycol(m,h2)];  // Y(n1,m,h1,h2)
00093 
00094             Sr += Ytemp*S21(m,n2,h2);  // that's it for the S matrix!
00095 
00096             Cr += Ytemp*C21(m,n2,h2) + conj(Y_n2_h2[Ycol(m,h1)]*C21(m,n1,h1));
00097             for (h = h_low; h <= h_high; ++h) {
00098 
00099               //Commented out; using Adot instead:
00100               //register complex sum(0.0);
00101               //register int i;
00102               //for (i = h_low; i <= h_high; ++i)
00103               //  sum += Cj(m,h,i)*conj(X_n2_h2[Xcol(m,i)]);
00104               //Cr += X_n1_h1[Xcol(m,h)]*sum;
00105               //sum = 0.0;
00106               //for (i = m_low; i <= m_high; ++i)
00107               //  sum += C22(m,i,h)*conj(Y_n2_h2[Ycol(i,h)]);
00108               //Cr += Y_n1_h1[Ycol(m,h)]*sum;
00109 
00110               Cr += X_n1_h1[Xcol(m,h)]*
00111                 Adot(&(X_n2_h2[Xcol(m,h_low)]), &Cj(m,h,h_low), h_size);
00112               Cr += Y_n1_h1[Ycol(m,h)]*
00113                 Adot(&(Y_n2_h2[Ycol(m_low,h)]), &C22(m,m_low,h), m_high);
00114 
00115             }// h
00116           }  // m
00117         }    // n2
00118       }      // n1
00119     }        // h2
00120   }          // h1
00121 
00122   return result;
00123  
00124 } // operator()
00125 
00126 
00127 // ********************************************************************
00128 // rebuild() and fill_data():
00129 
00130 void mixer::analyzer::rebuild()
00131 {
00132   if(!must_rebuild) return; // not necessary to rebuild
00133 
00134   must_rebuild = 0;
00135 
00136   int i, s;  // used to hold indexes or sizes
00137 
00138   // the index limits:
00139   int nj = mix.num_junctions;  // will need this often
00140   h_high = mix.max_harmonics;
00141   h_low = -h_high;
00142   h_size = 2*mix.max_harmonics + 1;
00143   n_low = nj + 1;  // first external port index
00144   // *** CHANGING m_low WILL REQUIRE CHANGES TO NEARLY ALL HELPER FUNCTIONS!!! ***
00145   m_low = 1;       // starting junction indexing from 1 vice 0
00146   m_high = nj;
00147 
00148   // size the STL vectors:
00149   linear_p.resize(h_high + 1);      // linear_p[0] will hold IF sdata
00150   linear_m.resize(h_high + 1);      // linear_m[0] will be unused
00151   junctions.resize(m_high + m_low); // junctions[m_low] will be lowest used
00152 
00153   // size the sdata elements of the STL vectors
00154   s = (terminate_flag)? nj : mix.rf_circuit->size();
00155   for (i = 1; i <= h_high; ++i) {
00156     // the RF sdatas
00157     linear_p[i].resize(s);
00158     linear_m[i].resize(s);
00159   }
00160   linear_p[0].resize(mix.if_circuit->size()); // the IF sdata
00161   for (i = m_low; i <= m_high; ++i) {
00162     // the junction sdatas
00163     if (junctions[i].mode() != Index_S) {
00164       junctions[i].S.reallocate(h_high,h_high,Index_S,Index_S);
00165       junctions[i].C.reallocate(h_high,h_high,Index_S,Index_S);
00166       junctions[i].B.reallocate(h_high,Index_S);
00167     }
00168     else {
00169       junctions[i].resize(h_high);
00170     }
00171   }
00172 
00173   // if necessary, rebuild the terminated rf circuit:
00174   if(terminate_flag) {
00175     int n;
00176     int num_ports = mix.rf_circuit->size() - nj;
00177     temp = circuit();  // clear out old construction
00178     for (n = 0; n < num_ports; ++n) {
00179       // connect terminators to external RF ports
00180       int p = nj + n + 1;
00181       temp.connect(*(mix.rf_circuit), p, 
00182                    ((mix.term[p-1])? *(mix.term[p-1]) : mix.default_term[n]), 1);
00183     }
00184     for (n = 1; n <= nj; ++n)
00185       temp.add_port(*(mix.rf_circuit), n); // ports to connect to junctions
00186   }
00187 
00188   // size the result sdata, and T_, X_, Y_
00189   s = (terminate_flag) ?
00190     mix.if_circuit->size() - nj :  // only IF ports are open
00191     mix.size();                    // all external ports are open
00192   result.resize(s);
00193   result.S.maximize();
00194   result.C.maximize();
00195   result.B.fillall(0.0).maximize(); // ensures source vector is all 0's.
00196   T_.reallocate(nj*h_size, nj*h_size, Index_1, Index_1).maximize();
00197   int max_ports;
00198   if (terminate_flag)
00199     max_ports = s;
00200   else
00201     max_ports = (mix.rf_circuit->size() > mix.if_circuit->size()) ?
00202       mix.rf_circuit->size() - nj : mix.if_circuit->size() - nj;
00203   X_.reallocate(max_ports*h_size, nj*h_size, Index_C, Index_1).maximize();
00204   Y_.reallocate(max_ports*h_size, nj*h_size, Index_C, Index_1).maximize();
00205 
00206 } // rebuild()   
00207 
00208 
00209 void mixer::analyzer::fill_data()
00210 {
00211   parameter IF_saved = device::f;
00212   double LO = mix.LO;
00213   const sdata * ps;
00214 
00215   // the IF circuit:
00216   ps = & mix.if_circuit->get_data();
00217   linear_p[0].S.copy(ps->S);
00218   linear_p[0].C.copy(ps->C);
00219 
00220   // the RF circuit:
00221   nport * rf = (terminate_flag) ? & temp : mix.rf_circuit;
00222   double fp = LO + IF_saved;  // harmonic = 1  (USB freq)
00223   double fm = LO - IF_saved;  // harmonic = -1 (LSB freq)
00224   for (int h = 1; h <= h_high; ++h, fp += LO, fm += LO) {
00225 
00226     // the + harmonics:
00227     device::f = fp;
00228     ps = & rf->get_data();
00229     linear_p[h].S.copy(ps->S);
00230     linear_p[h].C.copy(ps->C);
00231 
00232     // the - harmonics:
00233     device::f = fm;
00234     ps = & rf->get_data();
00235     linear_m[h].S.copy(ps->S).apply(conj);
00236     linear_m[h].C.copy(ps->C).apply(conj);
00237     // negative frequencies require conjugates
00238 
00239   }
00240   device::f = IF_saved;
00241 
00242   // the small_signal responses of the junctions:
00243   for (int m = m_low; m <= m_high; ++m) {
00244     junction * pj = mix.junc[m-m_low];
00245     junctions[m] = ydata_ptr(& pj->small_signal(IF_saved, h_high),
00246                              & pj->noise(IF_saved, device::T, h_high));
00247   }
00248 
00249 }
00250 
00251 
00252 // ********************************************************************
00253 // calc_T(), calc_X(), calc_Y():
00254 
00255 void mixer::analyzer::calc_T()
00256 {
00257   register int m2, m1, h2, h1;
00258 
00259   for (h1 = h_low; h1 <= h_high; ++h1) {
00260     for (h2 = h_low; h2 <= h_high; ++h2) {
00261       register int d = Delta(h1,h2);
00262       for (m1 = m_low; m1 <= m_high; ++m1) {
00263         register complex sj = Sj(m1,h1,h2);
00264         for (m2 = m_low; m2 <= m_high; ++m2) {
00265           T(m1,m2,h1,h2) = Delta(m1,m2)*d - sj*S22(m1,m2,h2);
00266   }}}}
00267   T_ = inverse(T_);
00268 }
00269 
00270 
00271 void mixer::analyzer::calc_X()
00272   // must execute calc_T() before calling calc_X()
00273 {
00274   register int m, m2, h2;
00275   int n, h1;
00276   const int nj = m_high - m_low + 1;  // number of junctions
00277 
00278   for (h1 = h_low; h1 <= h_high; ++h1) {
00279     int nh = n_high(h1); 
00280       for (n = n_low; n <= nh; ++n) {
00281         register complex * s12 = & S12(n,m_low,h1);
00282         for (h2 = h_low; h2 <= h_high; ++h2) {
00283           for (m2 = m_low; m2 <= m_high; ++m2) {
00284             register complex * t = & T(m_low,m2,h1,h2);
00285             register complex * x = & X(n,m2,h1,h2);
00286             (*x) = 0.0;
00287             for (m = 0; m < nj; ++m)
00288               (*x) += s12[m]*t[m];
00289   }}}}
00290 }
00291 
00292 
00293 void mixer::analyzer::calc_Y()
00294   // must execute calc_X() before calling calc_Y()
00295 {
00296   register int h, m, h2;
00297   int n, h1;
00298 
00299   for (h1 = h_low; h1 <= h_high; ++h1) {
00300     int nh = n_high(h1); 
00301       for (n = n_low; n <= nh; ++n) {
00302         register complex * xr = Xrow(n,h1);
00303         register complex * yr = Yrow(n,h1);
00304         for (h2 = h_low; h2 <= h_high; ++h2) {
00305           for (m = m_low; m <= m_high; ++m) {
00306             register complex * y = & yr[Ycol(m,h2)];
00307             (*y) = 0.0;
00308             for (h = h_low; h <= h_high; ++h)
00309               (*y) += xr[Xcol(m,h)]*Sj(m,h,h2);
00310   }}}}
00311 }
00312 
00313 
00314 // ********************************************************************
00315 // other analyzer private helper functions (matrix element access):
00316 // MANY OF THESE FUNCTIONS REQUIRE THAT m_low == 1
00317 
00318 int mixer::analyzer::n_high(int h)
00319 {
00320   if (h == 0)
00321     return mix.if_circuit->size();
00322 
00323   return (terminate_flag)? 0 : mix.rf_circuit->size();
00324 }
00325 
00326 
00327 // the following functions are used to access the linear element sdatas:
00328 
00329 inline complex & mixer::analyzer::S11(int n1, int n2, int h)
00330 { return (h < 0) ? linear_m[-h].S[n1][n2] : linear_p[h].S[n1][n2]; }
00331 
00332 
00333 inline complex & mixer::analyzer::S12(int n, int m, int h)
00334   // requires m_low == 1; if m_low == 0, use m+1 vice m below:
00335 { return (h < 0) ? linear_m[-h].S[n][m] : linear_p[h].S[n][m]; }
00336 
00337 
00338 inline complex & mixer::analyzer::S21(int m, int n, int h)
00339   // requires m_low == 1; if m_low == 0, use m+1 vice m below:
00340 { return (h < 0) ? linear_m[-h].S[m][n] : linear_p[h].S[m][n]; }
00341 
00342 
00343 inline complex & mixer::analyzer::S22(int m1, int m2, int h)
00344   // requires m_low == 1; if m_low == 0, use m+1 vice m below:
00345 { return (h < 0) ? linear_m[-h].S[m1][m2] : linear_p[h].S[m1][m2]; }
00346 
00347 
00348 inline complex & mixer::analyzer::C11(int n1, int n2, int h)
00349 { return (h < 0) ? linear_m[-h].C[n1][n2] : linear_p[h].C[n1][n2]; }
00350 
00351 
00352 inline complex & mixer::analyzer::C12(int n, int m, int h)
00353   // requires m_low == 1; if m_low == 0, use m+1 vice m below:
00354 { return (h < 0) ? linear_m[-h].C[n][m] : linear_p[h].C[n][m]; }
00355 
00356 
00357 inline complex & mixer::analyzer::C21(int m, int n, int h)
00358   // requires m_low == 1; if m_low == 0, use m+1 vice m below:
00359 { return (h < 0) ? linear_m[-h].C[m][n] : linear_p[h].C[m][n]; }
00360 
00361 
00362 inline complex & mixer::analyzer::C22(int m1, int m2, int h)
00363   // requires m_low == 1; if m_low == 0, use m+1 vice m below:
00364 { return (h < 0) ? linear_m[-h].C[m1][m2] : linear_p[h].C[m1][m2]; }
00365 
00366 
00367 // the following functions are used to access the junction sdatas
00368 
00369 inline complex & mixer::analyzer::Sj(int m, int h1, int h2)
00370 { return junctions[m].S[h1][h2]; } 
00371 
00372 inline complex & mixer::analyzer::Cj(int m, int h1, int h2)
00373 { return junctions[m].C[h1][h2]; } 
00374 
00375 
00376 // the following functions are used to access the result sdata
00377 // note that the index mapping is determined by mixer::port()
00378 
00379 inline complex & mixer::analyzer::S(int n1, int n2, int h1, int h2)
00380 { return result.S[mix.port(n1,h1)][mix.port(n2,h2)]; }
00381 
00382 
00383 inline complex & mixer::analyzer::C(int n1, int n2, int h1, int h2)
00384 { return result.C[mix.port(n1,h1)][mix.port(n2,h2)]; }
00385 
00386 
00387 // the following functions are used to access the T_, X_, Y_ matrices
00388 // THE FOLLOWING FUNCTIONS WILL REQUIRE CHANGES IF THE INDEX MODES OF
00389 // T_, X_, Y_, OR THE VALUE OF m_low ARE CHANGED IN rebuild().
00390 
00391 
00392 inline complex & mixer::analyzer::T(int m1, int m2, int h1, int h2)
00393   // T_ indexing is grouped as follows:
00394   // Lindex: m2,h2; Rindex: m1,h1  (ie, m2,h2 select the row of T_)
00395   // Lindex and Rindex:
00396   //   Grouped by h, then ordered by m within each h
00397   //   with Index_1 and m_low == 1, lowest element has (m,h) == (1,h_low)
00398   // So elements with constant m2,h1,h2 and varying m1 are contiguous
00399 { return T_[(h2+h_high)*m_high + m2][(h1+h_high)*m_high + m1]; }
00400 
00401 
00402 inline complex & mixer::analyzer::X(int n, int m, int h1, int h2)
00403   // X_ indexing is grouped as follows:
00404   // Lindex: n,h1; Rindex: m,h2  (ie, n,h1 select the row of X_)
00405   // Lindex:
00406   //   Grouped by n, then ordered by h1 within each n
00407   //   with Index_C, lowest element has (n,h1) == (n_low,h_low)
00408   // Rindex:
00409   //   Grouped by m, then ordered by h2 within each m
00410   //   with Index_1, and m_low == 1, lowest element has (m,h2) == (1,h_low)
00411   // So elements with constant n,m,h1 and varying h2 are contiguous
00412 { return X_[(n-n_low)*h_size + h1+h_high][m*h_size + h2-h_high];  }
00413 
00414 
00415 inline complex & mixer::analyzer::Y(int n, int m, int h1, int h2)
00416   // Y_ indexing is grouped as follows:
00417   // Lindex: n,h1; Rindex: m,h2  (ie, n,h1 select the row of Y_)
00418   // Lindex:
00419   //   Grouped by n, then ordered by h1 within each n
00420   //   with Index_C, lowest element has (n,h1) == (n_low,h_low)
00421   // Rindex:
00422   //   Grouped by h2, then ordered by m within each h2
00423   //   with Index_1 and m_low == 1, lowest element has (m,h2) == (1,h_low)
00424   // So elements with constant n,h1,h2 and varying m are contiguous
00425 { return Y_[(n-n_low)*h_size + h1+h_high][(h2+h_high)*m_high + m]; }
00426 
00427 
00428 // A use of the following functions is as follows:
00429 // X(n,m,h1,h2) ==> Xrow(n,h1)[Xcol(m,h2)]
00430 
00431 inline complex * mixer::analyzer::Xrow(int n, int h)
00432 { return X_[(n-n_low)*h_size + h+h_high]; }
00433 
00434 
00435 inline int mixer::analyzer::Xcol(int m, int h)
00436 { return m*h_size + h-h_high; }
00437 
00438 
00439 inline complex * mixer::analyzer::Yrow(int n, int h)
00440 { return Y_[(n-n_low)*h_size + h+h_high]; }
00441 
00442 
00443 inline int mixer::analyzer::Ycol(int m, int h)
00444 { return (h+h_high)*m_high + m; }

Please direct comments and corrections to supermix@submm.caltech.edu
Go to the supermix home page
Generated by doxygen1.2.7