From 2bb4745160f99b37fb2f54be00809edaf2e623d2 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Wed, 22 Apr 2026 14:38:25 +0200 Subject: [PATCH 1/8] Use probe table to infer Neuropixels type --- neo/rawio/spikeglxrawio.py | 90 +++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/neo/rawio/spikeglxrawio.py b/neo/rawio/spikeglxrawio.py index d59c2aa94..c3daff891 100644 --- a/neo/rawio/spikeglxrawio.py +++ b/neo/rawio/spikeglxrawio.py @@ -35,18 +35,14 @@ https://billkarsh.github.io/SpikeGLX/#metadata-guides https://github.com/SpikeInterface/spikeextractors/blob/master/spikeextractors/extractors/spikeglxrecordingextractor/spikeglxrecordingextractor.py -This reader handle: - -imDatPrb_type=1 (NP 1.0) -imDatPrb_type=21 (NP 2.0, single multiplexed shank) -imDatPrb_type=24 (NP 2.0, 4-shank) -imDatPrb_type=1030 (NP 1.0-NHP 45mm SOI90 - NHP long 90um wide, staggered contacts) -imDatPrb_type=1031 (NP 1.0-NHP 45mm SOI125 - NHP long 125um wide, staggered contacts) -imDatPrb_type=1032 (NP 1.0-NHP 45mm SOI115 / 125 linear - NHP long 125um wide, linear contacts) -imDatPrb_type=1022 (NP 1.0-NHP 25mm - NHP medium) -imDatPrb_type=1015 (NP 1.0-NHP 10mm - NHP short) - -Author : Samuel Garcia +For the "imec" device, this reader handles 1.0 and 2.0 Neuropixels probes. +The probe-type is identified by the `imDatPrb_pn` field in the meta file +and checked agains the ProbeTable info (https://raw.githubusercontent.com/billkarsh/ProbeTable/refs/heads/main/Tables/probe_features.json). +It uses the "datasheet" field in the meta file to identify the whether the probe is 1.0 or 2.0. +Neuropixels NXT/3.0 will return unscaled int16 data, since the gain for NP3.0 is not +yet implemented as it is not yet clear how to get it from the meta file. + +Author : Samuel Garcia, Alessio Buccino, Heberto Mayorquin Some functions are copied from Graham Findlay """ @@ -68,6 +64,9 @@ from .utils import get_memmap_shape +neuropixels_probe_features_file = Path(__file__).parents[1] / "resources" / "neuropixels_probe_features.json" + + class SpikeGLXRawIO(BaseRawWithBufferApiIO): """ Class for reading data from a SpikeGLX system @@ -679,36 +678,45 @@ def extract_stream_info(meta_file, meta): # metad['imroTbl'] contain two gain per channel AP and LF # except for the last fake channel per_channel_gain = np.ones(num_chan, dtype="float64") - if ( - "imDatPrb_type" not in meta - or meta["imDatPrb_type"] == "0" - or meta["imDatPrb_type"] in ("1015", "1016", "1022", "1030", "1031", "1032", "1100", "1121", "1123", "1300") - ): - # This work with NP 1.0 case with different metadata versions - # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md - if stream_kind == "ap": - index_imroTbl = 3 - elif stream_kind == "lf": - index_imroTbl = 4 - for c in range(num_chan - 1): - v = meta["imroTbl"][c].split(" ")[index_imroTbl] - per_channel_gain[c] = 1.0 / float(v) - gain_factor = float(meta["imAiRangeMax"]) / 512 - channel_gains = gain_factor * per_channel_gain * 1e6 - elif meta["imDatPrb_type"] in ("21", "24", "2003", "2004", "2013", "2014"): - # This work with NP 2.0 case with different metadata versions - # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md#imec - # We allow also LF streams for NP2.0 because CatGT can produce them - # See: https://github.com/SpikeInterface/spikeinterface/issues/1949 - if "imChan0apGain" in meta: - per_channel_gain[:-1] = 1 / float(meta["imChan0apGain"]) + probe_part_number = meta.get("imDatPrb_pn", None) + + if neuropixels_probe_features_file.exists() and probe_part_number is not None: + with open(neuropixels_probe_features_file, "r") as f: + probe_features = json.load(f) + features = probe_features[probe_part_number] + datasheet = features.get("datasheet", "unknown") + if "1.0" in datasheet: + # This work with NP 1.0 case with different metadata versions + # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md + if stream_kind == "ap": + index_imroTbl = 3 + elif stream_kind == "lf": + index_imroTbl = 4 + for c in range(num_chan - 1): + v = meta["imroTbl"][c].split(" ")[index_imroTbl] + per_channel_gain[c] = 1.0 / float(v) + gain_factor = float(meta["imAiRangeMax"]) / 512 + channel_gains = gain_factor * per_channel_gain * 1e6 + elif "2.0" in datasheet: + # This work with NP 2.0 case with different metadata versions + # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md#imec + # We allow also LF streams for NP2.0 because CatGT can produce them + # See: https://github.com/SpikeInterface/spikeinterface/issues/1949 + if "imChan0apGain" in meta: + per_channel_gain[:-1] = 1 / float(meta["imChan0apGain"]) + else: + per_channel_gain[:-1] = 1 / 80.0 + max_int = int(meta["imMaxInt"]) if "imMaxInt" in meta else 8192 + gain_factor = float(meta["imAiRangeMax"]) / max_int + channel_gains = gain_factor * per_channel_gain * 1e6 else: - per_channel_gain[:-1] = 1 / 80.0 - max_int = int(meta["imMaxInt"]) if "imMaxInt" in meta else 8192 - gain_factor = float(meta["imAiRangeMax"]) / max_int - channel_gains = gain_factor * per_channel_gain * 1e6 - else: - raise NotImplementedError("This meta file version of spikeglx" " is not implemented") + warn( + f"Unknown probe datasheet version for probe part number {probe_part_number}. " + f"Unitary gains will be used.", + UserWarning, + stacklevel=2, + ) + channel_gains = np.ones(num_chan, dtype="float64") elif meta.get("typeThis") == "obx": # OneBox case device = fname.split(".")[-2] if "." in fname else device From 4e2b453c7046a9cf1fffe6d73ef98308e3d1df2f Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Wed, 22 Apr 2026 14:57:28 +0200 Subject: [PATCH 2/8] Fix missing probe part number --- neo/rawio/spikeglxrawio.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/neo/rawio/spikeglxrawio.py b/neo/rawio/spikeglxrawio.py index c3daff891..c0a770f11 100644 --- a/neo/rawio/spikeglxrawio.py +++ b/neo/rawio/spikeglxrawio.py @@ -680,7 +680,7 @@ def extract_stream_info(meta_file, meta): per_channel_gain = np.ones(num_chan, dtype="float64") probe_part_number = meta.get("imDatPrb_pn", None) - if neuropixels_probe_features_file.exists() and probe_part_number is not None: + if probe_part_number is not None: with open(neuropixels_probe_features_file, "r") as f: probe_features = json.load(f) features = probe_features[probe_part_number] @@ -717,6 +717,13 @@ def extract_stream_info(meta_file, meta): stacklevel=2, ) channel_gains = np.ones(num_chan, dtype="float64") + else: + warn( + "Could not find probe part number in metadata. Unitary gains will be used.", + UserWarning, + stacklevel=2, + ) + channel_gains = np.ones(num_chan, dtype="float64") elif meta.get("typeThis") == "obx": # OneBox case device = fname.split(".")[-2] if "." in fname else device From 8c2738e6b879532f55c6cf5bf6c0d3fe89256fa8 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Wed, 22 Apr 2026 14:58:04 +0200 Subject: [PATCH 3/8] Fix missing import --- neo/rawio/spikeglxrawio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo/rawio/spikeglxrawio.py b/neo/rawio/spikeglxrawio.py index c0a770f11..ee51a0b2e 100644 --- a/neo/rawio/spikeglxrawio.py +++ b/neo/rawio/spikeglxrawio.py @@ -46,10 +46,11 @@ Some functions are copied from Graham Findlay """ -from pathlib import Path import os import re +from pathlib import Path from warnings import warn +import json import numpy as np From 8266a29e401b32d358b5c206df274966262a9aa1 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Wed, 22 Apr 2026 15:30:05 +0200 Subject: [PATCH 4/8] fix tests --- neo/rawio/spikeglxrawio.py | 66 +++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/neo/rawio/spikeglxrawio.py b/neo/rawio/spikeglxrawio.py index ee51a0b2e..2d4269311 100644 --- a/neo/rawio/spikeglxrawio.py +++ b/neo/rawio/spikeglxrawio.py @@ -680,40 +680,48 @@ def extract_stream_info(meta_file, meta): # except for the last fake channel per_channel_gain = np.ones(num_chan, dtype="float64") probe_part_number = meta.get("imDatPrb_pn", None) + with open(neuropixels_probe_features_file, "r") as f: + probe_features = json.load(f) if probe_part_number is not None: - with open(neuropixels_probe_features_file, "r") as f: - probe_features = json.load(f) - features = probe_features[probe_part_number] - datasheet = features.get("datasheet", "unknown") - if "1.0" in datasheet: - # This work with NP 1.0 case with different metadata versions - # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md - if stream_kind == "ap": - index_imroTbl = 3 - elif stream_kind == "lf": - index_imroTbl = 4 - for c in range(num_chan - 1): - v = meta["imroTbl"][c].split(" ")[index_imroTbl] - per_channel_gain[c] = 1.0 / float(v) - gain_factor = float(meta["imAiRangeMax"]) / 512 - channel_gains = gain_factor * per_channel_gain * 1e6 - elif "2.0" in datasheet: - # This work with NP 2.0 case with different metadata versions - # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md#imec - # We allow also LF streams for NP2.0 because CatGT can produce them - # See: https://github.com/SpikeInterface/spikeinterface/issues/1949 - if "imChan0apGain" in meta: - per_channel_gain[:-1] = 1 / float(meta["imChan0apGain"]) + features = probe_features["neuropixels_probes"].get(probe_part_number) + if features is not None: + datasheet = features.get("datasheet", "unknown") + if "1.0" in datasheet: + # This work with NP 1.0 case with different metadata versions + # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md + if stream_kind == "ap": + index_imroTbl = 3 + elif stream_kind == "lf": + index_imroTbl = 4 + for c in range(num_chan - 1): + v = meta["imroTbl"][c].split(" ")[index_imroTbl] + per_channel_gain[c] = 1.0 / float(v) + gain_factor = float(meta["imAiRangeMax"]) / 512 + channel_gains = gain_factor * per_channel_gain * 1e6 + elif "2.0" in datasheet: + # This work with NP 2.0 case with different metadata versions + # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md#imec + # We allow also LF streams for NP2.0 because CatGT can produce them + # See: https://github.com/SpikeInterface/spikeinterface/issues/1949 + if "imChan0apGain" in meta: + per_channel_gain[:-1] = 1 / float(meta["imChan0apGain"]) + else: + per_channel_gain[:-1] = 1 / 80.0 + max_int = int(meta["imMaxInt"]) if "imMaxInt" in meta else 8192 + gain_factor = float(meta["imAiRangeMax"]) / max_int + channel_gains = gain_factor * per_channel_gain * 1e6 else: - per_channel_gain[:-1] = 1 / 80.0 - max_int = int(meta["imMaxInt"]) if "imMaxInt" in meta else 8192 - gain_factor = float(meta["imAiRangeMax"]) / max_int - channel_gains = gain_factor * per_channel_gain * 1e6 + warn( + f"Unknown probe datasheet version for probe part number {probe_part_number}. " + f"Unitary gains will be used.", + UserWarning, + stacklevel=2, + ) + channel_gains = np.ones(num_chan, dtype="float64") else: warn( - f"Unknown probe datasheet version for probe part number {probe_part_number}. " - f"Unitary gains will be used.", + f"Unknown probe part number {probe_part_number}. Unitary gains will be used.", UserWarning, stacklevel=2, ) From a648fb319be311adc9a82360f08fb2f2dc7aaa7b Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 4 Jun 2026 17:04:28 +0200 Subject: [PATCH 5/8] Ramon's review --- neo/rawio/spikeglxrawio.py | 85 +++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/neo/rawio/spikeglxrawio.py b/neo/rawio/spikeglxrawio.py index 2d4269311..66d0d4ea7 100644 --- a/neo/rawio/spikeglxrawio.py +++ b/neo/rawio/spikeglxrawio.py @@ -683,56 +683,47 @@ def extract_stream_info(meta_file, meta): with open(neuropixels_probe_features_file, "r") as f: probe_features = json.load(f) - if probe_part_number is not None: - features = probe_features["neuropixels_probes"].get(probe_part_number) - if features is not None: - datasheet = features.get("datasheet", "unknown") - if "1.0" in datasheet: - # This work with NP 1.0 case with different metadata versions - # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md - if stream_kind == "ap": - index_imroTbl = 3 - elif stream_kind == "lf": - index_imroTbl = 4 - for c in range(num_chan - 1): - v = meta["imroTbl"][c].split(" ")[index_imroTbl] - per_channel_gain[c] = 1.0 / float(v) - gain_factor = float(meta["imAiRangeMax"]) / 512 - channel_gains = gain_factor * per_channel_gain * 1e6 - elif "2.0" in datasheet: - # This work with NP 2.0 case with different metadata versions - # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md#imec - # We allow also LF streams for NP2.0 because CatGT can produce them - # See: https://github.com/SpikeInterface/spikeinterface/issues/1949 - if "imChan0apGain" in meta: - per_channel_gain[:-1] = 1 / float(meta["imChan0apGain"]) - else: - per_channel_gain[:-1] = 1 / 80.0 - max_int = int(meta["imMaxInt"]) if "imMaxInt" in meta else 8192 - gain_factor = float(meta["imAiRangeMax"]) / max_int - channel_gains = gain_factor * per_channel_gain * 1e6 - else: - warn( - f"Unknown probe datasheet version for probe part number {probe_part_number}. " - f"Unitary gains will be used.", - UserWarning, - stacklevel=2, - ) - channel_gains = np.ones(num_chan, dtype="float64") + probe_part_number = meta.get("imDatPrb_pn", None) + if probe_part_number is None and meta.get("imProbeOpt") is not None: + probe_part_number = "NP1010" # Phase3A remap, matches probeinterface + if probe_part_number is None: + raise ValueError("Could not determine probe part number from metadata.") + + features = probe_features["neuropixels_probes"].get(probe_part_number) + if features is None: + raise ValueError(f"Probe part number {probe_part_number} not found in ProbeTable.") + + datasheet = features.get("datasheet", "unknown") + if "1.0" in datasheet: + # This work with NP 1.0 case with different metadata versions + # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md + if stream_kind == "ap": + index_imroTbl = 3 + elif stream_kind == "lf": + index_imroTbl = 4 + for c in range(num_chan - 1): + v = meta["imroTbl"][c].split(" ")[index_imroTbl] + per_channel_gain[c] = 1.0 / float(v) + gain_factor = float(meta["imAiRangeMax"]) / 512 + channel_gains = gain_factor * per_channel_gain * 1e6 + elif "2.0" in datasheet: + # This work with NP 2.0 case with different metadata versions + # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md#imec + # We allow also LF streams for NP2.0 because CatGT can produce them + # See: https://github.com/SpikeInterface/spikeinterface/issues/1949 + if "imChan0apGain" in meta: + per_channel_gain[:-1] = 1 / float(meta["imChan0apGain"]) else: - warn( - f"Unknown probe part number {probe_part_number}. Unitary gains will be used.", - UserWarning, - stacklevel=2, - ) - channel_gains = np.ones(num_chan, dtype="float64") + per_channel_gain[:-1] = 1 / 80.0 + max_int = int(meta["imMaxInt"]) if "imMaxInt" in meta else 8192 + gain_factor = float(meta["imAiRangeMax"]) / max_int + channel_gains = gain_factor * per_channel_gain * 1e6 else: - warn( - "Could not find probe part number in metadata. Unitary gains will be used.", - UserWarning, - stacklevel=2, + raise NotImplementedError( + f"Probe {probe_part_number} has datasheet {datasheet!r}, " + f"which is not currently supported by the SpikeGLX gain calculation. \n" + "Please open an issue at python-neo repo" ) - channel_gains = np.ones(num_chan, dtype="float64") elif meta.get("typeThis") == "obx": # OneBox case device = fname.split(".")[-2] if "." in fname else device From 53aa2fb855bbf7abb65e4278119dae59c0834dcd Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Mon, 8 Jun 2026 16:57:49 +0200 Subject: [PATCH 6/8] fix: improve logic for gain extraction in SpikeGLX --- neo/rawio/spikeglxrawio.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/neo/rawio/spikeglxrawio.py b/neo/rawio/spikeglxrawio.py index 66d0d4ea7..8ae8fe72a 100644 --- a/neo/rawio/spikeglxrawio.py +++ b/neo/rawio/spikeglxrawio.py @@ -68,6 +68,15 @@ neuropixels_probe_features_file = Path(__file__).parents[1] / "resources" / "neuropixels_probe_features.json" +def is_1_0_probe(features): + """ + Check if the probe is a Neuropixels 1.0 based on the datasheet / + description / databus_decoder field in the features dict. + """ + search_string = features.get("datasheet", features.get("description", features.get("databus_decoder", ""))) + return "1.0" in search_string + + class SpikeGLXRawIO(BaseRawWithBufferApiIO): """ Class for reading data from a SpikeGLX system @@ -693,8 +702,8 @@ def extract_stream_info(meta_file, meta): if features is None: raise ValueError(f"Probe part number {probe_part_number} not found in ProbeTable.") - datasheet = features.get("datasheet", "unknown") - if "1.0" in datasheet: + # For NP 1.0 probes, the gain is stored in the imroTbl field of the metadata, and the scaling factor is imAiRangeMax / 512 + if is_1_0_probe(features): # This work with NP 1.0 case with different metadata versions # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md if stream_kind == "ap": @@ -706,7 +715,7 @@ def extract_stream_info(meta_file, meta): per_channel_gain[c] = 1.0 / float(v) gain_factor = float(meta["imAiRangeMax"]) / 512 channel_gains = gain_factor * per_channel_gain * 1e6 - elif "2.0" in datasheet: + else: # This work with NP 2.0 case with different metadata versions # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md#imec # We allow also LF streams for NP2.0 because CatGT can produce them @@ -714,16 +723,18 @@ def extract_stream_info(meta_file, meta): if "imChan0apGain" in meta: per_channel_gain[:-1] = 1 / float(meta["imChan0apGain"]) else: - per_channel_gain[:-1] = 1 / 80.0 + # For 2.0+ probes, the gain is not configurable, so we can take it directly from the probe features. + gain_list = features["ap_gain_list"].split(",") + if len(gain_list) > 1: + raise ValueError( + f"Found multiple gains in probe features for stream kind {stream_kind}, but expected only one " + f"since gain is not configurable for NP 2.0 probes. Gain list: {gain_list}" + ) + default_gain = float(gain_list[0]) + per_channel_gain[:-1] = 1 / default_gain max_int = int(meta["imMaxInt"]) if "imMaxInt" in meta else 8192 gain_factor = float(meta["imAiRangeMax"]) / max_int channel_gains = gain_factor * per_channel_gain * 1e6 - else: - raise NotImplementedError( - f"Probe {probe_part_number} has datasheet {datasheet!r}, " - f"which is not currently supported by the SpikeGLX gain calculation. \n" - "Please open an issue at python-neo repo" - ) elif meta.get("typeThis") == "obx": # OneBox case device = fname.split(".")[-2] if "." in fname else device From 7e22f8caf92ae575901abf50c46f1825a29ec48a Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Tue, 9 Jun 2026 10:02:45 +0200 Subject: [PATCH 7/8] fix: logic in is_1_0_probe --- neo/rawio/spikeglxrawio.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/neo/rawio/spikeglxrawio.py b/neo/rawio/spikeglxrawio.py index 8ae8fe72a..8fddc5d02 100644 --- a/neo/rawio/spikeglxrawio.py +++ b/neo/rawio/spikeglxrawio.py @@ -73,7 +73,10 @@ def is_1_0_probe(features): Check if the probe is a Neuropixels 1.0 based on the datasheet / description / databus_decoder field in the features dict. """ - search_string = features.get("datasheet", features.get("description", features.get("databus_decoder", ""))) + datasheet_string = features.get("datasheet", "") + description_string = features.get("description", "") + databus_decoder_string = features.get("databus_decoder", "") + search_string = datasheet_string + description_string + databus_decoder_string return "1.0" in search_string From ba2b86b01c259e30facee9085fa6a0218c4d1966 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Wed, 10 Jun 2026 17:53:59 +0200 Subject: [PATCH 8/8] fix: make is_1_0_probe private --- neo/rawio/spikeglxrawio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/rawio/spikeglxrawio.py b/neo/rawio/spikeglxrawio.py index 8fddc5d02..39a519584 100644 --- a/neo/rawio/spikeglxrawio.py +++ b/neo/rawio/spikeglxrawio.py @@ -68,7 +68,7 @@ neuropixels_probe_features_file = Path(__file__).parents[1] / "resources" / "neuropixels_probe_features.json" -def is_1_0_probe(features): +def _is_1_0_probe(features): """ Check if the probe is a Neuropixels 1.0 based on the datasheet / description / databus_decoder field in the features dict. @@ -706,7 +706,7 @@ def extract_stream_info(meta_file, meta): raise ValueError(f"Probe part number {probe_part_number} not found in ProbeTable.") # For NP 1.0 probes, the gain is stored in the imroTbl field of the metadata, and the scaling factor is imAiRangeMax / 512 - if is_1_0_probe(features): + if _is_1_0_probe(features): # This work with NP 1.0 case with different metadata versions # https://github.com/billkarsh/SpikeGLX/blob/15ec8898e17829f9f08c226bf04f46281f106e5f/Markdown/Metadata_30.md if stream_kind == "ap":