From 1c636b2e79c1e98256261cee6850667c3a539ec8 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Mon, 13 Oct 2025 16:57:31 +0200 Subject: [PATCH 1/9] Add mux_index contact annotation --- src/probeinterface/neuropixels_tools.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/probeinterface/neuropixels_tools.py b/src/probeinterface/neuropixels_tools.py index d101b62..ac4eb70 100644 --- a/src/probeinterface/neuropixels_tools.py +++ b/src/probeinterface/neuropixels_tools.py @@ -317,13 +317,18 @@ def _make_npx_probe_from_description(probe_description, model_name, elec_ids, sh # annotate each contact with its mux channel num_adcs, num_channels_per_adc, mux_table = make_mux_table_array(mux_info) num_contacts = positions.shape[0] + # mux channel: which adc is used for each contact mux_channels = np.zeros(num_contacts, dtype="int64") + # mux index: order of sampling of the contact in the adc group + mux_index = np.zeros(num_contacts, dtype="int64") for adc_idx, mux_channels_per_adc in enumerate(mux_table): mux_channels_per_adc = mux_channels_per_adc[mux_channels_per_adc < num_contacts] mux_channels[mux_channels_per_adc] = adc_idx + mux_index[mux_channels_per_adc] = np.arange(len(mux_channels_per_adc)) probe.annotate(num_adcs=num_adcs) probe.annotate(num_channels_per_adc=num_channels_per_adc) probe.annotate_contacts(mux_channels=mux_channels) + probe.annotate_contacts(mux_index=mux_index) return probe @@ -1044,20 +1049,17 @@ def read_openephys( pt_metadata = np_probe_info["pt_metadata"] mux_info = np_probe_info["mux_info"] + probe = _make_npx_probe_from_description( + pt_metadata, probe_part_number, elec_ids, shank_ids=shank_ids, mux_info=mux_info + ) + # check if subset of channels chans_saved = get_saved_channel_indices_from_openephys_settings(settings_file, stream_name=stream_name) # if a recording state is found, slice probe if chans_saved is not None: - positions = positions[chans_saved] - if shank_ids is not None: - shank_ids = np.array(shank_ids)[chans_saved] - if elec_ids is not None: - elec_ids = np.array(elec_ids)[chans_saved] + probe = probe.get_slice(chans_saved) - probe = _make_npx_probe_from_description( - pt_metadata, probe_part_number, elec_ids, shank_ids=shank_ids, mux_info=mux_info - ) probe.serial_number = np_probe_info["serial_number"] probe.name = np_probe_info["name"] From 30db66fe9066fb78fe0868722cdc7434988ab912 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Tue, 21 Oct 2025 12:15:15 +0200 Subject: [PATCH 2/9] Remaming: mux_channel->adc_group -- mux_index->adc_sample_order --- src/probeinterface/neuropixels_tools.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/probeinterface/neuropixels_tools.py b/src/probeinterface/neuropixels_tools.py index ac4eb70..7c14663 100644 --- a/src/probeinterface/neuropixels_tools.py +++ b/src/probeinterface/neuropixels_tools.py @@ -122,7 +122,7 @@ def make_mux_table_array(mux_information) -> np.array: Returns ------- - mux_channels_array : np.array + adc_groups_array : np.array Array of which channels are in each adc group, shaped (number of `adc`s, number of channels in each `adc`). """ @@ -135,12 +135,12 @@ def make_mux_table_array(mux_information) -> np.array: num_adcs, num_channels_per_adc = map(int, adc_info[1:].split(",")) # Then remove the brackets, and split using " " to get each integer as a list - mux_channels = [ + adc_groups = [ np.array(each_mux.replace("(", "").replace(")", "").split(" ")).astype("int") for each_mux in split_mux ] - mux_channels_array = np.transpose(np.array(mux_channels)) + adc_groups_array = np.transpose(np.array(adc_groups)) - return num_adcs, num_channels_per_adc, mux_channels_array + return num_adcs, num_channels_per_adc, adc_groups_array def get_probe_contour_vertices(shank_width, tip_length, probe_length) -> list: @@ -318,17 +318,17 @@ def _make_npx_probe_from_description(probe_description, model_name, elec_ids, sh num_adcs, num_channels_per_adc, mux_table = make_mux_table_array(mux_info) num_contacts = positions.shape[0] # mux channel: which adc is used for each contact - mux_channels = np.zeros(num_contacts, dtype="int64") + adc_groups = np.zeros(num_contacts, dtype="int64") # mux index: order of sampling of the contact in the adc group - mux_index = np.zeros(num_contacts, dtype="int64") - for adc_idx, mux_channels_per_adc in enumerate(mux_table): - mux_channels_per_adc = mux_channels_per_adc[mux_channels_per_adc < num_contacts] - mux_channels[mux_channels_per_adc] = adc_idx - mux_index[mux_channels_per_adc] = np.arange(len(mux_channels_per_adc)) + adc_sample_order = np.zeros(num_contacts, dtype="int64") + for adc_idx, adc_groups_per_adc in enumerate(mux_table): + adc_groups_per_adc = adc_groups_per_adc[adc_groups_per_adc < num_contacts] + adc_groups[adc_groups_per_adc] = adc_idx + adc_sample_order[adc_groups_per_adc] = np.arange(len(adc_groups_per_adc)) probe.annotate(num_adcs=num_adcs) probe.annotate(num_channels_per_adc=num_channels_per_adc) - probe.annotate_contacts(mux_channels=mux_channels) - probe.annotate_contacts(mux_index=mux_index) + probe.annotate_contacts(adc_groups=adc_groups) + probe.annotate_contacts(adc_sample_order=adc_sample_order) return probe From 4c9b1bf0e06ceb21b6ba1629fd73946a9731e8db Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 23 Oct 2025 17:49:35 +0200 Subject: [PATCH 3/9] Update src/probeinterface/neuropixels_tools.py --- src/probeinterface/neuropixels_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/probeinterface/neuropixels_tools.py b/src/probeinterface/neuropixels_tools.py index 7c14663..efbe534 100644 --- a/src/probeinterface/neuropixels_tools.py +++ b/src/probeinterface/neuropixels_tools.py @@ -317,9 +317,9 @@ def _make_npx_probe_from_description(probe_description, model_name, elec_ids, sh # annotate each contact with its mux channel num_adcs, num_channels_per_adc, mux_table = make_mux_table_array(mux_info) num_contacts = positions.shape[0] - # mux channel: which adc is used for each contact + # ADC group: which adc is used for each contact adc_groups = np.zeros(num_contacts, dtype="int64") - # mux index: order of sampling of the contact in the adc group + # ADC sample order: order of sampling of the contact in the adc group adc_sample_order = np.zeros(num_contacts, dtype="int64") for adc_idx, adc_groups_per_adc in enumerate(mux_table): adc_groups_per_adc = adc_groups_per_adc[adc_groups_per_adc < num_contacts] From 7aa35c1c24d7ed4df91a26712e29638dc5389245 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Fri, 24 Oct 2025 09:47:10 +0200 Subject: [PATCH 4/9] Update src/probeinterface/neuropixels_tools.py --- src/probeinterface/neuropixels_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/probeinterface/neuropixels_tools.py b/src/probeinterface/neuropixels_tools.py index efbe534..2a51a0d 100644 --- a/src/probeinterface/neuropixels_tools.py +++ b/src/probeinterface/neuropixels_tools.py @@ -327,7 +327,7 @@ def _make_npx_probe_from_description(probe_description, model_name, elec_ids, sh adc_sample_order[adc_groups_per_adc] = np.arange(len(adc_groups_per_adc)) probe.annotate(num_adcs=num_adcs) probe.annotate(num_channels_per_adc=num_channels_per_adc) - probe.annotate_contacts(adc_groups=adc_groups) + probe.annotate_contacts(adc_group=adc_groups) probe.annotate_contacts(adc_sample_order=adc_sample_order) return probe From 0855236071f11854943c36b3f1b4608ebb8d99a3 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Fri, 24 Oct 2025 10:20:30 +0200 Subject: [PATCH 5/9] Fix tests and escape char --- src/probeinterface/neuropixels_tools.py | 29 +++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/probeinterface/neuropixels_tools.py b/src/probeinterface/neuropixels_tools.py index c4cff29..f829b4a 100644 --- a/src/probeinterface/neuropixels_tools.py +++ b/src/probeinterface/neuropixels_tools.py @@ -159,12 +159,12 @@ def get_probe_contour_vertices(shank_width, tip_length, probe_length) -> list: | | | | B +-------------------------------+ D (y = 0) - \ / - \ Tip region / - \ (tip_length) / - \ / - \ / - \ / + \\ / + \\ Tip region / + \\ (tip_length) / + \\ / + \\ / + \\ / +-----------------+ C (y = -tip_length) This function returns the vertices in the order [A, B, C, D, E] as a list of (x, y) coordinates. @@ -1119,14 +1119,25 @@ def read_openephys( probe = np_probe_info.get("probe") if probe is None: + # check if subset of channels + chans_saved = get_saved_channel_indices_from_openephys_settings(settings_file, stream_name=stream_name) + shank_ids = np_probe_info["shank_ids"] + elec_ids = np_probe_info["elec_ids"] + pt_metadata = np_probe_info["pt_metadata"] + mux_info = np_probe_info["mux_info"] + probe = _make_npx_probe_from_description( pt_metadata, probe_part_number, elec_ids, shank_ids=shank_ids, mux_info=mux_info ) - # check if subset of channels - chans_saved = get_saved_channel_indices_from_openephys_settings(settings_file, stream_name=stream_name) - # if a recording state is found, slice probe + if chans_saved is not None: + positions = positions[chans_saved] + if shank_ids is not None: + shank_ids = np.array(shank_ids)[chans_saved] + if elec_ids is not None: + elec_ids = np.array(elec_ids)[chans_saved] + if chans_saved is not None: probe = probe.get_slice(chans_saved) From bcd9d82dec484c650f250270ec51bda85b454229 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Fri, 24 Oct 2025 11:58:39 +0200 Subject: [PATCH 6/9] Clean up chans_Saved slicing --- src/probeinterface/neuropixels_tools.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/probeinterface/neuropixels_tools.py b/src/probeinterface/neuropixels_tools.py index f829b4a..4c41dd1 100644 --- a/src/probeinterface/neuropixels_tools.py +++ b/src/probeinterface/neuropixels_tools.py @@ -1120,7 +1120,6 @@ def read_openephys( if probe is None: # check if subset of channels - chans_saved = get_saved_channel_indices_from_openephys_settings(settings_file, stream_name=stream_name) shank_ids = np_probe_info["shank_ids"] elec_ids = np_probe_info["elec_ids"] pt_metadata = np_probe_info["pt_metadata"] @@ -1130,16 +1129,9 @@ def read_openephys( pt_metadata, probe_part_number, elec_ids, shank_ids=shank_ids, mux_info=mux_info ) - # if a recording state is found, slice probe - if chans_saved is not None: - positions = positions[chans_saved] - if shank_ids is not None: - shank_ids = np.array(shank_ids)[chans_saved] - if elec_ids is not None: - elec_ids = np.array(elec_ids)[chans_saved] - - if chans_saved is not None: - probe = probe.get_slice(chans_saved) + chans_saved = get_saved_channel_indices_from_openephys_settings(settings_file, stream_name=stream_name) + if chans_saved is not None: + probe = probe.get_slice(chans_saved) probe.serial_number = np_probe_info["serial_number"] probe.name = np_probe_info["name"] From 1c623d32d229330fe3136c711149a6e4de8f1c88 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Fri, 24 Oct 2025 16:09:46 +0200 Subject: [PATCH 7/9] add ap/lf sample frequency hz to refine num cycles computation --- src/probeinterface/neuropixels_tools.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/probeinterface/neuropixels_tools.py b/src/probeinterface/neuropixels_tools.py index 4c41dd1..0feb99f 100644 --- a/src/probeinterface/neuropixels_tools.py +++ b/src/probeinterface/neuropixels_tools.py @@ -324,6 +324,8 @@ def _make_npx_probe_from_description(probe_description, model_name, elec_ids, sh probe.annotate( adc_bit_depth=probe_description["adc_bit_depth"], num_readout_channels=probe_description["num_readout_channels"], + ap_sample_frequency_hz=probe_description["ap_sample_frequency_hz"], + lf_sample_frequency_hz=probe_description["lf_sample_frequency_hz"], ) # annotate with MUX table From 0c3bb6ef8f399d5ea188dcfb1711c5e291b34f52 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Fri, 24 Oct 2025 16:30:19 +0200 Subject: [PATCH 8/9] Update src/probeinterface/neuropixels_tools.py --- src/probeinterface/neuropixels_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/probeinterface/neuropixels_tools.py b/src/probeinterface/neuropixels_tools.py index 0feb99f..b3b979b 100644 --- a/src/probeinterface/neuropixels_tools.py +++ b/src/probeinterface/neuropixels_tools.py @@ -324,8 +324,8 @@ def _make_npx_probe_from_description(probe_description, model_name, elec_ids, sh probe.annotate( adc_bit_depth=probe_description["adc_bit_depth"], num_readout_channels=probe_description["num_readout_channels"], - ap_sample_frequency_hz=probe_description["ap_sample_frequency_hz"], - lf_sample_frequency_hz=probe_description["lf_sample_frequency_hz"], + ap_sample_frequency_hz=float(probe_description["ap_sample_frequency_hz"]), + lf_sample_frequency_hz=float(probe_description["lf_sample_frequency_hz"]), ) # annotate with MUX table From 1c832e4bb31be304668cb60a17da99adccf7ddd8 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Fri, 24 Oct 2025 16:30:48 +0200 Subject: [PATCH 9/9] Update src/probeinterface/neuropixels_tools.py --- src/probeinterface/neuropixels_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/probeinterface/neuropixels_tools.py b/src/probeinterface/neuropixels_tools.py index b3b979b..23d6531 100644 --- a/src/probeinterface/neuropixels_tools.py +++ b/src/probeinterface/neuropixels_tools.py @@ -322,8 +322,8 @@ def _make_npx_probe_from_description(probe_description, model_name, elec_ids, sh # set other key metadata annotations probe.annotate( - adc_bit_depth=probe_description["adc_bit_depth"], - num_readout_channels=probe_description["num_readout_channels"], + adc_bit_depth=int(probe_description["adc_bit_depth"]), + num_readout_channels=int(probe_description["num_readout_channels"]), ap_sample_frequency_hz=float(probe_description["ap_sample_frequency_hz"]), lf_sample_frequency_hz=float(probe_description["lf_sample_frequency_hz"]), )