Jump to content

Programming Reference:Events: Difference between revisions

From BCI2000 Wiki
Mellinger (talk | contribs)
Mellinger (talk | contribs)
 
(22 intermediate revisions by 3 users not shown)
Line 1: Line 1:
This page describes how to record asynchronous information ("events") along with brain signal data. This usage of the term "event" is unrelated to "events" as part of [[Programming Reference:Contents#Programming_Interface_Documentation|BCI2000 API class interfaces]].
As data are recorded by data acquisition hardware in chunks, BCI2000 processes them synchronously.
As data are recorded by data acquisition hardware in chunks, BCI2000 processes them synchronously.
Still, recording information that is asynchronous to brain signal data acquisition may be of interest to an experimenter. Typically, mouse or keyboard input may be of interest to record in an experiment, and ideally should provide a temporal resolution that corresponds to the brain signal's sampling rate.
Still, recording information that is asynchronous to brain signal data acquisition may be of interest to an experimenter. Typically, mouse or keyboard input may be of interest to record in an experiment, and ideally should provide a temporal resolution that corresponds to the brain signal's sampling rate.
Line 11: Line 13:
==Implementation==
==Implementation==
In the current version of BCI2000, events are mapped to [[BCI2000 Glossary#State|state variables]]. By comparing an event's time stamp to data acquisition time stamps, it is possible to associate events with single samples, and to map an event to a change in a state variable's value.
In the current version of BCI2000, events are mapped to [[BCI2000 Glossary#State|state variables]]. By comparing an event's time stamp to data acquisition time stamps, it is possible to associate events with single samples, and to map an event to a change in a state variable's value.
In order to make events available for writing, they must be first declared as in the following example:
BEGIN_EVENT_DEFINITIONS
  "MyEvent 10 0 0 0",
END_EVENT_DEFINITIONS
Here, an event's definition follows the rules applying to the definition of state variables: first, the name is given, then follow bit width and initial value. At the end, a pair of zeros needs to be added.


==Limitations==
==Limitations==
*Time stamps have millisecond resolution. Up to sampling rates of 1kHz, this is not a practical restriction.
*Time stamps have millisecond resolution. Up to sampling rates of 1kHz, this is not a practical restriction though.
*Association of event time stamps with sample positions cannot be more accurate than [[User Reference:Timing|time stamps assigned to data blocks]] by the data acquisition module. If data transmission latency between data acquisition hardware, and its associated jitter, are high compared to a single sample's duration, then event positions cannot be expected to be precise. Still, for data acquisition devices that are connected directly to the machine running the data acquisition module (i.e., not over a network connection), and for sampling rates below 1kHz, transmission latency and jitter may be expected to be below a sample's duration.
*Apart from the 1kHz limit given by time stamp resolution, the upper limit of temporal resolution is determined by the amplifier's maximum sampling rate.  
*Event time stamps must refer to the same physical time base that is used to stamp acquired data packets. This limits the use of events to BCI2000 modules that run on the same machine as the data acquisition module. While the event API is available to all BCI2000 modules, it is advisable to use events in data acquisition modules only, thereby ensuring correct operation also in situations where processing modules are distributed across multiple machines in a network.
*Association of event time stamps with sample positions cannot be more accurate than [[User Reference:Timing|time stamps assigned to data blocks]] by the data acquisition module. If data transmission latency between data acquisition hardware, and its associated jitter, are large compared to a single sample's duration, then event positions cannot be expected to be precise. Still, for data acquisition devices that are connected directly to the machine running the data acquisition module (i.e., not over a network connection), and for sampling rates below 1kHz, transmission latency and jitter may be expected to be below a sample's duration.
*Event time stamps must refer to the same physical time base that is used to stamp acquired data packets. This limits the use of events to BCI2000 modules that run on the same machine as the data acquisition module. To ensure correct operation also in situations where processing modules are distributed across multiple machines in a network, the event API may be used only in data acquisition modules.
*The <tt>bcievent</tt> asynchronous stream interface should only be called between "StartRun" and "StopRun".  Logging events with <tt>bcievent</tt> outside of a run could result in delayed state logging through the <tt>bcievent</tt> interface.


==Event API==
==Event API==
When including the <tt>src/shared/accessors/BCIEvent.h</tt> header file into a source code file, a symbol <tt>bcievent</tt> will be available.
When including the <tt>src/shared/accessors/BCIEvent.h</tt> header file into a source code file, a symbol <tt>bcievent</tt> will be available.
The <tt>bcievent</tt> symbol behaves like a <tt>std::ostream</tt>; writing to that stream creates an event, fills it descriptor with whatever has been written into the stream, and stamps the event with the current time.
The <tt>bcievent</tt> symbol behaves like a <tt>std::ostream</tt>; writing to that stream creates an event, fills its descriptor with whatever has been written into the stream, and stamps the event with current time.
The <tt>bcievent</tt> symbol's <tt>ostream</tt> behavior makes it convenient to include numeric information into the event's string descriptor.
The <tt>bcievent</tt> symbol's <tt>ostream</tt> behavior makes it convenient to include numeric information into the event's string descriptor.
To allow for true asynchrony, BCI2000 events may be issued from any thread within a BCI2000 module, i.e. the <tt>bcievent</tt> interface is thread-safe.
To allow for true asynchrony, BCI2000 events may be issued from any thread within a BCI2000 module, i.e. the <tt>bcievent</tt> interface is thread-safe.
Line 27: Line 37:
  <name> <value> [<duration>]
  <name> <value> [<duration>]
Currently, events are mapped to state variables, so the following restrictions apply:
Currently, events are mapped to state variables, so the following restrictions apply:
*The name field must match the name of a state variable, and that state variable must exist in the system before it appears in an event.
*The name field must match the name of an event kind state variable defined within BEGIN_EVENT_DEFINITIONS/END_EVENT_DEFINITIONS, and that state variable must exist in the system before it appears in an event.
*The value field specifies an unsigned integer value that can be represented by the number of bits reserved for the corresponding state variable.
*The value field must specify an unsigned integer value that "fits into" the number of bits reserved for the corresponding state variable.
*When specified, the duration must be 0. When mapping the event to a state variable, a duration of 0 means that the state variable is set to the specified value for a single sample only, and to zero for subsequent samples. A non-specified duration means that the state variable is set for all subsequent samples.
*Currently, the duration must either be omitted, or 0. When mapping the event to a state variable, a duration of 0 means that the state variable is set to the specified value for a single sample only, and to zero for subsequent samples. A non-specified duration means that the state variable is changed, and keeps its value for subsequent samples.
 
==Extended Syntax==
bcievent is actually a macro hiding the definition of an object of type BCIEvent. You may create such an object yourself, which is useful if you want to define the time stamp to use rather than have it taken automatically for you.
BCIEvent(PrecisionTime::Now()) << "MyEvent " << value;
will match the default behavior: The event is initialized with the current time stamp at creation.
BCIEvent(PrecisionTime::Now() + 5) << "MyEvent " << value;
will delay the event by 5ms. Be warned, however, that time stamps extending beyond the sample block currently digitized will not work properly, due to the way how events are aligned with data in time.
 
==Example==
In this example, an event "MyEvent" will be created, its value field set to <tt>currentValue</tt>, and its duration field empty.
Please note that the name is followed with a space character separating name and value.
The event will be time stamped when it is created, and will be sent when it goes out of scope, or is flushed explicitly. Unless
there is time-consuming code between event creation, and the end of scope, there is no need to flush it explicitly.
#include "BCIEvent.h"
...
{
  int currentValue;
  ...
  bcievent << "MyEvent " << currentValue; // MyEvent is time stamped here
  bcievent << "AnotherEvent " << currentValue << std::flush; // AnotherEvent is time stamped and sent here
  BCIEvent(PrecisionTime::Now()+5) << "Event3 " << currentValue; // Event3 is time stamped here with a delayed time stamp
  ...
} // MyEvent and Event3 are sent here


==See also==
==See also==
[[Programming Reference:Environment Class]], [[Technical Reference:State Definition]]
[[Programming Reference:Environment Class]], [[Technical Reference:State Definition]], [[Programming Tutorial:Implementing an Input Logger]]


Events are used to implement Keyboard, Mouse, and Joystick logging: [[User Reference:Logging Input]]
Events are used to implement keyboard, mouse, and joystick logging: [[User Reference:Logging Input]]


[[Category:Development]] [[Category:Data Acquisition]] [[Category:Framework API]]
[[Category:Development]] [[Category:Data Acquisition]] [[Category:Framework API]]

Latest revision as of 14:35, 12 August 2022

This page describes how to record asynchronous information ("events") along with brain signal data. This usage of the term "event" is unrelated to "events" as part of BCI2000 API class interfaces.

As data are recorded by data acquisition hardware in chunks, BCI2000 processes them synchronously. Still, recording information that is asynchronous to brain signal data acquisition may be of interest to an experimenter. Typically, mouse or keyboard input may be of interest to record in an experiment, and ideally should provide a temporal resolution that corresponds to the brain signal's sampling rate.

Concept

In the BCI2000 programming API, asynchronous information may be represented as Events.

Here, an Event is an object that

  • consists of two elements: a string descriptor and a time stamp, and
  • is recorded in a data file together with brain signals.

Implementation

In the current version of BCI2000, events are mapped to state variables. By comparing an event's time stamp to data acquisition time stamps, it is possible to associate events with single samples, and to map an event to a change in a state variable's value.

In order to make events available for writing, they must be first declared as in the following example:

BEGIN_EVENT_DEFINITIONS
  "MyEvent 10 0 0 0",
END_EVENT_DEFINITIONS

Here, an event's definition follows the rules applying to the definition of state variables: first, the name is given, then follow bit width and initial value. At the end, a pair of zeros needs to be added.

Limitations

  • Time stamps have millisecond resolution. Up to sampling rates of 1kHz, this is not a practical restriction though.
  • Apart from the 1kHz limit given by time stamp resolution, the upper limit of temporal resolution is determined by the amplifier's maximum sampling rate.
  • Association of event time stamps with sample positions cannot be more accurate than time stamps assigned to data blocks by the data acquisition module. If data transmission latency between data acquisition hardware, and its associated jitter, are large compared to a single sample's duration, then event positions cannot be expected to be precise. Still, for data acquisition devices that are connected directly to the machine running the data acquisition module (i.e., not over a network connection), and for sampling rates below 1kHz, transmission latency and jitter may be expected to be below a sample's duration.
  • Event time stamps must refer to the same physical time base that is used to stamp acquired data packets. This limits the use of events to BCI2000 modules that run on the same machine as the data acquisition module. To ensure correct operation also in situations where processing modules are distributed across multiple machines in a network, the event API may be used only in data acquisition modules.
  • The bcievent asynchronous stream interface should only be called between "StartRun" and "StopRun". Logging events with bcievent outside of a run could result in delayed state logging through the bcievent interface.

Event API

When including the src/shared/accessors/BCIEvent.h header file into a source code file, a symbol bcievent will be available. The bcievent symbol behaves like a std::ostream; writing to that stream creates an event, fills its descriptor with whatever has been written into the stream, and stamps the event with current time. The bcievent symbol's ostream behavior makes it convenient to include numeric information into the event's string descriptor. To allow for true asynchrony, BCI2000 events may be issued from any thread within a BCI2000 module, i.e. the bcievent interface is thread-safe.

Descriptor Syntax

An event descriptor consists of a name, a value, and optionally a duration, with data fields being separated with white space:

<name> <value> [<duration>]

Currently, events are mapped to state variables, so the following restrictions apply:

  • The name field must match the name of an event kind state variable defined within BEGIN_EVENT_DEFINITIONS/END_EVENT_DEFINITIONS, and that state variable must exist in the system before it appears in an event.
  • The value field must specify an unsigned integer value that "fits into" the number of bits reserved for the corresponding state variable.
  • Currently, the duration must either be omitted, or 0. When mapping the event to a state variable, a duration of 0 means that the state variable is set to the specified value for a single sample only, and to zero for subsequent samples. A non-specified duration means that the state variable is changed, and keeps its value for subsequent samples.

Extended Syntax

bcievent is actually a macro hiding the definition of an object of type BCIEvent. You may create such an object yourself, which is useful if you want to define the time stamp to use rather than have it taken automatically for you.

BCIEvent(PrecisionTime::Now()) << "MyEvent " << value;

will match the default behavior: The event is initialized with the current time stamp at creation.

BCIEvent(PrecisionTime::Now() + 5) << "MyEvent " << value;

will delay the event by 5ms. Be warned, however, that time stamps extending beyond the sample block currently digitized will not work properly, due to the way how events are aligned with data in time.

Example

In this example, an event "MyEvent" will be created, its value field set to currentValue, and its duration field empty. Please note that the name is followed with a space character separating name and value. The event will be time stamped when it is created, and will be sent when it goes out of scope, or is flushed explicitly. Unless there is time-consuming code between event creation, and the end of scope, there is no need to flush it explicitly.

#include "BCIEvent.h"
...
{
  int currentValue;
  ...
  bcievent << "MyEvent " << currentValue; // MyEvent is time stamped here
  bcievent << "AnotherEvent " << currentValue << std::flush; // AnotherEvent is time stamped and sent here
  BCIEvent(PrecisionTime::Now()+5) << "Event3 " << currentValue; // Event3 is time stamped here with a delayed time stamp
  ...
} // MyEvent and Event3 are sent here

See also

Programming Reference:Environment Class, Technical Reference:State Definition, Programming Tutorial:Implementing an Input Logger

Events are used to implement keyboard, mouse, and joystick logging: User Reference:Logging Input