///////////////////////////////////////////////////////////////////////////// // delay.cc // // SIMLIB version: 2.18 // Date: 2004-01-25 // // Copyright (c) 1991-2004 Petr Peringer // // This library is licensed under GNU Library GPL. See the file COPYING. // // // This module contains implementation of continuous delay class // // classes: // Delay -- real delay blocks // SIMLIB_Delay -- internal class for registration of delay blocks // SIMLIB_DelayBuffer -- delay memory // // LIMITS: // dt <= MaxStep --- problem with too small delay time // increasing dt --- problem with buffer length // //////////////////////////////////////////////////////////////////////////// // interface // #include "simlib.h" #include "delay.h" #include "internal.h" // we use standard C++ library (STL) #include // for buffer implementation #include // for list of delays //////////////////////////////////////////////////////////////////////////// // implementation // SIMLIB_IMPLEMENTATION // Delay subsystem public symbols class Delay; // local symbols: class SIMLIB_Delay; class SIMLIB_DelayBuffer; //////////////////////////////////////////////////////////////////////////// // SIMLIB_Delay --- list of all continuous delay blocks // class SIMLIB_Delay { static std::list *listptr; // list of delay objects -- singleton public: static void Register(Delay *p) { // must be called by Delay ctr if( listptr == 0 ) Initialize(); listptr->push_back(p); } static void UnRegister(Delay *p) {// must be called from Delay destructor listptr->remove(p); if( listptr->size() == 0 ) Destroy();// is really important??? } private: static void Initialize() { // initialize delay subsystem listptr = new std::list(); // create new list of delays // install 'hooks' into simulation control algorithm: INSTALL_HOOK( Delay, SIMLIB_Delay::SampleAll ); INSTALL_HOOK( DelayInit, SIMLIB_Delay::InitAll ); } static void Destroy() { // should be called by ExitSimulation()? ###??? delete listptr; // remove list listptr = 0; // disable hooks into simulation control algorithm INSTALL_HOOK( Delay, 0 ); INSTALL_HOOK( DelayInit, 0 ); } // SampleAll --- function to scan inputs of all delay objects static void SampleAll() { // called each continuous step (and more) if( listptr == 0 ) return; // ### should never be reached (2remove) std::list::iterator i; for( i=listptr->begin(); i!=listptr->end(); i++) // for each delay object (*i)->Sample(); // sample input value } // InitAll --- function to initialize all delay objects static void InitAll() { // called at Init() if( listptr == 0 ) return; // no delays ### std::list::iterator i; for( i=listptr->begin(); i!=listptr->end(); i++) // for each delay object (*i)->Init(); // set initial value } }; // static member must be initializad std::list *SIMLIB_Delay::listptr = 0; #ifndef SIMLIB_public_Delay_Buffer struct Delay::Buffer { // INTERFACE: memory for delayed signal virtual void put(double value, double time) = 0; virtual double get(double time) = 0; // with interpolation virtual void clear() = 0; // initialize buffer virtual ~Buffer() {}; }; #endif ///////////////////////////////////////////////////////////////////////////// // SIMLIB_DelayBuffer --- memory for delayed pairs (Time,value) // // this buffer inherits interface from Delay::Buffer (we can use various // implementations later) // method get() does linear interpolation ??? should be split ### !!!! // class SIMLIB_DelayBuffer : public Delay::Buffer { // memory for delayed signal struct Pair { // pair (t,val) for storing in buffer double time; double value; Pair(double Time, double Value) : time(Time), value(Value) {} bool operator == (Pair &p) { return p.time==time && p.value==value; } }; std::deque buf; // use deque -- should be good enough // deque::iterator lastOK; // for optimization (if LIMIT>2) Pair last_insert; // last inserted value (optimization) public: SIMLIB_DelayBuffer(): buf(), last_insert(-2,0) { /*empty*/ } virtual void clear() { last_insert = Pair(-2,0); // we need it for optimization buf.clear(); // empty buffer } virtual void put(double value, double time) { Pair p(time,value); #ifndef NO_DELAY_OPTIMIZATION // ??? can be improved using interpolation if( last_insert == p ) // do not allow duplicate records return; last_insert = p; #endif buf.push_back(p); // add to end } virtual double get(double time) // get delayed value (with interpolation) { // this code is EXPERIMENTAL !!! ### (not effective enough) //Print("bs=%d\n", buf.size()); Pair p(-1,0); Pair l(-1,0); std::deque::iterator i; int n; // number of checked records // ASSERT: there should be at least one record in the buffer for( n=0, i=buf.begin(); i!=buf.end(); ++i ) { l = p; p = *i; n++; if( p.time > time ) break; } // ASSERT: n>0 if( n < 2 ) // we want time before first recorded sample return p.value; // use first buffer value as default else { // at least two records read if( p.time < time ) { // too small delay ### SIMLIB_error(DelayTimeErr);// ###!!! do it better } // standard situation const int LIMIT=2; // >=2 --- allows change of delay if bigger // WARNING: slow for bigger LIMIT // remove old items in buffer for(; n>LIMIT; n--) buf.pop_front(); // remove front records // linear interpolation double dt = p.time - l.time; double dy = p.value - l.value; // ASSERT: dt > 0 return l.value + dy*(time-l.time)/dt; } } // get }; // class SIMLIB_DelayBuffer ///////////////////////////////////////////////////////////////////////////// // Delay constructor --- initialize and register delay block // Delay::Delay(Input i, double _dt, double ival) : aContiBlock1( i ), // input expression last_time( Time ), last_value( ival ), buffer( new SIMLIB_DelayBuffer ), // allocate delay buffer dt( _dt ), // delay time initval( ival ) // initial value of delay { // constructor body dprintf(("Delay::Delay(in=%p, dt=%g, ival=%g)", &i, _dt, ival)); SIMLIB_Delay::Register( this ); // register delay in list of delays Init(); // initialize -- important for dynamically created delays } ///////////////////////////////////////////////////////////////////////////// // Delay destructor --- remove buffer and delay from list // Delay::~Delay() { dprintf(("Delay::~Delay()")); delete buffer; // free the delay buffer SIMLIB_Delay::UnRegister(this); // remove from delay list } ///////////////////////////////////////////////////////////////////////////// // Delay::Init --- initialize delay status // called automatically by Init() // initialize: buffer and last output value //WARNING: does not set/change dt !!!### (if multiple experiments) void Delay::Init() { buffer->clear(); // empty buffer buffer->put( last_value=initval, last_time=Time ); // set initial value } ///////////////////////////////////////////////////////////////////////////// // Delay::Sample --- sample input value // void Delay::Sample() { dprintf(("Delay::Sample()")); buffer->put( InputValue(), Time ); // store into memory } ///////////////////////////////////////////////////////////////////////////// // Delay::Value --- get delay output value // double Delay::Value() { dprintf(("Delay::Value()")); double oldtime = Time - dt; // past time if( last_time != oldtime ) { // is not already computed? last_value = buffer->get( oldtime ); last_time = oldtime; } return last_value; } ///////////////////////////////////////////////////////////////////////////// // Delay::Set --- change delay time // // EXPERIMENTAL --- this should be used with care // double Delay::Set(double newdelay) { double last = dt; if( newdelay>=0 && newdelay<=Time ) // too weak condition ### dt = newdelay; return last; } ///////////////////////////////////////////////////////////////////////////// // end of module //