|
4 | 4 | from .utils import convert_to_number
|
5 | 5 |
|
6 | 6 |
|
| 7 | +AP_GAIN = 80 # For NP 2.0 probes; APGain = 80 for all AP (LF is computed from AP) |
| 8 | + |
| 9 | +# Imax values for different probe types - see metaguides (http://billkarsh.github.io/SpikeGLX/#metadata-guides) |
| 10 | +IMAX = {'neuropixels 1.0 - 3A': 512, |
| 11 | + 'neuropixels 1.0 - 3B': 512, |
| 12 | + 'neuropixels 2.0 - SS': 8192, |
| 13 | + 'neuropixels 2.0 - MS': 8192} |
| 14 | + |
| 15 | + |
7 | 16 | class SpikeGLX:
|
8 | 17 |
|
9 | 18 | def __init__(self, root_dir):
|
@@ -69,26 +78,31 @@ def get_channel_bit_volts(self, band='ap'):
|
69 | 78 | """
|
70 | 79 | Extract the AP and LF channels' int16 to microvolts
|
71 | 80 | Following the steps specified in: https://billkarsh.github.io/SpikeGLX/Support/SpikeGLX_Datafile_Tools.zip
|
72 |
| - dataVolts = dataInt * fI2V / gain |
| 81 | + dataVolts = dataInt * Vmax / Imax / gain |
73 | 82 | """
|
74 |
| - fI2V = float(self.apmeta.meta['imAiRangeMax']) / 512 |
| 83 | + vmax = float(self.apmeta.meta['imAiRangeMax']) |
75 | 84 |
|
76 | 85 | if band == 'ap':
|
| 86 | + imax = IMAX[self.apmeta.probe_model] |
77 | 87 | imroTbl_data = self.apmeta.imroTbl['data']
|
78 | 88 | imroTbl_idx = 3
|
| 89 | + |
79 | 90 | elif band == 'lf':
|
| 91 | + imax = IMAX[self.lfmeta.probe_model] |
80 | 92 | imroTbl_data = self.lfmeta.imroTbl['data']
|
81 | 93 | imroTbl_idx = 4
|
| 94 | + else: |
| 95 | + raise ValueError(f'Unsupported band: {band} - Must be "ap" or "lf"') |
82 | 96 |
|
83 | 97 | # extract channels' gains
|
84 | 98 | if 'imDatPrb_dock' in self.apmeta.meta:
|
85 | 99 | # NP 2.0; APGain = 80 for all AP (LF is computed from AP)
|
86 |
| - chn_gains = [80] * len(imroTbl_data) |
| 100 | + chn_gains = [AP_GAIN] * len(imroTbl_data) |
87 | 101 | else:
|
88 | 102 | # 3A, 3B1, 3B2 (NP 1.0)
|
89 | 103 | chn_gains = [c[imroTbl_idx] for c in imroTbl_data]
|
90 | 104 |
|
91 |
| - return fI2V / np.array(chn_gains) * 1e6 # convert to uV as well |
| 105 | + return vmax / imax / np.array(chn_gains) * 1e6 # convert to uV as well |
92 | 106 |
|
93 | 107 | def _read_bin(self, fname):
|
94 | 108 | nchan = self.apmeta.meta['nSavedChans']
|
@@ -245,23 +259,28 @@ def _parse_imrotbl(raw):
|
245 | 259 |
|
246 | 260 | @property
|
247 | 261 | def recording_channels(self):
|
| 262 | + """ |
| 263 | + Because you can selectively save channels, the |
| 264 | + ith channel in the file isn't necessarily the ith acquired channel. |
| 265 | + Use this function to convert from ith stored to original index. |
| 266 | +
|
| 267 | + Credit to https://billkarsh.github.io/SpikeGLX/Support/SpikeGLX_Datafile_Tools.zip |
| 268 | + OriginalChans() function |
| 269 | + """ |
248 | 270 | if self._recording_channels is None:
|
249 | 271 | if self.meta['snsSaveChanSubset'] == 'all':
|
250 | 272 | # output = int32, 0 to nSavedChans - 1
|
251 | 273 | self._recording_channels = np.arange(0, int(self.meta['nSavedChans']))
|
252 | 274 | else:
|
253 | 275 | # parse the snsSaveChanSubset string
|
254 | 276 | # split at commas
|
255 |
| - chStrList = self.meta['snsSaveChanSubset'].split(sep = ',') |
| 277 | + chStrList = self.meta['snsSaveChanSubset'].split(sep=',') |
256 | 278 | self._recording_channels = np.arange(0, 0) # creates an empty array of int32
|
257 | 279 | for sL in chStrList:
|
258 |
| - currList = sL.split(sep = ':') |
259 |
| - if len(currList) > 1: |
260 |
| - # each set of contiguous channels specified by |
261 |
| - # chan1:chan2 inclusive |
262 |
| - newChans = np.arange(int(currList[0]), int(currList[1]) + 1) |
263 |
| - else: |
264 |
| - newChans = np.arange(int(currList[0]), int(currList[0]) + 1) |
| 280 | + currList = sL.split(sep=':') |
| 281 | + # each set of continuous channels specified by chan1:chan2 inclusive |
| 282 | + newChans = np.arange(int(currList[0]), int(currList[min(1, len(currList))]) + 1) |
| 283 | + |
265 | 284 | self._recording_channels = np.append(self._recording_channels, newChans)
|
266 | 285 | return self._recording_channels
|
267 | 286 |
|
|
0 commit comments