/*$Id: d_trln.cc,v 26.19 2007/03/21 01:30:28 al Exp $ -*- C++ -*- * Copyright (C) 2001 Albert Davis * Author: Albert Davis * * This file is part of "Gnucap", the Gnu Circuit Analysis Package * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. *------------------------------------------------------------------ * Transmission line. (ideal lossless.) */ //testing=script,sparse 2006.07.17 #include "l_dispatcher.h" #include "m_wave.h" #include "e_elemnt.h" extern DISPATCHER device_dispatcher; /*--------------------------------------------------------------------------*/ namespace { /*--------------------------------------------------------------------------*/ enum {NUM_INIT_COND = 4}; /*--------------------------------------------------------------------------*/ class COMMON_TRANSLINE : public COMMON_COMPONENT { private: PARAMETER len; /* length multiplier */ PARAMETER R; PARAMETER L; PARAMETER G; PARAMETER C; PARAMETER z0; /* characteristic impedance */ PARAMETER td; /* delay time */ PARAMETER f; /* specification frequency */ PARAMETER nl; /* length (wavelengths) at f */ double ic[NUM_INIT_COND]; /* initial conditions: v1, i1, v2, i2 */ int icset; /* flag: initial condition set */ public: double real_z0; double real_td; private: explicit COMMON_TRANSLINE(const COMMON_TRANSLINE& p); public: explicit COMMON_TRANSLINE(int c=0); bool operator==(const COMMON_COMPONENT&)const; COMMON_COMPONENT* clone()const {return new COMMON_TRANSLINE(*this);} void parse(CS&); void print(OMSTREAM&, LANGUAGE)const; void elabo3(const COMPONENT*); //COMMON_COMPONENT* deflate(); //COMPONENT_COMMON/nothing //void tr_eval(ELEMENT*)const; //COMPONENT_COMMON //void ac_eval(ELEMENT*)const; //COMPONENT_COMMON //bool has_tr_eval()const; //EVAL_BM_BASE/true //bool has_ac_eval()const; //EVAL_BM_BASE/true const char* name()const {untested(); return "transline";} //bool parse_numlist(CS&); //COMPONENT_COMMON/nothing/ignored //bool parse_params(CS&); //COMPONENT_COMMON/nothing/ignored }; /*--------------------------------------------------------------------------*/ class DEV_TRANSLINE : public ELEMENT { private: WAVE _forward; WAVE _reflect; double _if0; // value of current source representing incident wave double _ir0; // value of current source representing reflected wave double _if1; // val of cs rep incident wave, one load ago double _ir1; // val of cs rep reflected wave, one load ago COMPLEX _y11;// AC equiv ckt COMPLEX _y12;// AC equiv ckt private: explicit DEV_TRANSLINE(const DEV_TRANSLINE& p) :ELEMENT(p), _forward(), _reflect(), _if0(0), _ir0(0), _if1(0), _ir1(0) {} public: explicit DEV_TRANSLINE(); private: // override virtual char id_letter()const {return 'T';} const char* dev_type()const {itested(); return "tline";} int max_nodes()const {return 4;} int min_nodes()const {return 4;} int out_nodes()const {untested();return 4;} int matrix_nodes()const {return 4;} int net_nodes()const {return 4;} CARD* clone()const {return new DEV_TRANSLINE(*this);} void parse_spice(CS&); //void print(OMSTREAM&,LANGUAGE)const; //COMPONENT //void elabo1(); //COMPONENT //void map_nodes(); //ELEMENT void precalc(); void tr_iwant_matrix(); void dc_begin() {untested();} void tr_begin(); //void tr_restore(); //CARD/nothing void dc_advance() {tr_advance();} void tr_advance(); bool tr_needs_eval()const; //void tr_queue_eval() //ELEMENT bool do_tr(); void tr_load(); DPAIR tr_review(); void tr_accept(); void tr_unload(); double tr_involts()const; //double tr_input()const //ELEMENT double tr_involts_limited()const; //double tr_input_limited()const //ELEMENT //double tr_probe_num(CS&)const; //ELEMENT wrong??? void ac_iwant_matrix() {ac_iwant_matrix_extended();} //void ac_begin(); //CARD/nothing void do_ac(); void ac_load(); COMPLEX ac_involts()const; //XPROBE ac_probe_ext(CS&)const; //ELEMENT wrong?? bool is_2port()const {untested(); return true;} private: void setinitcond(CS&); }; /*--------------------------------------------------------------------------*/ inline bool DEV_TRANSLINE::tr_needs_eval()const { assert(!is_q_for_eval()); return (_if0!=_if1 || _ir0!=_ir1); } /*--------------------------------------------------------------------------*/ inline double DEV_TRANSLINE::tr_involts()const { return dn_diff(_n[IN1].v0(), _n[IN2].v0()); } /*--------------------------------------------------------------------------*/ inline double DEV_TRANSLINE::tr_involts_limited()const { unreachable(); return volts_limited(_n[IN1],_n[IN2]); } /*--------------------------------------------------------------------------*/ inline COMPLEX DEV_TRANSLINE::ac_involts()const {untested(); return _n[IN1]->vac() - _n[IN2]->vac(); } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ const double _default_len (1); const double _default_R (0); const double _default_L (NOT_INPUT); const double _default_G (0); const double _default_C (NOT_INPUT); const double _default_z0 (50.); const double _default_td (NOT_INPUT); const double _default_f (NOT_INPUT); const double _default_nl (0.25); const double LINLENTOL = .000001; static COMMON_TRANSLINE Default_TRANSLINE(CC_STATIC); /*--------------------------------------------------------------------------*/ COMMON_TRANSLINE::COMMON_TRANSLINE(int c) :COMMON_COMPONENT(c), len(_default_len), R(_default_R), L(_default_L), G(_default_G), C(_default_C), z0(_default_z0), td(_default_td), f(_default_f), nl(_default_nl), icset(false), real_z0(NOT_INPUT), real_td(NOT_INPUT) { for (int i = 0; i < NUM_INIT_COND; ++i) { ic[i] = 0.; } } /*--------------------------------------------------------------------------*/ COMMON_TRANSLINE::COMMON_TRANSLINE(const COMMON_TRANSLINE& p) :COMMON_COMPONENT(p), len(p.len), R(p.R), L(p.L), G(p.G), C(p.C), z0(p.z0), td(p.td), f(p.f), nl(p.nl), icset(p.icset), real_z0(p.real_z0), real_td(p.real_td) { for (int i = 0; i < NUM_INIT_COND; ++i) { ic[i] = p.ic[i]; } } /*--------------------------------------------------------------------------*/ bool COMMON_TRANSLINE::operator==(const COMMON_COMPONENT& x)const { const COMMON_TRANSLINE* p = dynamic_cast(&x); bool rv = p && len == p->len && R == p->R && L == p->L && G == p->G && C == p->C && z0 == p->z0 && td == p->td && f == p->f && nl == p->nl && icset == p->icset && COMMON_COMPONENT::operator==(x); if (rv) { for (int i=0; iic[i]; } }else{ } return rv; } /*--------------------------------------------------------------------------*/ void COMMON_TRANSLINE::parse(CS& cmd) { int here = cmd.cursor(); do{ ONE_OF || get(cmd, "LEN", &len) || get(cmd, "R", &R) || get(cmd, "L", &L) || get(cmd, "G", &G) || get(cmd, "C", &C) || get(cmd, "Z0", &z0) || get(cmd, "Zo", &z0) || get(cmd, "Delay",&td) || get(cmd, "TD", &td) || get(cmd, "Freq", &f) || get(cmd, "Nl", &nl) ; if (cmd.pmatch("Ic")) {untested(); icset = true; for (int i = 0; i < NUM_INIT_COND; ++i) {untested(); ic[i] = cmd.ctof(); } }else{ } }while (cmd.more() && !cmd.stuck(&here)); cmd.check(bWARNING, "what's this?"); } /*--------------------------------------------------------------------------*/ void COMMON_TRANSLINE::print(OMSTREAM& o, LANGUAGE lang)const { print_pair_if_has_value(o, lang, "len", len); print_pair_if_has_value(o, lang, "R", R); print_pair_if_has_value(o, lang, "L", L); print_pair_if_has_value(o, lang, "G", G); print_pair_if_has_value(o, lang, "C", C); print_pair_if_has_value(o, lang, "Z0", z0); print_pair_if_has_value(o, lang, "TD", td); print_pair_if_has_value(o, lang, "F", f); print_pair_if_has_value(o, lang, "NL", nl); if (icset) {untested(); o << " IC="; for (int i = 0; i < NUM_INIT_COND; ++i) {untested(); o << ic[i] << ' '; } }else{ } } /*--------------------------------------------------------------------------*/ void COMMON_TRANSLINE::elabo3(const COMPONENT* c) { assert(c); const CARD_LIST* par_scope = c->scope(); assert(par_scope); COMMON_COMPONENT::elabo3(c); len.e_val(_default_len, par_scope); R.e_val(_default_R, par_scope); L.e_val(_default_L, par_scope); G.e_val(_default_G, par_scope); C.e_val(_default_C, par_scope); z0.e_val(_default_z0, par_scope); td.e_val(_default_td, par_scope); f.e_val(_default_f, par_scope); nl.e_val(_default_nl, par_scope); { // real_td if (td.has_value()) {untested(); real_td = len * td; if (f.has_value()) {untested(); // check for conflicts if (!conchk(td, nl/f, OPT::vntol)) {untested(); error(bDANGER, "td, f&nl conflict. using td\n"); }else{untested(); } }else{untested(); } }else if (f.has_value()) { real_td = len * nl / f; }else if (L.has_value() && C.has_value()) {untested(); real_td = len * sqrt(L * C); }else{untested(); assert(real_td == NOT_INPUT); error(bDANGER, "can't determine length\n"); } } { // real_z0 if (z0.has_value()) { real_z0 = z0; if (L.has_value() && C.has_value()) {untested(); error(bDANGER, "redundant specification both Z0 and LC, using Z0\n"); }else{ } }else{untested(); if (L.has_value() && C.has_value()) {untested(); real_z0 = sqrt(L / C); }else{untested(); assert(_default_z0 == 50.); error(bDANGER, "can't determine Z0, assuming 50\n"); real_z0 = _default_z0; } } } } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ DEV_TRANSLINE::DEV_TRANSLINE() :ELEMENT(), _forward(), _reflect(), _if0(0), _ir0(0), _if1(0), _ir1(0), _y11(), _y12() { attach_common(&Default_TRANSLINE); } /*--------------------------------------------------------------------------*/ void DEV_TRANSLINE::parse_spice(CS& cmd) { assert(has_common()); COMMON_TRANSLINE* c = prechecked_cast(common()->clone()); assert(c); parse_Label(cmd); parse_nodes(cmd, max_nodes(), min_nodes(), 0/*no model_name*/, 0/*start*/); c->parse(cmd); attach_common(c); set_converged(); } /*--------------------------------------------------------------------------*/ /* precalc: called once to set things up. */ void DEV_TRANSLINE::precalc() { const COMMON_TRANSLINE* c=prechecked_cast(common()); assert(c); _forward.set_delay(c->real_td); _reflect.set_delay(c->real_td); } /*--------------------------------------------------------------------------*/ void DEV_TRANSLINE::tr_iwant_matrix() { aa.iwant(_n[OUT1].m_(),_n[OUT2].m_()); aa.iwant(_n[IN1].m_(), _n[IN2].m_()); lu.iwant(_n[OUT1].m_(),_n[OUT2].m_()); lu.iwant(_n[IN1].m_(), _n[IN2].m_()); } /*--------------------------------------------------------------------------*/ /* first setup, initial dc, empty the lines */ void DEV_TRANSLINE::tr_begin() { _forward.initialize(); _reflect.initialize(); } /*--------------------------------------------------------------------------*/ /* before anything else .. see what is coming out * _if0 = output current .. * The "wave" class stores voltages, but we need currents, * because the simulator uses the Norton equivalent circuit. * This makes the Thevenin to Norton conversion. */ void DEV_TRANSLINE::tr_advance() { const COMMON_TRANSLINE* c=prechecked_cast(common()); assert(c); _if0 = _forward.v_out(SIM::time0)/c->real_z0; _ir0 = _reflect.v_out(SIM::time0)/c->real_z0; } /*--------------------------------------------------------------------------*/ /* usually nothing, always converged. It is all done in advance and accept. * UNLESS ... it is a very short line .. then we fake it here. * very short line means delay is less than internal time step. */ bool DEV_TRANSLINE::do_tr() { // code to deal with short lines goes here. //if (_if0 != _if1 || _ir0 != _ir1) { if (!conchk(_if0, _if1, OPT::abstol, OPT::reltol*.01) || !conchk(_ir0, _ir1, OPT::abstol, OPT::reltol*.01)) { q_load(); }else{ } assert(converged()); return true; } /*--------------------------------------------------------------------------*/ void DEV_TRANSLINE::tr_load() { double lvf = NOT_VALID; // load value, forward double lvr = NOT_VALID; // load value, reflected if (!is_inc_mode()) { const COMMON_TRANSLINE* c = prechecked_cast(common()); assert(c); aa.load_symmetric(_n[OUT1].m_(), _n[OUT2].m_(), 1/c->real_z0); aa.load_symmetric(_n[IN1].m_(), _n[IN2].m_(), 1/c->real_z0); lvf = _if0; lvr = _ir0; }else{ lvf = dn_diff(_if0, _if1); lvr = dn_diff(_ir0, _ir1); } if (lvf != 0.) { if (_n[OUT1].m_() != 0) { _n[OUT1].i() += lvf; }else{untested(); } if (_n[OUT2].m_() != 0) {untested(); _n[OUT2].i() -= lvf; }else{ } }else{ } if (lvr != 0.) { if (_n[IN1].m_() != 0) { _n[IN1].i() += lvr; }else{untested(); } if (_n[IN2].m_() != 0) {untested(); _n[IN2].i() -= lvr; }else{ } }else{ } _if1 = _if0; _ir1 = _ir0; } /*--------------------------------------------------------------------------*/ /* limit the time step to no larger than a line length. */ DPAIR DEV_TRANSLINE::tr_review() { q_accept(); const COMMON_TRANSLINE* c=prechecked_cast(common()); assert(c); return DPAIR(SIM::time0 + c->real_td, NEVER); // ok to miss the spikes, for now } /*--------------------------------------------------------------------------*/ /* after this step is all done, determine the reflections and send them on. */ void DEV_TRANSLINE::tr_accept() { trace1(short_label().c_str(), SIM::time0); _reflect.push(SIM::time0, _forward.v_reflect(SIM::time0, tr_outvolts())); _forward.push(SIM::time0, _reflect.v_reflect(SIM::time0, tr_involts())); } /*--------------------------------------------------------------------------*/ void DEV_TRANSLINE::tr_unload() {untested(); } /*--------------------------------------------------------------------------*/ void DEV_TRANSLINE::do_ac() { const COMMON_TRANSLINE*c=prechecked_cast(common()); assert(c); double lenth = SIM::freq * c->real_td * 4; /* length in quarter waves */ double dif = lenth - floor(lenth+.5); /* avoid divide by zero if close to */ if (std::abs(dif) < LINLENTOL) { /* resonance by tweeking a little */ error(bDEBUG, long_label() + ": transmission line too close to resonance\n"); lenth = (dif<0.) ? floor(lenth+.5)-LINLENTOL : floor(lenth+.5)+LINLENTOL; }else{ } lenth *= (M_PI_2); /* now in radians */ _y12 = COMPLEX(0., -1. / (c->real_z0 * sin(lenth))); _y11 = COMPLEX(0., tan(lenth/2) / c->real_z0) + _y12; ac_load(); } /*--------------------------------------------------------------------------*/ void DEV_TRANSLINE::ac_load() { acx.load_symmetric(_n[OUT1].m_(), _n[OUT2].m_(), _y11); acx.load_symmetric(_n[IN1].m_(), _n[IN2].m_(), _y11); acx.load_asymmetric(_n[OUT1].m_(),_n[OUT2].m_(), _n[IN2].m_(), _n[IN1].m_(), _y12); acx.load_asymmetric(_n[IN1].m_(), _n[IN2].m_(), _n[OUT2].m_(), _n[OUT1].m_(), _y12); } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ DEV_TRANSLINE p1; DISPATCHER::INSTALL d1(&device_dispatcher, "T,tline", &p1); } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/