From 0aa281029704ccb9d515c5b384afbacd349cdfd9 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 2 Nov 2023 13:02:31 +0100 Subject: [PATCH 01/10] Explicitly add NP-Opto and new NP2 OE test --- src/probeinterface/io.py | 34 +- .../OE_Neuropix-PXI-NP2-4shank/settings.xml | 324 ++++++++++++++++++ tests/test_io/test_openephys.py | 14 +- 3 files changed, 365 insertions(+), 7 deletions(-) create mode 100644 tests/data/openephys/OE_Neuropix-PXI-NP2-4shank/settings.xml diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index f5cbe8f..cc01e04 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -965,6 +965,26 @@ def write_csv(file, probe): "ap_hp_filters", ), }, + # NP-Opto + "opto": { + "probe_name": "Neuropixels Opto", + "x_pitch": 48, + "y_pitch": 20, + "contact_width": 12, + "stagger": 0.0, + "shank_pitch": 0, + "shank_number": 1, + "ncol": 2, + "polygon": polygon_description["default"], + "fields_in_imro_table": ( + "channel_ids", + "banks", + "references", + "ap_gains", + "lf_gains", + "ap_hp_filters", + ), + }, } @@ -1437,6 +1457,7 @@ def read_openephys( contact_ids = [] pname = np_probe.attrib["probe_name"] + headstage_part_number = np_probe.attrib.get("headstage_part_number", "") if "2.0" in pname: x_shift = -8 if "Multishank" in pname: @@ -1446,12 +1467,16 @@ def read_openephys( elif "NHP" in pname: ptype = 0 x_shift = -11 - elif "1.0" in pname: - ptype = 0 - x_shift = -11 elif "Ultra" in pname: ptype = 1100 x_shift = -8 + elif "1.0" in pname and "OPTO" in headstage_part_number: + # take care of OE NPIX v<0.4.1, where Opto probes were named 1.0 + ptype = "opto" + x_shift = -11 + elif "1.0" in pname: + ptype = 0 + x_shift = -11 else: # Probe type unknown ptype = None x_shift = 0 @@ -1479,8 +1504,9 @@ def read_openephys( else: contact_ids.append(f"e{contact_id}") + model_name = npx_probe[ptype]["probe_name"] if ptype is not None else pname np_probe_dict = { - "model_name": pname, + "model_name": model_name, "shank_ids": shank_ids, "contact_ids": contact_ids, "positions": positions, diff --git a/tests/data/openephys/OE_Neuropix-PXI-NP2-4shank/settings.xml b/tests/data/openephys/OE_Neuropix-PXI-NP2-4shank/settings.xml new file mode 100644 index 0000000..4d39cc1 --- /dev/null +++ b/tests/data/openephys/OE_Neuropix-PXI-NP2-4shank/settings.xml @@ -0,0 +1,324 @@ + + + + + 0.6.5 + 8 + 3 Oct 2023 18:57:09 + Windows 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/test_io/test_openephys.py b/tests/test_io/test_openephys.py index a9efb15..761ff21 100644 --- a/tests/test_io/test_openephys.py +++ b/tests/test_io/test_openephys.py @@ -16,6 +16,14 @@ def test_NP2(): assert "2.0 - Single Shank" in probe.model_name +def test_NP2_four_shank(): + # NP2 + probe = read_openephys(data_path / "OE_Neuropix-PXI-NP2-4shank" / "settings.xml") + # on this case, only shanks 2-3 are used + assert probe.get_shank_count() == 2 + assert "2.0 - Four Shank" in probe.model_name + + def test_NP1_subset(): # NP1 - 200 channels selected by recording_state in Record Node probe_ap = read_openephys( @@ -89,13 +97,13 @@ def test_multiple_probes(): ) assert probeB2.get_shank_count() == 1 - assert "2.0 - Multishank" in probeB2.model_name + assert "2.0 - Four Shank" in probeB2.model_name ypos = probeB2.contact_positions[:, 1] assert np.min(ypos) >= 0 -def test_np_otpo_with_sync(): +def test_np_opto_with_sync(): probe = read_openephys(data_path / "OE_Neuropix-PXI-opto-with-sync" / "settings.xml") assert probe.model_name == "Neuropixels Opto" assert probe.get_shank_count() == 1 @@ -111,7 +119,7 @@ def test_older_than_06_format(): ) assert probe.get_shank_count() == 4 - assert "2.0 - Multishank" in probe.model_name + assert "2.0 - Four Shank" in probe.model_name ypos = probe.contact_positions[:, 1] assert np.min(ypos) >= 0 From d971925c1a3b6e11eee2f82876c27f5f89a849ba Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 12:05:14 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/probeinterface/io.py | 2 +- tests/data/openephys/OE_Neuropix-PXI-NP2-4shank/settings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index cc01e04..c682d0b 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -1473,7 +1473,7 @@ def read_openephys( elif "1.0" in pname and "OPTO" in headstage_part_number: # take care of OE NPIX v<0.4.1, where Opto probes were named 1.0 ptype = "opto" - x_shift = -11 + x_shift = -11 elif "1.0" in pname: ptype = 0 x_shift = -11 diff --git a/tests/data/openephys/OE_Neuropix-PXI-NP2-4shank/settings.xml b/tests/data/openephys/OE_Neuropix-PXI-NP2-4shank/settings.xml index 4d39cc1..5e08ec2 100644 --- a/tests/data/openephys/OE_Neuropix-PXI-NP2-4shank/settings.xml +++ b/tests/data/openephys/OE_Neuropix-PXI-NP2-4shank/settings.xml @@ -321,4 +321,4 @@ - \ No newline at end of file + From 5b623b887c64a889f0fa27f20f1d9366eec0e420 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 2 Nov 2023 17:13:34 +0100 Subject: [PATCH 03/10] Use probe_part_number to reduce NP type ambiguity --- src/probeinterface/io.py | 165 +++++++++++++++++---------------- tests/test_io/test_imro.py | 4 + tests/test_io/test_spikeglx.py | 19 ++-- 3 files changed, 99 insertions(+), 89 deletions(-) diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index cc01e04..f066582 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -711,8 +711,8 @@ def write_csv(file, probe): npx_probe = { # Neuropixels 1.0 # This probably should be None or something else because NOT ONLY the neuropixels 1.0 have that imDatPrb_type - 0: { - "probe_name": "Neuropixels 1.0", + "0": { + "model_name": "Neuropixels 1.0", "x_pitch": 32, "y_pitch": 20, "contact_width": 12, @@ -729,10 +729,11 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), + "x_shift": -11 }, # Neuropixels 2.0 - Single Shank - Prototype - 21: { - "probe_name": "Neuropixels 2.0 - Single Shank - Prototype", + "21": { + "model_name": "Neuropixels 2.0 - Single Shank - Prototype", "x_pitch": 32, "y_pitch": 15, "contact_width": 12, @@ -742,10 +743,11 @@ def write_csv(file, probe): "ncol": 2, "polygon": polygon_description["default"], "fields_in_imro_table": ("channel_ids", "banks", "references", "elec_ids"), + "x_shift": -8 }, # Neuropixels 2.0 - Four Shank - Prototype - 24: { - "probe_name": "Neuropixels 2.0 - Four Shank - Prototype", + "24": { + "model_name": "Neuropixels 2.0 - Four Shank - Prototype", "x_pitch": 32, "y_pitch": 15, "contact_width": 12, @@ -761,10 +763,11 @@ def write_csv(file, probe): "references", "elec_ids", ), + "x_shift": -8 }, # Neuropixels 2.0 - Single Shank - Commercial without metal cap - 2003: { - "probe_name": "Neuropixels 2.0 - Single Shank", + "2003": { + "model_name": "Neuropixels 2.0 - Single Shank", "x_pitch": 32, "y_pitch": 15, "contact_width": 12, @@ -774,10 +777,11 @@ def write_csv(file, probe): "ncol": 2, "polygon": polygon_description["default"], "fields_in_imro_table": ("channel_ids", "banks", "references", "elec_ids"), + "x_shift": -8 }, # Neuropixels 2.0 - Single Shank - Commercial with metal cap - 2004: { - "probe_name": "Neuropixels 2.0 - Single Shank", + "2004": { + "model_name": "Neuropixels 2.0 - Single Shank", "x_pitch": 32, "y_pitch": 15, "contact_width": 12, @@ -787,10 +791,11 @@ def write_csv(file, probe): "ncol": 2, "polygon": polygon_description["default"], "fields_in_imro_table": ("channel_ids", "banks", "references", "elec_ids"), + "x_shift": -8 }, # Neuropixels 2.0 - Four Shank - Commercial without metal cap - 2013: { - "probe_name": "Neuropixels 2.0 - Four Shank", + "2013": { + "model_name": "Neuropixels 2.0 - Four Shank", "x_pitch": 32, "y_pitch": 15, "contact_width": 12, @@ -806,10 +811,11 @@ def write_csv(file, probe): "references", "elec_ids", ), + "x_shift": -8 }, # Neuropixels 2.0 - Four Shank - Commercial with metal cap - 2014: { - "probe_name": "Neuropixels 2.0 - Four Shank", + "2014": { + "model_name": "Neuropixels 2.0 - Four Shank", "x_pitch": 32, "y_pitch": 15, "contact_width": 12, @@ -825,10 +831,11 @@ def write_csv(file, probe): "references", "elec_ids", ), + "x_shift": -8 }, # Experimental probes previous to 1.0 "Phase3a": { - "probe_name": "Phase3a", + "model_name": "Phase3a", "x_pitch": 32, "y_pitch": 20, "contact_width": 12, @@ -844,10 +851,11 @@ def write_csv(file, probe): "ap_gains", "lf_gains", ), + "x_shift": -11 }, # Neuropixels 1.0-NHP Short (10mm) - 1015: { - "probe_name": "Neuropixels 1.0-NHP - short", + "1015": { + "model_name": "Neuropixels 1.0-NHP - short", "x_pitch": 32, "y_pitch": 20, "contact_width": 12, @@ -864,10 +872,11 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), + "x_shift": -11 }, # Neuropixels 1.0-NHP Medium (25mm) - 1022: { - "probe_name": "Neuropixels 1.0-NHP - medium", + "1022": { + "model_name": "Neuropixels 1.0-NHP - medium", "x_pitch": 103, "y_pitch": 20, "contact_width": 12, @@ -884,10 +893,11 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), + "x_shift": -11 }, # Neuropixels 1.0-NHP 45mm SOI90 - NHP long 90um wide, staggered contacts - 1030: { - "probe_name": "Neuropixels 1.0-NHP - long SOI90 staggered", + "1030": { + "model_name": "Neuropixels 1.0-NHP - long SOI90 staggered", "x_pitch": 56, "y_pitch": 20, "stagger": 12, @@ -904,10 +914,11 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), + "x_shift": -11 }, # Neuropixels 1.0-NHP 45mm SOI125 - NHP long 125um wide, staggered contacts - 1031: { - "probe_name": "Neuropixels 1.0-NHP - long SOI125 staggered", + "1031": { + "model_name": "Neuropixels 1.0-NHP - long SOI125 staggered", "x_pitch": 91, "y_pitch": 20, "contact_width": 12, @@ -924,10 +935,11 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), + "x_shift": -11 }, # 1.0-NHP 45mm SOI115 / 125 linear - NHP long 125um wide, linear contacts - 1032: { - "probe_name": "Neuropixels 1.0-NHP - long SOI125 linear", + "1032": { + "model_name": "Neuropixels 1.0-NHP - long SOI125 linear", "x_pitch": 103, "y_pitch": 20, "contact_width": 12, @@ -944,10 +956,11 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), + "x_shift": -11 }, # Ultra probe - 1100: { - "probe_name": "Ultra probe", + "1100": { + "model_name": "Neuropixels Ultra", "x_pitch": 6, "y_pitch": 6, "contact_width": 5, @@ -964,10 +977,11 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), + "x_shift": -8 }, # NP-Opto - "opto": { - "probe_name": "Neuropixels Opto", + "1300": { + "model_name": "Neuropixels Opto", "x_pitch": 48, "y_pitch": 20, "contact_width": 12, @@ -984,24 +998,37 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), + "x_shift": -11 }, } # Map imDatPrb_pn (probe number) to imDatPrb_type (probe type) when the latter is missing probe_number_to_probe_type = { - "PRB_1_4_0480_1": 0, - "PRB_1_4_0480_1_C": 0, - "NP1010": 0, - "NP1015": 1015, - "NP1022": 1022, - "NP1030": 1030, - "NP1031": 1031, - "NP1032": 1032, - None: 0, + # NP1.0 + "PRB_1_4_0480_1": "0", + "PRB_1_4_0480_1_C": "0", + "NP1010": "0", + None: "0", # for old version without a probe number we assume 1.0 + # NHP probes + "NP1015": "1015", + "NP1022": "1022", + "NP1030": "1030", + "NP1031": "1031", + "NP1032": "1032", + # NP2.0 + "NP2000": "21", + "NP2010": "24", + "NP2013": "2013", + "NP2014": "2014", + "NP2003": "2003", + "NP2004": "2004", + "PRB2_1_2_0640_0": "21", + # Other probes + "NP1100": "1100", # Ultra probe + "NP1300": "1300", # Opto probe } - def read_imro(file_path: Union[str, Path]) -> Probe: """ Read probe position from the imro file used in input of SpikeGlx and Open-Ephys for neuropixels probes. @@ -1046,22 +1073,23 @@ def _read_imro_string(imro_str: str, imDatPrb_pn: Optional[str] = None) -> Probe """ imro_table_header_str, *imro_table_values_list, _ = imro_str.strip().split(")") - imro_table_header = tuple(map(int, imro_table_header_str[1:].split(","))) - if len(imro_table_header) == 3: - # In older versions of neuropixel arrays (phase 3A), imro tables were structured differently. - probe_serial_number, probe_option, num_contact = imro_table_header - imDatPrb_type = "Phase3a" - elif len(imro_table_header) == 2: - imDatPrb_type, num_contact = imro_table_header - else: - raise ValueError(f"read_imro error, the header has a strange length: {imro_table_header}") - if imDatPrb_type in [0, None]: + if imDatPrb_pn is None: + if len(imro_table_header) == 3: + # In older versions of neuropixel arrays (phase 3A), imro tables were structured differently. + probe_serial_number, probe_option, num_contact = imro_table_header + imDatPrb_type = "Phase3a" + elif len(imro_table_header) == 2: + imDatPrb_type, num_contact = imro_table_header + else: + raise ValueError(f"read_imro error, the header has a strange length: {imro_table_header}") + imDatPrb_type = str(imDatPrb_type) + else: imDatPrb_type = probe_number_to_probe_type[imDatPrb_pn] probe_description = npx_probe[imDatPrb_type] - probe_name = probe_description["probe_name"] + model_name = probe_description["model_name"] fields = probe_description["fields_in_imro_table"] contact_info = {k: [] for k in fields} @@ -1099,7 +1127,7 @@ def _read_imro_string(imro_str: str, imDatPrb_pn: Optional[str] = None) -> Probe positions = np.stack((x_pos, y_pos), axis=1) # construct Probe object - probe = Probe(ndim=2, si_units="um", model_name=probe_name, manufacturer="IMEC") + probe = Probe(ndim=2, si_units="um", model_name=model_name, manufacturer="IMEC") probe.set_contacts( positions=positions, shapes="square", @@ -1154,21 +1182,21 @@ def write_imro(file: str | Path, probe: Probe): annotations = probe.contact_annotations ret = [f"({probe_type},{len(data)})"] - if probe_type == 0: + if probe_type in (0, "0"): for ch in range(len(data)): ret.append( f"({ch} 0 {annotations['references'][ch]} {annotations['ap_gains'][ch]} " f"{annotations['lf_gains'][ch]} {annotations['ap_hp_filters'][ch]})" ) - elif probe_type == 21: + elif probe_type in ("21", "2003", "2004"): for ch in range(len(data)): ret.append( f"({data['device_channel_indices'][ch]} {annotations['banks'][ch]} " f"{annotations['references'][ch]} {data['contact_ids'][ch][1:]})" ) - elif probe_type == 24: + elif probe_type in ("24", "2013", "2014"): for ch in range(len(data)): ret.append( f"({data['device_channel_indices'][ch]} {data['shank_ids'][ch]} {annotations['banks'][ch]} " @@ -1456,30 +1484,9 @@ def read_openephys( positions = np.array([xpos, ypos]).T contact_ids = [] - pname = np_probe.attrib["probe_name"] - headstage_part_number = np_probe.attrib.get("headstage_part_number", "") - if "2.0" in pname: - x_shift = -8 - if "Multishank" in pname: - ptype = 24 - else: - ptype = 21 - elif "NHP" in pname: - ptype = 0 - x_shift = -11 - elif "Ultra" in pname: - ptype = 1100 - x_shift = -8 - elif "1.0" in pname and "OPTO" in headstage_part_number: - # take care of OE NPIX v<0.4.1, where Opto probes were named 1.0 - ptype = "opto" - x_shift = -11 - elif "1.0" in pname: - ptype = 0 - x_shift = -11 - else: # Probe type unknown - ptype = None - x_shift = 0 + probe_part_number = np_probe.get("probe_part_number", "") + ptype = probe_number_to_probe_type.get(probe_part_number, None) + x_shift = npx_probe[ptype]["x_shift"] if ptype is not None else 0 if fix_x_position_for_oe_5 and oe_version < parse("0.6.0") and shank_ids is not None: positions[:, 1] = positions[:, 1] - npx_probe[ptype]["shank_pitch"] * shank_ids @@ -1504,7 +1511,7 @@ def read_openephys( else: contact_ids.append(f"e{contact_id}") - model_name = npx_probe[ptype]["probe_name"] if ptype is not None else pname + model_name = npx_probe[ptype]["model_name"] if ptype is not None else "Unknown" np_probe_dict = { "model_name": model_name, "shank_ids": shank_ids, diff --git a/tests/test_io/test_imro.py b/tests/test_io/test_imro.py index 58d0670..9e6e269 100644 --- a/tests/test_io/test_imro.py +++ b/tests/test_io/test_imro.py @@ -50,3 +50,7 @@ def test_raising_error_when_writing_with_wrong_type(tmp_path): def test_non_standard_file(): with pytest.raises(ValueError): probe = read_imro(data_path / "test_non_standard.imro") + + +if __name__ == "__main__": + test_reading_old_imro(Path("tmp")) diff --git a/tests/test_io/test_spikeglx.py b/tests/test_io/test_spikeglx.py index 1f66693..48281ba 100644 --- a/tests/test_io/test_spikeglx.py +++ b/tests/test_io/test_spikeglx.py @@ -68,7 +68,7 @@ def test_NP2_4_shanks(): assert probe.model_name == "Neuropixels 2.0 - Four Shank - Prototype" assert probe.manufacturer == "IMEC" - assert probe.annotations["probe_type"] == 24 + assert probe.annotations["probe_type"] == "24" assert probe.ndim == 2 assert probe.get_shank_count() == 4 @@ -92,7 +92,7 @@ def test_NP2_2013_all(): assert probe.model_name == "Neuropixels 2.0 - Four Shank" assert probe.manufacturer == "IMEC" - assert probe.annotations["probe_type"] == 2013 + assert probe.annotations["probe_type"] == "2013" assert probe.ndim == 2 # all channels are from the first shank @@ -117,7 +117,7 @@ def test_NP2_2013_subset(): assert probe.model_name == "Neuropixels 2.0 - Four Shank" assert probe.manufacturer == "IMEC" - assert probe.annotations["probe_type"] == 2013 + assert probe.annotations["probe_type"] == "2013" assert probe.ndim == 2 # all channels are from the first shank @@ -142,7 +142,7 @@ def test_NP2_4_shanks_with_different_electrodes_saved(): assert probe.model_name == "Neuropixels 2.0 - Four Shank - Prototype" assert probe.manufacturer == "IMEC" - assert probe.annotations["probe_type"] == 24 + assert probe.annotations["probe_type"] == "24" assert probe.ndim == 2 assert probe.get_shank_count() == 4 @@ -193,7 +193,7 @@ def test_NPH_long_staggered(): assert probe.model_name == "Neuropixels 1.0-NHP - long SOI90 staggered" assert probe.manufacturer == "IMEC" - assert probe.annotations["probe_type"] == 1030 + assert probe.annotations["probe_type"] == "1030" assert probe.ndim == 2 assert probe.get_shank_count() == 1 @@ -248,7 +248,7 @@ def test_NPH_short_linear_probe_type_0(): assert probe.model_name == "Neuropixels 1.0-NHP - short" assert probe.manufacturer == "IMEC" - assert probe.annotations["probe_type"] == 1015 + assert probe.annotations["probe_type"] == "1015" assert probe.ndim == 2 assert probe.get_shank_count() == 1 @@ -297,9 +297,9 @@ def test_ultra_probe(): # Data provided by Alessio probe = read_spikeglx(data_path / "npUltra.meta") - assert probe.model_name == "Ultra probe" + assert probe.model_name == "Neuropixels Ultra" assert probe.manufacturer == "IMEC" - assert probe.annotations["probe_type"] == 1100 + assert probe.annotations["probe_type"] == "1100" # Test contact geometry contact_width = 5.0 @@ -326,5 +326,4 @@ def test_CatGT_NP1(): if __name__ == "__main__": - test_NP2_2013_all() - test_NP2_2013_subset() + test_NP2_1_shanks() From 510fcc4a1d9521a6cad05a81dfc6068db0aa78c9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:17:09 +0000 Subject: [PATCH 04/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/probeinterface/io.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index 861ed35..f159c67 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -729,7 +729,7 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), - "x_shift": -11 + "x_shift": -11, }, # Neuropixels 2.0 - Single Shank - Prototype "21": { @@ -743,7 +743,7 @@ def write_csv(file, probe): "ncol": 2, "polygon": polygon_description["default"], "fields_in_imro_table": ("channel_ids", "banks", "references", "elec_ids"), - "x_shift": -8 + "x_shift": -8, }, # Neuropixels 2.0 - Four Shank - Prototype "24": { @@ -763,7 +763,7 @@ def write_csv(file, probe): "references", "elec_ids", ), - "x_shift": -8 + "x_shift": -8, }, # Neuropixels 2.0 - Single Shank - Commercial without metal cap "2003": { @@ -777,7 +777,7 @@ def write_csv(file, probe): "ncol": 2, "polygon": polygon_description["default"], "fields_in_imro_table": ("channel_ids", "banks", "references", "elec_ids"), - "x_shift": -8 + "x_shift": -8, }, # Neuropixels 2.0 - Single Shank - Commercial with metal cap "2004": { @@ -791,7 +791,7 @@ def write_csv(file, probe): "ncol": 2, "polygon": polygon_description["default"], "fields_in_imro_table": ("channel_ids", "banks", "references", "elec_ids"), - "x_shift": -8 + "x_shift": -8, }, # Neuropixels 2.0 - Four Shank - Commercial without metal cap "2013": { @@ -811,7 +811,7 @@ def write_csv(file, probe): "references", "elec_ids", ), - "x_shift": -8 + "x_shift": -8, }, # Neuropixels 2.0 - Four Shank - Commercial with metal cap "2014": { @@ -831,7 +831,7 @@ def write_csv(file, probe): "references", "elec_ids", ), - "x_shift": -8 + "x_shift": -8, }, # Experimental probes previous to 1.0 "Phase3a": { @@ -851,7 +851,7 @@ def write_csv(file, probe): "ap_gains", "lf_gains", ), - "x_shift": -11 + "x_shift": -11, }, # Neuropixels 1.0-NHP Short (10mm) "1015": { @@ -872,7 +872,7 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), - "x_shift": -11 + "x_shift": -11, }, # Neuropixels 1.0-NHP Medium (25mm) "1022": { @@ -893,7 +893,7 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), - "x_shift": -11 + "x_shift": -11, }, # Neuropixels 1.0-NHP 45mm SOI90 - NHP long 90um wide, staggered contacts "1030": { @@ -914,7 +914,7 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), - "x_shift": -11 + "x_shift": -11, }, # Neuropixels 1.0-NHP 45mm SOI125 - NHP long 125um wide, staggered contacts "1031": { @@ -935,7 +935,7 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), - "x_shift": -11 + "x_shift": -11, }, # 1.0-NHP 45mm SOI115 / 125 linear - NHP long 125um wide, linear contacts "1032": { @@ -956,7 +956,7 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), - "x_shift": -11 + "x_shift": -11, }, # Ultra probe "1100": { @@ -977,7 +977,7 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), - "x_shift": -8 + "x_shift": -8, }, # NP-Opto "1300": { @@ -998,7 +998,7 @@ def write_csv(file, probe): "lf_gains", "ap_hp_filters", ), - "x_shift": -11 + "x_shift": -11, }, } @@ -1009,7 +1009,7 @@ def write_csv(file, probe): "PRB_1_4_0480_1": "0", "PRB_1_4_0480_1_C": "0", "NP1010": "0", - None: "0", # for old version without a probe number we assume 1.0 + None: "0", # for old version without a probe number we assume 1.0 # NHP probes "NP1015": "1015", "NP1022": "1022", @@ -1025,10 +1025,11 @@ def write_csv(file, probe): "NP2004": "2004", "PRB2_1_2_0640_0": "21", # Other probes - "NP1100": "1100", # Ultra probe - "NP1300": "1300", # Opto probe + "NP1100": "1100", # Ultra probe + "NP1300": "1300", # Opto probe } + def read_imro(file_path: Union[str, Path]) -> Probe: """ Read probe position from the imro file used in input of SpikeGlx and Open-Ephys for neuropixels probes. From cb20be87d63a4ea9e62ae29a66d54068209256dc Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 2 Nov 2023 17:29:24 +0100 Subject: [PATCH 05/10] Small fix --- src/probeinterface/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index 861ed35..8d962fb 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -1182,7 +1182,7 @@ def write_imro(file: str | Path, probe: Probe): annotations = probe.contact_annotations ret = [f"({probe_type},{len(data)})"] - if probe_type in (0, "0"): + if probe_type == "0": for ch in range(len(data)): ret.append( f"({ch} 0 {annotations['references'][ch]} {annotations['ap_gains'][ch]} " From 2aaf634091f0b8d5962d7675755f2273438219b5 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 2 Nov 2023 17:38:59 +0100 Subject: [PATCH 06/10] Raise error if probe part number not supported --- src/probeinterface/io.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index 1f71b5d..ed52399 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -1004,7 +1004,7 @@ def write_csv(file, probe): # Map imDatPrb_pn (probe number) to imDatPrb_type (probe type) when the latter is missing -probe_number_to_probe_type = { +probe_part_number_to_probe_type = { # NP1.0 "PRB_1_4_0480_1": "0", "PRB_1_4_0480_1_C": "0", @@ -1087,7 +1087,9 @@ def _read_imro_string(imro_str: str, imDatPrb_pn: Optional[str] = None) -> Probe raise ValueError(f"read_imro error, the header has a strange length: {imro_table_header}") imDatPrb_type = str(imDatPrb_type) else: - imDatPrb_type = probe_number_to_probe_type[imDatPrb_pn] + if imDatPrb_pn not in probe_part_number_to_probe_type: + raise NotImplementedError(f"Probe part number {imDatPrb_pn} is not supported yet") + imDatPrb_type = probe_part_number_to_probe_type[imDatPrb_pn] probe_description = npx_probe[imDatPrb_type] model_name = probe_description["model_name"] @@ -1484,8 +1486,10 @@ def read_openephys( ypos = np.array([float(electrode_ypos.attrib[ch]) for ch in channel_names]) positions = np.array([xpos, ypos]).T - probe_part_number = np_probe.get("probe_part_number", "") - ptype = probe_number_to_probe_type.get(probe_part_number, None) + probe_part_number = np_probe.get("probe_part_number", None) + if probe_part_number not in probe_part_number_to_probe_type: + raise NotImplementedError(f"Probe part number {probe_part_number} is not supported yet") + ptype = probe_part_number_to_probe_type[probe_part_number] x_shift = npx_probe[ptype]["x_shift"] if ptype is not None else 0 if fix_x_position_for_oe_5 and oe_version < parse("0.6.0") and shank_ids is not None: From 61f7c285f22145d478555834c2e70a3b6e91a6a9 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 2 Nov 2023 17:44:22 +0100 Subject: [PATCH 07/10] Fix NP-ultra probe part numbers (NP1100 and NP1110) --- src/probeinterface/io.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index ed52399..a9a0199 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -1026,6 +1026,7 @@ def write_csv(file, probe): "PRB2_1_2_0640_0": "21", # Other probes "NP1100": "1100", # Ultra probe + "NP1110": "1100", # Ultra probe (Switchable) "NP1300": "1300", # Opto probe } From 2dcb7a531c131c314d6c61ab4c2ad3e5d1c57614 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 2 Nov 2023 17:48:01 +0100 Subject: [PATCH 08/10] Add bank info to UHD --- src/probeinterface/io.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index a9a0199..147d36a 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -1003,6 +1003,8 @@ def write_csv(file, probe): } +# TODO: unify implementation with https://github.com/jenniferColonell/SGLXMetaToCoords/blob/main/SGLXMetaToCoords.py + # Map imDatPrb_pn (probe number) to imDatPrb_type (probe type) when the latter is missing probe_part_number_to_probe_type = { # NP1.0 @@ -1025,8 +1027,8 @@ def write_csv(file, probe): "NP2004": "2004", "PRB2_1_2_0640_0": "21", # Other probes - "NP1100": "1100", # Ultra probe - "NP1110": "1100", # Ultra probe (Switchable) + "NP1100": "1100", # Ultra probe - 1 bank + "NP1110": "1100", # Ultra probe - 16 banks "NP1300": "1300", # Opto probe } From ce447305589b9f355dd4a92338f411808bfa3195 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 2 Nov 2023 17:50:15 +0100 Subject: [PATCH 09/10] Wrong code --- src/probeinterface/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index 147d36a..d9df143 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -1028,7 +1028,7 @@ def write_csv(file, probe): "PRB2_1_2_0640_0": "21", # Other probes "NP1100": "1100", # Ultra probe - 1 bank - "NP1110": "1100", # Ultra probe - 16 banks + "NP1110": "1110", # Ultra probe - 16 banks "NP1300": "1300", # Opto probe } From aecd15c2c5eac7ca268511af6ec329dddaa38e9a Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 2 Nov 2023 17:52:58 +0100 Subject: [PATCH 10/10] oups --- src/probeinterface/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/probeinterface/io.py b/src/probeinterface/io.py index d9df143..147d36a 100644 --- a/src/probeinterface/io.py +++ b/src/probeinterface/io.py @@ -1028,7 +1028,7 @@ def write_csv(file, probe): "PRB2_1_2_0640_0": "21", # Other probes "NP1100": "1100", # Ultra probe - 1 bank - "NP1110": "1110", # Ultra probe - 16 banks + "NP1110": "1100", # Ultra probe - 16 banks "NP1300": "1300", # Opto probe }