User Reference:SignalSharing
Synopsis
SignalSharing uniquely allows for asynchronous processing. Data can be received and sent outside of the standard BCI2000 pipeline (at any point in the filter chain!), allowing for timescales independent of the BCI2000 block-wise processing. This is useful for applications such as real-time visualization, or signal processing algorithms that take a substantial amount of time to compute an output.
SignalSharing taps into the BCI2000 processing pipeline by receiving any filter output signal through a combination of a TCP connection, and shared memory. In addition, signal and state data may be modified from the client side, i.e. they may be injected into BCI2000.
Function
The SignalSharing component in BCI2000 shares its input signal through a GenericSignal object which has been linked to a shared memory block using GenericSignal::ShareAcrossModules(). A dedicated thread waits for signal updates from the BCI2000 side, and sends signal and state data out to a separate application waiting on a TCP/IP connection. Also, the separate application may send signal and state data back to BCI2000 through the same connection.
When the client application is running on a separate machine, full signal data are sent over the network. When the client is running on the same machine, only a reference to a shared memory block is sent. On the client side, a flag in the signal data block indicates whether full signal data is transmitted, or just the name of a shared memory block to which the client may connect in order to read data.
To facilitate consistency with the BCI2000 model of operation, SignalSharing is operating as a TCP client rather than a server. When BCI2000 goes into Initialization state, the client tries to connect to a TCP server that will receive the data. This will appear confusing at first because the client application needs to act as a server, but it is more flexible as the BCI2000 SignalSharing component may open and close connections, and send data, as it fits the current BCI2000 state of operation.
Data Transmission
Immediately after opening the connection, SignalSharing sends a number of Parameter messages to share the parameters that are present in the system. The client may choose to ignore them, or use them for its own purposes.
Following those parameter messages, SignalSharing sends a SignalProperties message to announce meta-information about the filter's output signal, such as channel names, physical units, etc. The sourceID field of the signal properties message equals the string "Signal".
If configured to send state information, SignalSharing will send another SignalProperties message conveying meta-information about a signal that accommodates state information. This message's sourceID field will equal the string "States".
Subsequently, signal data are sent whenever a signal leaves the filter for which SignalSharing is enabled. The signal data's sourceID field will contain the string "Signal".
Signal data contains a flag that indicates whether full signal data is sent, or just the name of a shared memory block. For local connections (i.e., connections to a local address of the machine BCI2000 is running on), the name of a shared memory block is sent, so you may attach to that block and read data from there. Between two subsequent times the user presses "Set Config" in the Operator interface, the shared memory block will remain the same, i.e. the signal data message will always contain the same shared memory name. Still, the message is necessary to indicate that a new data block is available.
A signal message contains the specification of a number format, i.e. one of int16, int32, or float32. If transmitted through a channel such as a TCP connection, the signal's binary representation will be a sequence of numbers according to the number format. In shared memory, however, signal representation is always IEEE 754 64-bit double format.
If configured to do so, SignalSharing will follow each brain signal data message with another signal message which holds state information. The state information message is a signal data message in which individual states correspond to channels, and state samples correspond to elements. This signal data's sourceID field will contain the string "States".
Depending on position in the signal processing chain, the number of state samples may differ from the number of signal samples/elements because the number of state samples will always be identical to brain signal source block size, whereas the number of signal samples/elements depends on the position in the chain.
Back Channel
SignalSharing allows to send signal data as well as state data back to BCI2000. Signal data must have the same dimensions as the signal received through SignalSharing, and is sent as a signal data message. Similarly, state data is sent as a state message. In this state message, Length, ByteLocation, and BitLocation fields are ignored; only Name and Value fields are used.
To allow for synchronization between signal and state changes received from the client, no action is taken until another message is received. That message is a special system command message with content EndOfData. Once this message has been received, signal and state changes will be applied the next time the shared filter's data have been updated inside the BCI2000 processing chain.
Configuration
Similar to visualizations, the system creates two parameters for each active filter, and places those under the SignalSharing tab in the Operator's config dialog (or multiple SignalSharing tabs in more complex signal processing configurations).
By default, these parameters are empty strings; to activate SignalSharing for a certain filter, set the parameter Share<FilterName> to an ip address and port to connect to, e.g.
localhost:1879
To activate state sharing in addition to signal sharing, set the parameter Share<FilterName>States to a list of state names. In the transmitted signal that holds state information, channels will correspond to states in the order specified in the Share<FilterName>States parameter. Also, any state whose value is going to be changed from the client side must be listed in the Share<FilterName>States parameter.
Client Examples
- An example for a SignalSharing client application in C++ using the SignalSharingClient Library: SignalSharingClientLibDemo
- An example for a SignalSharing client application in C++ using the BCI2000 C++ framework: SignalSharingDemoClient C++ App
- An example for a SignalSharing client application in Python: SignalSharing Python Demo
See also
Technical Reference:BCI2000 Messages, User Tutorial:BCI2000Remote, Programming Reference:GenericSignal Class, Technical Reference:SignalSharingClient Library, Programming Reference:SignalSharingClientLibDemo Programming Reference:SignalSharingDemoClient C++ App, Programming Reference:SignalSharing Python Demo