Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3dc5729
Test IBL extractors tests failing for PI update
alejoe91 Dec 29, 2025
d1a0532
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Jan 6, 2026
33c6769
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Jan 16, 2026
2c94bac
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Jan 20, 2026
a412bd8
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Feb 2, 2026
504e19d
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Feb 12, 2026
cd09c19
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Feb 19, 2026
a40d073
Merge branch 'main' of github.com:alejoe91/spikeinterface
alejoe91 Feb 24, 2026
a1da327
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Mar 2, 2026
ef19a8e
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Mar 3, 2026
a098b51
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Mar 6, 2026
61c317a
Fix OpenEphys tests
alejoe91 Mar 6, 2026
c9ff247
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Mar 9, 2026
3520138
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Mar 16, 2026
f61329d
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Mar 16, 2026
d64ae6a
Merge branch 'main' of github.com:alejoe91/spikeinterface
alejoe91 Mar 16, 2026
aef197d
Merge branch 'main' of github.com:SpikeInterface/spikeinterface
alejoe91 Mar 17, 2026
62f79b4
Propagate oebin_file to probeinterface.read_openephys
alejoe91 Mar 17, 2026
305b974
Remove old logic and add comment
alejoe91 Mar 17, 2026
4dd501e
Add tests with onebox and non-trivial wiring
alejoe91 Mar 18, 2026
55bce5b
Merge branch 'main' into oebin-for-openephys-probe
alejoe91 Mar 18, 2026
24705b9
import numpy
alejoe91 Mar 18, 2026
8aa352a
Merge branch 'oebin-for-openephys-probe' of github.com:alejoe91/spike…
alejoe91 Mar 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 9 additions & 27 deletions src/spikeinterface/extractors/neoextractors/openephys.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,54 +315,36 @@ def __init__(

# find settings file
if "#" in stream_name:
record_node, oe_stream = stream_name.split("#")
record_node, oe_stream_name = stream_name.split("#")
else:
record_node = ""
oe_stream = stream_name
exp_ids = sorted(list(self.neo_reader.folder_structure[record_node]["experiments"].keys()))
oe_stream_name = stream_name
node_structure = self.neo_reader.folder_structure[record_node]
exp_ids = sorted(list(node_structure["experiments"].keys()))
if block_index is None:
exp_id = exp_ids[0]
else:
exp_id = exp_ids[block_index]
rec_ids = sorted(
list(self.neo_reader.folder_structure[record_node]["experiments"][exp_id]["recordings"].keys())
)
rec_ids = sorted(list(node_structure["experiments"][exp_id]["recordings"].keys()))

# do not load probe for NIDQ stream or if load_sync_channel is True
if "NI-DAQmx" not in stream_name and not load_sync_channel:
settings_file = self.neo_reader.folder_structure[record_node]["experiments"][exp_id]["settings_file"]
settings_file = node_structure["experiments"][exp_id]["settings_file"]

if Path(settings_file).is_file():
probe = probeinterface.read_openephys(
settings_file=settings_file, stream_name=stream_name, raise_error=False
settings_file=settings_file, stream_name=oe_stream_name, raise_error=False
)
else:
probe = None

if probe is not None:
# Ensure device channel index corresponds to channel_ids
probe_channel_names = probe.contact_annotations.get("channel_name", None)
if probe_channel_names is not None and not np.array_equal(probe_channel_names, self.channel_ids):
if set(probe_channel_names) == set(self.channel_ids):
device_channel_indices = []
probe_channel_names = list(probe_channel_names)
device_channel_indices = np.zeros(len(self.channel_ids), dtype=int)
for i, ch in enumerate(self.channel_ids):
index_in_probe = probe_channel_names.index(ch)
device_channel_indices[index_in_probe] = i
probe.set_device_channel_indices(device_channel_indices)
else:
warnings.warn(
"Channel names in the probe do not match the channel ids from Neo. "
"Cannot set device channel indices, but this might lead to incorrect probe geometries"
)

if probe.shank_ids is not None:
self.set_probe(probe, in_place=True, group_mode="by_shank")
else:
self.set_probe(probe, in_place=True)
# get inter-sample shifts based on the probe information and mux channels
sample_shifts = get_neuropixels_sample_shifts_from_probe(probe, stream_name=self.stream_name)
sample_shifts = get_neuropixels_sample_shifts_from_probe(probe)
if sample_shifts is not None:
self.set_property("inter_sample_shift", sample_shifts)

Expand All @@ -371,7 +353,7 @@ def __init__(
stream_folders = []
for segment_index, rec_id in enumerate(rec_ids):
stream_folder = (
recording_folder / f"experiment{exp_id}" / f"recording{rec_id}" / "continuous" / oe_stream
recording_folder / f"experiment{exp_id}" / f"recording{rec_id}" / "continuous" / oe_stream_name
)
stream_folders.append(stream_folder)
if load_sync_timestamps:
Expand Down
2 changes: 1 addition & 1 deletion src/spikeinterface/extractors/neoextractors/spikeglx.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def __init__(
self.set_probe(probe, in_place=True)

# get inter-sample shifts based on the probe information and mux channels
sample_shifts = get_neuropixels_sample_shifts_from_probe(probe, stream_name=self.stream_name)
sample_shifts = get_neuropixels_sample_shifts_from_probe(probe)
if sample_shifts is not None:
self.set_property("inter_sample_shift", sample_shifts)
else:
Expand Down
5 changes: 1 addition & 4 deletions src/spikeinterface/extractors/neuropixels_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@
from probeinterface import Probe


def get_neuropixels_sample_shifts_from_probe(probe: Probe, stream_name: str = "ap") -> np.ndarray:
def get_neuropixels_sample_shifts_from_probe(probe: Probe) -> np.ndarray:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't this break for some people if they are using this function? if we think they are not should we make this private?

"""
Get the inter-sample shifts for Neuropixels probes based on the probe information.
Parameters
----------
probe : Probe
The probe object containing channel and ADC information.
stream_name : str, default: "ap"
The name of the stream for which to calculate the sample shifts.
This is used for Neuropixels 1.0 technology to correctly set the number of cycles.
Returns
-------
Expand Down
26 changes: 23 additions & 3 deletions src/spikeinterface/extractors/tests/test_neoextractors.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import importlib.util

import pytest
import numpy as np

from spikeinterface import get_global_dataset_folder
from spikeinterface.extractors.extractor_classes import (
Expand Down Expand Up @@ -121,9 +122,6 @@ class OpenEphysBinaryRecordingTest(RecordingCommonTestSuite, unittest.TestCase):
("openephysbinary/v0.5.x_two_nodes", {"stream_id": "0"}),
("openephysbinary/v0.5.x_two_nodes", {"stream_id": "1"}),
("openephysbinary/v0.6.x_neuropixels_multiexp_multistream", {"stream_id": "0", "block_index": 0}),
# TODO: block_indices 1/2 of v0.6.x_neuropixels_multiexp_multistream have a mismatch in the channel names between
# the settings files (starting with CH0) and structure.oebin (starting at CH1).
# Currently, the extractor will skip remapping to match order in oebin and settings file, raising a warning
("openephysbinary/v0.6.x_neuropixels_multiexp_multistream", {"stream_id": "1", "block_index": 1}),
(
"openephysbinary/v0.6.x_neuropixels_multiexp_multistream",
Expand All @@ -134,8 +132,30 @@ class OpenEphysBinaryRecordingTest(RecordingCommonTestSuite, unittest.TestCase):
"openephysbinary/v0.6.x_neuropixels_multiexp_multistream",
{"stream_id": "2", "block_index": 2, "load_sync_timestamps": True},
),
(
"openephysbinary/v0.6.x_onebox_neuropixels",
{"stream_name": "Record Node 101#OneBox-100.ProbeA-AP", "block_index": 0},
),
(
"openephysbinary/v0.6.x_onebox_neuropixels_nontrivial_wiring",
{"stream_name": "Record Node 101#OneBox-111.ProbeA", "block_index": 0},
),
]

def test_non_trivial_wiring(self):
"""
Test that we can load the probe information and sample shifts for a one box neuropixels recording with
non trivial wiring.
"""
folder_path = local_folder / "openephysbinary/v0.6.x_onebox_neuropixels_nontrivial_wiring"
stream_name = "Record Node 101#OneBox-111.ProbeA"
block_index = 0

recording = self.ExtractorClass(folder_path, stream_name=stream_name, block_index=block_index)
# check that channel_ids and settings_channel_key contact annotations are correctly loaded
probe = recording.get_probe()
np.testing.assert_array_equal(recording.channel_ids, probe.contact_annotations["settings_channel_key"])


class OpenEphysBinaryEventTest(EventCommonTestSuite, unittest.TestCase):
ExtractorClass = OpenEphysBinaryEventExtractor
Expand Down
Loading