From 9af8c889b7676c96b53cd4532ee5f3adb0b0dc3d Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Thu, 21 May 2026 14:40:05 -0600 Subject: [PATCH 1/2] more improvements spikegadgets --- neo/rawio/spikegadgetsrawio.py | 22 +++++++++++++++++++- neo/test/rawiotest/test_spikegadgetsrawio.py | 1 + 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/neo/rawio/spikegadgetsrawio.py b/neo/rawio/spikegadgetsrawio.py index ecd607a6e..ea88dfe70 100644 --- a/neo/rawio/spikegadgetsrawio.py +++ b/neo/rawio/spikegadgetsrawio.py @@ -177,9 +177,29 @@ def _intan_hwchans_in_binary_order(self, sconf, num_ephy_channels, num_ephy_chan intan_chans_per_chip = int(sconf.attrib.get("chanPerChip", 32)) # RHD2132 default for legacy files hw_chans_in_xml = [int(schan.attrib["hwChan"]) for trode in sconf for schan in trode] + if intan_chans_per_chip > num_ephy_channels: + # chanPerChip larger than the total channel count is structurally impossible + # for any real Intan chip layout. Reported in #1830 with the example value + # 1645402192, suspected to come from a Trodes header-write bug. + self.logger.warning( + "SpikeGadgets chanPerChip=%d exceeds num_ephy_channels=%d; " + "treating as invalid and falling back to XML document order. " + "This usually indicates a header-write bug in Trodes " + "(originally reported in python-neo PR #1830). The reader's " + "channel ids and sample data are unaffected, but downstream " + "code should not trust chanPerChip on this recording.", + intan_chans_per_chip, + num_ephy_channels, + ) + channels_fit_chip_layout = intan_chans_per_chip > 0 and num_ephy_channels % intan_chans_per_chip == 0 if not channels_fit_chip_layout: - return hw_chans_in_xml + # The Trodes writer (recordThread.cpp) writes the raw acquisition + # buffer in hwChan-indexed order regardless of deviceType, so the + # correct ordering for the fallback path is hwChan ascending. Sort + # rather than trust XML document order in case the workspace + # reordered SpikeNTrodes. + return sorted(hw_chans_in_xml) # Reproduce the chip-interleaved hwChan sequence (local-channel outer, chip inner) # so that hwchans_in_binary_order[i] is the hwChan whose data lives at byte pair i. diff --git a/neo/test/rawiotest/test_spikegadgetsrawio.py b/neo/test/rawiotest/test_spikegadgetsrawio.py index f254d22d5..62570a4c0 100644 --- a/neo/test/rawiotest/test_spikegadgetsrawio.py +++ b/neo/test/rawiotest/test_spikegadgetsrawio.py @@ -18,6 +18,7 @@ class TestSpikeGadgetsRawIO( "spikegadgets/W122_06_09_2019_1_fromSD.rec", "spikegadgets/SpikeGadgets_test_data_2xNpix1.0_20240318_173658.rec", "spikegadgets/neuropixels2_4shank/20260122_134412_merged_cropped_1min_NP2.rec", + "spikegadgets/msh_1024ch/msh_1024ch.rec", ] def test_parse_header_missing_channels(self): From 1c14f969aa81cb5625e50d8728e19c3456d966a1 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Thu, 21 May 2026 14:47:56 -0600 Subject: [PATCH 2/2] improve warning and comments messages, I have a Zach on my shoulder --- neo/rawio/spikegadgetsrawio.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/neo/rawio/spikegadgetsrawio.py b/neo/rawio/spikegadgetsrawio.py index ea88dfe70..73fdcc76e 100644 --- a/neo/rawio/spikegadgetsrawio.py +++ b/neo/rawio/spikegadgetsrawio.py @@ -182,23 +182,24 @@ def _intan_hwchans_in_binary_order(self, sconf, num_ephy_channels, num_ephy_chan # for any real Intan chip layout. Reported in #1830 with the example value # 1645402192, suspected to come from a Trodes header-write bug. self.logger.warning( - "SpikeGadgets chanPerChip=%d exceeds num_ephy_channels=%d; " - "treating as invalid and falling back to XML document order. " - "This usually indicates a header-write bug in Trodes " - "(originally reported in python-neo PR #1830). The reader's " - "channel ids and sample data are unaffected, but downstream " - "code should not trust chanPerChip on this recording.", - intan_chans_per_chip, - num_ephy_channels, + f"SpikeGadgets chanPerChip={intan_chans_per_chip} exceeds " + f"num_ephy_channels={num_ephy_channels}; treating as invalid " + f"and falling back to XML document order. This could indicate " + f"a bug; verify that channel order matches your expectation, " + f"and please open an issue at " + f"https://github.com/NeuralEnsemble/python-neo/issues if you " + f"encounter this. See PR #1830 for an earlier report." ) channels_fit_chip_layout = intan_chans_per_chip > 0 and num_ephy_channels % intan_chans_per_chip == 0 if not channels_fit_chip_layout: - # The Trodes writer (recordThread.cpp) writes the raw acquisition - # buffer in hwChan-indexed order regardless of deviceType, so the - # correct ordering for the fallback path is hwChan ascending. Sort - # rather than trust XML document order in case the workspace - # reordered SpikeNTrodes. + # The Trodes always write the data so the binary + # stream is hwChan-ascending for every SpikeGadgets device. Source: + # `Trodes/src-threads/recordThread.cpp` lines ~281-298 in the + # canonical Trodes repo at https://bitbucket.org/mkarlsso/trodes + # (checked May 2026). The correct ordering for the fallback path + # is therefore hwChan ascending; sort rather than trust XML + # document order in case the user reordered the xml return sorted(hw_chans_in_xml) # Reproduce the chip-interleaved hwChan sequence (local-channel outer, chip inner)