@@ -23,39 +23,67 @@ def __init__(self, root_dir):
2323 name & associated meta - no interpretation of g0_t0.imec, etc is
2424 performed at this layer.
2525 '''
26+ self ._apmeta , self ._apdata = None , None
27+ self ._lfmeta , self ._lfdata = None , None
2628
27- self .root_dir = root_dir
29+ self .root_dir = pathlib . Path ( root_dir )
2830
2931 meta_filepath = next (pathlib .Path (root_dir ).glob ('*.ap.meta' ))
30- self .npx_meta = SpikeGLXMeta (meta_filepath )
31-
3232 self .root_name = meta_filepath .name .replace ('.ap.meta' , '' )
33- self ._apdata = None
34- self ._lfdata , self ._lfmeta = None , None
33+
34+ @property
35+ def apmeta (self ):
36+ if self ._apmeta is None :
37+ self ._apmeta = SpikeGLXMeta (self .root_dir / (self .root_name + '.ap.meta' ))
38+ return self ._apmeta
3539
3640 @property
3741 def apdata (self ):
38- if self ._apdata is not None :
39- return self ._apdata
40- else :
41- return self ._read_bin ( self . root_dir / ( self . root_name + '.ap.bin' ))
42+ if self ._apdata is None :
43+ self ._apdata = self . _read_bin ( self . root_dir / ( self . root_name + '.ap.bin' ))
44+ self . _apdata = self . _apdata * self . get_channel_bit_volts ( 'ap' )
45+ return self ._apdata
4246
4347 @property
4448 def lfmeta (self ):
45- if self ._lfmeta is not None :
46- return self ._lfmeta
47- else :
48- return _read_meta (self .root_dir / (self .root_name + '.lf.meta' ))
49+ if self ._lfmeta is None :
50+ self ._lfmeta = SpikeGLXMeta (self .root_dir / (self .root_name + '.lf.meta' ))
51+ return self ._lfmeta
4952
5053 @property
5154 def lfdata (self ):
52- if self ._lfdata is not None :
53- return self ._lfdata
55+ if self ._lfdata is None :
56+ self ._lfdata = self ._read_bin (self .root_dir / (self .root_name + '.lf.bin' ))
57+ self ._lfdata = self ._lfdata * self .get_channel_bit_volts ('lf' )
58+ return self ._lfdata
59+
60+ def get_channel_bit_volts (self , band = 'ap' ):
61+ """
62+ Extract the AP and LF channels' int16 to microvolts
63+ Following the steps specified in: https://billkarsh.github.io/SpikeGLX/Support/SpikeGLX_Datafile_Tools.zip
64+ dataVolts = dataInt * fI2V / gain
65+ """
66+ fI2V = float (self .apmeta .meta ['imAiRangeMax' ]) / 512
67+
68+ if band == 'ap' :
69+ imroTbl_data = self .apmeta .imroTbl ['data' ]
70+ imroTbl_idx = 3
71+ elif band == 'lf' :
72+ imroTbl_data = self .lfmeta .imroTbl ['data' ]
73+ imroTbl_idx = 4
74+
75+ # extract channels' gains
76+ if 'imDatPrb_dock' in self .apmeta .meta :
77+ # NP 2.0; APGain = 80 for all AP (LF is computed from AP)
78+ chn_gains = [80 ] * len (imroTbl_data )
5479 else :
55- return self ._read_bin (self .root_dir / (self .root_name + '.lf.bin' ))
80+ # 3A, 3B1, 3B2 (NP 1.0)
81+ chn_gains = [c [imroTbl_idx ] for c in imroTbl_data ]
82+
83+ return fI2V / np .array (chn_gains ) * 1e6
5684
5785 def _read_bin (self , fname ):
58- nchan = self .npx_meta .meta ['nSavedChans' ]
86+ nchan = self .apmeta .meta ['nSavedChans' ]
5987 dtype = np .dtype ((np .int16 , nchan ))
6088 return np .memmap (fname , dtype , 'r' )
6189
@@ -70,9 +98,9 @@ def extract_spike_waveforms(self, spikes, channel, n_wf=500, wf_win=(-32, 32), b
7098 """
7199
72100 data = self .apdata
73- channel_idx = [np .where (self .npx_meta .recording_channels == chn )[0 ][0 ] for chn in channel ]
101+ channel_idx = [np .where (self .apmeta .recording_channels == chn )[0 ][0 ] for chn in channel ]
74102
75- spikes = np .round (spikes * self .npx_meta .meta ['imSampRate' ]).astype (int ) # convert to sample
103+ spikes = np .round (spikes * self .apmeta .meta ['imSampRate' ]).astype (int ) # convert to sample
76104 # ignore spikes at the beginning or end of raw data
77105 spikes = spikes [np .logical_and (spikes > - wf_win [0 ], spikes < data .shape [0 ] - wf_win [- 1 ])]
78106
@@ -125,6 +153,8 @@ def __init__(self, meta_filepath):
125153
126154 self .recording_channels = [c [0 ] for c in self .imroTbl ['data' ]] if self .imroTbl else None
127155
156+ self ._chan_gains = None
157+
128158 @staticmethod
129159 def _parse_chanmap (raw ):
130160 '''
0 commit comments