@@ -715,7 +715,7 @@ def _interpret_imro_string(imro_table_string: str, probe_part_number: str) -> di
715715 for field , field_value in zip (imro_fields , values ):
716716 imro_per_channel [field ].append (field_value )
717717
718- # Resolve electrode IDs for probe types whose IMRO format does not include them.
718+ # Resolve activate electrodes (i.e. `electrodes` entry) for probe types whose IMRO format does not include them.
719719 # NP2.x+ probes have "electrode" directly in the IMRO table. NP1.x probes encode
720720 # electrode selection indirectly and need computation.
721721 if "electrode" not in imro_per_channel :
@@ -776,6 +776,17 @@ def _resolve_active_contacts_for_np1110(imro_per_channel: dict, imro_table_strin
776776 if "group" not in imro_per_channel :
777777 return
778778
779+ # TODO: Remove this warning once we have test data for NP1110 recordings.
780+ warnings .warn (
781+ "NP1110 (Neuropixels 1.0 UHD2 active) support is experimental. "
782+ "The active electrode selection logic is translated directly from SpikeGLX "
783+ "(https://github.com/billkarsh/SpikeGLX, Src-imro/IMROTbl_T1110.cpp) but has not "
784+ "been validated against real NP1110 recordings. Please double-check the electrode "
785+ "selection and report any issues at https://github.com/SpikeInterface/probeinterface/issues" ,
786+ UserWarning ,
787+ stacklevel = 3 , # Points to read_imro / read_spikeglx (caller of _interpret_imro_string)
788+ )
789+
779790 # Extract col_mode from IMRO header: (type,col_mode,ref_id,ap_gain,lf_gain,ap_hipas_flt)
780791 header_str = imro_table_string .strip ().split (")" )[0 ][1 :] # remove leading "("
781792 header_fields = header_str .split ("," )
@@ -784,51 +795,54 @@ def _resolve_active_contacts_for_np1110(imro_per_channel: dict, imro_table_strin
784795 groups_bankA = imro_per_channel ["bankA" ]
785796 groups_bankB = imro_per_channel ["bankB" ]
786797
798+ # With pain in my heart, I am following here the C++ convention of terse naming from the
799+ # original SpikeGLX implementation (IMROTbl_T1110.cpp). The purpose is to make it easy to
800+ # spot differences when comparing against the original code, until we have real NP1110 test
801+ # data to validate against and feel comfortable (if ever) renaming to our own conventions.
787802 col_tbl = [0 , 3 , 1 , 2 , 1 , 2 , 0 , 3 ]
788803
789- def _grp_idx (ch ):
804+ def grpIdx (ch ):
790805 return 2 * ((ch % 384 ) // 32 ) + ((ch % 384 ) & 1 )
791806
792- def _col (ch , bank ):
793- grp_index = _grp_idx (ch )
794- grp_col = col_tbl [4 * (bank & 1 ) + (grp_index % 4 )]
807+ def col (ch , bank ):
808+ grp_col = col_tbl [4 * (bank & 1 ) + (grpIdx (ch ) % 4 )]
795809 crossed = (bank // 4 ) & 1
796810 ingrp_col = ((((ch % 64 ) % 32 ) // 2 ) & 1 ) ^ crossed
797811 if ch & 1 :
798812 return 2 * grp_col + (1 - ingrp_col )
799813 else :
800814 return 2 * grp_col + ingrp_col
801815
802- def _row (ch , bank ):
803- grp_index = _grp_idx (ch )
804- grp_row = grp_index // 4
816+ def row (ch , bank ):
817+ grp_row = grpIdx (ch ) // 4
805818 ingrp_row = ((ch % 64 ) % 32 ) // 4
806819 if ch & 1 :
807820 b0_row = 8 * grp_row + (7 - ingrp_row )
808821 else :
809822 b0_row = 8 * grp_row + ingrp_row
810823 return 48 * bank + b0_row
811824
812- def _bank_for_channel (ch , bankA , bankB ):
825+ def bank (ch , bankA , bankB ):
813826 if col_mode == 2 : # ALL
814827 return bankA
815828 # INNER (0) or OUTER (1): choose bankA or bankB based on column position
816- c = _col (ch , bankA )
817- if c < 4 :
818- use_bankA = (c % 2 == 0 ) if col_mode == 1 else (c % 2 == 1 )
829+ c = col (ch , bankA )
830+ if c <= 3 :
831+ if col_mode == 1 : # OUTER
832+ return bankA if not (c & 1 ) else bankB
833+ else : # INNER
834+ return bankA if (c & 1 ) else bankB
819835 else :
820- use_bankA = (c % 2 == 1 ) if col_mode == 1 else (c % 2 == 0 )
821- return bankA if use_bankA else bankB
836+ if col_mode == 1 : # OUTER
837+ return bankA if (c & 1 ) else bankB
838+ else : # INNER
839+ return bankA if not (c & 1 ) else bankB
822840
823841 electrode_ids = []
824842 for ch in range (384 ):
825- grp = _grp_idx (ch )
826- bankA = groups_bankA [grp ]
827- bankB = groups_bankB [grp ]
828- bank = _bank_for_channel (ch , bankA , bankB )
829- row = _row (ch , bank )
830- col = _col (ch , bank )
831- electrode_ids .append (8 * row + col )
843+ grp = grpIdx (ch )
844+ b = bank (ch , groups_bankA [grp ], groups_bankB [grp ])
845+ electrode_ids .append (8 * row (ch , b ) + col (ch , b ))
832846
833847 imro_per_channel ["electrode" ] = electrode_ids
834848 # Also add the "channel" key (0-383) since the IMRO entries are per-group, not per-channel
0 commit comments