def onpick(evt): col = int(round(evt.mouseevent.xdata)) row = int(round(evt.mouseevent.ydata)) h = evt.artist pylab.axes(h.spaceax) h.channels.plot(h.img[:,col], clim=h.get_clim(), drawnow=False) pylab.axes(h.timeax) SigTools.plot(h.timebase, h.img[row], drawnow=False) pylab.gca().set(ylim=h.get_clim(), xlim=(h.timebase[0],h.timebase[-1])) pylab.draw()
def onpick(evt): col = int(round(evt.mouseevent.xdata)) row = int(round(evt.mouseevent.ydata)) h = evt.artist pylab.axes(h.spaceax) h.channels.plot(h.img[:, col], clim=h.get_clim(), drawnow=False) pylab.axes(h.timeax) SigTools.plot(h.timebase, h.img[row], drawnow=False) pylab.gca().set(ylim=h.get_clim(), xlim=(h.timebase[0], h.timebase[-1])) pylab.draw()
def TimingWindow(filename='.', ind=-1, save=None): """ Recreate BCI2000's timing window offline, from a saved .dat file specified by <filename>. It is also possible to supply a directory name as <filename>, and an index <ind> (default value -1 meaning "the last run") to choose a file automatically from that directory. Based on BCI2000's src/shared/modules/signalsource/DataIOFilter.cpp where the timing window content is computed in DataIOFilter::Process(), this is what appears to happen: Begin SampleBlock #t: Enter SignalSource module's first Process() method (DataIOFilter::Process) Save previous SampleBlock to file Wait to acquire new SampleBlock from hardware +--------- Measure SourceTime in SignalSource module | | | Make a local copy of all states (NB: all except SourceTime were set during #t-1) ---+ B| R| S| Pipe the signal through the rest of BCI2000 | | | +- Measure StimulusTime in Application module, on leaving last Process() method | | | | | | | | | Begin SampleBlock #t+1: | | +----- Enter SignalSource module's first Process() method (DataIOFilter::Process) | | Save data from #t, SourceTime state from #t, and other states from #t-1, to file <--+ | Wait to acquire new SampleBlock from hardware +--------- Measure SourceTime in SignalSource module Make a local copy of all states (NB: all except SourceTime were set during #t) Leave DataIOFilter::Process() and pipe the signal through the rest of BCI2000 Measure StimulusTime in Application module, on leaving last Process() method B stands for Block duration. R stands for Roundtrip time (visible in VisualizeTiming, not reconstructable from the .dat file) S is the filter cascade time (marked "Stimulus" in the VisualizeTiming window). Note that, on any given SampleBlock as saved in the file, SourceTime will be *greater* than any other timestamp states (including StimulusTime), because it is the only state that is updated in time to be saved with the data packet it belongs to. All the others lag by one packet. This is corrected for at the point commented with ??? in the Python code. """ if hasattr(filename, 'filename'): filename = filename.filename b = bcistream(filename=filename, ind=ind) out = SigTools.sstruct() out.filename = b.filename #print "decoding..." sig, states = b.decode('all') #print "done" b.close() dT, T, rT = {}, {}, {} statenames = ['SourceTime', 'StimulusTime' ] + ['PythonTiming%02d' % (x + 1) for x in range(2)] statenames = [s for s in statenames if s in states] for key in statenames: dT[key], T[key] = SigTools.unwrapdiff(states[key].flatten(), base=65536, dtype=numpy.float64) sel, = numpy.where(dT['SourceTime']) for key in statenames: dT[key] = dT[key][sel[1:]] if key == 'SourceTime': tsel = sel[:-1] # ??? why the shift else: tsel = sel[1:] # ??? relative to here? T[key] = T[key][tsel + 1] t0 = T['SourceTime'][0] for key in statenames: T[key] -= t0 t = T['SourceTime'] / 1000 expected = b.samples2msec(b.params['SampleBlockSize']) datestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(b.datestamp)) paramstr = ', '.join([ '%s=%s' % (x, b.params[x]) for x in [ 'SampleBlockSize', 'SamplingRate', 'VisualizeTiming', 'VisualizeSource' ] ]) chainstr = '-'.join([ x for x, y in b.params['SignalSourceFilterChain'] + b.params['SignalProcessingFilterChain'] + b.params['ApplicationFilterChain'] ]) titlestr = '\n'.join([b.filename, datestamp, paramstr, chainstr]) SigTools.plot(t[[0, -1]], [expected] * 2, drawnow=False) SigTools.plot(t, dT['SourceTime'], hold=True, drawnow=False) for key in statenames: if key == 'SourceTime': continue rT[key] = T[key] - T['SourceTime'] SigTools.plot(t, rT[key], hold=True, drawnow=False) import pylab pylab.title(titlestr) pylab.grid(True) pylab.xlabel('seconds') pylab.ylabel('milliseconds') ymin, ymax = pylab.ylim() pylab.ylim(ymax=max(ymax, expected * 2)) pylab.xlim(xmax=t[-1]) pylab.draw() out.params = SigTools.sstruct(b.params) out.summarystr = titlestr out.t = t out.SourceTime = T['SourceTime'] out.StimulusTime = T['StimulusTime'] out.BlockDuration = dT['SourceTime'] out.BlockDuration2 = dT['StimulusTime'] out.ProcessingTime = out.StimulusTime - out.SourceTime out.ExpectedBlockDuration = expected out.rT = rT out.dT = dT out.T = T if save: pylab.gcf().savefig(save, orientation='landscape') return out
def TimingWindow(filename='.', ind=-1, save=None): """ Recreate BCI2000's timing window offline, from a saved .dat file specified by <filename>. It is also possible to supply a directory name as <filename>, and an index <ind> (default value -1 meaning "the last run") to choose a file automatically from that directory. Based on BCI2000's src/shared/modules/signalsource/DataIOFilter.cpp where the timing window content is computed in DataIOFilter::Process(), this is what appears to happen: Begin SampleBlock #t: Enter SignalSource module's first Process() method (DataIOFilter::Process) Save previous SampleBlock to file Wait to acquire new SampleBlock from hardware +--------- Measure SourceTime in SignalSource module | | | Make a local copy of all states (NB: all except SourceTime were set during #t-1) ---+ B| R| S| Pipe the signal through the rest of BCI2000 | | | +- Measure StimulusTime in Application module, on leaving last Process() method | | | | | | | | | Begin SampleBlock #t+1: | | +----- Enter SignalSource module's first Process() method (DataIOFilter::Process) | | Save data from #t, SourceTime state from #t, and other states from #t-1, to file <--+ | Wait to acquire new SampleBlock from hardware +--------- Measure SourceTime in SignalSource module Make a local copy of all states (NB: all except SourceTime were set during #t) Leave DataIOFilter::Process() and pipe the signal through the rest of BCI2000 Measure StimulusTime in Application module, on leaving last Process() method B stands for Block duration. R stands for Roundtrip time (visible in VisualizeTiming, not reconstructable from the .dat file) S is the filter cascade time (marked "Stimulus" in the VisualizeTiming window). Note that, on any given SampleBlock as saved in the file, SourceTime will be *greater* than any other timestamp states (including StimulusTime), because it is the only state that is updated in time to be saved with the data packet it belongs to. All the others lag by one packet. This is corrected for at the point commented with ??? in the Python code. """ if hasattr(filename, 'filename'): filename = filename.filename b = bcistream(filename=filename, ind=ind) out = SigTools.sstruct() out.filename = b.filename #print "decoding..." sig,states = b.decode('all') #print "done" b.close() dT,T,rT = {},{},{} statenames = ['SourceTime', 'StimulusTime'] + ['PythonTiming%02d' % (x+1) for x in range(2)] statenames = [s for s in statenames if s in states] for key in statenames: dT[key],T[key] = SigTools.unwrapdiff(states[key].flatten(), base=65536, dtype=numpy.float64) sel, = numpy.where(dT['SourceTime']) for key in statenames: dT[key] = dT[key][sel[1:]] if key == 'SourceTime': tsel = sel[:-1] # ??? why the shift else: tsel = sel[1:] # ??? relative to here? T[key] = T[key][tsel+1] t0 = T['SourceTime'][0] for key in statenames: T[key] -= t0 t = T['SourceTime'] / 1000 expected = b.samples2msec(b.params['SampleBlockSize']) datestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(b.datestamp)) paramstr = ', '.join(['%s=%s' % (x,b.params[x]) for x in ['SampleBlockSize', 'SamplingRate', 'VisualizeTiming', 'VisualizeSource']]) chainstr = '-'.join([x for x,y in b.params['SignalSourceFilterChain']+b.params['SignalProcessingFilterChain']+b.params['ApplicationFilterChain']]) titlestr = '\n'.join([b.filename, datestamp, paramstr, chainstr]) SigTools.plot(t[[0,-1]], [expected]*2, drawnow=False) SigTools.plot(t, dT['SourceTime'], hold=True, drawnow=False) for key in statenames: if key == 'SourceTime': continue rT[key] = T[key] - T['SourceTime'] SigTools.plot(t, rT[key], hold=True, drawnow=False) import pylab pylab.title(titlestr) pylab.grid(True) pylab.xlabel('seconds') pylab.ylabel('milliseconds') ymin,ymax = pylab.ylim(); pylab.ylim(ymax=max(ymax,expected*2)) pylab.xlim(xmax=t[-1]) pylab.draw() out.params = SigTools.sstruct(b.params) out.summarystr = titlestr out.t = t out.SourceTime = T['SourceTime'] out.StimulusTime = T['StimulusTime'] out.BlockDuration = dT['SourceTime'] out.BlockDuration2 = dT['StimulusTime'] out.ProcessingTime = out.StimulusTime - out.SourceTime out.ExpectedBlockDuration = expected out.rT = rT out.dT = dT out.T = T if save: pylab.gcf().savefig(save, orientation='landscape') return out