diff --git a/src/spikeinterface/extractors/neoextractors/openephys.py b/src/spikeinterface/extractors/neoextractors/openephys.py index 05a3c82115..5dc9220aa5 100644 --- a/src/spikeinterface/extractors/neoextractors/openephys.py +++ b/src/spikeinterface/extractors/neoextractors/openephys.py @@ -324,14 +324,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 d9df77dc99..adc50df12f 100644 --- a/src/spikeinterface/extractors/neoextractors/spikegadgets.py +++ b/src/spikeinterface/extractors/neoextractors/spikegadgets.py @@ -1,10 +1,13 @@ from pathlib import Path +import warnings +import numpy as np -import packaging - -import packaging.version 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 @@ -56,12 +59,35 @@ def __init__( ) self._kwargs.update(dict(file_path=str(Path(file_path).absolute()), stream_id=stream_id)) - probegroup = probeinterface.read_spikegadgets(file_path, raise_error=False) - # TODO: add adc sample shifts and saturation levels if available in the probe metadata + if probeinterface.has_spikegadgets_neuropixels_probes(file_path): + probegroup = probeinterface.read_spikegadgets_neuropixels(file_path) + + # get inter-sample shifts based on the probe information and mux channels + # SpikeGadgets writes multiple probes in the same file and the contacts are + # interleaved. The `device_channel_indices` of the probe is used to map the + # sample shifts to the correct channels. + # We instantiate the sample_shifts array with -1 to indicate channels for + # which we don't have a sample shift (sample shifts are [0-1] by definition) + sample_shifts = -1 * np.ones(self.get_num_channels()) + 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[probe.device_channel_indices] = 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 probegroup is not None: self.set_probegroup(probegroup, in_place=True) + if np.all(sample_shifts != -1): + 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)}