Skip to content

Add Neuropixels SpikeGLX reader support to NDR#92

Merged
stevevanhooser merged 4 commits intomainfrom
claude/neuropixels-research-YKKwm
Mar 21, 2026
Merged

Add Neuropixels SpikeGLX reader support to NDR#92
stevevanhooser merged 4 commits intomainfrom
claude/neuropixels-research-YKKwm

Conversation

@stevevanhooser
Copy link
Copy Markdown
Contributor

Summary

This PR adds comprehensive support for reading Neuropixels data acquired with SpikeGLX software to the Neuroscience Data Reader (NDR) framework. The implementation includes a complete reader class, format handlers, metadata parsing, and extensive unit tests.

Key Changes

  • New Reader Class (ndr.reader.neuropixelsGLX): Implements the NDR reader interface for SpikeGLX AP-band binary files, handling channel enumeration, sample rate reporting, data reading, and time management.

  • Format Support Functions:

    • ndr.format.neuropixelsGLX.readmeta(): Parses SpikeGLX .meta text files into structured data
    • ndr.format.neuropixelsGLX.header(): Extracts standardized recording parameters (sample rate, channel counts, voltage ranges, probe info) from metadata
    • ndr.format.neuropixelsGLX.read(): Reads interleaved int16 binary data with support for partial channel/time ranges
    • ndr.format.neuropixelsGLX.samples2volts(): Converts raw int16 samples to voltage using per-channel gains from imroTbl
  • Comprehensive Testing:

    • Parameterized unit test class (TestNeuropixelsGLX) covering full 384-channel and subset recordings
    • Tests verify metadata parsing, channel listing, sample rates, data integrity, and time range calculations
    • Functional test script for manual validation with synthetic data
  • Documentation: Added detailed format specification document explaining SpikeGLX file organization, binary format, metadata fields, and NDR design decisions.

  • Reader Registration: Updated ndr_reader_types.json to register the new reader with multiple type aliases (neuropixelsGLX, neuropixels, spikeglx, imec).

Implementation Details

  • Handles both full 384-channel recordings and user-selected channel subsets via snsSaveChanSubset parsing
  • Exposes neural channels as analog_in (ai1..aiN), sync channel as digital_in (di1), and computed time as time (t1)
  • Supports both AP-band and LF-band streams with automatic stream type detection
  • Correctly parses per-channel gains from imroTbl for accurate voltage conversion
  • Uses efficient binary matrix reading via ndr.format.binarymatrix.read for partial file access
  • Returns data in native int16 format to preserve precision; voltage conversion is optional via samples2volts()

https://claude.ai/code/session_01RMRXufEXExJQUhrnsgrtP2

claude added 2 commits March 20, 2026 17:12
Implement format functions and reader class for Neuropixels probes
acquired with SpikeGLX:

Format functions (ndr.format.neuropixelsGLX):
- readmeta: Parse .meta key=value files into structs
- header: Extract standardized recording parameters (sample rate,
  channel counts, gains, voltage range, channel subsets)
- read: Read interleaved int16 binary data with time/channel subsetting
- samples2volts: Convert raw int16 to voltage using per-channel gains

Reader class (ndr.reader.neuropixelsGLX):
- Handles one probe's AP stream per instance (.ap.bin + .ap.meta)
- Neural channels as analog_in (ai1..aiN), sync as digital_in (di1)
- Returns raw int16 for efficient storage; use samples2volts for voltage
- Supports channel subsets (fewer than 384 channels saved)
- Registered in ndr_reader_types.json as neuropixelsGLX/neuropixels/spikeglx/imec

Tests:
- matlab.unittest parameterized tests (384, 32, 8 channel configs)
- Legacy-style test function matching existing NDR test conventions
- Synthetic data generation with known values for verification

Documentation:
- NeuropixelsGLX_format.md covering file layout, naming conventions,
  binary format, metadata fields, voltage conversion, and design decisions

