stream
Cold stream of decoded chunks. On collection it starts the device stream (control 0x01) and decodes each notification into one EegChunk; on completion it stops the stream (control 0x00, best-effort). The notification listener is attached BEFORE the start write, so the first packets cannot slip past it, and each packet is stamped (EegChunk.hostTimeNs) as it leaves the transport — before the queue to the decoder, so a slow collector delays chunks but not their timestamps. The queue is UNBOUNDED, so a transiently slow collector never causes packets to be discarded (which would show up as device-side loss in missedSamples). A backlog costs memory (~500 B/packet), never data; only a consumer that is sustainedly slower than real time can grow it. Each chunk also carries per-active-channel EegChunk.contact; device-side losses (gap-filled samples) accumulate into missedSamples, undecodable packets into undecodablePackets, counter discontinuities into sampleCounterJumps.
Sample indexing starts at the first packet of each collection: firmware 3.7.0 restarts the device counter at 0 on every start, older firmware kept it running across STOP/START, and neither may be gap-filled as loss (see DecodeState.anchorOnNextPacket).
Fails with BrainAccessError.StreamStartFailed when the device cannot start its ADC (firmware 3.7.0+), and with BrainAccessError.ConfigRejected when it refuses the config.
outputMode selects electrode data or the ADS1299 test signal. On a device that has the output-mode characteristic (supportsTestSignal) the mode is written on EVERY start — the device keeps the last value until the host disconnects (firmware 3.7.0; earlier builds until power-off), so a test signal left on would otherwise stream as if it were EEG. On older firmware nothing is written and only OutputMode.NORMAL is possible (BrainAccessError.TestSignalNotSupported otherwise). The test signal replaces the impedance measurement, so it requires com.neurotec.brainaccess.protocol.ImpedanceMode.OFF.
digitalInput also streams the headset's digital input (ADS chip 0's GPIO1, sampled with each conversion): EegChunk.digitalInput then carries its level per sample, and every packet ends with a digital-input block the codec strips. It needs the stream-options characteristic (supportsDigitalInput, firmware 3.8.1+): BrainAccessError.DigitalInputNotSupported otherwise, before anything is written. The device applies the option to ONE start and clears it at every start (even a failed one), at a subscribe to the packets and at a disconnect, so it is written on every start that wants it — after the packet listener is attached, right before STREAM_START — and never otherwise: with digitalInput = false nothing is written to the characteristic, on any firmware. A failing option write fails the stream rather than streaming zeros.
A config with no active channel (DeviceConfig.activeCount 0) fails with IllegalArgumentException before anything is written: its packets carry no EEG data and cannot be decoded.
On a device with the time-sync characteristic (firmware 3.8.0+) the stream also pings it, from the moment the start is confirmed until the stream ends — never before the START write, never after the STOP write — so sampleIndexAt can place host times on the device's sample counter; see timeSyncInfo. This only adds GATT reads of that characteristic: no write of the stream's sequence changes, and a device without it is never asked.