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

mixer.cc

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