Qucs-core  0.0.19
diac.cpp
Go to the documentation of this file.
00001 /*
00002  * diac.cpp - diac class implementation
00003  *
00004  * Copyright (C) 2008 Stefan Jahn <stefan@lkcc.org>
00005  * Copyright (C) 2008 Michael Margraf <Michael.Margraf@alumni.TU-Berlin.DE>
00006  *
00007  * This is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 2, or (at your option)
00010  * any later version.
00011  *
00012  * This software is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this package; see the file COPYING.  If not, write to
00019  * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
00020  * Boston, MA 02110-1301, USA.
00021  *
00022  * $Id$
00023  *
00024  */
00025 
00026 #if HAVE_CONFIG_H
00027 # include <config.h>
00028 #endif
00029 
00030 #include "component.h"
00031 #include "device.h"
00032 #include "devstates.h"
00033 #include "diac.h"
00034 
00035 #define NODE_A1 0 /* anode 1 */
00036 #define NODE_A2 1 /* anode 2 (cathode) */
00037 #define NODE_IN 2 /* internal node */
00038 
00039 using namespace qucs;
00040 using namespace qucs::device;
00041 
00042 // Constructor for the diac.
00043 diac::diac () : circuit (3) {
00044   type = CIR_DIAC;
00045 }
00046 
00047 // Callback for initializing the DC analysis.
00048 void diac::initDC (void) {
00049   Ud_last = 0.0;
00050   // allocate MNA matrices
00051   allocMatrixMNA ();
00052   // create internal node
00053   setInternalNode (NODE_IN, "int");
00054 }
00055 
00056 // Callback for DC analysis.
00057 void diac::calcDC (void) {
00058   calcTheModel (false);
00059 }
00060 
00061 void diac::calcTheModel (bool last) {
00062   // get device properties
00063   nr_double_t Ubo = getPropertyDouble ("Vbo");
00064   nr_double_t Ibo = getPropertyDouble ("Ibo");
00065   nr_double_t Is  = getPropertyDouble ("Is");
00066   nr_double_t N   = getPropertyDouble ("N");
00067   nr_double_t gi  = 1.0 / getPropertyDouble ("Ri");
00068   nr_double_t T   = getPropertyDouble ("Temp");
00069 
00070   bool isOn;
00071   if (last)
00072     Ud = fabs (Ud_last);
00073   else
00074     Ud = fabs (real (getV (NODE_A1) - getV (NODE_IN)));
00075   isOn = Ud > (Ibo / gi);
00076 
00077   nr_double_t Ut, Ieq, Vd;
00078 
00079   if (isOn)
00080     Ut = N * celsius2kelvin (T) * kBoverQ;
00081   else
00082     Ut  = Ubo / std::log (Ibo / Is);
00083 
00084   Vd = Ud = real (getV (NODE_IN) - getV (NODE_A2));
00085   Ud = fabs (Ud) / Ut;
00086   Id = sign (Vd) * Is;
00087 
00088   if (Ud >= 80.0) {
00089     Id *= std::exp (80.0) * (1.0 + Ud - 80.0) - 1.0;
00090     Ud  = 80.0;
00091   }
00092   else
00093     Id *= std::exp (Ud) - 1.0;
00094 
00095   gd  = Is / Ut * std::exp (Ud);
00096   Ieq = Id - Vd * gd;
00097 
00098   // fill in I-Vector
00099   setI (NODE_A2, +Ieq);
00100   setI (NODE_IN, -Ieq);
00101   setI (NODE_A1, +0.0);
00102 
00103   // fill in G-Matrix
00104   setY (NODE_A2, NODE_A2, +gd); setY (NODE_IN, NODE_IN, +gd);
00105   setY (NODE_A2, NODE_IN, -gd); setY (NODE_IN, NODE_A2, -gd);
00106   setY (NODE_A1, NODE_A1, +gi); addY (NODE_IN, NODE_IN, +gi);
00107   setY (NODE_A1, NODE_IN, -gi); setY (NODE_IN, NODE_A1, -gi);
00108 }
00109 
00110 // Saves operating points (voltages).
00111 void diac::saveOperatingPoints (void) {
00112   nr_double_t Vd = real (getV (NODE_IN) - getV (NODE_A2));
00113   nr_double_t Vi = real (getV (NODE_A1) - getV (NODE_IN));
00114   setOperatingPoint ("Vd", Vd);
00115   setOperatingPoint ("Vi", Vi);
00116 }
00117 
00118 // Loads operating points (voltages).
00119 void diac::loadOperatingPoints (void) {
00120   Ud = getOperatingPoint ("Vd");
00121   Ui = getOperatingPoint ("Vi");
00122 }
00123 
00124 // Calculates and saves operating points.
00125 void diac::calcOperatingPoints (void) {
00126   nr_double_t Cj0 = getPropertyDouble ("Cj0");
00127   // calculate capacitances and charges
00128   nr_double_t Ci;
00129   Ci = Cj0;
00130   Qi = Cj0 * Ud;
00131   // save operating points
00132   setOperatingPoint ("gi", gi);
00133   setOperatingPoint ("gd", gd);
00134   setOperatingPoint ("Id", Id);
00135   setOperatingPoint ("Ci", Ci);
00136 }
00137 
00138 // Callback for initializing the AC analysis.
00139 void diac::initAC (void) {
00140   initDC ();
00141 }
00142 
00143 // Build admittance matrix for AC and SP analysis.
00144 matrix diac::calcMatrixY (nr_double_t frequency) {
00145   nr_double_t gd = getOperatingPoint ("gd");
00146   nr_double_t gi = getOperatingPoint ("gi");
00147   nr_double_t Ci = getOperatingPoint ("Ci");
00148   nr_complex_t yd = nr_complex_t (gd, Ci * 2.0 * pi * frequency);
00149   matrix y (3);
00150   y.set (NODE_A2, NODE_A2, +yd);
00151   y.set (NODE_IN, NODE_IN, +yd +gi);
00152   y.set (NODE_A2, NODE_IN, -yd);
00153   y.set (NODE_IN, NODE_A2, -yd);
00154   y.set (NODE_A1, NODE_A1, +gi);
00155   y.set (NODE_A1, NODE_IN, -gi);
00156   y.set (NODE_IN, NODE_A1, -gi);
00157   return y;
00158 }
00159 
00160 // Callback for the AC analysis.
00161 void diac::calcAC (nr_double_t frequency) {
00162   setMatrixY (calcMatrixY (frequency));
00163 }
00164 
00165 // Callback for S-parameter analysis.
00166 void diac::calcSP (nr_double_t frequency) {
00167   setMatrixS (ytos (calcMatrixY (frequency)));
00168 }
00169 
00170 #define qState 0 // charge state
00171 #define cState 1 // current state
00172 
00173 // Callback for initializing the TR analysis.
00174 void diac::initTR (void) {
00175   setStates (2);
00176   initDC ();
00177   time_prev = -1.0;
00178 }
00179 
00180 // Callback for the TR analysis.
00181 void diac::calcTR (nr_double_t time) {
00182   if (time_prev < time) {
00183     time_prev = time;
00184     Ud_last = real (getV (NODE_A1) - getV (NODE_IN));
00185   }
00186   calcTheModel (true);
00187 
00188   saveOperatingPoints ();
00189   loadOperatingPoints ();
00190   calcOperatingPoints ();
00191 
00192   nr_double_t Ci = getOperatingPoint ("Ci");
00193   transientCapacitance (qState, NODE_IN, NODE_A2, Ci, Ud, Qi);
00194 }
00195 
00196 // properties
00197 PROP_REQ [] = {
00198   { "Ibo", PROP_REAL, { 50e-6, PROP_NO_STR }, PROP_POS_RANGEX },
00199   { "Vbo", PROP_REAL, { 30, PROP_NO_STR }, PROP_POS_RANGEX },
00200   PROP_NO_PROP };
00201 PROP_OPT [] = {
00202   { "Cj0", PROP_REAL, { 10e-12, PROP_NO_STR }, PROP_POS_RANGE },
00203   { "Is", PROP_REAL, { 1e-10, PROP_NO_STR }, PROP_POS_RANGE },
00204   { "N", PROP_REAL, { 2.0, PROP_NO_STR }, PROP_RNGII (0.1, 100) },
00205   { "Ri", PROP_REAL, { 10.0, PROP_NO_STR }, PROP_POS_RANGEX },
00206   { "Temp", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
00207   PROP_NO_PROP };
00208 struct define_t diac::cirdef =
00209   { "Diac", 2, PROP_COMPONENT, PROP_NO_SUBSTRATE, PROP_NONLINEAR, PROP_DEF };