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