Programming Reference:BufferedADC Class

From BCI2000 Wiki
Revision as of 15:55, 30 January 2012 by Mellinger (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

BufferedADC is a base class for signal source components (ADCs). Unlike the more GenericADC class, it provides buffering for data packets read from the ADC. This helps avoiding data loss during intervals in which BCI2000 processing time exceeds the physical duration of a data block.

In addition to the main thread, BufferedADC maintains a separate thread in which data acquisition is running, and internally uses synchronizers to avoid race conditions between these two threads. In most cases, descendants of BufferedADC need not be aware of this multithreading because BufferedADC interface functions are not executed concurrently. An exception to this is when BufferedADC::OnProcess() is implemented, and accesses resources common to both threads.

To interface with an ADC device, a descendant of BufferedADC provides implementations of the following functions:

Constructor

As is common for BCI2000 filters, the constructor defines configuration parameters.

OnPreflight( SignalProperties& output ) const

This is a forward of the standard Preflight() function as documented for BCI2000 filters, except that it takes a single output signal properties argument, and none for input. The output argument should be modified to match the ADC's number of channels by calling output.SetChannels(), and the sample block size by calling output.SetElements(). Also, Preflight() should check whether the device will be able to acquire data using the current parameter configuration in conjunction with its current physical state (e.g., physically connected, switched on, etc.). Ideally, this is done by configuring the device, and acquiring a few samples.

OnInitialize( const SignalProperties& output )

This is a forward of the standard Initialize() function, except that it only takes a single output properties argument. From here, do all initialization except actually starting data acquisition.

OnProcess()

This function is called when the main thread enters the GenericADC::Process() function, before the main thread waits for the acquisition thread to deliver data. In most cases, you will not implement this function. A notable exception is when your code needs to read from state variables, e.g. to set the amplifier's digital output. This function may run concurrently with DoAcquire(), so you need to serialize access to common resources with a synchronizer to avoid race conditions. Note that improper synchronization may force both threads into progressing in synchrony, which will obstruct the benefit of multi-threaded buffering. Modern amplifier APIs allow for concurrent access to the amplifier from multiple threads, so you will likely not need the additional synchronizer.

OnStartAcquisition()

This function is called when the acquisition thread is started, and should put the ADC device into acquisition mode.

OnStopAcquisition()

This function is called before the acquisition thread is terminated, and should put the ADC device into idling mode.

DoAcquire( GenericSignal& output )

This function is called repeatedly while the acquisition thread is running. It should block until data becomes available, and then copy data from the ADC device API's buffer into the GenericSignal object provided that is proviced as an argument.

Parameters

SourceBufferSize

Allows to choose the size of the buffer, in sample blocks when no unit is given, or in seconds when followed with an "s". Not that the minimum buffer size corresponds to the duration of two sample blocks.

States

SourceTime

This is the system-wide acquisition time stamp associated with each data block. After DoAcquire() has finished, BufferedADC obtains a system time stamp with millisecond accuracy, and stores it in the sample buffer. As soon as a buffered data block is released into the main processing thread, the SourceTime state is assigned the block's associated time stamp from the buffer. This ensures that a data block's time stamp always matches the time of its physical acquisition, rather than its release into the processing chain.

See also

Programming Tutorial:Implementing a Data Acquisition Module, Programming Reference:GenericFilter Class, Programming Reference:GenericADC Class