Contributions:BlackrockGemini
Synopsis
The BlackrockGemini signal source module acquires data from Blackrock Microsystems Gemini acquisition hardware over Ethernet, using the Cerebus SDK (cbsdk). All channel configuration — enabled channels, per-channel sampling rate, gain, and label — is set in Blackrock's Central application and imported into BCI2000 automatically at SetConfig time.
Location
http://www.bci2000.org/svn/trunk/src/contrib/SignalSource/BlackrockGemini
Versioning
Authors
Griffin Milsap (griffin.milsap@gmail.com), Jürgen Mellinger (mellinger@neurotechcenter.org), Nicholas Luczak (luczak@neurotechcenter.org)
Source Code Revisions
- Initial development: 8061
- Tested under: 9216
- Known to compile under: 9216
- Broken since: N/A
System Setup
Network Configuration
The module talks to the Gemini Hub over UDP and is sensitive to packet loss. Connect the BCI2000 machine and the Gemini Hub to a single commercial-grade switch, or use a crossover cable directly between the two. Do not place a second switch, or a consumer-grade router, in between.
Configure the BCI2000 machine's network interface as follows:
- IP address:
192.168.137.XXX, whereXXXis below 128, not 10, and ideally below 16. Make sure no other computer on the switch uses the same address. - Subnet mask:
255.255.255.0 - Default gateway: blank
To verify connectivity, open a command prompt and run:
ping 192.168.137.128
That is the Gemini Hub's default address. If the hub responds, the module should be able to reach it.
Configuring Channels in Central
All amplifier-side configuration is done in Blackrock's Central software. The Gemini Hub always digitises at 30 kHz; each channel can be independently enabled for continuous streaming at 500 Hz, 1 kHz, 2 kHz, 10 kHz, or 30 kHz. The hub decimates non-30 kHz streams (with antialiasing) before transmission.
A sample group may mix neural-amplifier channels and analog-input channels (sync pulses, eye-tracker outputs, photodiode signals, and so on). Any channel enabled for continuous streaming at the SamplingRate selected in BCI2000 will appear in the signal, regardless of the bank it lives on. Within the signal, channels are grouped by Blackrock instrument id (neural-amp channels first, analog inputs next), and within each instrument they appear in the order reported by Central.
Parameters
All parameters live under Source -- Signal Properties.
SamplingRate
Selects the sample group to record from. Valid values are 500 Hz, 1000 Hz, 2000 Hz, 10000 Hz, and 30000 Hz. When SetConfig is pressed, the module collects every channel currently enabled at this rate in Central and configures BCI2000 to acquire them.
SampleBlockSize
Samples per block per channel. Set to auto to pick a default that clocks BCI2000 at approximately 50 Hz:
int gGroupRates[] = { 0, 500, 1000, 2000, 10000, 30000 }; // samples per second
int gBlockSizes[] = { 0, 10, 20, 40, 200, 600 }; // samples per block (~50 Hz)
Any positive integer is accepted. Clocking BCI2000 faster than 50 Hz is generally a bad idea.
SourceCh
Total number of channels. Set to auto; the module fills this in from the Central configuration at SetConfig time.
ChannelNames
Channel labels. Populated automatically from the Channel Label column in Central. Use auto.
SourceChOffset / SourceChGain
Per-channel offset and gain. Populated automatically from each channel's cbSCALING limits reported by cbsdk. Use auto.
States
NSPSyncState
A two-bit state used internally by the module's start-of-run state machine. Not intended for downstream analysis.
BlackrockSourceTimeHigh / BlackrockSourceTimeLow
The upper and lower 32 bits of the 64-bit per-sample timestamp produced by the Gemini Hub. Concatenate as
timestamp_ns = (BlackrockSourceTimeHigh << 32) | BlackrockSourceTimeLow
to obtain nanoseconds since the Unix epoch (1970-01-01 UTC, 1 ns resolution).
Under the hood, these states are populated by reserving the final two rows of the source signal and labelling them @BlackrockSourceTimeHigh and @BlackrockSourceTimeLow; the @ prefix instructs BCI2000 to route those samples into the corresponding state rather than leave them in the signal.
Implementation Notes
This section is intended for programmers extending, porting, or debugging the module.
Class Overview
BlackrockADC derives from BufferedADC. Acquisition is callback-driven:
OnStartAcquisition()opens the cbsdk connection and registersDataCallbackwithcbSdkRegisterCallback(CBSDKCALLBACK_CONTINUOUS). Every incomingcbPKT_GROUPis dispatched toOnData()on a cbsdk thread.OnData()assembles one composite sample per timestep and pushes it intomDataPacketBuffers. When enough samples are queued for a full block, it signalsmDataAvailable.DoAcquire(), running on BCI2000's source thread, blocks onmDataAvailableand drains a block's worth of composite samples into the output signal.mDataMutex(astd::recursive_mutex) protects the per-instrument queues, the composite buffers, and the layout vectors.
Per-timestep Packet Pairing
On Gemini, each timestep is broadcast as one cbPKT_GROUP per instrument — for example, one for the front-end amplifier and a second for the AINP board. The cbpkt_header.time field in these packets is a per-packet send time, not a shared sample time, so channels cannot be paired across instruments by matching timestamps.
The module pairs them by arrival order instead:
- Each instrument has its own FIFO in
mInstrumentQueues. OnData()pushes every packet into the FIFO for itscbpkt_header.instrument.- While every FIFO is non-empty, the fronts are popped and concatenated into one composite frame in
mDataPacketBuffers, with each instrument's samples written at its reservedbaseOffsetinside the frame. - A resync guard flushes every FIFO if any one of them exceeds
kMaxInstrumentQueueDepth(8192 packets). This re-aligns pairing from a clean state if an instrument falls far behind.
Channel Discovery
GetChannelConfig() deliberately does not use cbSdkGetSampleGroupList(). On Gemini, that API reports only front-end channels in the requested sample group; AINP channels configured for the same rate stream as cbPKT_GROUP packets under a different cbpkt_header.instrument and are omitted from the list.
Instead, the module probes every channel id from 1 through cbNUM_ANALOG_CHANS with cbSdkGetChannelConfig(), filters by chanInfo.smpgroup == iGroup, groups the survivors by chanInfo.cbpkt_header.instrument, and emits them in ascending instrument-id order. As a side effect, this yields the instrument id used to route each channel's packets in OnData().
Build
The CMake target is Windows-only. It links against the prebuilt Cerebus SDK in lib/x64/cbsdkx64.lib (x64) or lib/x86/cbsdk.lib (x86). The headers cbsdk.h and cbhwlib.h ship in include/. Batch files are copied to prog/batch, and the BlackrockGemini.prm fragment to parms/fragments/amplifiers, by BCI2000_ADD_SIGNAL_SOURCE_MODULE.
The module has been built and run against both the official Blackrock cbsdk release and the open-source CereLink fork; either is compatible.