https://claude.ai/code/session_01RMRXufEXExJQUhrnsgrtP2
Use V = int16 * imAiRangeMax / imMaxInt / gain instead of
Vrange / (2 * maxInt * gain). Numerically identical for symmetric
ranges but matches the official SGLX_readMeta.m convention.

https://claude.ai/code/session_01RMRXufEXExJQUhrnsgrtP2
% See also: ndr.reader.base
end

function ec = epochclock(obj, epochstreams, epoch_select)

Check warning

Code scanning / Code Analyzer

Input argument might be unused. Consider replacing the argument with ~ instead. Warning

Input argument might be unused. Consider replacing the argument with ~ instead.
% See also: ndr.reader.base
end

function ec = epochclock(obj, epochstreams, epoch_select)

Check warning

Code scanning / Code Analyzer

Input argument might be unused. Consider replacing the argument with ~ instead. Warning

Input argument might be unused. Consider replacing the argument with ~ instead.
% See also: ndr.reader.base
end

function ec = epochclock(obj, epochstreams, epoch_select)

Check warning

Code scanning / Code Analyzer

Input argument might be unused. Consider replacing the argument with ~ instead. Warning

Input argument might be unused. Consider replacing the argument with ~ instead.
ec = {ndr.time.clocktype('dev_local_time')};
end

function t0t1 = t0_t1(obj, epochstreams, epoch_select)

Check warning

Code scanning / Code Analyzer

Input argument might be unused. Consider replacing the argument with ~ instead. Warning

Input argument might be unused. Consider replacing the argument with ~ instead.
t0t1 = {[0 t_end]};
end

function channels = getchannelsepoch(obj, epochstreams, epoch_select)

Check warning

Code scanning / Code Analyzer

Input argument might be unused. Consider replacing the argument with ~ instead. Warning

Input argument might be unused. Consider replacing the argument with ~ instead.
case {'analog_in', 'ai'}
% Read neural channels (1-based channel numbers map to
% file columns 1:n_neural_chans)
[data, ~] = ndr.format.neuropixelsGLX.read(binfile, -Inf, Inf, ...

Check warning

Code scanning / Code Analyzer

Value assigned to variable might be unused. Consider replacing the variable with ~ instead. Warning

Value assigned to variable might be unused. Consider replacing the variable with ~ instead.
end
end

function sr = samplerate(obj, epochstreams, epoch_select, channeltype, channel)

Check warning

Code scanning / Code Analyzer

Input argument might be unused. Consider replacing the argument with ~ instead. Warning

Input argument might be unused. Consider replacing the argument with ~ instead.
end
end

function sr = samplerate(obj, epochstreams, epoch_select, channeltype, channel)

Check warning

Code scanning / Code Analyzer

Input argument might be unused. Consider replacing the argument with ~ instead. Warning

Input argument might be unused. Consider replacing the argument with ~ instead.
sr = repmat(info.sample_rate, size(channel));
end

function metafile = filenamefromepochfiles(obj, filename_array)

Check warning

Code scanning / Code Analyzer

Input argument might be unused. Consider replacing the argument with ~ instead. Warning

Input argument might be unused. Consider replacing the argument with ~ instead.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 20, 2026

Test Results

92 tests  +48   92 ✅ +48   4s ⏱️ +2s
12 suites + 3    0 💤 ± 0 
 1 files   ± 0    0 ❌ ± 0 

Results for commit 32bc03f. ± Comparison against base commit b579098.

♻️ This comment has been updated with latest results.

claude and others added 2 commits March 21, 2026 13:36
Reduce per-channel offset from 100 to 50 so that max test value
(383*50+1000=20150) stays within int16 range (32767). Previously
channels >= 329 would saturate, causing size mismatches in
testReadAllChannels assertions.

https://claude.ai/code/session_01RMRXufEXExJQUhrnsgrtP2
@stevevanhooser stevevanhooser merged commit 284fdc1 into main Mar 21, 2026
@stevevanhooser stevevanhooser deleted the claude/neuropixels-research-YKKwm branch March 21, 2026 13:55
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