4242imDatPrb_type=24 (NP 2.0, 4-shank) 
4343
4444Author : Samuel Garcia 
45+ Some functions are copied from Graham Findlay 
4546""" 
4647
4748import  warnings 
@@ -90,6 +91,7 @@ def _parse_header(self):
9091        for  info  in  self .signals_info_list :
9192            # key is (seg_index, stream_name) 
9293            key  =  (info ['seg_index' ], info ['stream_name' ])
94+             assert  key  not  in self .signals_info_dict 
9395            self .signals_info_dict [key ] =  info 
9496
9597            # create memmap 
@@ -166,7 +168,7 @@ def _parse_header(self):
166168                    # need probeinterface to be installed 
167169                    import  probeinterface 
168170                    info  =  self .signals_info_dict [seg_index , stream_name ]
169-                     if  'imroTbl'  in  info ['meta' ] and  info ['signal_kind ' ] ==  'ap' :
171+                     if  'imroTbl'  in  info ['meta' ] and  info ['stream_kind ' ] ==  'ap' :
170172                        # only for ap channel 
171173                        probe  =  probeinterface .read_spikeglx (info ['meta_file' ])
172174                        loc  =  probe .contact_positions 
@@ -233,6 +235,11 @@ def _get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop,
233235def  scan_files (dirname ):
234236    """ 
235237    Scan for pairs of `.bin` and `.meta` files and return information about it. 
238+ 
239+     After exploring the folder, the segment index (`seg_index`) is construct as follow: 
240+       * if only one `gate_num=0` then `trigger_num` = `seg_index` 
241+       * if only one `trigger_num=0` then `gate_num` = `seg_index` 
242+       * if both are increasing then seg_index increased by gate_num, trigger_num order. 
236243    """ 
237244    info_list  =  []
238245
@@ -245,16 +252,99 @@ def scan_files(dirname):
245252            if  meta_filename .exists () and  bin_filename .exists ():
246253                meta  =  read_meta_file (meta_filename )
247254                info  =  extract_stream_info (meta_filename , meta )
255+ 
248256                info ['meta_file' ] =  str (meta_filename )
249257                info ['bin_file' ] =  str (bin_filename )
250258                info_list .append (info )
251259
260+             # Let see if this will be anoying or not. 
252261            if  bin_filename .stat ().st_size  !=  meta ['fileSizeBytes' ]:
253262                warnings .warn ('.meta file has faulty value for .bin file size on disc' )
254263
264+     # the segment index will depend on both 'gate_num' and 'trigger_num' 
265+     # so we order by 'gate_num' then 'trigger_num' 
266+     # None is before any int 
267+     def  make_key (info ):
268+         k0  =  info ['gate_num' ]
269+         if  k0  is  None :
270+             k0  =  - 1 
271+         k1  =  info ['trigger_num' ]
272+         if  k1  is  None :
273+             k1  =  - 1 
274+         return  (k0 , k1 )
275+     order_key  =  list ({make_key (info ) for  info  in  info_list })
276+     order_key  =  sorted (order_key )
277+     for  info  in  info_list :
278+         info ['seg_index' ] =  order_key .index (make_key (info ))
279+ 
255280    return  info_list 
256281
257282
283+ def  parse_spikeglx_fname (fname ):
284+     """ 
285+     Parse recording identifiers from a SpikeGLX style filename. 
286+ 
287+     spikeglx naming follow this rules: 
288+     https://github.com/billkarsh/SpikeGLX/blob/master/Markdown/UserManual.md#gates-and-triggers 
289+ 
290+     Example file name structure: 
291+     Consider the filenames: `Noise4Sam_g0_t0.nidq.bin` or `Noise4Sam_g0_t0.imec0.lf.bin` 
292+     The filenames consist of 3 or 4 parts separated by `.` 
293+     1. "Noise4Sam_g0_t0" will be the `name` variable. This choosen by the user at recording time. 
294+     2. "_g0_" is the "gate_num" 
295+     3. "_t0_" is the "trigger_num" 
296+     4. "nidq" or "imec0" will give the `device` 
297+     5. "lf" or "ap" will be the `stream_kind` 
298+         `stream_name` variable is the concatenation of `device.stream_kind` 
299+ 
300+     This function is copied/modified from Graham Findlay. 
301+ 
302+     Notes: 
303+        * Sometimes the original file name is modified by the user and "_gt0_" or "_t0_" 
304+           are manually removed. In that case gate_name and trigger_num will be None. 
305+ 
306+     Parameters 
307+     --------- 
308+     fname: str 
309+         The filename to parse without the extension, e.g. "my-run-name_g0_t1.imec2.lf" 
310+     Returns 
311+     ------- 
312+     run_name: str 
313+         The run name, e.g. "my-run-name". 
314+     gate_num: int or None 
315+         The gate identifier, e.g. 0. 
316+     trigger_num: int or None 
317+         The trigger identifier, e.g. 1. 
318+     device: str 
319+         The probe identifier, e.g. "imec2" 
320+     stream_kind: str or None 
321+         The data type identifier, "lf" or "ap" or None 
322+     """ 
323+     r  =  re .findall (r'(\S*)_g(\d*)_t(\d*)\.(\S*).(ap|lf)' , fname )
324+     if  len (r ) ==  1 :
325+         # standard case with probe 
326+         run_name , gate_num , trigger_num , device , stream_kind  =  r [0 ]
327+     else :
328+         r  =  re .findall (r'(\S*)_g(\d*)_t(\d*)\.(\S*)' , fname )
329+         if  len (r ) ==  1 :
330+             # case for nidaq 
331+             run_name , gate_num , trigger_num , device  =  r [0 ]
332+             stream_kind  =  None 
333+         else :
334+             # the naming do not correspond lets try something more easy 
335+             r  =  re .findall (r'(\S*)\.(\S*).(ap|lf)' , fname )
336+             if  len (r ) ==  1 :
337+                 run_name , device , stream_kind  =  r [0 ]
338+                 gate_num , trigger_num  =  None , None 
339+ 
340+     if  gate_num  is  not None :
341+         gate_num  =  int (gate_num )
342+     if  trigger_num  is  not None :
343+         trigger_num  =  int (trigger_num )
344+ 
345+     return  (run_name , gate_num , trigger_num , device , stream_kind )
346+ 
347+ 
258348def  read_meta_file (meta_file ):
259349    """parse the meta file""" 
260350    with  open (meta_file , mode = 'r' ) as  f :
@@ -281,27 +371,13 @@ def extract_stream_info(meta_file, meta):
281371    """Extract info from the meta dict""" 
282372
283373    num_chan  =  int (meta ['nSavedChans' ])
374+     fname  =  Path (meta_file ).stem 
375+     run_name , gate_num , trigger_num , device , stream_kind  =  parse_spikeglx_fname (fname )
376+     device  =  fname .split ('.' )[1 ]
284377
285-     # Example file name structure: 
286-     # Consider the filenames: `Noise4Sam_g0_t0.nidq.bin` or `Noise4Sam_g0_t0.imec0.lf.bin` 
287-     # The filenames consist of 3 or 4 parts separated by `.` 
288-     # 1. "Noise4Sam_g0_t0" will be the `name` variable. This is chosen by the user 
289-     #    at recording time. 
290-     # 2. "_gt0_" will give the `seg_index` (here 0) 
291-     # 3. "nidq" or "imec0" will give the `device` variable 
292-     # 4. "lf" or "ap" will be the `signal_kind` variable 
293-     # `stream_name` variable is the concatenation of `device.signal_kind` 
294-     name  =  Path (meta_file ).stem 
295-     r  =  re .findall (r'_g(\d*)_t' , name )
296-     if  len (r ) ==  0 :
297-         # when manual renaming _g0_ can be removed 
298-         seg_index  =  0 
299-     else :
300-         seg_index  =  int (r [0 ][0 ])
301-     device  =  name .split ('.' )[1 ]
302378    if  'imec'  in  device :
303-         signal_kind  =  name .split ('.' )[2 ]
304-         stream_name  =  device  +  '.'  +  signal_kind 
379+         stream_kind  =  fname .split ('.' )[2 ]
380+         stream_name  =  device  +  '.'  +  stream_kind 
305381        units  =  'uV' 
306382        # please note the 1e6 in gain for this uV 
307383
@@ -313,16 +389,16 @@ def extract_stream_info(meta_file, meta):
313389            # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_3A.md#imec 
314390            # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_3B1.md#imec 
315391            # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_3B2.md#imec 
316-             if  signal_kind  ==  'ap' :
392+             if  stream_kind  ==  'ap' :
317393                index_imroTbl  =  3 
318-             elif  signal_kind  ==  'lf' :
394+             elif  stream_kind  ==  'lf' :
319395                index_imroTbl  =  4 
320396            for  c  in  range (num_chan  -  1 ):
321397                v  =  meta ['imroTbl' ][c ].split (' ' )[index_imroTbl ]
322398                per_channel_gain [c ] =  1.  /  float (v )
323399            gain_factor  =  float (meta ['imAiRangeMax' ]) /  512 
324400            channel_gains  =  gain_factor  *  per_channel_gain  *  1e6 
325-         elif  meta ['imDatPrb_type' ] in  ('21' , '24' ) and  signal_kind  ==  'ap' :
401+         elif  meta ['imDatPrb_type' ] in  ('21' , '24' ) and  stream_kind  ==  'ap' :
326402            # This work with NP 2.0 case with different metadata versions 
327403            # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_20.md#channel-entries-by-type 
328404            # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_20.md#imec 
@@ -334,7 +410,7 @@ def extract_stream_info(meta_file, meta):
334410            raise  NotImplementedError ('This meta file version of spikeglx' 
335411                                      'is not implemented' )
336412    else :
337-         signal_kind  =  '' 
413+         stream_kind  =  '' 
338414        stream_name  =  device 
339415        units  =  'V' 
340416        channel_gains  =  np .ones (num_chan )
@@ -352,17 +428,18 @@ def extract_stream_info(meta_file, meta):
352428        channel_gains  =  per_channel_gain  *  gain_factor 
353429
354430    info  =  {}
355-     info ['name ' ] =  name 
431+     info ['fname ' ] =  fname 
356432    info ['meta' ] =  meta 
357433    for  k  in  ('niSampRate' , 'imSampRate' ):
358434        if  k  in  meta :
359435            info ['sampling_rate' ] =  float (meta [k ])
360436    info ['num_chan' ] =  num_chan 
361437
362438    info ['sample_length' ] =  int (meta ['fileSizeBytes' ]) //  2  //  num_chan 
363-     info ['seg_index' ] =  seg_index 
439+     info ['gate_num' ] =  gate_num 
440+     info ['trigger_num' ] =  trigger_num 
364441    info ['device' ] =  device 
365-     info ['signal_kind ' ] =  signal_kind 
442+     info ['stream_kind ' ] =  stream_kind 
366443    info ['stream_name' ] =  stream_name 
367444    info ['units' ] =  units 
368445    info ['channel_names' ] =  [txt .split (';' )[0 ] for  txt  in  meta ['snsChanMap' ]]
0 commit comments