@@ -293,12 +293,20 @@ def __init__(self, meta_filepath):
293
293
"Probe Serial Number not found in"
294
294
' either "imProbeSN" or "imDatPrb_sn"'
295
295
)
296
+ # Get probe part number
297
+ self .probe_PN = self .meta .get ("imDatPrb_pn" , "3A" )
296
298
299
+ # Parse channel info
297
300
self .chanmap = (
298
301
self ._parse_chanmap (self .meta ["~snsChanMap" ])
299
302
if "~snsChanMap" in self .meta
300
303
else None
301
304
)
305
+ self .geommap = (
306
+ self ._parse_geommap (self .meta ["~snsGeomMap" ])
307
+ if "~snsGeomMap" in self .meta
308
+ else None
309
+ )
302
310
self .shankmap = (
303
311
self ._parse_shankmap (self .meta ["~snsShankMap" ])
304
312
if "~snsShankMap" in self .meta
@@ -370,6 +378,39 @@ def _parse_shankmap(raw):
370
378
371
379
return res
372
380
381
+ @staticmethod
382
+ def _parse_geommap (raw ):
383
+ """
384
+ The shankmap contains details on the shank info
385
+ for each electrode sites of the sites being recorded only
386
+ Parsing from the `~snsGeomMap` (available with SpikeGLX 20230202-phase30 and later)
387
+
388
+ https://github.com/billkarsh/SpikeGLX/blob/master/Markdown/Metadata_30.md
389
+ Parse shank map header structure. Converts:
390
+
391
+ '(x,y,z)(a:b:c:d)...(a:b:c:d)'
392
+ a: zero-based shank #
393
+ b: x-coordinate (um) of elecrode center
394
+ c: z-coordinate (um) of elecrode center
395
+ d: 0/1 `used` flag (included in spatial average or not)
396
+ e.g:
397
+
398
+ '(1,2,480)(0:0:192:1)...(0:1:191:1)'
399
+
400
+ into dict of form:
401
+
402
+ {'shape': [x,y,z], 'data': [[a,b,c,d],...]}
403
+ """
404
+ res = {"shape" : None , "data" : []}
405
+
406
+ for u in (i .rstrip (")" ) for i in raw .split ("(" ) if i != "" ):
407
+ if "," in u :
408
+ res ["header" ] = [d for d in u .split ("," )]
409
+ else :
410
+ res ["data" ].append ([int (d ) for d in u .split (":" )])
411
+
412
+ return res
413
+
373
414
@staticmethod
374
415
def _parse_imrotbl (raw ):
375
416
"""
@@ -400,6 +441,31 @@ def _parse_imrotbl(raw):
400
441
401
442
return res
402
443
444
+ def _transform_geom_to_shank (self ):
445
+ if self .geommap is None :
446
+ raise ValueError ("Geometry Map not available" )
447
+
448
+ from . import probe_geometry
449
+
450
+ probe_params = {
451
+ n : v
452
+ for n , v in zip (
453
+ probe_geometry .geom_param_names , probe_geometry .M [self .probe_PN ]
454
+ )
455
+ }
456
+ probe_params ["probe_type" ] = self .probe_PN
457
+ elec_pos_df = probe_geometry .build_npx_probe (** probe_params )
458
+
459
+ res = {"shape" : self .geommap ["shape" ], "data" : []}
460
+ for shank , x_coord , y_coord , is_used in self .geommap ["data" ]:
461
+ matched_elec = elec_pos_df .query (
462
+ f"x_coord=={ x_coord } & y_coord=={ y_coord } & shank=={ shank } "
463
+ )
464
+ shank_col , shank_row = matched_elec .shank_col , matched_elec .shank_row
465
+ res ["data" ].append ([shank , shank_col , shank_row , is_used ])
466
+
467
+ return res
468
+
403
469
def get_recording_channels_indices (self , exclude_sync = False ):
404
470
"""
405
471
The indices of recorded channels (in chanmap)
0 commit comments