00001 // mixer.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 // 3/16/99: Added mixer::freq() 00033 // 12/17/98: Use new mixer::analyzer class for small signal calculations. 00034 // 11/11/98: Changed table access to new syntax 00035 // 10/5/98: Adding code for smart rebuilding of data structures 00036 // 9/14/98: Improved auto_balance(). Added copy constructor and =. 00037 // 9/9/98: removed check of LO from flag_mixer_incomplete() 00038 // 8/28/98: fixed calculations so device::f can safely shadow another 00039 // 8/28/98: Fixed mixer::port() so it always uses IF circuit (not bias 00040 // circuit if device::f == 0, as it did before); added size(). 00041 // 8/27/98: Added mixer_currents 00042 // 7/28/98: Added <math.h> to includes, since SIScmplx.h doesn't. 00043 // 7/27/98: Added I_junc() and V_junc(); LO freq is now a parameter 00044 // initialize pcdata in constructor so get_last_data always works. 00045 // 7/21/98: changed flag_balance_invalid() to flag_state_invalid(); 00046 // some minor cosmetic changes to avoid line wraps on printouts; 00047 // starting full balance() implementation, in balance.cc 00048 // 7/20/98: complete and tested except for balance() 00049 // 7/17/98: another bug fix in recalc: selecting IF circuit. Also 00050 // fixed order of validity checks in recalc, added code 00051 // for initialize_operating_state(). 00052 // 7/16/98: fixed bug in recalc(); added debug output 00053 // 7/15/98: Filled out most code except for harmonic balance 00054 00055 #include "mixer.h" 00056 #include "units.h" 00057 #include "error.h" 00058 #include "sources.h" 00059 #include <math.h> 00060 00061 // class mixer: ======================================================= 00062 00063 unsigned mixer::global_mixer_index = 0; 00064 00065 // ******************************************************************** 00066 // constructors and operator = 00067 00068 // the constructors have to set up the balancer and analyzer members 00069 // to look at this particular mixer object (using *this); 00070 // operator = must not upset this happy condition 00071 00072 mixer::mixer() : 00073 max_harmonics(1), num_junctions(0), LO_saved(0.0), 00074 balance_init_flag(0), auto_balance_flag(0), balance_not_ok_flag(0), 00075 bias_circuit(0), if_circuit(0), rf_circuit(0), 00076 term(0), default_term(0), 00077 junc(0), 00078 balance_(*this), ssignal_(*this), tsignal_(*this) 00079 { 00080 info.source = false; // a mixer is not a source 00081 LO.set_min(0.0); 00082 ssignal_.terminate_rf(0); // this object does a normal analysis 00083 tsignal_.terminate_rf(1); // this object does a terminated rf_circuit analysis 00084 } 00085 00086 mixer::mixer(const mixer & m) : 00087 LO(m.LO), 00088 max_harmonics(m.max_harmonics), 00089 num_junctions(m.num_junctions), 00090 LO_saved(m.LO_saved), 00091 balance_init_flag(m.balance_init_flag), 00092 auto_balance_flag(m.auto_balance_flag), 00093 balance_not_ok_flag((num_junctions != 0)), 00094 bias_circuit(m.bias_circuit), 00095 if_circuit(m.if_circuit), 00096 rf_circuit(m.rf_circuit), 00097 term(m.term), default_term(m.default_term), 00098 junc(m.junc), 00099 balance_(*this), ssignal_(*this), tsignal_(*this) 00100 { 00101 info.source = false; // a mixer is not a source 00102 LO.set_min(0.0); 00103 ssignal_.terminate_rf(0); // this object does a normal analysis 00104 tsignal_.terminate_rf(1); // this object does a terminated rf_circuit analysis 00105 } 00106 00107 mixer & mixer::operator = (const mixer & m) 00108 { 00109 // check for self assignment: 00110 if (& m == this) return *this; 00111 00112 LO = m.LO; 00113 max_harmonics = m.max_harmonics, 00114 num_junctions = m.num_junctions, 00115 LO_saved = m.LO_saved; 00116 balance_init_flag = m.balance_init_flag; 00117 auto_balance_flag = m.auto_balance_flag; 00118 bias_circuit = m.bias_circuit; 00119 if_circuit = m.if_circuit; 00120 rf_circuit = m.rf_circuit; 00121 term = m.term; 00122 default_term = m.default_term; 00123 junc = m.junc; 00124 changed(); 00125 00126 return *this; 00127 } 00128 00129 // ******************************************************************** 00130 // harmonics and junctions 00131 00132 mixer & mixer::harmonics(int N) 00133 { 00134 if (N < 1) 00135 error::fatal("Number of harmonics in a mixer must be a positive integer."); 00136 max_harmonics = N; 00137 int flag = balance_not_ok_flag; // save flag 00138 changed(); // this will invalidate a previous balance, 00139 balance_not_ok_flag = flag; // but don't invalidate the previous balance 00140 return *this; 00141 } 00142 00143 mixer & mixer::add_junction (junction & J) 00144 { 00145 if(J.type() != junction::Y_type) 00146 error::fatal("Mixer can only handle Y_type junctions."); 00147 ++num_junctions; 00148 junc.push_back(&J); 00149 LO_saved = 0; 00150 changed(); 00151 return *this; 00152 } 00153 00154 mixer & mixer::void_junctions() 00155 { 00156 num_junctions = 0; 00157 junc.resize(0); 00158 00159 LO_saved = 0; 00160 balance_not_ok_flag = 0; // no junctions, so no balance needed 00161 00162 changed(); 00163 return *this; 00164 } 00165 00166 // ******************************************************************** 00167 // LO frequency and linear circuit elements 00168 00169 mixer & mixer::set_bias(nport & c) 00170 { 00171 bias_circuit = &c; 00172 changed(); 00173 return *this; 00174 } 00175 00176 mixer & mixer::set_if(nport & c) 00177 { 00178 if_circuit = &c; 00179 changed(); 00180 return *this; 00181 } 00182 00183 mixer & mixer::set_rf(nport & c) 00184 { 00185 rf_circuit = &c; 00186 term.resize(0); term.resize(c.size()); // clear out previous terminators 00187 default_term.resize(c.size()); 00188 changed(); 00189 return *this; 00190 } 00191 00192 mixer & mixer::set_balance_terminator(nport & c, int p) 00193 { 00194 if ((rf_circuit == 0) || (rf_circuit->size() < p) || (p < 1)) 00195 error::warning("Ignoring attempt to set a terminator to an invalid port\ 00196 in mixer."); 00197 else if (c.size() != 1) 00198 error::fatal("Mixer balance terminators must be 1-ports."); 00199 else 00200 term[p-1] = &c; 00201 00202 changed(); 00203 return *this; 00204 } 00205 00206 int mixer::port(int p, int h) 00207 { 00208 // port ordering is: 00209 // (1) all open IF circuit ports 00210 // (2) RF circuit at harmonic = 1: first open port at USB, then at LSB 00211 // (3) all succeeding RF circuit open ports, USB then LSB at each port 00212 // (4) same as (2) and (3) at harmonic = 2 00213 // (5) continue for all higher harmonics 00214 00215 p -= num_junctions; // don't count the ports connected to junctions 00216 if((p <= 0)||(abs(h) > max_harmonics)) 00217 return 0; // invalid argument 00218 00219 nport *c = if_circuit; 00220 if (c == 0) 00221 return 0; // can't do anything if no circuit 00222 int ports = c->size() - num_junctions; // open IF ports 00223 00224 if (h == 0) 00225 return (ports < p) ? 0 : p; // check for valid IF port index 00226 // |h| > 0 00227 c = rf_circuit; 00228 if (c == 0) 00229 return 0; // now we can't do anything if no RF circuit 00230 int out = ports + 1; // skip over IF ports 00231 ports = c->size() - num_junctions; // open RF ports per harmonic 00232 if (p > ports) return 0; // invalid index to an RF port 00233 out += 2 * ports * ( abs(h) - 1 ); // skip lower harmonic port indexes 00234 out += 2 * ( p - 1 ); // skip to open port p 00235 if (h < 0) ++out; // LSB comes after USB 00236 00237 return out; 00238 } 00239 00240 double mixer::freq(int p) 00241 { 00242 if(LO == 0.0 || p < 1 || rf_circuit == 0 || if_circuit == 0) 00243 return 0; // can't get a frequency 00244 00245 int IF_size = if_circuit->size() - num_junctions; 00246 int RF_size = rf_circuit->size() - num_junctions; 00247 00248 if(p <= IF_size) 00249 return device::f; // an IF port 00250 else 00251 p -= IF_size; 00252 00253 double freq = device::f; 00254 freq *= (p == (p>>1)<<1)? -1.0 : 1.0; // freq is a lower sideband if p is even 00255 freq += LO * (1 + (p-1)/(2*RF_size)); // an integer divide to get harmonic number 00256 00257 return freq; 00258 } 00259 00260 int mixer::size() 00261 { 00262 // mixer must be complete to get a nonzero size 00263 if (flag_mixer_incomplete()) 00264 return 0; 00265 else 00266 return if_circuit->size() - num_junctions 00267 + 2*max_harmonics*(rf_circuit->size() - num_junctions); 00268 } 00269 00270 00271 // ******************************************************************** 00272 // status and mode flags 00273 00274 int mixer::flag_mixer_incomplete() const 00275 { 00276 // this function examines the circuit elements for completeness and 00277 // consistency. The balance and recalc routines check this 00278 // function return value before proceding. 00279 00280 if ((if_circuit == 0)||(rf_circuit == 0)) 00281 return 1; // essential linear circuits not assigned 00282 00283 if ((bias_circuit == 0)&&(num_junctions != 0)) 00284 return 1; // junctions exist but no bias circuit 00285 00286 if (((bias_circuit != 0)&&(bias_circuit->size() < 1))|| 00287 (if_circuit->size() < 1)|| 00288 (rf_circuit->size() < 1)) 00289 return 1; // circuits must know their sizes 00290 00291 // note: only if_circuit must have an open port after attaching junctions 00292 if ((num_junctions != 0)&& 00293 ((bias_circuit->size() != num_junctions)|| 00294 (if_circuit->size() <= num_junctions)|| 00295 (rf_circuit->size() < num_junctions))) 00296 return 1; // circuits don't have enough ports 00297 00298 return 0; // all checks passed 00299 } 00300 00301 int mixer::flag_state_invalid() const 00302 { 00303 // this function polls all asigned junctions for a nonzero 00304 // junction::call_large_signal() 00305 00306 int out = 0; // hold the output value 00307 for (unsigned i = 0; i < junc.size(); ++i) 00308 out = out || junc[i]->call_large_signal(); 00309 return out; 00310 } 00311 00312 int mixer::flag_balance_inaccurate() const 00313 { 00314 return (balance_not_ok_flag || (num_junctions != 0 && LO_saved != LO)); 00315 } 00316 00317 mixer & mixer::initialize_mode(int f) 00318 { 00319 balance_init_flag = f; 00320 return *this; 00321 } 00322 00323 mixer & mixer::auto_balance(int f) 00324 { 00325 if ((f >= 0) && (f <= 2)) auto_balance_flag = f; 00326 return *this; 00327 } 00328 00329 00330 // ******************************************************************** 00331 // operating state calculations 00332 00333 mixer & mixer::save_operating_state(Matrix & V) 00334 { 00335 int maxindex = max_harmonics; 00336 for(int n = 0; n < num_junctions; ++n) { 00337 int m = junc[n]->V().maxindex(); if (m < 0) m = 0; 00338 if (m > maxindex) maxindex = m; 00339 } 00340 00341 V.reallocate(num_junctions, maxindex+1, Index_C, Index_C).maximize(); 00342 00343 for(int n = 0; n < num_junctions; ++n) 00344 V.fillrow(n, junc[n]->V()); 00345 00346 return *this; 00347 } 00348 00349 mixer & mixer::initialize_operating_state(const Matrix & V) 00350 { 00351 if((num_junctions != 0) && (LO <= 0)) 00352 error::fatal("Must have a positive LO frequency during\ 00353 mixer::initialize_operating_state()."); 00354 00355 if((V.Lmode != Index_C)||(V.Rmode != Index_C)) 00356 error::fatal("Matrix argument to mixer::initialize_operating_state() must\ 00357 be Index_C."); 00358 00359 int max = num_junctions; 00360 if (V.Lsize < max) { 00361 error::warning("Argument to mixer::initialize_operating_state() too small\ 00362 to set all junctions."); 00363 max = V.Lsize; 00364 } 00365 else if (V.Lsize > max) { 00366 error::warning("Argument to mixer::initialize_operating_state() has too\ 00367 many rows - extra ignored."); 00368 } 00369 00370 int h = V.Rmaxindex(); if (h < 0) h = 0; 00371 h = (h > max_harmonics)? h : max_harmonics; 00372 for(int n = 0; n < max; ++n) 00373 junc[n]->large_signal(row(n,V), LO, h); 00374 00375 if (max > 0) LO_saved = LO; 00376 // assume the state came from a previous full balance: 00377 if (max == num_junctions) balance_not_ok_flag = 0; 00378 return *this; 00379 } 00380 00381 00382 mixer & mixer::initialize_operating_state() 00383 { 00384 // need a complete mixer to do this, and positive LO freq 00385 if (flag_mixer_incomplete()) 00386 error::fatal("Must correctly add all elements\ 00387 before calling mixer::initialize_operating_state()."); 00388 00389 if((num_junctions != 0) && (LO <= 0)) 00390 error::fatal("Must have a positive LO frequency during\ 00391 mixer::initialize_operating_state()."); 00392 00393 // call the routine to perform the initialization: 00394 balance_.i_state(); 00395 00396 // this wasn't a full balance, so: 00397 balance_not_ok_flag = 1; 00398 return *this; 00399 } 00400 00401 00402 void mixer::auto_state() // a private function 00403 { 00404 switch (auto_balance_flag) { 00405 00406 default: 00407 case mixer::Off: { 00408 // no balance attempt 00409 if (flag_state_invalid()) 00410 error::fatal("Invalid junction operating state during mixer small\ 00411 signal analysis."); 00412 break; 00413 } 00414 00415 case mixer::Always: { 00416 if ( balance() ) // balance attempt fails 00417 error::fatal("Autobalance attempt failed during mixer small signal\ 00418 analysis."); 00419 break; 00420 } 00421 00422 case mixer::Smart: { 00423 if ((flag_state_invalid()||flag_balance_inaccurate()) // trigger balance 00424 && balance() ) // and balance attempt fails 00425 error::fatal("Autobalance attempt failed during mixer small signal\ 00426 analysis."); 00427 } 00428 00429 } // switch 00430 00431 // we only get here if the operating states are valid 00432 } 00433 00434 00435 mixer & mixer::balance_parameters(int m, double t_1, double t_m, double t_x, double a) 00436 { balance_.parameters(m,t_1,t_m,t_x,a); return *this; } 00437 00438 00439 // ******************************************************************** 00440 // return operating state data 00441 00442 Vector mixer::I_junc(int m) 00443 { 00444 Vector result(num_junctions, Index_1); 00445 if (m < 0) { 00446 error::warning("Passed a negative harmonic index to mixer::I_junc()."); 00447 return result; 00448 } 00449 for(int n = 1; n <= num_junctions; ++n) 00450 result[n] = junc[n-1]->I().read(int(m)); 00451 return result; 00452 } 00453 00454 Vector mixer::V_junc(int m) 00455 { 00456 Vector result(num_junctions, Index_1); 00457 if (m < 0) { 00458 error::warning("Passed a negative harmonic index to mixer::V_junc()."); 00459 return result; 00460 } 00461 for(int n = 1; n <= num_junctions; ++n) 00462 result[n] = junc[n-1]->V().read(int(m)); 00463 return result; 00464 } 00465 00466 // ******************************************************************** 00467 // small signal response calculations 00468 00469 void mixer::recalc() 00470 { 00471 // if the LO frequency is not set, or the mixer is incompletely specified, 00472 // or device::f < 0, or >= LO frequency fatal errors result: 00473 if (flag_mixer_incomplete()) 00474 error::fatal("Must correctly add all elements before\ 00475 using mixer."); 00476 if((num_junctions != 0) && (LO <= 0)) 00477 error::fatal("Must have a positive LO frequency before\ 00478 using mixer."); 00479 if (device::f < 0) 00480 error::fatal("device::f must be nonnegative for mixer IF frequency."); 00481 if (device::f >= LO) 00482 error::fatal("device::f must be less than LO frequency for mixer."); 00483 00484 // calling auto_state() ensures that the junctions all have valid operating 00485 // states before the small signal mixer analysis is performed. 00486 // It will not return if the states remain invalid. 00487 00488 auto_state(); 00489 unsigned local_mixer_index = ++global_mixer_index; // the only mixer? 00490 data_ptr = & ssignal_(); // call the analyzer 00491 if (local_mixer_index != global_mixer_index) 00492 // uh-oh, a mixer in the linear circuits 00493 error::fatal("Can't analyze a circuit with more than one mixer."); 00494 00495 } 00496 00497 00498 const sdata & mixer::get_term_data() 00499 { 00500 // if the LO frequency is not set, or the mixer is incompletely specified, 00501 // or device::f < 0, or >= LO frequency fatal errors result: 00502 if (flag_mixer_incomplete()) 00503 error::fatal("Must correctly add all elements before\ 00504 using mixer."); 00505 if((num_junctions != 0) && (LO <= 0)) 00506 error::fatal("Must have a positive LO frequency before\ 00507 using mixer."); 00508 if (device::f < 0) 00509 error::fatal("device::f must be nonnegative for mixer IF frequency."); 00510 if (device::f >= LO) 00511 error::fatal("device::f must be less than LO frequency for mixer."); 00512 00513 // calling auto_state() ensures that the junctions all have valid operating 00514 // states before the small signal mixer analysis is performed. 00515 // It will not return if the states remain invalid. 00516 00517 auto_state(); 00518 unsigned local_mixer_index = ++global_mixer_index; // the only mixer? 00519 const sdata *ps = & tsignal_(); // call the terminated-circuit analyzer 00520 if (local_mixer_index != global_mixer_index) 00521 // uh-oh, a mixer in the linear circuits 00522 error::fatal("Can't analyze a circuit with more than one mixer."); 00523 return *ps; 00524 00525 } 00526 00527 00528 // class mixer_currents: ============================================== 00529 00530 mixer_currents::mixer_currents(mixer & m, int num_junctions) : 00531 data_ptr_nport(), 00532 mix(m), n(num_junctions), h(0), 00533 junctions(n), 00534 freq(0.0, mix.LO) 00535 { 00536 info.active = info.noise = false; // noiseless current sources 00537 int i; 00538 for (i = 0; i < n; ++i) { 00539 junctions[i].source_f = &freq; 00540 c.add_port(junctions[i], 1); 00541 } 00542 } 00543 00544 00545 mixer_currents & mixer_currents::set_harmonic(int harm) 00546 { 00547 if(harm < 0) 00548 error::fatal("Can't use a negative harmonic in mixer_currents."); 00549 freq.set(double(harm), mix.LO); 00550 return *this; 00551 } 00552 00553 00554 void mixer_currents::recalc() 00555 { 00556 Vector Amps(mix.I_junc(h)); 00557 for (int i = 0; i < n; ++i) { 00558 junctions[i].sink_current = abs(Amps.read(i+1)); 00559 junctions[i].sink_phase = arg(Amps.read(i+1)); 00560 } 00561 data_ptr = & c.get_data(); 00562 }
Please direct comments and corrections to
supermix@submm.caltech.edu
Go to the supermix home page
Generated by
1.2.7