Skip to content

DAQ system: read raw epochs at 0-based samples, as ingested ones (fix off-by-one) - #348

Merged
stevevanhooser merged 1 commit into
mainfrom
claude/daq-sample-indexing
Nov 1, 2026
Merged

stevevanhooser merged 1 commit into
mainfrom
claude/daq-sample-indexing

Conversation

@stevevanhooser

Copy link
Copy Markdown
Contributor

Why

ndi_daq_system_mfdaq.readchannels_epochsamples read a raw epoch one sample later than the same epoch ingested.

In this port, sample positions are 0-based: epochtimes2samples, epochsamples2times and the ingested reader all use them (see docs/developer_notes/ndi_xlang_principles.md, NDI-python#313). A raw reader's readchannels_epochsamples, though, takes 1-based samples, as in MATLAB. The DAQ system passed its numbers to both readers unchanged.

For the example Intan session, readchannels_epochsamples("analog_in", [1], 1, 1, 5):

Epoch Values
raw [0, -15.015, -10.92, -30.81, -22.425]
ingested [-15.015, -10.92, -30.81, -22.425, -12.48]

The probe layer is affected too. ndi_probe_timeseries_mfdaq.readtimeseriesepoch turns times into 0-based samples with epochtimes2samples, reads the data through the DAQ system, and computes the returned times with epochsamples2times. On a raw epoch, the data were therefore one sample off from their own time vector. Ingested epochs were right.

What

  • ndi_daq_system_mfdaq.readchannels_epochsamples adds 1 to s0/s1 before calling a raw reader. The ingested path is unchanged.
  • Docstrings that called these samples 1-indexed are corrected: ndi_daq_system_mfdaq.readchannels_epochsamples and ndi_probe_timeseries_mfdaq.read_epochsamples.
  • The system's bridge entry for readchannels_epochsamples is corrected the same way.
  • CHANGELOG entry under Fixed.

The raw readers' own 1-based readchannels_epochsamples is not changed: their bridge entries record it, tests pin it, and ingestion depends on it. Only the DAQ system, which callers use, now converts between the two.

Testing

  • New tests/test_daq_system_sample_indexing.py, on the example Intan session:
    • sample 0 through the DAQ system is the raw reader's first sample;
    • the raw time channel at samples 10..19 matches epochsamples2times(10..19), the pairing a probe returns;
    • raw and ingested reads agree, for both epochs, for ai and time, at the start and mid-epoch.
  • All three tests fail on main and pass with the fix.
  • Full suite locally: 6528 passed, 352 skipped.
  • Lint (black, ruff) is clean.

Generated by Claude Code

ndi_daq_system_mfdaq.readchannels_epochsamples takes 0-based sample
positions, as epochtimes2samples and epochsamples2times do in this port
(docs/developer_notes/ndi_xlang_principles.md). It passed them unchanged to
both readers, but a raw reader's readchannels_epochsamples takes 1-based
samples while the ingested reader takes 0-based ones. So a raw epoch read
one sample later than the same epoch ingested, and a probe's raw data were
one sample off from the times returned with them. The raw path now adds 1.

The system and probe docstrings, which called these 1-indexed, and the
system's bridge entry are corrected. tests/test_daq_system_sample_indexing.py
checks, on the example Intan session, that sample 0 is the first sample, that
the raw time channel matches epochsamples2times, and that raw and ingested
reads agree; all three fail without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v7R5rnKYwncJkrNZRXhtw
@stevevanhooser
stevevanhooser merged commit db5eaf2 into main Nov 1, 2026
15 checks passed
@stevevanhooser
stevevanhooser deleted the claude/daq-sample-indexing branch November 1, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants