4242imDatPrb_type=24 (NP 2.0, 4-shank)
4343
4444Author : Samuel Garcia
45+ Some functions are copied from Graham Findlay
4546"""
4647
4748from .baserawio import (BaseRawIO , _signal_channel_dtype , _signal_stream_dtype ,
@@ -165,7 +166,7 @@ def _parse_header(self):
165166 # need probeinterface to be installed
166167 import probeinterface
167168 info = self .signals_info_dict [seg_index , stream_name ]
168- if 'imroTbl' in info ['meta' ] and info ['signal_kind ' ] == 'ap' :
169+ if 'imroTbl' in info ['meta' ] and info ['stream_kind ' ] == 'ap' :
169170 # only for ap channel
170171 probe = probeinterface .read_spikeglx (info ['meta_file' ])
171172 loc = probe .contact_positions
@@ -232,6 +233,12 @@ def _get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop,
232233def scan_files (dirname ):
233234 """
234235 Scan for pairs of `.bin` and `.meta` files and return information about it.
236+
237+ After exploring the folder, the segment index (`seg_index`) is construct as follow:
238+ * if only one `gate_num=0` then `trigger_num` = `seg_index`
239+ * if only one `trigger_num=0` then `gate_num` = `seg_index`
240+ * if both are increasing then seg_index increased by gate_num, trigger_num order.
241+
235242 """
236243 info_list = []
237244
@@ -247,10 +254,83 @@ def scan_files(dirname):
247254 info ['meta_file' ] = str (meta_filename )
248255 info ['bin_file' ] = str (bin_filename )
249256 info_list .append (info )
250-
257+
258+ total_gate = max ([info ['gate_num' ] for info in info_list ]) + 1
259+ total_trigger_per_gate = []
260+ for gate_num in range (total_gate ):
261+ max_trigger = 0
262+ for info in info_list :
263+ if info ['gate_num' ] != gate_num :
264+ continue
265+ max_trigger = max (max_trigger , info ['trigger_num' ])
266+ total_trigger_per_gate .append (max_trigger + 1 )
267+
268+ for info in info_list :
269+ g , t = info ['gate_num' ], info ['trigger_num' ]
270+ if g == 0 :
271+ seg_index = t
272+ else :
273+ seg_index = sum (total_trigger_per_gate [:g ]) + t
274+ info ['seg_index' ] = seg_index
275+
251276 return info_list
252277
253278
279+ def parse_spikeglx_fname (fname ):
280+ """
281+ Parse recording identifiers from a SpikeGLX style filename.
282+
283+ spikeglx naming follow this rules:
284+ https://github.com/billkarsh/SpikeGLX/blob/master/Markdown/UserManual.md#gates-and-triggers
285+
286+ Example file name structure:
287+ Consider the filenames: `Noise4Sam_g0_t0.nidq.bin` or `Noise4Sam_g0_t0.imec0.lf.bin`
288+ The filenames consist of 3 or 4 parts separated by `.`
289+ 1. "Noise4Sam_g0_t0" will be the `name` variable. This choosen by the user at recording time.
290+ 2. "_g0_" is the "gate_num"
291+ 3. "_t0_" is the "trigger_num"
292+ 4. "nidq" or "imec0" will give the `device`
293+ 5. "lf" or "ap" will be the `stream_kind`
294+ `stream_name` variable is the concatenation of `device.stream_kind`
295+
296+ This function is copied/modified from Graham Findlay.
297+
298+ Notes:
299+ * Sometimes the original file name is modified by the user and "_gt0_" or "_t0_"
300+ are manually removed. In that case gate_name and trigger_num will be None.
301+
302+ Parameters
303+ ---------
304+ fname: str
305+ The filename to parse without the extension, e.g. "my-run-name_g0_t1.imec2.lf"
306+ Returns
307+ -------
308+ run_name: str
309+ The run name, e.g. "my-run-name".
310+ gate_num: int or None
311+ The gate identifier, e.g. 0.
312+ trigger_num: int or None
313+ The trigger identifier, e.g. 1.
314+ device: str
315+ The probe identifier, e.g. "imec2"
316+ stream_kind: str
317+ The data type identifier, "lf" or "ap"
318+ """
319+ r = re .findall (r'(\S*)_g(\d*)_t(\d*)\.(\S*).(ap|lf)' , fname )
320+ if len (r ) > 0 :
321+ # standard case
322+ run_name , gate_num , trigger_num , device , stream_kind = r [0 ]
323+ gate_num = int (gate_num )
324+ trigger_num = int (trigger_num )
325+ else :
326+ # the naming do not correspond lets try something more easy
327+ r = re .findall (r'(\S*)\.(\S*).(ap|lf)' , fname )
328+ if len (r ) > 0 :
329+ run_name , device , stream_kind = r [0 ]
330+ gate_num , trigger_num = None , None
331+ return (run_name , gate_num , trigger_num , device , stream_kind )
332+
333+
254334def read_meta_file (meta_file ):
255335 """parse the meta file"""
256336 with open (meta_file , mode = 'r' ) as f :
@@ -277,27 +357,15 @@ def extract_stream_info(meta_file, meta):
277357 """Extract info from the meta dict"""
278358
279359 num_chan = int (meta ['nSavedChans' ])
280-
281- # Example file name structure:
282- # Consider the filenames: `Noise4Sam_g0_t0.nidq.bin` or `Noise4Sam_g0_t0.imec0.lf.bin`
283- # The filenames consist of 3 or 4 parts separated by `.`
284- # 1. "Noise4Sam_g0_t0" will be the `name` variable. This choosen by the user
285- # at recording time.
286- # 2. "_gt0_" will give the `seg_index` (here 0)
287- # 3. "nidq" or "imec0" will give the `device` variable
288- # 4. "lf" or "ap" will be the `signal_kind` variable
289- # `stream_name` variable is the concatenation of `device.signal_kind`
290- name = Path (meta_file ).stem
291- r = re .findall (r'_g(\d*)_t' , name )
292- if len (r ) == 0 :
293- # when manual renaming _g0_ can be removed
294- seg_index = 0
295- else :
296- seg_index = int (r [0 ][0 ])
297- device = name .split ('.' )[1 ]
360+
361+ fname = Path (meta_file ).stem
362+
363+ run_name , gate_num , trigger_num , device , stream_kind = parse_spikeglx_fname (fname )
364+
365+ device = fname .split ('.' )[1 ]
298366 if 'imec' in device :
299- signal_kind = name .split ('.' )[2 ]
300- stream_name = device + '.' + signal_kind
367+ stream_kind = fname .split ('.' )[2 ]
368+ stream_name = device + '.' + stream_kind
301369 units = 'uV'
302370 # please note the 1e6 in gain for this uV
303371
@@ -309,16 +377,16 @@ def extract_stream_info(meta_file, meta):
309377 # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_3A.md#imec
310378 # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_3B1.md#imec
311379 # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_3B2.md#imec
312- if signal_kind == 'ap' :
380+ if stream_kind == 'ap' :
313381 index_imroTbl = 3
314- elif signal_kind == 'lf' :
382+ elif stream_kind == 'lf' :
315383 index_imroTbl = 4
316384 for c in range (num_chan - 1 ):
317385 v = meta ['imroTbl' ][c ].split (' ' )[index_imroTbl ]
318386 per_channel_gain [c ] = 1. / float (v )
319387 gain_factor = float (meta ['imAiRangeMax' ]) / 512
320388 channel_gains = gain_factor * per_channel_gain * 1e6
321- elif meta ['imDatPrb_type' ] in ('21' , '24' ) and signal_kind == 'ap' :
389+ elif meta ['imDatPrb_type' ] in ('21' , '24' ) and stream_kind == 'ap' :
322390 # This work with NP 2.0 case with different metadata versions
323391 # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_20.md#channel-entries-by-type
324392 # https://github.com/billkarsh/SpikeGLX/blob/gh-pages/Support/Metadata_20.md#imec
@@ -330,7 +398,7 @@ def extract_stream_info(meta_file, meta):
330398 raise NotImplementedError ('This meta file version of spikeglx'
331399 'is not implemented' )
332400 else :
333- signal_kind = ''
401+ stream_kind = ''
334402 stream_name = device
335403 units = 'V'
336404 channel_gains = np .ones (num_chan )
@@ -348,17 +416,19 @@ def extract_stream_info(meta_file, meta):
348416 channel_gains = per_channel_gain * gain_factor
349417
350418 info = {}
351- info ['name ' ] = name
419+ info ['fname ' ] = fname
352420 info ['meta' ] = meta
353421 for k in ('niSampRate' , 'imSampRate' ):
354422 if k in meta :
355423 info ['sampling_rate' ] = float (meta [k ])
356424 info ['num_chan' ] = num_chan
357425
358426 info ['sample_length' ] = int (meta ['fileSizeBytes' ]) // 2 // num_chan
359- info ['seg_index' ] = seg_index
427+ #~ info['seg_index'] = seg_index
428+ info ['gate_num' ] = gate_num
429+ info ['trigger_num' ] = trigger_num
360430 info ['device' ] = device
361- info ['signal_kind ' ] = signal_kind
431+ info ['stream_kind ' ] = stream_kind
362432 info ['stream_name' ] = stream_name
363433 info ['units' ] = units
364434 info ['channel_names' ] = [txt .split (';' )[0 ] for txt in meta ['snsChanMap' ]]
0 commit comments