Timing is a critical issue in a system that processes data in real time. Such a system must process data faster than they are acquired; this is the real-time constraint. In BCI2000, data is acquired and processed in sample blocks. Ideally, these sample blocks are acquired in regular intervals. To work in real-time, the system needs to finish processing, user display, and data storage within a block duration, i.e., the duration corresponding to a single sample block.
Roundtrip time is the time needed for a sample block to traverse the core modules. Starting with the acquisition of a sample block, a block's roundtrip includes the time spent on signal processing and stimulus display. The roundtrip finishes when the block enters the data acquisition module again.
To fulfill the real-time constraint, roundtrip time may not exceed the physical duration of a sample block. For stable system operation, a weaker condition is sufficient: only the roundtrip's average value needs to stay below a sample block duration.
Measuring Block Duration
Immediately after data block has been acquired from hardware, the DataIOFilter writes a 16-bit millisecond-resolution time stamp into the SourceTime state. Block duration is measured as the difference between two consecutive time stamps.
Measuring Roundtrip Time
Roundtrip is measured by subtracting a data block's time stamp from the current time when it enters the data acquisition module coming from the application module.
Measuring Source-to-Stimulus Delay
In the StimulusTime state, the application module stores a time stamp when the module's Process() function has finished. By this time, video memory should have been updated, given that the module enforces a display update at the end of its Process() function; generally, this is done by calling the GUI::GraphDisplay::Update() function for the module's display window. Modules that use the ApplicationWindow class for stimulus display (as most do), don't need to call GUI::GraphDisplay::Update(), as this is taken care for by the ApplicationWindow class after all calls to GenericFilter::Process() have returned.
Once the stimulus time stamp has reached the source module, the DataIOFilter subtracts the source time stamp from the stimulus time stamp to compute the source-to-stimulus delay. Unlike roundtrip time, measurement of the source-to-stimulus delay requires that data acquisition and application modules share a common time base; when BCI2000 is distributed over multiple machines in a network, source-to-stimulus delay cannot be measured.
When switched on via the VisualizeTiming parameter, timing data are displayed in a visualization window. In this window, theoretical sample block length is indicated with a tickmark on the y axis, with scaling such that a range of 0..2 physical sample block lengths is displayed.
Timing values are displayed separately for actual block duration, roundtrip time, and source-to-stimulus delay. Using this graph, the experimentator can keep track of
- regularity of data acquisition -- the block duration curve should be a straight line;
- real-time operation -- roundtrip time should stay below theoretical sample block length;
- module communication overhead -- this is represented by the difference between roundtrip and stimulus curves.
During a run, BCI2000 continously monitors roundtrip time to ensure proper timing, and issues warnings when roundtrip time approaches more than 75% of a sample block duration. Once roundtrip time consistently exceeds sample block duration, the current run is aborted with an error message.
There are situations in which this behavior is undesired, such as when debugging or profiling BCI2000 modules. To handle these cases, the SignalGenerator and FilePlayback source modules provide a command line switch that disables timing evaluation.
- Make sure that you are starting up BCI2000 from a batch file rather than BCI2000Launcher.
- Edit the batch file, and add --EvaluateTiming=0 to the command line that starts up the source module.