From 6fc5d2726da1e24a25f3f084894281b361b8a972 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Fri, 22 May 2026 12:18:43 -0600 Subject: [PATCH 1/2] use new neurpixels detectors in open ephys and spikegadgets --- .../extractors/neoextractors/openephys.py | 12 +++++------- .../extractors/neoextractors/spikegadgets.py | 10 ++-------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/spikeinterface/extractors/neoextractors/openephys.py b/src/spikeinterface/extractors/neoextractors/openephys.py index f421d285f5..739425b6c7 100644 --- a/src/spikeinterface/extractors/neoextractors/openephys.py +++ b/src/spikeinterface/extractors/neoextractors/openephys.py @@ -321,14 +321,12 @@ def __init__( if "NI-DAQmx" not in stream_name: 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=oe_stream_name, raise_error=False + if Path(settings_file).is_file() and probeinterface.has_neuropixels_probes( + settings_file, stream_name=oe_stream_name + ): + probe = probeinterface.read_openephys_neuropixels( + settings_file=settings_file, stream_name=oe_stream_name ) - else: - probe = None - - if probe is not None: if probe.shank_ids is not None: self.set_probe(probe, in_place=True, group_mode="by_shank") else: diff --git a/src/spikeinterface/extractors/neoextractors/spikegadgets.py b/src/spikeinterface/extractors/neoextractors/spikegadgets.py index 1f1f808ba8..95c3a6886b 100644 --- a/src/spikeinterface/extractors/neoextractors/spikegadgets.py +++ b/src/spikeinterface/extractors/neoextractors/spikegadgets.py @@ -1,8 +1,5 @@ from pathlib import Path -import packaging - -import packaging.version import probeinterface from spikeinterface.core.core_tools import define_function_from_class @@ -56,11 +53,8 @@ def __init__( ) self._kwargs.update(dict(file_path=str(Path(file_path).absolute()), stream_id=stream_id)) - probegroup = None # TODO remove once probeinterface is updated to 0.2.22 in the pyproject.toml - if packaging.version.parse(probeinterface.__version__) > packaging.version.parse("0.2.21"): - probegroup = probeinterface.read_spikegadgets(file_path, raise_error=False) - - if probegroup is not None: + if probeinterface.has_spikegadgets_neuropixels_probes(file_path): + probegroup = probeinterface.read_spikegadgets_neuropixels(file_path) self.set_probegroup(probegroup, in_place=True) @classmethod From e4dacc5069fbf0d327a55719ba41b76af0c80349 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 11 Jun 2026 18:21:57 +0200 Subject: [PATCH 2/2] feat: add ADC sample shift and saturation threshold to spikegadgets reader --- .../extractors/neoextractors/spikegadgets.py | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/spikeinterface/extractors/neoextractors/spikegadgets.py b/src/spikeinterface/extractors/neoextractors/spikegadgets.py index b4b7cf02f6..a1e38bca62 100644 --- a/src/spikeinterface/extractors/neoextractors/spikegadgets.py +++ b/src/spikeinterface/extractors/neoextractors/spikegadgets.py @@ -1,7 +1,13 @@ from pathlib import Path +import warnings +import numpy as np import probeinterface from spikeinterface.core.core_tools import define_function_from_class +from spikeinterface.extractors.neuropixels_utils import ( + get_neuropixels_sample_shifts_from_probe, + compute_saturation_threshold_from_probe, +) from .neobaseextractor import NeoBaseRecordingExtractor @@ -55,9 +61,27 @@ def __init__( if probeinterface.has_spikegadgets_neuropixels_probes(file_path): probegroup = probeinterface.read_spikegadgets_neuropixels(file_path) - # TODO: add adc sample shifts and saturation levels if available in the probe metadata self.set_probegroup(probegroup, in_place=True) + # get inter-sample shifts based on the probe information and mux channels + sample_shifts = np.array([]) + saturation_thresholds_uV = [] + for probe in probegroup.probes: + sample_shifts_probe = get_neuropixels_sample_shifts_from_probe(probe) + if sample_shifts_probe is not None: + sample_shifts = np.concatenate([sample_shifts, sample_shifts_probe]) + # add saturation levels if available + saturation_threshold_uV_probe = compute_saturation_threshold_from_probe(probe, self.stream_id) + if saturation_threshold_uV_probe is not None: + saturation_thresholds_uV.append(saturation_threshold_uV_probe) + + if len(sample_shifts) > self.get_num_channels(): + self.set_property("inter_sample_shift", sample_shifts) + if len(set(saturation_thresholds_uV)) == 1: + self.annotate(saturation_threshold_uV=saturation_thresholds_uV[0]) + else: + warnings.warn("Multiple saturation thresholds found for different probes, unable to annotate.") + @classmethod def map_to_neo_kwargs(cls, file_path): neo_kwargs = {"filename": str(file_path)}