FieldTrip buffer C++ implementation
This page describes the C++ wrappers around the reference implementation and a generic framework for writing an acquisition driver using a set of ready-made C++ modules.
Components
What follows is a list of C++ classes and their purpose.
MultiChannelFilter
Online filtering (IIR) of multi-channel signals, using the same filter coefficients for each channel. This is a template class that allows arbitrary data types to be used for the input/output signal, as well as the internally used data type to hold the filter states. For example, you can lowpass-filter 10 channels of 32-bit integer signals and use a double-precision floating point representation internally by calling
MultiChannelFilter<int,double> *filter = new MultiChannelFilter<int,double>(10, 4); // 4th-order filter filter->setButterLP(0.1); // normalised frequency, 1=Nyquist filter->process(out, in); // out and in point to 10 integers each (=one sample)
TemplateVectorMath
A small helper class to carry out vector operations (as used in MultiChannelFilter) on arbitrary data types.
GDF_Writer
A class that allows to create GDF files (v2.20). It provides helper functions for setting up the header, and adding blocks of data to the file. For example, to write 1 status + 10 continuous channels as 32-bit integers, you would call something like
GDF_Writer gdfWriter = new GDF_Writer(1+10, 500, GDF_INT32); gdfWriter->setLabel(0, "Status"); for (int i=0;i<10;i++) { gdfWriter->setLabel(1+i, yourLabel[i]); // e.g. yourLabel[0] is label of first cont. channel gdfWriter->setPhysicalLimits(1+i, -262144.0, 262144.0); // maximum physical value = 262144 ... gdfWriter->setPhysDimCode(1+i, GDF_MICRO + GDF_VOLT); // ... in units of microVolt } ... gdfWriter->createAndWriteHeader(filename); ... gdfWriter->addSamples(num, data); // data must point to 11*num integer values
ChannelSelection & SignalConfiguration
The ChannelSelection
class wraps up a list of channel indices and labels. It's used twice within
the SignalConfiguration
class: Once for describing a selection of channels for streaming, and once for saving
to disk. On top of that, the SignalConfiguration
provides a few commonly used properties such as the desired
bandwidth (cutoff frequency) and filter order for lowpass filtering, a decimation factor for downsampling streamed
data and so on. It also provides functions for parsing these configuration options from a text file (see the
wiki page for the Biosemi acquisition driver for an example).
ChannelSelection
could actually be used in both ways:
- Say you have a hardware device with a fixed list of channels, which are not necessarily labeled. In this case, you would select the desired channels by their index, and then attach a name as in the configuration file described above.
- For other systems (e.g., CTF MEG) you might be given the channel names from the hardware vendor. In this case, you would specify the channel selection by a list of names, and then match those to the HW vendors list to determine which the channel indices (not implemented yet).
StringServer
The StringServer
class wraps up a mechanism to listen to ASCII messages on a TCP port. Multiple clients can connect
at the same time. Commands should be terminated by a line-feed (\n
). The server will respond by line-feed terminated
ASCII strings in the same way. This is used to control the runtime behaviour of the OnlineDataManager
(see below).
OnlineDataManager
This class will eventually wrap up all the generic parts of streaming data to a FieldTrip buffer and saving it to a GDF file at the same time. The general scheme looks like this
Setup HW device Setup OnlineDataManager (ODM) (read configuration file from disk) LOOP Check ODM status (in particular requests from the StringServer) Poll hardware (=> yields n new samples) Let ODM provide a new empty block (n samples) Fill samples (all HW channels) into that block Add events to ODM's event list Let ODM handle this block (stream out selected channels) (write selected channels to disk) ENDLOOP
Saving to disk will be handled in a separate thread to allow for smallest possible latencies + jitter.
Commands that influence runtime behaviour
The OnlineDataManager will listen on a certain TCP port for incoming commands given as line-feed-terminated ASCII strings such as:
STREAM START
will enable streaming.STREAM STOP
will disable streaming.STREAM SELECT index=label index=label …
to select channels for streaming. Spaces can be used in labels by using double quotes, e.g.STREAM SELECT 1=“Channel 1”
STREAM FILTER bandwidth order factor
to enable low-pass filtering and downsamplingSTREAM STATUS
to retrieve information about running state. The response will be of the formnumacquired=N numstreamed=S downsample=D bandwidth=B bworder=O
where
N
is the number of continuously sampled channels from the hardware,S
is the number of channels selected for streaming,D
is the downsampling factor,B
is the bandwidth (cut-off frequency of lowpass filter), andO
is the order of the Butterworth filter used. Notice that forO=0
no filtering takes place, no matter which bandwidth is reported.SAVE START
will enable saving to GDF. In case no new filename has been set (see below), a suffix_Si
withi
being a counter will be appended.SAVE STOP
will disable saving to GDF. The current file will be closed orderly.SAVE SELECT index=label index=label …
to select channels for streaming.SAVE FILE filename
to set the filename for the next GDF file to be written.SAVE STATUS
to retrieve information about running state. The response will be of the formnumacquired=N numsaved=S saving=T savingto="filename"
where
N
is the number of continuously sampled channels from the hardware,S
is the number of channels selected for staving,T
is eithertrue
orfalse
depending on whether saving is enabled, andfilename
describes the file currently written to. Notice that the latter is always empty (“”) in casesaving=false
.STATUS
to retrieve information about running state (both streaming and saving). The response is a combination of the responses orSAVE STATUS
andSTREAM STATUS
.
Generally, the SELECT, FILTER and FILE commands will only be accepted after a corresponding STOP command.