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 //
00003 // a trivially simple, 1 SIS junction mixer simulation
00004 // (with way too many comments, probably)
00005 
00006 #include "supermix.h"
00007 
00008 // Since SIS junctions are not linear devices, SuperMix does not
00009 // simulate an SIS junction as a circuit element directly. Instead,
00010 // SuperMix includes the class "mixer" which combines nonlinear
00011 // junction elements with the linear RF, IF, and DC bias embedding
00012 // networks to synthesize a single, multi-frequency nport which models
00013 // the response of the circuitry as a harmonic mixer.
00014 
00015 // This program illustrates the basic procedure for building a mixer
00016 // model.
00017 
00018 
00019 
00020 int main()
00021 {
00022   // -----------------------------------------------------------------
00023   // THE GLOBAL VALUES 
00024 
00025   device::T  = 4.2*Kelvin;     // our superconducting receiver
00026   device::f  = 1*GHz;          // the IF output frequency 
00027   device::Z0 = 50.*Ohm;        // the S matrix normalizing impedance
00028 
00029 
00030   // -----------------------------------------------------------------
00031   // SIS DC IV CHARACTERISTIC CURVE DATA
00032 
00033   // The most important defining feature of an SIS device is its DC
00034   // current-voltage characteristic. SuperMix requires this
00035   // characteristic to be specified by providing two properly
00036   // normalized IV curve data files. The two required IV curves are:
00037   //
00038   //   (1) DC I-V characteristic with voltages normalized so that the
00039   //       gap voltage ~ 1.0, and the slope of the I(V) curve far
00040   //       above the gap == 1.0.
00041   //
00042   //   (2) Kramers-Kronig transform of the DC I-V characteristic using
00043   //       the same voltage and current normalization scale factors as
00044   //       used in the DC I-V characteristic.
00045   //
00046   // The files should start with Voltage = 0.0 and extend through
00047   // positive values up to at least twice the gap voltage for curve
00048   // (1) and four times the gap voltage for curve (2). The files
00049   // should contain sorted voltage - current pairs, one per line. The
00050   // voltages need not be equally spaced.
00051   //
00052   // Examine the files "idc.dat" and "ikk.dat" included with this
00053   // program file for examples. Note how comment lines can be included
00054   // in the files. These files were produced using the example
00055   // programs makeiv.cc and makeikk.cc
00056 
00057   // The class "ivcurve", defined in "junction.h", is used to access
00058   // the IV data:
00059 
00060   ivcurve IV("idc.dat","ikk.dat");
00061 
00062 
00063   // -----------------------------------------------------------------
00064   // DEFINING AN SIS DEVICE
00065 
00066   // An SIS junction device is created by declaring an object of type
00067   // sis_device, which is derived from class junction. The definition
00068   // of sis_device is in "sisdevice.h", the definition of junction is
00069   // in "junction.h". There are several member parameters of the
00070   // device which must be set to fully define the device:
00071 
00072   sis_device SIS;
00073     SIS.set_iv(IV);        // the IV curve object for this junction
00074     SIS.Vn  = 2.8*mVolt;   // the voltage normalization of the IV curves
00075     SIS.Rn  = 10*Ohm;      // the current normalization is Vn/Rn
00076     SIS.Cap = 140*fFarad;  // the junction capacitance
00077 
00078   // Vn is nominally the gap voltage of the SIS; this is how the IV
00079   // curves should have their voltages normalized. Rn is the SIS
00080   // normal resistance, so that 1/Rn is the slope of the real SIS DC
00081   // IV curve far above the gap. The normalized IV curve has slope = 1
00082   // above the gap.
00083   //
00084   // If the sis_device is given a nonzero capacitance, be sure you do
00085   // not include its capacitance as part of the linear embedding
00086   // network to be described below; either include it there or in the
00087   // sis_device, but not both.
00088 
00089   // If we were going to simulate a multi-SIS circuit, we would need
00090   // more sis_device declarations; each SIS in a multi-SIS circuit
00091   // needs its own sis_device object.
00092 
00093 
00094   // -----------------------------------------------------------------
00095   // THE RF AND IF EMBEDDING NETWORKS
00096 
00097   // We'll use an especially simple RF embedding network: The antenna
00098   // impedance, which we assume to be real, in parallel with a tuning
00099   // inductance to tune out the SIS junction capacitance. Assume we
00100   // want the receiver to operate at 345 GHz.
00101 
00102   double Ftune = 345*GHz;   // receiver design frequency
00103   double Zant  = 8*Ohm;     // the antenna impedance
00104 
00105   // Here's the tuning inductance:
00106 
00107   inductor Tune;
00108     Tune.parallel();
00109     Tune.L = 1.0/(2*Pi*Ftune * 2*Pi*Ftune * SIS.Cap);
00110 
00111   // Here's the antenna. We use a transformer to transform the RF
00112   // input to the antenna impedance. We'll use port 1 as the RF input
00113   // and port 2 to present the correct impedance to the SIS. The
00114   // definition for transformer is in "transformer.h"
00115 
00116   transformer Ant;
00117     Ant.Z2 = Zant;   // Z2 is the impedance for port 2.
00118 
00119   // The complete RF embedding network combines these two
00120   // components. When we build a mixer using the embedding networks,
00121   // we have to connect the SIS junction to port 1 of the network, so
00122   // we must be careful to add ports in the right order using
00123   // add_port():
00124 
00125   circuit RF;
00126     RF.connect(Ant, 2, Tune, 1);
00127     RF.add_port(Tune, 2);         // port 1 must be for the SIS
00128     RF.add_port(Ant, 1);          // so port 2 is the RF input
00129 
00130   // The IF embedding network will be a simple transformer presenting
00131   // the IF embedding impedance to the junction (of course, the SIS
00132   // has a nonzero capacitance, so that automatically gets included in
00133   // the calculations at the IF frequency as well).
00134 
00135   double ZIF  = 20*Ohm;     // IF presents 2*Rn to the SIS
00136 
00137   transformer IF;
00138     IF.Z1 = ZIF;            // port 1 must be reserved for the SIS
00139 
00140   // Port 2 of device IF will be the IF ouput port. 
00141 
00142 
00143   // -----------------------------------------------------------------
00144   // THE DC BIAS NETWORK
00145 
00146   // The DC bias network must have exactly as many ports as there are
00147   // SIS junctions in the mixer circuit, which is only 1 in this
00148   // case. We'll use a perfect voltage source for the bias
00149   // supply. Sources, all 1-ports, are defined in "sources.h".
00150 
00151   voltage_source BIAS;
00152     BIAS.source_f = 0.0;              // source frequency is DC
00153     BIAS.source_voltage = 2.2*mVolt;  // about the middle of the step
00154 
00155 
00156   // -----------------------------------------------------------------
00157   // THE LOCAL OSCILLATOR POWER SOURCE
00158 
00159   // We'll create a local oscillator source which we can tell the
00160   // mixer to connect to the RF input during large signal, harmonic
00161   // balance calculations. The particular source used, a generator, is
00162   // also defined in "sources.h".
00163 
00164   generator LO;
00165     LO.source_f = Ftune;              // source frequency is 345 GHz
00166     LO.source_power = 100*Nano*Watt;  // for about 1.4 mV peak at SIS
00167     LO.Temp = 0;                      // useful for mixer noise calc's
00168 
00169 
00170   // -----------------------------------------------------------------
00171   // BUILDING THE MIXER
00172 
00173   // Class mixer, defined in "mixer.h", connects nonlinear devices
00174   // like our SIS into linear embedding networks and allows us to
00175   // perform harmonic balance calculations and small signal
00176   // analyses. Here's how to set one up:
00177 
00178   mixer Mix;
00179 
00180     Mix.add_junction(SIS);   // call once for each SIS device
00181 
00182     Mix.set_rf(RF);          // we provide the linear networks
00183     Mix.set_if(IF);
00184     Mix.set_bias(BIAS);
00185 
00186     // the LO source is only connected during harmonic balance; here's
00187     // how we tell the mixer to use it (it's called a balance
00188     // terminator since it terminates the RF input port during
00189     // harmonic balance calculations):
00190 
00191     Mix.set_balance_terminator(LO, 2);  // terminates port 2 of RF
00192 
00193     // Mixers need to know two frequencies: the IF frequency and the
00194     // LO frequency. The global frequency parameter device::f will
00195     // provide the IF frequency, but a special mixer call must set the
00196     // mixer's internal LO frequency parameter. Here we tell that
00197     // parameter to shadow our LO source frequency by passing the
00198     // address of its frequency parameter.
00199 
00200     Mix.set_LO(& LO.source_f);  // we shadow the LO source frequency
00201 
00202     // Now the description of our mixer is complete. The only thing
00203     // remaining is to tell the mixer how many harmonics to use in its
00204     // calculations (we can change this number at any time by calling
00205     // harmonics() again):
00206 
00207     Mix.harmonics(3);  // use fundamental + 2 harmonics
00208 
00209   // Let's confirm that our mixer is complete by asking it:
00210   cout << "# Mixer complete? " 
00211        << ( ( Mix.flag_mixer_incomplete() ) ? "No" : "Yes" )
00212        << endl;
00213 
00214 
00215   // -----------------------------------------------------------------
00216   // USING THE MIXER: PORTS
00217 
00218   // Mixers are nports, but complicated ones, especially if the number
00219   // of harmonics is large. The mixer has ports for the IF and for the
00220   // RF at each sideband, upper and lower, at each harmonic. Keeping
00221   // track of the port indexing for a mixer can be difficult, so the
00222   // mixer class provides a member function to tell you the port index
00223   // you need. Let's use it now:
00224 
00225   // The IF circuit's output is at its port 2; the IF is harmonic 0:
00226 
00227   int IF_PORT  = Mix.port(2, 0);
00228 
00229   // The RF circuit's input is at its port 2, the Upper Side Band of
00230   // the fundamental RF frequency is harmonic +1, the Lower Side Band
00231   // is harmonic -1:
00232 
00233   int USB_PORT = Mix.port(2, 1);
00234 
00235   // Here are the port index values:
00236   cout << "# IF port: " << IF_PORT
00237        << " , USB port: " << USB_PORT << endl;
00238 
00239 
00240   // -----------------------------------------------------------------
00241   // USING THE MIXER: HARMONIC BALANCE
00242 
00243   // The operating state of the nonlinear SIS junction must be
00244   // determined before we can perform a circuit (small signal)
00245   // analysis of our receiver. This is the purpose of the mixer
00246   // harmonic balance routine:
00247 
00248   Mix.initialize_operating_state();  // call this before the first balance
00249   Mix.balance();                     // that's all there is to it
00250 
00251   // Whenever you change bias voltage or LO power or frequency, call
00252   // balance() to recalculate the proper SIS operating state. If the
00253   // change is more than just a small incremental change, it can speed
00254   // up the balance slightly if you call initialize_operating_state()
00255   // first.
00256 
00257   // To look at the SIS operating state, class junction provides the
00258   // member functions V() and I(), which output vectors of the DC and
00259   // RMS harmonic voltages and currents across the juction:
00260 
00261   cout << endl;
00262   complex::out_degree();
00263   complex::out_delimited();
00264   cout << "SIS Operating State (mag,deg) at DC and 1, 2, 3 harmonic:"
00265        << endl;
00266   cout << "V (mVolt): " << SIS.V()/mVolt << endl;
00267   cout << "I (mAmp):  " << SIS.I()/mAmp << endl;
00268 
00269 
00270   // -----------------------------------------------------------------
00271   // USING THE MIXER: SMALL SIGNAL ANALYSIS
00272 
00273   // Once the operating state of the mixer has been set, just use
00274   // get_data() like you would for any other nport in order to
00275   // calculate the small signal response:
00276 
00277   sdata s = Mix.get_data();   // s holds the small-signal response
00278 
00279   // Let's look at the mixer gain in dB:
00280   double gain_db = s.SdB(IF_PORT, USB_PORT);
00281   cout << endl;
00282   cout << "Gain: " << gain_db << " dB" << endl;
00283 
00284   // Mixer noise is trickier. Since a mixer is not a 2-port (all those
00285   // sidebands, remember), just calling s.tn() will give a misleading
00286   // answer. We use another small-signal analysis function of mixer,
00287   // get_term_data(). get_term_data() terminates all RF ports, as in a
00288   // harmonic balance calculation, and then performs a small signal
00289   // analysis. The result is just a 1-port sdata, if there is only a
00290   // single IF output port.
00291 
00292   sdata sterm = Mix.get_term_data();
00293 
00294   // Since we set the temperature of our LO source to zero, all our RF
00295   // inputs are terminated with matched, 0 Kelvin terminations. The
00296   // output noise power at the IF is now the noise due to the RF quantum
00297   // noise limit + any additional noise added by the receiver.
00298 
00299   double output_noise = sterm.C[IF_PORT][IF_PORT].real;  // C is complex
00300 
00301   // If we take this noise and divide by the mixer gain to refer it
00302   // back to the RF input, then we get the proper single sideband
00303   // mixer noise temperature. Since the mixer gain is the magnitude
00304   // squared of the S parameter connecting IF to USB:
00305 
00306   double input_noise = output_noise/norm(s.S[IF_PORT][USB_PORT]);
00307   cout << "SSB Tn: " << input_noise/Kelvin << " Kelvin" << endl;
00308 
00309   // If we were just to use the tn() member function of the sdata s,
00310   // we would get:
00311 
00312   cout << "Wrong noise: " << s.tn(IF_PORT, USB_PORT) << " Kelvin" << endl;
00313 
00314   // This is only the excess noise of the SIS mixer over the much
00315   // greater quantum noise being introduced from all sidebands.
00316 
00317 
00318 }

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