Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)