/////////////////////////////////////////////////////////////////////////////
// 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 <deque> // for buffer implementation
#include <list> // 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<Delay *> *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<Delay*>(); // 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<Delay *>::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<Delay *>::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<Delay *> *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<Pair> buf; // use deque -- should be good enough
// deque<Pair>::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<Pair>::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
//
syntax highlighted by Code2HTML, v. 0.9.1