Skip to content

vhlabvisspike2: decode stimulus events from stimtimes.txt and stims.mat - #351

Merged
stevevanhooser merged 1 commit into
mainfrom
claude/vhlabvisspike2-stimulus-decoding
Nov 2, 2026
Merged

stevevanhooser merged 1 commit into
mainfrom
claude/vhlabvisspike2-stimulus-decoding

Conversation

@stevevanhooser

Copy link
Copy Markdown
Contributor

Problem

ndi.setup.daq.reader.mfdaq.stimulus.vhlabvisspike2 was a bare subclass of the CED Spike2 reader. The mk1–mk3 and e1–e3 stimulus channels were therefore looked up as channel numbers in spike2data.smr. MATLAB instead decodes them from the epoch directory.

Change

This ports +ndi/+setup/+daq/+reader/+mfdaq/+stimulus/vhlabvisspike2.m, covering epochclock, getchannelsepoch, samplerate and readevents_epochsamples_native:

  • It reads stimtimes.txt and stims.mat (MTI2) through vhlib.StimDecode.read_stimtimes_txt / getstimscript and uses vlt.data.dropnan. These come from vhlab-library-python and vhlab-toolbox-python, which are already dependencies, so nothing is reimplemented. vhlab-NewStim-python is not needed because only the stimid and startStopTimes fields of MTI2 are read. ndi.daq.metadatareader.NewStimStims reads stimulus parameters from the same file and does not overlap with this.
  • It applies stimtimes2stimtimes_mapping.txt and mti2stimtimes_mapping.txt when they are present. Each lists 1-based indices to keep, and NaN entries are dropped.
  • When the trigger counts differ it raises a ValueError with MATLAB's wording.
  • When a trial's Spike2 id differs from mod(mti.stimid,256) it raises VHLabVisSpike2StimulusMismatchError. This is a ValueError subclass whose .identifier is ndi:setup:daq:reader:mfdaq:stimulus:vhlabvisspike2:StimulusMismatch. The message follows vhlab importer: robust handling of legacy cell intervals and cell names VH-Lab/NDI-matlab#1114 (9fe55861a): Epoch t00006: stimulus number N does not line up …, followed by the path and a pointer to the mapping files.
  • Stimulus on/off/setup/clear times are computed with timeshift = stimtimes(i) - startStopTimes(2), and results are limited to the window t0 <= t <= t1.

Bridge

INTERFACE UPDATE: this adds a new src/ndi/setup/daq/reader/mfdaq/stimulus/ndi_matlab_python_bridge.yaml with a vhlabvisspike2 class entry, examined against NDI-matlab fe64a9f53. It records:

  • the dependency on #1114;
  • that matlab_branch is deliberately not set, because the file is already on main. When #1114 merges, the drift check will flag the entry for re-examination;
  • one MATLAB quirk that is not copied: MATLAB indexes marker and event results inconsistently ({i} vs {end+1}).

Tests

tests/test_setup_vhlabvisspike2.py builds synthetic epoch directories containing stimtimes.txt and a minimal stims.mat. It covers:

  • records that agree, including an id ≥ 256;
  • the time window;
  • a count mismatch;
  • an id mismatch, checking that the message names the epoch;
  • a mapping file that fixes a run stopped early (Spike2 has 3 triggers, stims.mat has 5);
  • a stimtimes mapping file containing NaN.

black --check, ruff check and the full pytest tests/ all pass. The bridge tests also pass when run against a full NDI-matlab checkout.

Follow-up

VH-Lab/NDI-matlab#1114 has not merged yet. Once it does:

  • add a note to the setup entry in the root bridge (src/ndi/ndi_matlab_python_bridge.yaml) saying that #1114's other changes are all in +ndi/+setup/+conv/+vhlab, which has no Python port;
  • re-sync this entry's hash.

Generated by Claude Code

The Python reader was a bare subclass of the CED Spike2 reader, so the
mk1-mk3 / e1-e3 stimulus channels were looked up in spike2data.smr
instead of being decoded from the epoch directory. Port MATLAB's
epochclock, getchannelsepoch, samplerate and
readevents_epochsamples_native:

- read stimtimes.txt and stims.mat through vhlib.StimDecode
  (read_stimtimes_txt, getstimscript) from vhlab-library-python, an
  existing dependency, rather than reimplementing them;
- apply stimtimes2stimtimes_mapping.txt and mti2stimtimes_mapping.txt
  (1-based indices to keep, NaN dropped);
- raise if the trigger counts differ, and raise
  VHLabVisSpike2StimulusMismatchError (identifier
  ndi:setup:daq:reader:mfdaq:stimulus:vhlabvisspike2:StimulusMismatch)
  if a trial's Spike2 id differs from mod(mti.stimid, 256), with the
  wording of VH-Lab/NDI-matlab#1114;
- map setup/off/clear into Spike2 time via
  stimtimes(i) - startStopTimes(2).

Adds a bridge file for ndi.setup.daq.reader.mfdaq.stimulus with the
vhlabvisspike2 entry, and tests on a synthetic epoch directory: records
agree, count mismatch, id mismatch naming the epoch, and a mapping file
fixing a run that stopped early.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v7R5uJJtaq8SVB4eEwnBR
@stevevanhooser
stevevanhooser merged commit 3073b81 into main Nov 2, 2026
15 of 16 checks passed
@stevevanhooser
stevevanhooser deleted the claude/vhlabvisspike2-stimulus-decoding branch November 2, 2026 13:17
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