Skip to content

Commit b5c11f0

Browse files
author
Thinh Nguyen
committed
extract and apply bit-volts conversion for spikeglx loader
1 parent 8ceeb0b commit b5c11f0

File tree

2 files changed

+52
-22
lines changed

2 files changed

+52
-22
lines changed

elements_ephys/ephys.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,14 @@ def make(self, key):
231231
lfp = spikeglx_recording.lfdata[:, :-1].T # exclude the sync channel
232232

233233
self.insert1(dict(key,
234-
lfp_sampling_rate=spikeglx_recording.lfmeta['imSampRate'],
235-
lfp_time_stamps=np.arange(lfp.shape[1]) / spikeglx_recording.lfmeta['imSampRate'],
234+
lfp_sampling_rate=spikeglx_recording.lfmeta.meta['imSampRate'],
235+
lfp_time_stamps=np.arange(lfp.shape[1]) / spikeglx_recording.lfmeta.meta['imSampRate'],
236236
lfp_mean=lfp.mean(axis=0)))
237237

238238
q_electrodes = probe.ProbeType.Electrode * probe.ElectrodeConfig.Electrode * EphysRecording & key
239239
electrodes = []
240240
for recorded_site in np.arange(lfp.shape[0]):
241-
shank, shank_col, shank_row, _ = spikeglx_recording.npx_meta.shankmap['data'][recorded_site]
241+
shank, shank_col, shank_row, _ = spikeglx_recording.apmeta.shankmap['data'][recorded_site]
242242
electrodes.append((q_electrodes
243243
& {'shank': shank,
244244
'shank_col': shank_col,

elements_ephys/readers/spikeglx.py

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)