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
1.2.7