def __call__(self, start, stop, chans=None): """Called when Stream object is called using (). start and stop indicate start and end timepoints in us wrt t=0. Returns the corresponding WaveForm object with just the specified chans""" if chans is None: chans = self.chans if not set(chans).issubset(self.chans): raise ValueError("requested chans %r are not a subset of available enabled " "chans %r in %s stream" % (chans, self.chans, self.kind)) nchans = len(chans) chanis = self.ADchans.searchsorted(chans) rawtres = self.rawtres # float us resample = self.sampfreq != self.rawsampfreq or self.shcorrect == True if resample: # excess data in us at either end, to eliminate interpolation distortion at # key.start and key.stop xs = KERNELSIZE * rawtres # float us else: xs = 0.0 # stream limits, in sample indices: t0i = intround(self.t0 / rawtres) t1i = intround(self.t1 / rawtres) # get a slightly greater range of raw data (with xs) than might be needed: t0xsi = intfloor((start - xs) / rawtres) # round down to nearest mult of rawtres t1xsi = intceil((stop + xs) / rawtres) # round up to nearest mult of rawtres # stay within stream limits, thereby avoiding interpolation edge effects: t0xsi = max(t0xsi, t0i) t1xsi = min(t1xsi, t1i) # convert back to nearest float us: t0xs = t0xsi * rawtres t1xs = t1xsi * rawtres # these are slice indices, so don't add 1: ntxs = t1xsi - t0xsi # int tsxs = np.linspace(t0xs, t0xs+(ntxs-1)*rawtres, ntxs) #print('ntxs: %d' % ntxs) # slice out excess data on requested channels, init as int32 so we have bitwidth # to rescale and zero, convert to int16 later: dataxs = np.int32(self.wavedata[chanis, t0xsi:t1xsi]) # bitshift left by 4 to scale 12 bit values to use full 16 bit dynamic range, same as # * 2**(16-12) == 16. This provides more fidelity for interpolation, reduces uV per # AD to about 0.02 if self.bitshift: dataxs <<= self.bitshift # data is still int32 at this point # do any resampling if necessary: if resample: #tresample = time.time() dataxs, tsxs = self.resample(dataxs, tsxs, chans) #print('resample took %.3f sec' % (time.time()-tresample)) # now trim down to just the requested time range: lo, hi = tsxs.searchsorted([start, stop]) data = dataxs[:, lo:hi] ts = tsxs[lo:hi] # should be safe to convert back down to int16 now: data = np.int16(data) return WaveForm(data=data, ts=ts, chans=chans)
def __call__(self, start, stop, chans=None): """Called when Stream object is called using (). start and stop indicate start and end timepoints in us wrt t=0. Returns the corresponding WaveForm object with just the specified chans""" if chans is None: chans = self.chans if not set(chans).issubset(self.chans): raise ValueError("requested chans %r are not a subset of available enabled " "chans %r in %s stream" % (chans, self.chans, self.kind)) nchans = len(chans) rawtres = self.rawtres # float us resample = self.sampfreq != self.rawsampfreq or self.shcorrect == True if resample: # excess data in us at either end, to eliminate interpolation distortion at # key.start and key.stop xs = KERNELSIZE * rawtres # float us else: xs = 0.0 # stream limits, in sample indices: t0i = intround(self.t0 / rawtres) t1i = intround(self.t1 / rawtres) # get a slightly greater range of raw data (with xs) than might be needed: t0xsi = intfloor((start - xs) / rawtres) # round down to nearest mult of rawtres t1xsi = intceil((stop + xs) / rawtres) # round up to nearest mult of rawtres # stay within stream limits, thereby avoiding interpolation edge effects: t0xsi = max(t0xsi, t0i) t1xsi = min(t1xsi, t1i) # convert back to nearest float us: t0xs = t0xsi * rawtres t1xs = t1xsi * rawtres # these are slice indices, so don't add 1: ntxs = t1xsi - t0xsi # int tsxs = np.linspace(t0xs, t0xs+(ntxs-1)*rawtres, ntxs) #print('ntxs: %d' % ntxs) # init data as int32 so we have bitwidth to rescale and zero, convert to int16 later dataxs = np.zeros((nchans, ntxs), dtype=np.int32) # any gaps will have zeros # Find all contiguous tranges that t0xs and t1xs span, if any. Note that this # can now deal with case where len(trangeis) > 1. Test by asking for a slice # longer than any one trange or gap between tranges, like by calling: # >>> self.hpstream(201900000, 336700000) # on file ptc15.74. trangeis, = np.where((self.tranges[:, 0] <= t1xs) & (t0xs < self.tranges[:, 1])) tranges = [] if len(trangeis) > 0: tranges = self.tranges[trangeis] #print('tranges:'); print(tranges) # collect relevant records from spanned tranges, if any: records = [] for trange in tranges: trrec0i, trrec1i = self.records['TimeStamp'].searchsorted(trange) trrecis = np.arange(trrec0i, trrec1i) trrts = self.records['TimeStamp'][trrecis] trrecs = self.records[trrecis] rec0i, rec1i = trrts.searchsorted([t0xs, t1xs]) rec0i = max(rec0i-1, 0) recis = np.arange(rec0i, rec1i) records.append(trrecs[recis]) if len(records) > 0: records = np.concatenate(records) # load up data+excess, from all relevant records # TODO: fix code duplication #tload = time.time() if self.kind == 'highpass': # straightforward chanis = self.layout.ADchanlist.searchsorted(chans) for record in records: # iterating over highpass records d = self.f.loadContinuousRecord(record)[chanis] # record's data on chans nt = d.shape[1] t0i = intround(record['TimeStamp'] / rawtres) t1i = t0i + nt # source indices st0i = max(t0xsi - t0i, 0) st1i = min(t1xsi - t0i, nt) # destination indices dt0i = max(t0i - t0xsi, 0) dt1i = min(t1i - t0xsi, ntxs) dataxs[:, dt0i:dt1i] = d[:, st0i:st1i] else: # kind == 'lowpass', need to load chans from subsequent records chanis = [ int(np.where(chan == self.layout.chans)[0]) for chan in chans ] """NOTE: if the above raises an error it may be because this particular combination of LFP chans was incorrectly parsed due to a bug in the .srf file, and a manual remapping needs to be added to Surf.File.fixLFPlabels()""" # assume all lpmc records are same length: nt = intround(records[0]['NumSamples'] / self.nADchans) d = np.zeros((nchans, nt), dtype=np.int32) for record in records: # iterating over lowpassmultichan records for i, chani in enumerate(chanis): lprec = self.f.lowpassrecords[record['lpreci']+chani] d[i] = self.f.loadContinuousRecord(lprec) t0i = intround(record['TimeStamp'] / rawtres) t1i = t0i + nt # source indices st0i = max(t0xsi - t0i, 0) st1i = min(t1xsi - t0i, nt) # destination indices dt0i = max(t0i - t0xsi, 0) dt1i = min(t1i - t0xsi, ntxs) dataxs[:, dt0i:dt1i] = d[:, st0i:st1i] #print('record.load() took %.3f sec' % (time.time()-tload)) # bitshift left to scale 12 bit values to use full 16 bit dynamic range, same as # * 2**(16-12) == 16. This provides more fidelity for interpolation, reduces uV per # AD to about 0.02 dataxs <<= 4 # data is still int32 at this point # do any resampling if necessary: if resample: #tresample = time.time() dataxs, tsxs = self.resample(dataxs, tsxs, chans) #print('resample took %.3f sec' % (time.time()-tresample)) # now trim down to just the requested time range: lo, hi = tsxs.searchsorted([start, stop]) data = dataxs[:, lo:hi] ts = tsxs[lo:hi] # should be safe to convert back down to int16 now: data = np.int16(data) return WaveForm(data=data, ts=ts, chans=chans)
def __call__(self, start, stop, chans=None): """Called when Stream object is called using (). start and stop indicate start and end timepoints in us wrt t=0. Returns the corresponding WaveForm object with just the specified chans""" if chans is None: chans = self.chans if not set(chans).issubset(self.chans): raise ValueError("requested chans %r are not a subset of available enabled " "chans %r in %s stream" % (chans, self.chans, self.kind)) nchans = len(chans) chanis = self.f.fileheader.chans.searchsorted(chans) rawtres = self.rawtres resample = self.sampfreq != self.rawsampfreq or self.shcorrect == True # excess data in us at either end, to eliminate filtering and interpolation # edge effects: #print('NSXXSPOINTS: %d' % NSXXSPOINTS) xs = intround(NSXXSPOINTS * rawtres) #print('xs: %d, rawtres: %g' % (xs, rawtres)) # stream limits, in us and in sample indices, wrt t=0 and sample=0: t0, t1, nt = self.t0, self.t1, self.f.nt t0i, t1i = self.f.t0i, self.f.t1i # get a slightly greater range of raw data (with xs) than might be needed: t0xsi = intfloor((start - xs) / rawtres) # round down to nearest mult of rawtres t1xsi = intceil((stop + xs) / rawtres) # round up to nearest mult of rawtres # stay within stream limits, thereby avoiding interpolation edge effects: t0xsi = max(t0xsi, t0i) t1xsi = min(t1xsi, t1i) # convert back to nearest float us: t0xs = t0xsi * rawtres t1xs = t1xsi * rawtres # these are slice indices, so don't add 1: ntxs = t1xsi - t0xsi # int tsxs = np.linspace(t0xs, t0xs+(ntxs-1)*rawtres, ntxs) #print('ntxs: %d' % ntxs) # init data as int32 so we have bitwidth to rescale and zero, then convert to int16 dataxs = np.zeros((nchans, ntxs), dtype=np.int32) # any gaps will have zeros #tload = time.time() # load up data+excess, same data for high and low pass, difference will only be in the # filtering. It would be convenient to immediately subsample to get lowpass, but that's # not a valid thing to do: you can only subsample after filtering. # source indices: st0i = max(t0xsi - t0i, 0) st1i = min(t1xsi - t0i, nt) assert st1i-st0i == ntxs # destination indices: dt0i = max(t0i - t0xsi, 0) dt1i = min(t1i - t0xsi, ntxs) dataxs[:, dt0i:dt1i] = self.f.data[chanis, st0i:st1i] #print('data load took %.3f sec' % (time.time()-tload)) #print('filtmeth: %s' % self.filtmeth) if self.filtmeth == None: pass elif self.filtmeth == 'BW': # high-pass filter using butterworth filter: dataxs, b, a = filterord(dataxs, sampfreq=self.rawsampfreq, f0=BWF0, f1=None, order=BWORDER, rp=None, rs=None, btype='highpass', ftype='butter') elif self.filtmeth == 'WMLDR': # high-pass filter using wavelet multi-level decomposition and reconstruction: ## TODO: fix weird slow wobbling of amplitude as a function of exactly what ## the WMLDR filtering time range happens to be. Setting a much bigger xs ## helps, but only until you move xs amount of time away from the start of ## the recording dataxs = WMLDR(dataxs) else: raise ValueError('unknown filter method %s' % self.filtmeth) # do any resampling if necessary: if resample: #tresample = time.time() dataxs, tsxs = self.resample(dataxs, tsxs, chans) #print('resample took %.3f sec' % (time.time()-tresample)) #nresampletxs = len(tsxs) #print('ntxs, nresampletxs: %d, %d' % (ntxs, nresampletxs)) #assert ntxs == len(tsxs) # now trim down to just the requested time range: lo, hi = tsxs.searchsorted([start, stop]) data = dataxs[:, lo:hi] ts = tsxs[lo:hi] #print(0, lo, hi, nresampletxs) # should be safe to convert back down to int16 now: data = np.int16(data) return WaveForm(data=data, ts=ts, chans=chans)