The DE primitives discussed so far fire in response to input events. In order to build signal generators, or source primitives, or primitives with outputs but no inputs, we need another class of DE primitive, called a self-scheduling primitive.
A self-scheduling primitive fools the scheduler by generating its own input events. These feedback events trigger the primitive firings. An event generator is a special case of a delay primitive, its role is mainly to control the time spacing of source events. The values of the source events can be determined by a functional block attached to the output of the event generator (e.g. Const, Ramp, etc).
Method |
Description |
---|---|
int canGetFired () |
return 1 if the primitive is enabled for firing |
void refireAtTime (double t) |
schedule the primitive to fire again at time t |
void begin () |
schedule the primitive to fire at time zero |
A self-scheduling primitive is derived from class DERepeatStar, which in turn is derived from class DEStar. The DERepeatStar class has two special methods to facilitate the self-scheduling function: refireAtTime and canGetFired. These are summarized in above table. The DE primitive Poisson illustrates these:
defprimitive { name { Poisson } domain { DE } desc { Generate events according to a Poisson process. The mean inter-arrival time and magnitude of the events are given as parameters. } derived { RepeatStar } output { name { output } type { float } } defparameter { name { MeanTime } type { float } default { "1.0" } desc { "The mean inter-arrival time." } } defparameter { name { Magnitude } type { float } default { "1.0" } desc { "The magnitude of samples generated." } } defparameter { name { Seed } type { int } default { "-1" } } hinclude { <Rng/Exponential.h> } protected { Rng::Exponential mRandom; bool zeroFiring; } setup { mRandom.setSeed( (int)Seed ); mRandom.setMean(MeanTime); zeroFiring = true; } go { // Generate the output event // (Recall that the first event comes out at time 0). if (zeroFiring) zeroFiring = false; else output.put(completionTime) << double(Magnitude); // and schedule the next firing refireAtTime(completionTime); // Generate an exponential random variable. double p = mRandom(); // Turn it into an exponential, and add to completionTime completionTime += p; } }
The Poisson primitive generates a Poisson process. The inter-arrival time of events is exponentially distributed with the parameter meanTime. Refer to Using Random Numbers for information about the random number generation. The method refireAtTime launches an event onto a feedback arc that is invisible to the programmer. The feedback event triggers the self-scheduling primitive some time later.
Note that the feedback event for the next execution is generated in the current execution. To initiate this process, an event is placed on the feedback arc by the DERepeatStar::begin method, before the scheduler runs.
The DERepeatStar class can also be used for other purposes besides event generation. For example, a sampler primitive might be written to fire itself at regular intervals using the refireAtTime method.
Another uncommon named method, canGetFired is seldom used in the primitive definitions. The method checks for the existence of a new feedback event, and returns TRUE if it is there, or FALSE otherwise.
The internal feedback arc consists of an input and an output porthole that are automatically created and connected together, with a delay marker added to prevent the scheduler from complaining about a delay-free loop. This effectively assumes that refire requests will always be for time-stamps greater than the current time.
Sometimes the programmer of a primitive derived from DERepeatStar needs to be explicitly aware of these portholes. In particular, they should be taken into account when considering whether a primitive is delay-type. Setting delayType in a DERepeatStar derivative asserts that not only do none of the primitive’s visible input portholes trigger output events with zero delay, but refire events do not either. Frequently this is a false statement. It’s usually safer to write triggers directives that indicate that specific input portholes cannot trigger zero-delay outputs. Since the feedback portholes have a delay marker, it is never necessary to mention the feedback output porthole in triggers directives, even for an input porthole that gives rise to refireAtTime requests, the scheduler is uninterested in zero-delay paths to the feedback output.
The event passed across the feedback arc is an ordinary FLOAT particle, normally having value zero. Sometimes it can be useful to store extra information in the feedback event. The refireAtTime method accepts an optional second parameter that gives the numeric value to place in the feedback event. Fetching the value currently requires direct access to the feedback input port, for example:
if (feedbackIn->dataNew)
{
double feedbackValue = double(feedbackIn->get());
...
}
A future version of DERepeatStar might provide some syntactic changes to hide the details of this operation.