What’s new in 2.3.1#

2.3.1 stamps every LSL sample with the time the headset took it. It builds on 2.3.0, which added support for firmware 3.8 (digital input, time-synchronised markers), higher sample rates, charging settings, and an LSL stream that follows the BrainAccess Board LSL contract. The complete list is in CHANGELOG.md in the distribution ZIP.

New in 2.3.1#

  • LSL timestamps from the headset’s clock (firmware 3.8.0+): streamToLsl stamps every sample with the moment the headset took it, to within about 1 ms, instead of the moment its packet reached the phone. The first seconds of a stream are held until the clock is fitted. Without time sync (older firmware, impedance mode) chunks are stamped on arrival after a 5 s hold at the stream’s start. BrainAccessLslOutlet takes an optional sampleTimes function for the same.

  • session.sampleHostTimeNs(sample) (the host System.nanoTime() time of a device sample) and session.sampleNearest(hostTimeNs) (the sample nearest a host time); both return null without a valid device-clock fit.

Upgrading from 2.2.0#

  • LSL outlet. The stream follows the BrainAccess Board LSL contract v1: new column order (contact_* directly after the EEG columns, then IMU, Sample, Streaming, Battery), canonical types and units (ACCEL, GYRO, CONTACT; microvolts, m/s2), double64 samples, source_id = the advertised BLE name and stream name <BLE name>_<MODEL>_BrainAccess. streamToLsl(scope, outputMode, software, digitalInput) replaces streamToLsl(scope, streamName); BrainAccessLslOutlet now takes a StreamMetadata. LSL apps declare the JNA dependency (see Installation).

  • New errors. BrainAccessError gains StreamStartFailed and DigitalInputNotSupported; an exhaustive when needs branches for both.

  • Stricter configs. DeviceConfig accepts only gains 1, 2, 4, 6, 8, 12, 24 (0 on an inactive channel), and configure() / stream() refuse a config with no active channel.

  • Impedance sample numbers. Each impedance value carries the sample number it was measured at; nothing is gap-filled between values (see Impedance).

  • Custom transports and fakes. GattConnection gains an abstract link and a readTimed with a default, and must finish an attribute operation once issued even if its caller is cancelled. EegChunk’s constructor takes wireLastSampleIndex and packetBytes.

New in 2.3.0#

  • Digital input (firmware 3.8.1+): stream(digitalInput = true), EegChunk.digitalInput, and the Digital LSL column.

  • Markers on the right sample: session.sampleIndexAt(hostTimeNs), backed by the headset’s time-sync service on firmware 3.8.0+; session.timeSyncInfo().

  • Higher sample rates per board: 2000 Hz in DeviceConfig.SUPPORTED_SRATES; hardware.supportedSrates adds 1 kHz on HALO V3 and 1 and 2 kHz on EXG8.

  • Charging settings: readChargingSettings() / writeChargingSettings() (stay on while charging, idle power-off), plus chargerConnected and batteryLow.

  • Diagnostics: link (BLE link parameters), readBatteryGauge(), the undecodablePackets / sampleCounterJumps / stalePackets counters, and EegChunk.wireLastSampleIndex / packetBytes.

  • Recording metadata: session.streamMetadata(...) and StreamMetadata.toJson() (Board’s StreamMetadata schema v1); session.name (advertised BLE name).

Improvements in 2.3.0#

  • Sample numbers are exact for recordings of any length.

  • Cancelling a stream’s collector stops the headset’s stream; repeated stop/start on one connection is reliable.

  • stream() reports StreamStartFailed when the headset cannot start its converter.

  • Packet handling follows firmware 3.8.x, including variable-length packets and counter restarts at each stream start.

  • Impedance at high configured rates streams at a rate where the measurement is valid (DeviceConfig.deviceSrate).

Tested with every headset model (HALO, MINI, MIDI, MAXI, EXG8) on firmware 3.8.8, and with MINI, MIDI, MAXI and EXG8 on firmware 3.8.9.