def initialize(self): self.debug=self.setting['debug'] == "true" print "Debug: ", self.debug self.stream_type=self.setting['Stream type'] # total channels for all streams self.channelCount = 0 all_streams = self.setting['Get all streams'] == "true" self.overcomeCheck = self.setting['Overcome code check'] == "true" self.stream_name=self.setting['Stream name'] # in case !all_streams print "Looking for streams of type: " + self.stream_type streams = resolve_stream('type',self.stream_type) print "Nb streams: " + str( len(streams)) if not all_streams: print "Will only select (first) stream named: " + self.stream_name self.nb_streams = 1 else: self.nb_streams = len(streams) # create inlets to read from each stream self.inlets = [] # retrieve also corresponding StreamInfo for future uses (eg sampling rate) self.infos = [] # save inlets and info + build signal header for stream in streams: # do not set max_buflen because we *should not* be spammed by values inlet = StreamInlet(stream, max_buflen=1) info = inlet.info() name = info.name() print "Stream name: " + name # if target one stream, ignore false ones if not all_streams and name != self.stream_name: continue print "Nb channels: " + str(info.channel_count()) self.channelCount += info.channel_count() stream_freq = info.nominal_srate() print "Sampling frequency: " + str(stream_freq) if stream_freq != 0: print "WARNING: Wrong stream?" self.inlets.append(inlet) self.infos.append(info) # if we're still here when we target a stream, it means we foand it if not all_streams: print "Found target stream" break # we need at least one stream before we let go if self.channelCount <= 0: raise Exception("Error: no stream found.") # we append to the box output a stimulation header. This is just a header, dates are 0. self.output[0].append(OVStimulationHeader(0., 0.))
class BetaInlet(object): def __init__(self): print("looking for an EEG stream...") streams = resolve_byprop("type", "EEG") # create a new inlet to read from the stream proc_flags = proc_clocksync | proc_dejitter | proc_monotonize self.inlet = StreamInlet(streams[0], processing_flags=proc_flags) # The following is an example of how to read stream info stream_info = self.inlet.info() stream_Fs = stream_info.nominal_srate() stream_xml = stream_info.desc() chans_xml = stream_xml.child("channels") chan_xml_list = [] ch = chans_xml.child("channel") while ch.name() == "channel": chan_xml_list.append(ch) ch = ch.next_sibling("channel") self.channel_names = [ch_xml.child_value("label") for ch_xml in chan_xml_list] print("Reading from inlet named {} with channels {} sending data at {} Hz".format(stream_info.name(), self.channel_names, stream_Fs)) def update(self): max_samps = 3276*2 data = np.nan * np.ones((max_samps, len(self.channel_names)), dtype=np.float32) _, timestamps = self.inlet.pull_chunk(max_samples=max_samps, dest_obj=data) data = data[:len(timestamps), :] print("Beta inlet retrieved {} samples.".format(len(timestamps))) return data, np.asarray(timestamps)
def initialize(self): self.initLabel = 0 self.debug=self.setting['debug'] == "true" print "Debug: ", self.debug self.stream_type=self.setting['Stream type'] self.stream_name=self.setting['Stream name'] # total channels for all streams self.channelCount = 0 #self.stream_name=self.setting['Stream name'] # in case !all_streams print "Looking for streams of type: " + self.stream_type streams = resolve_stream('type',self.stream_type) print "Nb streams: " + str( len(streams)) self.nb_streams = len(streams) if self.nb_streams == 0: raise Exception("Error: no stream found.") self.inlet = StreamInlet(streams[0], max_buflen=1) self.info = self.inlet.info() self.channelCount = self.info.channel_count() print "Stream name: " + self.info.name() stream_freq = self.info.nominal_srate() if stream_freq != 0: raise Exception("Error: no irregular stream found.") # we append to the box output a stimulation header. This is just a header, dates are 0. self.output[0].append(OVStimulationHeader(0., 0.)) self.init = False
class LSLInlet: def __init__(self, name=LSL_STREAM_NAMES[2], max_chunklen=8, n_channels=20): streams = resolve_byprop('name', name, timeout=LSL_RESOLVE_TIMEOUT) self.inlet = None self.dtype = 'float64' if len(streams) > 0: self.inlet = StreamInlet(streams[0], max_buflen=1, max_chunklen=max_chunklen) # self.dtype = fmt2string[self.inlet.info().channel_format()] print(self.dtype) self.n_channels = n_channels if n_channels else self.inlet.info().channel_count() def get_next_chunk(self): # get next chunk chunk, timestamp = self.inlet.pull_chunk() # convert to numpy array chunk = np.array(chunk, dtype=self.dtype) # return first n_channels channels or None if empty chunk return chunk[:, :self.n_channels] if chunk.shape[0] > 0 else None def update_action(self): pass def save_info(self, file): with open(file, 'w') as f: f.write(self.inlet.info().as_xml()) def get_frequency(self): return self.inlet.info().nominal_srate() def get_n_channels(self): return self.n_channels def get_channels_labels_bad(self): time.sleep(0.001) labels = [] ch = self.inlet.info().desc().child("channels").child("channel") for k in range(self.get_n_channels()): labels.append(ch.child_value("label")) ch = ch.next_sibling() return def get_channels_labels(self): return ch_names[:self.n_channels] def disconnect(self): del self.inlet self.inlet = None
def __init__(self, name=LSL_STREAM_NAMES[2], max_chunklen=8, n_channels=20): streams = resolve_byprop('name', name, timeout=LSL_RESOLVE_TIMEOUT) self.inlet = None self.dtype = 'float64' if len(streams) > 0: self.inlet = StreamInlet(streams[0], max_buflen=1, max_chunklen=max_chunklen) # self.dtype = fmt2string[self.inlet.info().channel_format()] print(self.dtype) self.n_channels = n_channels if n_channels else self.inlet.info().channel_count()
def __init__(self): app.Canvas.__init__(self, title='Use your wheel to zoom!', keys='interactive') # first resolve an EEG stream on the lab network print("looking for an EEG stream...") streams = resolve_stream('name', 'RandomSpehricalData') streamInfo = streams[0] # create a new inlet to read from the stream self.inlet = StreamInlet(streamInfo) # Number of cols and rows in the table. self.nrows = streamInfo.channel_count() n = streamInfo.nominal_srate() ncols = 1 # Number of signals. m = self.nrows*ncols # Various signal amplitudes. amplitudes = .1 + .2 * np.random.rand(m, 1).astype(np.float32) # Generate the signals as a (m, n) array. self.y = amplitudes * np.random.randn(m, n).astype(np.float32) color = np.repeat(np.random.uniform(size=(m, 3), low=.5, high=.9), n, axis=0).astype(np.float32) # Signal 2D index of each vertex (row and col) and x-index (sample index # within each signal). index = np.c_[np.repeat(np.repeat(np.arange(ncols), self.nrows), n), np.repeat(np.tile(np.arange(self.nrows), ncols), n), np.tile(np.arange(n), m)].astype(np.float32) self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) self.program['a_position'] = self.y.reshape(-1, 1) self.program['a_color'] = color self.program['a_index'] = index self.program['u_scale'] = (1., 1.) self.program['u_size'] = (self.nrows, ncols) self.program['u_n'] = n gloo.set_viewport(0, 0, *self.physical_size) self._timer = app.Timer('auto', connect=self.on_timer, start=True) gloo.set_state(clear_color='black', blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self.sampleFromLSL = None self.show()
def __init__(self, stream_type='PPG', stream_id=None, buflen=5): """ stream_type: LSL type of the stream to check stream_id: will select specifically one stream based on its name "[stream_type]_[stream_id]" """ # first resolve said stream type on the network streams = resolve_stream('type',stream_type) self.nb_streams = 0 if len(streams) < 1: raise NameError('LSLTypeNotFound') print "Detecting", len(streams), stream_type, "streams" # create inlets to read from each stream self.inlets = [] # retrieve also corresponding StreamInfo for future uses (eg sampling rate) self.infos = [] for stream in streams: inlet = StreamInlet(stream, max_buflen=buflen) info = inlet.info() # if an ID is specified, will look only for it, otherwise add everything if stream_id is not None: if info.name() == stream_type + "_" + str(stream_id): # check that there is a unique stream with this name to stop right there any ambiguity if self.nb_streams > 0: raise NameError('LSLDuplicateStreamName') else: self.inlets.append(inlet) self.infos.append(info) self.nb_streams = self.nb_streams + 1 else: self.inlets.append(inlet) self.infos.append(info) self.nb_streams = self.nb_streams + 1 if stream_id and self.nb_streams < 1: raise NameError('LSLStreamNameNotFound') # init list of samples self.samples = [] * self.nb_streams
class MarkerInlet(object): def __init__(self): self.task = {'phase':'precue', 'class':1, 'target':1} print("Looking for stream with type Markers") streams = resolve_bypred("type='Markers'", minimum=1) proc_flags = 0 # Marker events are relatively rare. No need to post-process. self.inlet = StreamInlet(streams[0], processing_flags=proc_flags) # The following is an example of how to read stream info stream_info = self.inlet.info() stream_Fs = stream_info.nominal_srate() stream_xml = stream_info.desc() chans_xml = stream_xml.child("channels") chan_xml_list = [] ch = chans_xml.child("channel") while ch.name() == "channel": chan_xml_list.append(ch) ch = ch.next_sibling("channel") stream_ch_names = [ch_xml.child_value("label") for ch_xml in chan_xml_list] print("Reading from inlet named {} with channels {}".format(stream_info.name(), stream_ch_names)) def update(self): marker_samples, marker_timestamps = self.inlet.pull_chunk(timeout=0.0) if (marker_timestamps): [phase_str, class_str, targ_str] = marker_samples[-1][0].split(', ') if phase_str in ['TargetCue']: self.task['phase'] = 'cue' elif phase_str in ['GoCue']: self.task['phase'] = 'go' elif phase_str in ['Miss', 'Hit']: self.task['phase'] = 'evaluate' elif phase_str[:8] == 'NewTrial': self.task['phase'] = 'precue' else: print(phase_str) self.task['class'] = int(class_str.split(' ')[1]) self.task['target'] = int(targ_str.split(' ')[1]) print("Marker inlet updated with task {}".format(self.task))
def __init__(self): self.task = {'phase':'precue', 'class':1, 'target':1} print("Looking for stream with type Markers") streams = resolve_bypred("type='Markers'", minimum=1) proc_flags = 0 # Marker events are relatively rare. No need to post-process. self.inlet = StreamInlet(streams[0], processing_flags=proc_flags) # The following is an example of how to read stream info stream_info = self.inlet.info() stream_Fs = stream_info.nominal_srate() stream_xml = stream_info.desc() chans_xml = stream_xml.child("channels") chan_xml_list = [] ch = chans_xml.child("channel") while ch.name() == "channel": chan_xml_list.append(ch) ch = ch.next_sibling("channel") stream_ch_names = [ch_xml.child_value("label") for ch_xml in chan_xml_list] print("Reading from inlet named {} with channels {}".format(stream_info.name(), stream_ch_names))
def __init__(self): print("looking for an EEG stream...") streams = resolve_byprop("type", "EEG") # create a new inlet to read from the stream proc_flags = proc_clocksync | proc_dejitter | proc_monotonize self.inlet = StreamInlet(streams[0], processing_flags=proc_flags) # The following is an example of how to read stream info stream_info = self.inlet.info() stream_Fs = stream_info.nominal_srate() stream_xml = stream_info.desc() chans_xml = stream_xml.child("channels") chan_xml_list = [] ch = chans_xml.child("channel") while ch.name() == "channel": chan_xml_list.append(ch) ch = ch.next_sibling("channel") self.channel_names = [ch_xml.child_value("label") for ch_xml in chan_xml_list] print("Reading from inlet named {} with channels {} sending data at {} Hz".format(stream_info.name(), self.channel_names, stream_Fs))
import sys sys.path.append('..') from pylsl import StreamInlet, resolve_stream # first resolve a marker stream on the lab network print("looking for a marker stream...") streams = resolve_stream('type', 'Markers') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) while True: # get a new sample (you can also omit the timestamp part if you're not interested in it) sample, timestamp = inlet.pull_sample() print("Event: ", sample[0], " at time: ", timestamp)
class EEGStateAdapter: # n_timepoints def __init__(self, n_freq=30, n_chan=8, eeg_feed_rate=250, samples_per_output=1, spectrogram_timespan=10, n_spectrogram_timepoints=10): self.num_channels = n_chan self.num_freqs = n_freq self.n_spectrogram_timepoints = n_spectrogram_timepoints self.eeg_fifo_len = spectrogram_timespan * eeg_feed_rate #assuming spectrogram_timespan is in seconds # Verify this is an int, then cast to int assert self.eeg_fifo_len.is_integer(), "Spectrogram timespan (" + str( spectrogram_timespan) + ") * SPS (" + str( eeg_feed_rate) + ") must be an integer, is: " + str( self.eeg_fifo_len) self.eeg_fifo_len = int(self.eeg_fifo_len) self.cache_interval = int(samples_per_output) self.eeg_thread_event = Event() self.eeg_data_cache = list() self.eeg_fifo = deque([], maxlen=self.eeg_fifo_len) # Init other adapters self.imprintAdapter = ImprintAdapter(dbg=False) self.rpvAdapter = RewardPunishAdapter(dbg=False) self.rpv_data_dict = dict() self.imprint_data_dict = dict() def sync_state_labels(self): ''' Grab all available labels data from imprint and rpv adapters and sync timestamps. ''' ''' syncing data is basically a matter of comparing timestamps and associating the closest ones. This should be easy if we are storing lots of data... we can collect it all to be able to associate the closest, then we can clear cache after a second or so. ''' # Sync rpv data self.rpv_data_dict = self.sync_data(self.eeg_data_cache, self.rpvAdapter.get_data()) # Sync Imprint data self.imprint_data_dict = self.sync_data(self.eeg_data_cache, self.imprintAdapter.get_data()) def retrieve_latest_data(self): ''' For V,F - T,F - V,B - PI,B ''' if len(self.eeg_data_cache) > 0: #============================================================================== # data = ( # np.asarray([d[0] for d in self.eeg_data_cache]), # np.ones((1,1,10,240)), #np.empty(0),#[], # # np.asarray([(1,0)]),# np.empty(0),#[], #np.ones((1,1,10,240)), # np.ones((1,1,10,240)),#np.empty(0),#[], #np.ones((1,1,10,240)), # np.asarray([44]) # #np.empty(0)#[] # ) # self.clear_caches() # return data #============================================================================== # Sync timestamps self.sync_state_labels() # remove timestamps from eeg data eeg_data = np.asarray([d[0] for d in self.eeg_data_cache]) # setup structure for tensorflow model rpv_inputs = np.asarray(self.rpv_data_dict['inputs']) rpv_labels = np.asarray(self.rpv_data_dict['labels']) assert rpv_inputs.shape[0] == rpv_labels.shape[ 0] #TODO error string here imp_inputs = np.asarray(self.imprint_data_dict['inputs']) imp_labels = np.asarray( self.imprint_data_dict['labels']) #TODO error assert imp_inputs.shape[0] == imp_labels.shape[0] data = eeg_data, rpv_inputs, rpv_labels, imp_inputs, imp_labels # clear caches self.clear_caches() return data else: return ([], [], [], [], []) def launch_eeg_adapter(self, manual_stream_select=True): self.imprintAdapter.launch_imprint_adapter() self.rpvAdapter.launch_rpv_adapter() print("Resolving EEG marker stream...") streams = resolve_stream('type', 'PERIODO') snum = 0 if manual_stream_select: for i, s in enumerate(streams): print(i, s.name()) snum = input("Select EEGStateAdapter stream: ") self.inlet = StreamInlet(streams[int(snum)]) # launch thread self.eeg_thread_event.set() thread = Thread(target=self.eeg_rx_thread) thread.start() def stop_eeg_thread(self): self.imprintAdapter.stop_imprint_thread() self.rpvAdapter.stop_rpv_thread() self.eeg_thread_event.clear() def clear_caches(self, clear_subadapters=False): self.rpv_data_dict = dict() self.imprint_data_dict = dict() self.eeg_data_cache = list() if clear_subadapters: self.rpvAdapter.get_data() self.imprintAdapter.get_data() def eeg_rx_thread(self): ''' Receiver will need to select correct stream, then continuously accept and process commands as they arrive. ''' rx_counter = 0 fifo_idx = np.linspace(0, self.eeg_fifo_len - 1, self.n_spectrogram_timepoints).astype(int) while self.eeg_thread_event.isSet(): # get command eeg_periodo, timestamp = self.inlet.pull_sample(timeout=1) if eeg_periodo == None: continue #if timed out, check if thread is sitll alive assert len( eeg_periodo ) == self.num_channels * self.num_freqs #lsl output is flattened periodo # add new periodogram to fifo self.eeg_fifo.append(eeg_periodo) # inc rx count rx_counter += 1 # cache if apt. if (len(self.eeg_fifo) == self.eeg_fifo_len) and (rx_counter % self.cache_interval == 0): self.eeg_data_cache += [ (np.asarray(self.eeg_fifo)[fifo_idx, :], timestamp) ] def sync_data(self, _inputs, labels): ''' assume inputs and labels both lists of tuples with first val value, second val timestamp. assume we have many more inputs than labels strategy is to search for the closest label for each input, also we can only, use one input per label so if the next closest is further than a removed one then we would like to collect statistics on that. ''' ts_diffs = [] synced_inputs = [] synced_outputs = [] # copy inputs inputs = list(_inputs) previously_used = list() # loop over labels to find closest input for label in labels: # extract timestamps to array inputs_ts = np.asarray([i[1] for i in inputs]) # extract label ts label_ts = label[1] # find nearest input to label inputs_ts = abs(inputs_ts - label_ts) amin = np.argmin(inputs_ts) # check if the value at this index has been used before; if so, skip the label (undefined behaviour) if amin in previously_used: continue previously_used.append(amin) closest_input = inputs[amin][0] # collect metrics ts_diffs += [inputs_ts[amin]] # add pair #synced_pairs += [(closest_input, label[0])] synced_inputs += [closest_input] synced_outputs += [label[0]] # delete used input # del inputs[amin] return { 'inputs': synced_inputs, 'labels': synced_outputs, 'diffs': ts_diffs }
import numpy as np from pylsl import StreamInlet, resolve_stream, local_clock import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui plot_duration = 2.0 # first resolve an EEG stream on the lab network print("looking for an EEG stream...") streams = resolve_stream('type', 'EEG') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) # Create the pyqtgraph window win = pg.GraphicsWindow() win.setWindowTitle('LSL Plot ' + inlet.info().name()) plt = win.addPlot() plt.setLimits(xMin=0.0, xMax=plot_duration, yMin=-1.0 * (inlet.channel_count - 1), yMax=1.0) t0 = [local_clock()] * inlet.channel_count curves = [] for ch_ix in range(inlet.channel_count): curves += [plt.plot()] def update(): global inlet, curves, t0
class Recorder(): def __init__(self, config, maxbuffer_size, q_from_display_to_recorder=None): # initialize basic configuration self.config = config self.maxbuffer_size = maxbuffer_size self.q_from_display_to_recorder = q_from_display_to_recorder #self.q_from_recorder_to_decoder = Queue() # private variables, changed by commands form q self.inlet_state = False # patient states: # -1 - none (not recording) # 0 - rest # 1 - objects # 2 - actions self.patient_state = 0 # picture state: # 0 - none # 1 - start # 2 - stop self.picture_state = 0 self.pause = 0 self.patient_state_paused = -1 self.picture_pause = 0 # initialize configuration based on saving method (though buffer or without it) self.memory = [[], [], []] self.picture_indices = [[], [], []] self.pause_mark = False self.index_picture_start = -1 self.index_picture_stop = -1 self.index_pause = -1 self.picture_end = False # resolve lsl stream stream_name = self.config['general']['lsl_stream_name'] streams = resolve_stream('name', stream_name) self._printm('Resolving stream \'{}\', {} streams found'.format( stream_name, len(streams))) self.inlet = StreamInlet(streams[0], self.maxbuffer_size) self._printm('Stream resolved') # not used def record(self): self._printm( 'Start recording, if \'Recording...\' progress bar is not filling, check lsl input stream' ) self._resolve_q() with Bar('Recording...', max=1000) as bar: while self.inlet_state: self._resolve_q() sample, timestamp = self.inlet.pull_sample(timeout=0.0) if bar.index < 999: bar.next() elif bar.index == 999: bar.next() bar.finish() # if patient state is 'none' - skip if self.patient_state == -1: continue elif self.pause: self.pause_mark = True continue # if timestamp exists, concatenate sample with previous data if timestamp: sample_index = len(self.memory[self.patient_state]) if self.picture_state == 1: self.index_picture_start = (sample_index, self.picture_state) elif self.picture_state == 2: self.index_picture_stop = (sample_index, self.picture_state) self.picture_end = True if self.pause_mark: self.index_pause = (sample_index - 1, self.picture_state) self.pause_mark = False sample = np.reshape(np.asarray(sample), (1, 69)) timestamp = np.array([[timestamp]]) picture_type_array = np.array([[self.patient_state]]) picture_state = np.array([[self.picture_state]]) self.picture_state = 0 picture_pause = np.array([[self.picture_pause]]) self.picture_pause = 0 big_sample = np.concatenate( (sample, timestamp, picture_type_array, picture_pause, picture_state), axis=1) self.memory[self.patient_state].append(big_sample) if self.picture_end: if self._good_picture(): self.picture_indices.append( (self.index_picture_start, self.index_picture_stop)) #ecog_picture = np.vstack(self.memory[self.patient_state][self.indx_picture_begining:]) self._printm('Stop recording') t1 = time.time() self._save() t2 = time.time() self._printm('Data saved: {}s:'.format(t2 - t1)) def _good_picture(self): same_patient_state = self.index_picture_start[ 1] == self.index_picture_stop[1] pause_not_inside_picture = not (self.index_picture_start[1] == self.index_pause[1] and \ self.index_picture_start[0] <= self.index_pause[0]) return same_patient_state and pause_not_inside_picture # not used def _save(self): experiment_data_path = Path( self.config['paths']['experiment_data_path']) dataset_width = self.config['recorder'].getint('dataset_width') groups = self.config['recorder']['group_names'].split(' ') with h5py.File(experiment_data_path, 'a') as file: for i in range(len(self.memory)): if len(self.memory[i]) > 0: stacked_data = np.vstack(self.memory[i]) stacked_indices = np.vstack(self.picture_indices[i]) file[groups[i] + '/raw_data'] = stacked_data file[groups[i] + '/picture_indices'] = stacked_indices self.memory[i] = [] self.picture_indices[i] = [] self._printm('Saved {}, {}, {} pictures'.format( groups[i], stacked_data.shape, stacked_indices.shape[0])) else: empty_shape = (0, dataset_width) file.create_dataset(groups[i] + '/raw_data', empty_shape) file.create_dataset(groups[i] + '/picture_indices', (0, 2)) self._printm('Saved {}, {}'.format(groups[i], empty_shape)) file.create_dataset('fs', data=np.array( self.config['recorder'].getint('fs'))) # resolve commands from Display object to navigate recording of data def _resolve_q(self): while not self.q_from_display_to_recorder.empty(): key, value = self.q_from_display_to_recorder.get() if self.config['general'].getboolean('debug_mode'): pass #self._printm('key: {}, value: {}'.format(key, value)) if key == 'inlet_state': self.inlet_state = value elif key == 'patient_state': self.patient_state = value elif key == 'picture_state': self.picture_state = value elif key == 'pause': self.pause = value else: self._printm('wrong key in queue: {}'.format(key)) def _printm(self, message): print('{} {}: '.format(time.strftime('%H:%M:%S'), type(self).__name__) + message)
import sys sys.path.append( '..') # help python find pylsl relative to this example program from pylsl import StreamInlet, resolve_stream # first resolve an EEG stream on the lab network print("looking for an ECG stream...") streams = resolve_stream('type', 'ECG') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) while True: # get a new sample (you can also omit the timestamp part if you're not interested in it) chunk, timestamps = inlet.pull_chunk() if timestamps: print(timestamps, chunk)
exptime = 900 dt_rate = 0.1 # allocating buffers received_data_buf = np.zeros((numch, exptime*srate*1.2)) states_predicted_buf = np.zeros((1, exptime*srate*1.2)) pos = 0 pos_pred = 0 # first resolve an EEG stream on the lab network print("looking for an EEG stream...") streams = resolve_stream('type', 'Data') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) globalstart = time.time() while (time.time() - globalstart < exptime): startwhile = time.time() chunk, timestamp = inlet.pull_chunk() np_ar_chunk = np.asarray(chunk) chunk_size = np_ar_chunk.shape[0] if chunk_size > 0: data_chunk_test = np_ar_chunk.T
cap.append_child_value("size", "54") cap.append_child_value("labelscheme", "10-20") # create outlet for the stream outlet = StreamOutlet(info) # (...normally here one might start sending data into the outlet...) # === the following could run on another computer === # first we resolve a stream whose name is MetaTester (note that there are # other ways to query a stream, too - for instance by content-type) results = resolve_stream("name", "MetaTester") # open an inlet so we can read the stream's data (and meta-data) inlet = StreamInlet(results[0]) # get the full stream info (including custom meta-data) and dissect it info = inlet.info() print("The stream's XML meta-data is: ") print(info.as_xml()) print("The manufacturer is: %s" % info.desc().child_value("manufacturer")) print("Cap circumference is: %s" % info.desc().child("cap").child_value("size")) print("The channel labels are as follows:") ch = info.desc().child("channels").child("channel") for k in range(info.channel_count()): print(" " + ch.child_value("label")) ch = ch.next_sibling() time.sleep(3)
from pylsl import StreamInlet, resolve_stream import time numStreams = 3 # first resolve an EEG stream on the lab network print("looking for an EEG stream...") stream1 = resolve_stream('type', 'EEG') stream2 = resolve_stream('type', 'AUX') stream3 = resolve_stream('type', 'FFT') # create a new inlet to read from the stream inlet = StreamInlet(stream1[0]) inlet2 = StreamInlet(stream2[0]) inlet3 = StreamInlet(stream3[0]) def testLSLSamplingRates(): print("Testing Sampling Rates...") start = time.time() numSamples1 = 0 numSamples2 = 0 numSamples3 = 0 while time.time() < start + 5: # get a new sample (you can also omit the timestamp part if you're not # interested in it) for i in range(numStreams): if i == 0: chunk, timestamps = inlet.pull_chunk() if timestamps: numSamples1 += 1 elif i == 1:
# first create a new stream info (here we set the name to BioSemi, # the content-type to EEG, 8 channels, 100 Hz, and float-valued data) The # last value would be the serial number of the device or some other more or # less locally unique identifier for the stream as far as available (you # could also omit it but interrupted connections wouldn't auto-recover) fs = 1000 info = StreamInfo('python', 'EEG', 2) # next make an outlet outlet = StreamOutlet(info) from pylsl import StreamInlet, resolve_stream print('resolving stream') streams = resolve_stream('name', 'matlab') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) print('resolved') t = 0 mean_time = 0 while True: #time.sleep(0.002) t += 1 clock = local_clock() outlet.push_sample([0, 1]) sample, timestamp = inlet.pull_sample(timeout=1) dt = local_clock() - clock mean_time += dt print(mean_time / t, dt) #time.sleep(0.001)
import bci_workshop_tools as BCIw # Our own functions for the workshop if __name__ == "__main__": """ 1. CONNECT TO EEG STREAM """ # Search for active LSL stream print('Looking for an EEG stream...') streams = resolve_byprop('type', 'EEG', timeout=2) if len(streams) == 0: raise RuntimeError('Can\'t find EEG stream.') # Set active EEG stream to inlet and apply time correction print("Start acquiring data") inlet = StreamInlet(streams[0], max_chunklen=12) eeg_time_correction = inlet.time_correction() # Get the stream info, description, sampling frequency, number of channels info = inlet.info() description = info.desc() fs = int(info.nominal_srate()) n_channels = info.channel_count() # Get names of all channels ch = description.child('channels').first_child() ch_names = [ch.child_value('label')] for i in range(1, n_channels): ch = ch.next_sibling() ch_names.append(ch.child_value('label'))
def initialize(self): # settings are retrieved in the dictionary try: self.samplingFrequency = int(self.setting['Sampling frequency']) except: print "Sampling frequency not set or error while parsing." self.samplingFrequency = 0 print "Sampling frequency: " + str(self.samplingFrequency) self.epochSampleCount = int(self.setting['Generated epoch sample count']) self.stream_type=self.setting['Stream type'] # total channels for all streams self.channelCount = 0 all_streams = self.setting['Get all streams'] == "true" self.stream_name=self.setting['Stream name'] # in case !all_streams print "Looking for streams of type: " + self.stream_type streams = resolve_stream('type',self.stream_type) print "Nb streams: " + str( len(streams)) if not all_streams: print "Will only select (first) stream named: " + self.stream_name self.nb_streams = 1 else: self.nb_streams = len(streams) # create inlets to read from each stream self.inlets = [] # retrieve also corresponding StreamInfo for future uses (eg sampling rate) self.infos = [] # save inlets and info + build signal header for stream in streams: inlet = StreamInlet(stream) info = inlet.info() name = info.name() print "Stream name: " + name # if target one stream, ignore false ones if not all_streams and name != self.stream_name: continue print "Nb channels: " + str(info.channel_count()) self.channelCount += info.channel_count() stream_freq = info.nominal_srate() print "Sampling frequency: " + str(stream_freq) if self.samplingFrequency == 0: print "Set sampling frequency to:" + str(stream_freq) self.samplingFrequency = stream_freq elif self.samplingFrequency != stream_freq: print "WARNING: sampling frequency of current stream (" + str(stream_freq) + ") differs from option set to box (" + str(self.samplingFrequency) + ")." for i in range(info.channel_count()): self.dimensionLabels.append(name + ":" + str(i)) # We must delay real inlet/info init because we may know the defifitive sampling frequency # limit buflen just to what we need to fill each chuck, kinda drift correction # TODO: not a very pretty code... buffer_length = int(ceil(float(self.epochSampleCount) / self.samplingFrequency)) print "LSL buffer length: " + str(buffer_length) inlet = StreamInlet(stream, max_buflen=buffer_length) info = inlet.info() self.inlets.append(inlet) self.infos.append(info) # if we're still here when we target a stream, it means we foand it if not all_streams: print "Found target stream" break # we need at least one stream before we let go if self.channelCount <= 0: raise Exception("Error: no stream found.") # backup last values pulled in case pull(timeout=0) return None later self.last_values = self.channelCount*[0] self.dimensionLabels += self.epochSampleCount*[''] self.dimensionSizes = [self.channelCount, self.epochSampleCount] self.signalHeader = OVSignalHeader(0., 0., self.dimensionSizes, self.dimensionLabels, self.samplingFrequency) self.output[0].append(self.signalHeader) #creation of the first signal chunk self.endTime = 1.*self.epochSampleCount/self.samplingFrequency self.signalBuffer = numpy.zeros((self.channelCount, self.epochSampleCount)) self.updateTimeBuffer() self.updateSignalBuffer()
ch.append_child_value("label",label) ch.append_child_value("unit","microvolts") ch.append_child_value("type","EEG") info.desc().append_child_value("manufacturer","SCCN") cap = info.desc().append_child("cap") cap.append_child_value("name","EasyCap") cap.append_child_value("size","54") cap.append_child_value("labelscheme","10-20") # create outlet for the stream outlet = StreamOutlet(info) # === the following could run on another computer === # resolve the stream and open an inlet results = resolve_stream("name","MetaTester") inlet = StreamInlet(results[0]) # get the full stream info (including custom meta-data) and dissect it inf = inlet.info() print "The stream's XML meta-data is: " print inf.as_xml() print "The manufacturer is: " + inf.desc().child_value("manufacturer") print "The cap circumference is: " + inf.desc().child("cap").child_value("size") print "The channel labels are as follows:" ch = inf.desc().child("channels").child("channel") for k in range(info.channel_count()): print " " + ch.child_value("label") ch = ch.next_sibling() time.sleep(3)
class LSLViewer(): def __init__(self, stream, fig, axes, window, scale, dejitter=True): """Init""" self.stream = stream self.window = window self.scale = scale self.dejitter = dejitter self.inlet = StreamInlet(stream, max_chunklen=buf) self.filt = True info = self.inlet.info() description = info.desc() self.sfreq = info.nominal_srate() self.n_samples = int(self.sfreq * self.window) self.n_chan = info.channel_count() ch = description.child('channels').first_child() ch_names = [ch.child_value('label')] for i in range(self.n_chan): ch = ch.next_sibling() ch_names.append(ch.child_value('label')) self.ch_names = ch_names fig.canvas.mpl_connect('key_press_event', self.OnKeypress) fig.canvas.mpl_connect('button_press_event', self.onclick) self.fig = fig self.axes = axes sns.despine(left=True) self.data = np.zeros((self.n_samples, self.n_chan)) self.times = np.arange(-self.window, 0, 1. / self.sfreq) impedances = np.std(self.data, axis=0) lines = [] for ii in range(self.n_chan): line, = axes.plot(self.times[::subsample], self.data[::subsample, ii] - ii, lw=1) lines.append(line) self.lines = lines axes.set_ylim(-self.n_chan + 0.5, 0.5) ticks = np.arange(0, -self.n_chan, -1) axes.set_xlabel('Time (s)') axes.xaxis.grid(False) axes.set_yticks(ticks) ticks_labels = [ '%s - %.1f' % (ch_names[ii], impedances[ii]) for ii in range(self.n_chan) ] axes.set_yticklabels(ticks_labels) self.display_every = int(0.2 / (12 / self.sfreq)) # self.bf, self.af = butter(4, np.array([1, 40])/(self.sfreq/2.), # 'bandpass') self.bf = firwin(32, np.array([1, 40]) / (self.sfreq / 2.), width=0.05, pass_zero=False) self.af = [1.0] zi = lfilter_zi(self.bf, self.af) self.filt_state = np.tile(zi, (self.n_chan, 1)).transpose() self.data_f = np.zeros((self.n_samples, self.n_chan)) def update_plot(self): k = 0 while self.started: samples, timestamps = self.inlet.pull_chunk(timeout=1.0, max_samples=12) if timestamps: if self.dejitter: timestamps = np.float64(np.arange(len(timestamps))) timestamps /= self.sfreq timestamps += self.times[-1] + 1. / self.sfreq self.times = np.concatenate([self.times, timestamps]) self.n_samples = int(self.sfreq * self.window) self.times = self.times[-self.n_samples:] self.data = np.vstack([self.data, samples]) self.data = self.data[-self.n_samples:] #filt_samples, self.filt_state = lfilter( # self.bf, self.af, # samples, # axis=0, zi=self.filt_state) filt_samples = mne.filter.filter_data(self.data, 256, 8, 12, fir_design='firwin') #filt_samples = [math.sqrt(part.real**2 + part.imag**2) for part in filt_samples] self.data_f = np.vstack([self.data_f, filt_samples]) self.data_f = self.data_f[-self.n_samples:] k += 1 if k == self.display_every: if self.filt: plot_data = self.data_f elif not self.filt: plot_data = self.data - self.data.mean(axis=0) for ii in range(self.n_chan): self.lines[ii].set_xdata(self.times[::subsample] - self.times[-1]) self.lines[ii].set_ydata(plot_data[::subsample, ii] / self.scale - ii) impedances = np.std(plot_data, axis=0) ticks_labels = [ '%s - %.2f' % (self.ch_names[ii], impedances[ii]) for ii in range(self.n_chan) ] self.axes.set_yticklabels(ticks_labels) self.axes.set_xlim(-self.window, 0) self.fig.canvas.draw() k = 0 else: sleep(0.2) def onclick(self, event): print((event.button, event.x, event.y, event.xdata, event.ydata)) def OnKeypress(self, event): if event.key == '/': self.scale *= 1.2 elif event.key == '*': self.scale /= 1.2 elif event.key == '+': self.window += 1 elif event.key == '-': if self.window > 1: self.window -= 1 elif event.key == 'd': self.filt = not (self.filt) def start(self): self.started = True self.thread = Thread(target=self.update_plot) self.thread.daemon = True self.thread.start() def stop(self): self.started = False
print("looking for an EEG stream...") streams = resolve_byprop('type', 'EEG', timeout=2) if len(streams) == 0: print("No EEG stream running yet. Trying to start the Muse EEG stream ...") eeg_stream = subprocess.Popen([currentpath + "/bci-stream"]) sleep(muse_connect_timout) streams = resolve_byprop('type', 'EEG', timeout=2) if len(streams) == 0: raise (RuntimeError, "Cant find EEG stream") else: print("Success: found Muse EEG stream") print("Start aquiring data") inlet = StreamInlet(streams[0], max_chunklen=12) eeg_time_correction = inlet.time_correction() inlet_marker = False #print("looking for a Markers stream...") #marker_streams = resolve_byprop('type', 'Markers', timeout=2) #if marker_streams: # inlet_marker = StreamInlet(marker_streams[0]) # marker_time_correction = inlet_marker.time_correction() #else: # inlet_marker = False # print("Cant find Markers stream") info = inlet.info() description = info.desc()
def record( duration: int, filename=None, dejitter=False, data_source="EEG", continuous: bool = True, ) -> None: chunk_length = LSL_EEG_CHUNK if data_source == "PPG": chunk_length = LSL_PPG_CHUNK if data_source == "ACC": chunk_length = LSL_ACC_CHUNK if data_source == "GYRO": chunk_length = LSL_GYRO_CHUNK if not filename: filename = os.path.join( os.getcwd(), "%s_recording_%s.csv" % (data_source, strftime('%Y-%m-%d-%H.%M.%S', gmtime()))) channel_idx = 1 num_of_data = 178 existing = pd.DataFrame() model = load_model( "/home/pi/.virtualenvs/muse_lsl_env/lib/python3.7/site-packages/muselsl/Epilepsy.h5" ) print("Initialized Variables") print("Looking for a %s stream..." % (data_source)) streams = resolve_byprop('type', data_source, timeout=LSL_SCAN_TIMEOUT) if len(streams) == 0: print("Can't find %s stream." % (data_source)) return print("Started acquiring data.") inlet = StreamInlet(streams[0], max_chunklen=chunk_length) # eeg_time_correction = inlet.time_correction() print("Looking for a Markers stream...") marker_streams = resolve_byprop('name', 'Markers', timeout=LSL_SCAN_TIMEOUT) if marker_streams: inlet_marker = StreamInlet(marker_streams[0]) else: inlet_marker = False print("Can't find Markers stream.") info = inlet.info() description = info.desc() Nchan = info.channel_count() ch = description.child('channels').first_child() ch_names = [ch.child_value('label')] for i in range(1, Nchan): ch = ch.next_sibling() ch_names.append(ch.child_value('label')) res = [] timestamps = [] markers = [] t_init = time() time_correction = inlet.time_correction() last_written_timestamp = None print('Start recording at time t=%.3f' % t_init) print('Time correction: ', time_correction) while (time() - t_init) < duration: try: data, timestamp = inlet.pull_chunk(timeout=1.0, max_samples=chunk_length) if timestamp: # print("Data: " + str(data)) new_arr = pd.DataFrame(data) combine = [existing, new_arr] existing = pd.concat(combine).reset_index(drop=True) # print(len(existing)) if len(existing) >= num_of_data: row = existing[0:num_of_data] row = row[channel_idx] row = row.values.reshape(-1, 178, 1) existing.drop(existing.index[0:178], inplace=True) existing = existing.reset_index(drop=True) predictions = model.predict( (row[:, ::4] - row.mean()) / row.std()) result = np.argmax(predictions[0]) + 1 print("Result: " + str(result)) res.append(data) timestamps.extend(timestamp) tr = time() if inlet_marker: marker, timestamp = inlet_marker.pull_sample(timeout=0.0) if timestamp: markers.append([marker, timestamp]) # Save every 5s if continuous and (last_written_timestamp is None or last_written_timestamp + 5 < timestamps[-1]): _save( filename, res, timestamps, time_correction, dejitter, inlet_marker, markers, ch_names, last_written_timestamp=last_written_timestamp, ) last_written_timestamp = timestamps[-1] except KeyboardInterrupt: break time_correction = inlet.time_correction() print("Time correction: ", time_correction) _save( filename, res, timestamps, time_correction, dejitter, inlet_marker, markers, ch_names, ) print("Done - wrote file: {}".format(filename))
class lsl_inlet(po): def __init__(self, info, block_size=4, processing_flags=0, cb=None): self.inlet = StreamInlet(info, max_buflen=block_size, max_chunklen=block_size, processing_flags=processing_flags) self.info = self.inlet.info() po.__init__(self, self.info.nominal_srate(), self.info.channel_count(), block_size) self.internal_data_buffer = data_buffer(self.inlet.channel_count, block_size=block_size, block_cnt=4) self.internal_time_stamp_buffer = data_buffer(1, block_size=block_size, block_cnt=4) self.external_data_buffer = data_buffer(self.inlet.channel_count, block_size=block_size) self.external_time_stamp_buffer = data_buffer(1, block_size=block_size) self.thread = None self.running = False self._lock = threading.Lock() self.new_samples = 0 self.cb = None self.wrt_ptr = 0 self.rd_ptr = 0 self.new_chunk_sz = 0 self.cb = cb def __del__(self): self.kill_listener() def kill_listener(self): if self.running is True: self.running = False self.thread.join() def pull_frames(self): while self.running is True: chunk, timestamps = self.inlet.pull_chunk() if (len(chunk) != 0): self._lock.acquire() self.internal_data_buffer.write_raw(np.array(chunk)) self.internal_time_stamp_buffer.write_raw( np.array([timestamps])) self.new_samples += len(chunk) self.new_chunk_sz = len(chunk) self.wrt_ptr = self.internal_data_buffer.wrt_ptr self.rd_ptr = self.rd_ptr - self.block_size # todo: safety check if rd_ptr < -block_size, we are in trouble... if self.rd_ptr < 0: self.rd_ptr += self.block_size while self.new_samples >= self.external_data_buffer.frame_cnt: frames = self.internal_data_buffer.read( self.external_data_buffer) self.cb(frames) #self.external_data_buffer = self.internal_data_buffer.read(self.external_data_buffer) #self.external_time_stamp_buffer = self.internal_time_stamp_buffer.read(self.external_time_stamp_buffer) self.new_samples -= self.external_data_buffer.frame_cnt self._lock.release() else: time.sleep(.01) def launch_listener(self): self.running = True self.thread = threading.Thread(target=self.pull_frames) self.thread.start() def process(self, frames): #self._lock.acquire() frames = self.internal_data_buffer.read(frames) #self._lock.release() return frames
import time; import numpy as np from pylsl import StreamInlet, resolve_stream #Input: #1: Number of secs for test #2: Datetime #3: Sample id channel_len = 8 ignore_first_secs = 0.01 ignore_last_secs = 0.01 # first resolve an EEG stream on the lab network print("Looking for an EEG stream") streams = resolve_stream("type","EEG",) inlet = StreamInlet(streams[0]) print("Stream Found") datastream = [] time.sleep(ignore_first_secs); timeout = time.time() + float(sys.argv[2]) - ignore_last_secs while True: if time.time() > timeout: break #sample[0] has the data, sample[1] has a timestamp sample = inlet.pull_sample() datastream.append(sample[0]) #Build folder structure zpad = 6
class P300Window(object): def __init__(self, master: Tk): self.master = master master.title('P300 speller') #Parameters self.imagesize = 125 self.images_folder_path = '../utils/images/' #use utils/char_generator to generate any image you want self.flash_image_path = '../utils/images/flash_images/einstein.jpg' self.number_of_rows = 6 self.number_of_columns = 6 #make sure you have 6 x 6 amount of images in the images_folder_path self.flash_mode = 2 #single element #1 for columns and rows; currently is NOT working yet; if I have time, will revisit self.flash_duration = 100 #soa self.break_duration = 125 #iti self.trials = 6 #number of letters self.delay = 2500 #interval between trial self.letter_idx = 0 #did not include numbers yet! self.random_letter = random.choices( string.ascii_lowercase, k=self.trials) #randomize [self.trials] number letters self.word = ''.join(self.random_letter) # Variables self.usable_images = [] self.image_labels = [] self.flash_sequence = [] self.flash_image = None self.sequence_number = 0 self.lsl_output = None self.running = 0 #for pause self.image_frame = Frame(self.master) self.image_frame.grid(row=0, column=0, rowspan=self.number_of_rows, columnspan=self.number_of_columns) self.start_btn_text = StringVar() self.start_btn_text.set('Start') self.start_btn = Button(self.master, textvariable=self.start_btn_text, command=self.start) self.start_btn.grid(row=self.number_of_rows + 3, column=self.number_of_columns - 1) self.pause_btn = Button(self.master, text='Pause', command=self.pause) self.pause_btn.grid(row=self.number_of_rows + 3, column=self.number_of_columns - 4) #-4 for center self.pause_btn.configure(state='disabled') self.close_btn = Button(self.master, text='Close', command=master.quit) self.close_btn.grid(row=self.number_of_rows + 3, column=0) fontStyle = tkFont.Font(family="Courier", size=40) self.output = Text(root, height=1, font=fontStyle) self.output.tag_configure("red", foreground="red") self.output.tag_configure("green", foreground="green") self.output.configure(width=10) self.output.insert("end", " ") self.output.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 4) self.outputlabel = Label(root, text="Output: ", font=fontStyle) self.outputlabel.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 5) self.targetlabel = Label(root, text="Target: ", font=fontStyle) self.targetlabel.grid(row=self.number_of_rows + 1, column=self.number_of_columns - 5) self.show_highlight_letter(0) # Initialization self.show_images() self.create_flash_sequence() self.lsl_output = self.create_lsl_output() def open_images(self): self.usable_images = [] self.highlight_letter_images = [] letter_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_images/*.png'))) #currently, still did not flash number yet! number_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_images/*.png'))) letter_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_highlight_images/*.png'))) number_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_highlight_images/*.png'))) for number_image in number_images: letter_images.append(number_image) #print("Paths: ", letter_images) min_number_of_images = self.number_of_columns * self.number_of_rows if len(letter_images) < min_number_of_images: print('To few images in folder: ' + self.images_folder_path) return # Convert and resize images for image_path in letter_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.usable_images.append(Tkimage) # Convert and resize images for image_path in letter_highlight_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.highlight_letter_images.append(Tkimage) flash_img = Image.open(self.flash_image_path) flash_img_res = flash_img.resize((self.imagesize, self.imagesize), Image.BICUBIC) self.flash_image = ImageTk.PhotoImage(flash_img_res) def show_images(self): self.open_images() if self.usable_images == []: print('No images opened') return num_rows = self.number_of_rows num_cols = self.number_of_columns # Arrange images for r in range(0, num_rows): for c in range(0, num_cols): current_image = self.usable_images[r * num_cols + c] label = Label(self.image_frame, image=current_image) label.image = current_image label.grid(row=r, column=c) self.image_labels.append(label) def create_lsl_output(self): """Creates an LSL Stream outlet""" info = StreamInfo(name='LetterMarkerStream', type='LetterFlashMarkers', channel_count=1, channel_format='int8', nominal_srate=IRREGULAR_RATE, source_id='lettermarker_stream', handle=None) return StreamOutlet(info) #for sending the predicted classes def create_flash_sequence(self): self.flash_sequence = [] num_rows = self.number_of_rows num_cols = self.number_of_columns maximum_number = num_rows * num_cols flash_sequence = [] for i in range(10000): seq = list(range(maximum_number)) #generate 0 to maximum_number random.shuffle(seq) #shuffle flash_sequence.extend(seq) self.flash_sequence = flash_sequence def start(self): self.read_lsl_marker() self.running = 1 letter = self.word[0] image_index = string.ascii_lowercase.index(letter) self.highlight_image(image_index) self.start_btn.configure(state='disabled') self.pause_btn.configure(state='normal') def pause(self): self.running = 0 self.start_btn_text.set('Resume') self.start_btn.configure(state='normal') self.pause_btn.configure(state='disabled') def start_flashing(self): if self.sequence_number == len( self.flash_sequence ): #stop flashing if all generated sequence number runs out print('All elements had flashed - run out of juice') self.running = 0 self.sequence_number = 0 return if self.running == 0: print('Flashing paused at sequence number ' + str(self.sequence_number)) return result = self.marker_result() if (result): print("Marker received: ", result[0][0]) receive = result[0][0] else: receive = 0 element_to_flash = self.flash_sequence[self.sequence_number] letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) #pushed markers to LSL stream timestamp = local_clock() print("Letter: ", image_index, " Element flash: ", [element_to_flash + 1], timestamp) self.lsl_output.push_sample([element_to_flash + 1], timestamp) # add 1 to prevent 0 in markers self.flash_single_element(element_to_flash) if not (receive): self.master.after(self.break_duration, self.start_flashing) else: if ((image_index + 1) == receive): self.output.insert("end", self.pos_to_char(receive), "green") else: self.output.insert("end", self.pos_to_char(receive), "red") self.letter_idx += 1 if (self.letter_idx == len(self.word)): return letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) self.master.after(self.break_duration, self.highlight_target, image_index) self.sequence_number = self.sequence_number + 1 #change flash position def pos_to_char(self, pos): return chr(pos - 1 + 97) def highlight_target(self, image_index): self.show_highlight_letter(self.letter_idx) self.highlight_image(image_index) def change_image(self, label, img): label.configure(image=img) label.image = img def highlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.highlight_letter_images[element_no]) self.master.after(self.delay, self.unhighlight_image, element_no) def unhighlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) self.master.after(self.flash_duration, self.start_flashing) def show_highlight_letter(self, pos): fontStyle = tkFont.Font(family="Courier", size=40) fontStyleBold = tkFont.Font(family="Courier bold", size=40) text = Text(root, height=1, font=fontStyle) text.tag_configure("bold", font=fontStyleBold) text.tag_configure("center", justify='center') for i in range(0, len(self.word)): if (i != pos): text.insert("end", self.word[i]) else: text.insert("end", self.word[i], "bold") text.configure(state="disabled", width=10) text.tag_add("center", "1.0", "end") text.grid(row=self.number_of_rows + 1, column=self.number_of_columns - 4) def flash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.flash_image) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.flash_image) self.master.after(self.flash_duration, self.unflash_row_or_col, rc_number) def unflash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) def flash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.flash_image) self.master.after(self.flash_duration, self.unflash_single_element, element_no) def unflash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) def marker_result(self): marker, timestamp = self.inlet_marker.pull_chunk() return marker def read_lsl_marker(self): print("looking for a Markers stream...") marker_streams = resolve_byprop('name', 'ResultMarkerStream') if marker_streams: self.inlet_marker = StreamInlet(marker_streams[0]) marker_time_correction = self.inlet_marker.time_correction() print("Found Markers stream")
T = np.linspace(0, epoch_length, num=int(sampling_rate * epoch_length)) reference1 = signalReference(T, Fre[0], harmonic) reference2 = signalReference(T, Fre[1], harmonic) reference3 = signalReference(T, Fre[2], harmonic) # the send results streams info = StreamInfo('ResultMarker', 'Markers', 1, 0, 'string', 'myuniquesourceid24445') outlet = StreamOutlet(info) # first resolve an EEG stream on the lab network streams_data = resolve_stream('type', 'EEG') streams_marker = resolve_stream('name', 'SSVEPMarkerStream') # create a new inlet to read from the stream inlet_data = StreamInlet(streams_data[0]) inlet_marker = StreamInlet(streams_marker[0]) time.sleep(2) print('Please start MATLAB .....') while True: start_time = 0.0 corr = [0] * len(Fre) Corr = [0] * len(Fre) OUT = 0 markers, timestamp_marker = inlet_marker.pull_chunk() data, timestamp_data = inlet_data.pull_chunk() # pull_chunk data = np.array(data)
class MyOVBox(OVBox): def __init__(self): OVBox.__init__(self) # the initialize method reads settings and outputs the first header def initialize(self): self.initLabel = 0 self.debug=self.setting['debug'] == "true" print "Debug: ", self.debug self.stream_type=self.setting['Stream type'] self.stream_name=self.setting['Stream name'] # total channels for all streams self.channelCount = 0 #self.stream_name=self.setting['Stream name'] # in case !all_streams print "Looking for streams of type: " + self.stream_type streams = resolve_stream('type',self.stream_type) print "Nb streams: " + str( len(streams)) self.nb_streams = len(streams) if self.nb_streams == 0: raise Exception("Error: no stream found.") self.inlet = StreamInlet(streams[0], max_buflen=1) self.info = self.inlet.info() self.channelCount = self.info.channel_count() print "Stream name: " + self.info.name() stream_freq = self.info.nominal_srate() if stream_freq != 0: raise Exception("Error: no irregular stream found.") # we append to the box output a stimulation header. This is just a header, dates are 0. self.output[0].append(OVStimulationHeader(0., 0.)) self.init = False # The process method will be called by openvibe on every clock tick def process(self): # A stimulation set is a chunk which starts at current time and end time is the time step between two calls # init here and filled within triger() self.stimSet = OVStimulationSet(self.getCurrentTime(), self.getCurrentTime()+1./self.getClock()) if self.init == False : local_time = local_clock() initSecond=int(local_time) initMillis=int((local_time-initSecond)*1000) self.stimSet.append(OVStimulation(self.initLabel, self.getCurrentTime(), 0.)) self.stimSet.append(OVStimulation(initSecond, self.getCurrentTime(), 0.)) self.stimSet.append(OVStimulation(initMillis, self.getCurrentTime(), 0.)) self.init=True # read all available stream samples=[] sample,timestamp = self.inlet.pull_sample(0) while sample != None: samples += sample sample,timestamp = self.inlet.pull_sample(0) # every value will be converted to openvibe code and a stim will be create for label in samples: label = str(label) if self.debug: print "Got label: ", label self.stimSet.append(OVStimulation(float(label), self.getCurrentTime(), 0.)) # even if it's empty we have to send stim list to keep the rest in sync self.output[0].append(self.stimSet) def uninitialize(self): # we send a stream end. end = self.getCurrentTime() self.output[0].append(OVStimulationEnd(end, end)) self.inlet.close_stream()
assert len(sys.argv) > 0, 'No experiment name provided' filename = sys.argv[1] timequant = 0.100 #s print("looking for device stream") device = OpenBCI8 bci_info = resolve_byprop('source_id', device.source_id, timeout=1)[0] print("Device stream discovered") print('Looking for GUI source') experiment_info = resolve_byprop('source_id', Experiment.source_id, timeout=100)[0] print('GUI source discovered') #print(experiment_info, bci_info) experiment_inlet = StreamInlet(experiment_info) bci_inlet = StreamInlet(bci_info) bci_results = [[], []] experiment_results = [[], []] print('Recording') while True: # get a new sample (you can also omit the timestamp part if you're not # interested in it) bci_values, bci_timestamps = bci_inlet.pull_chunk(max_samples=device.sfreq * 60 *6) bci_results[0].extend(bci_values) bci_results[1].extend(bci_timestamps) experiment_values, experiment_timestamps = experiment_inlet.pull_chunk() experiment_results[0].extend(experiment_values)
def record(duration, filename=None, dejitter=False, data_source="EEG"): chunk_length = LSL_EEG_CHUNK if data_source == "PPG": chunk_length = LSL_PPG_CHUNK if data_source == "ACC": chunk_length = LSL_ACC_CHUNK if data_source == "GYRO": chunk_length = LSL_GYRO_CHUNK if not filename: filename = os.path.join( os.getcwd(), "%s_recording_%s.csv" % (data_source, strftime("%Y-%m-%d-%H.%M.%S", gmtime())), ) print("Looking for a %s stream..." % (data_source)) streams = resolve_byprop("type", data_source, timeout=LSL_SCAN_TIMEOUT) if len(streams) == 0: print("Can't find %s stream." % (data_source)) return print("Started acquiring data.") inlet = StreamInlet(streams[0], max_chunklen=chunk_length) # eeg_time_correction = inlet.time_correction() print("Looking for a Markers stream...") marker_streams = resolve_byprop("name", "Markers", timeout=LSL_SCAN_TIMEOUT) if marker_streams: inlet_marker = StreamInlet(marker_streams[0]) else: inlet_marker = False print("Can't find Markers stream.") info = inlet.info() description = info.desc() Nchan = info.channel_count() ch = description.child("channels").first_child() ch_names = [ch.child_value("label")] for i in range(1, Nchan): ch = ch.next_sibling() ch_names.append(ch.child_value("label")) res = [] timestamps = [] markers = [] t_init = time() time_correction = inlet.time_correction() print("Start recording at time t=%.3f" % t_init) print("Time correction: ", time_correction) while (time() - t_init) < duration: try: data, timestamp = inlet.pull_chunk(timeout=1.0, max_samples=chunk_length) if timestamp: res.append(data) timestamps.extend(timestamp) if inlet_marker: marker, timestamp = inlet_marker.pull_sample(timeout=0.0) if timestamp: markers.append([marker, timestamp]) except KeyboardInterrupt: break time_correction = inlet.time_correction() print("Time correction: ", time_correction) res = np.concatenate(res, axis=0) timestamps = np.array(timestamps) + time_correction if dejitter: y = timestamps X = np.atleast_2d(np.arange(0, len(y))).T lr = LinearRegression() lr.fit(X, y) timestamps = lr.predict(X) res = np.c_[timestamps, res] data = pd.DataFrame(data=res, columns=["timestamps"] + ch_names) if inlet_marker and markers: n_markers = len(markers[0][0]) for ii in range(n_markers): data["Marker%d" % ii] = 0 # process markers: for marker in markers: # find index of markers ix = np.argmin(np.abs(marker[1] - timestamps)) for ii in range(n_markers): data.loc[ix, "Marker%d" % ii] = marker[0][ii] directory = os.path.dirname(filename) if not os.path.exists(directory): os.makedirs(directory) data.to_csv(filename, float_format="%.3f", index=False) print("Done - wrote file: " + filename + ".")
default=[0, 1, 2, 3], help='channel number to use. If not specified, all the channels are used') args = parser.parse_args() """ 1. CONNECT TO EEG STREAM """ # Search for active LSL stream print('Looking for an EEG stream...') streams = resolve_byprop('type', 'EEG', timeout=2) if len(streams) == 0: raise RuntimeError('Can\'t find EEG stream.') # Set active EEG stream to inlet and apply time correction print("Start acquiring data") inlet = StreamInlet(streams[0], max_chunklen=12) eeg_time_correction = inlet.time_correction() # Get the stream info, description, sampling frequency, number of channels info = inlet.info() description = info.desc() fs = int(info.nominal_srate()) n_channels = info.channel_count() # Get names of all channels ch = description.child('channels').first_child() ch_names = [ch.child_value('label')] for i in range(1, n_channels): ch = ch.next_sibling() ch_names.append(ch.child_value('label'))
class P300Window(object): def __init__(self, master: Tk): self.master = master master.title('P300 speller') #Parameters self.imagesize = 125 self.images_folder_path = '../utils/images/' #use utils/char_generator to generate any image you want self.flash_image_path = '../utils/images/flash_images/einstein.jpg' self.number_of_rows = 6 self.number_of_columns = 6 #make sure you have 6 x 6 amount of images in the images_folder_path self.flash_mode = 2 #single element #1 for columns and rows; currently is NOT working yet; if I have time, will revisit self.flash_duration = 100 #flash duration self.break_duration = 150 #isi self.trials = 6 #number of letters self.delay = 1000 #interval between trial self.letter_idx = 0 # Param for creating sequence self.num_sequence = 100 #Parameter for random # self.number_of_symbols =36 # self.fashed_per_iteration = 2 # self.number_of_iterations = 6 # self.stimuli_per_iteration = self.number_of_symbols * self.fashed_per_iteration / self.number_of_iterations #did not include numbers yet! self.random_letter = random.choices( string.ascii_lowercase, k=self.trials) #randomize [self.trials] number letters self.word = ''.join(self.random_letter) # Variables self.usable_images = [] self.image_labels = [] self.flash_sequence = [] self.flash_image = None self.sequence_number = 0 self.lsl_output = None # self.running = 0 #for pause self.image_frame = Frame(self.master) self.image_frame.grid(row=0, column=0, rowspan=self.number_of_rows, columnspan=self.number_of_columns) self.start_btn_text = StringVar() self.start_btn_text.set('Start') self.start_btn = Button(self.master, textvariable=self.start_btn_text, command=self.start) self.start_btn.grid(row=self.number_of_rows + 3, column=self.number_of_columns - 1) # self.pause_btn = Button(self.master, text='Pause', command=self.pause) # self.pause_btn.grid(row=self.number_of_rows + 3, column=self.number_of_columns - 4) #-4 for center # self.pause_btn.configure(state='disabled') self.close_btn = Button(self.master, text='Close', command=master.quit) self.close_btn.grid(row=self.number_of_rows + 3, column=0) fontStyle = tkFont.Font(family="Courier", size=40) self.output = Text(root, height=1, font=fontStyle) self.output.tag_configure("red", foreground="red") self.output.tag_configure("green", foreground="green") self.output.configure(width=10) self.output.insert("end", " ") self.output.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 4) self.outputlabel = Label(root, text="Output: ", font=fontStyle) self.outputlabel.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 5) self.targetlabel = Label(root, text="Target: ", font=fontStyle) self.targetlabel.grid(row=self.number_of_rows + 1, column=self.number_of_columns - 5) self.show_highlight_letter(0) # Initialization self.show_images() self.create_flash_sequence() self.lsl_output = self.create_lsl_output() def open_images(self): self.usable_images = [] self.highlight_letter_images = [] letter_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_images/*.png'))) #currently, still did not flash number yet! number_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_images/*.png'))) letter_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_highlight_images/*.png'))) number_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_highlight_images/*.png'))) for number_image in number_images: letter_images.append(number_image) #print("Paths: ", letter_images) min_number_of_images = self.number_of_columns * self.number_of_rows if len(letter_images) < min_number_of_images: print('To few images in folder: ' + self.images_folder_path) return # Convert and resize images for image_path in letter_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.usable_images.append(Tkimage) # Convert and resize images for image_path in letter_highlight_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.highlight_letter_images.append(Tkimage) flash_img = Image.open(self.flash_image_path) flash_img_res = flash_img.resize((self.imagesize, self.imagesize), Image.BICUBIC) self.flash_image = ImageTk.PhotoImage(flash_img_res) def show_images(self): self.open_images() if self.usable_images == []: print('No images opened') return num_rows = self.number_of_rows num_cols = self.number_of_columns # Arrange images for r in range(0, num_rows): for c in range(0, num_cols): current_image = self.usable_images[r * num_cols + c] label = Label(self.image_frame, image=current_image) label.image = current_image label.grid(row=r, column=c) self.image_labels.append(label) def create_lsl_output(self): """Creates an LSL Stream outlet""" info = StreamInfo(name='LetterMarkerStream', type='LetterFlashMarkers', channel_count=1, channel_format='int8', nominal_srate=IRREGULAR_RATE, source_id='lettermarker_stream', handle=None) return StreamOutlet(info) #for sending the predicted classes # def create_flash_sequence_old(self): # num_rows = 6 # num_cols = 6 # maximum_number = num_rows * num_cols # number_count = np.array([0] * maximum_number) # seq = [] # next_number = [random.randint(0, maximum_number)] # count = 1 # while (len(seq) / 6 < 100): # if (len(seq) > 0): # neighbor_list = seq[-count:] #the same set, with size of -1 to -6 # left_list = [x-1 for x in neighbor_list] # neighbor of each neighbor in neighbor_list # right_list = [x+1 for x in neighbor_list] # up_list = [x-6 for x in neighbor_list] # bot_list = [x+6 for x in neighbor_list] # combine_list =[] # combine_list.extend(left_list) # combine_list.extend(right_list) # combine_list.extend(up_list) # combine_list.extend(bot_list) # #should not be same element as previous set (*2), and should not be same element in the combine_list # if (next_number not in seq[-CONCURRENT_ELEMENTS*2:] and next_number not in combine_list): # seq.extend(next_number) # count = ((count) % 6) + 1 #this count makes sure we have one set of 6 # else: # seq.extend(next_number) # left_over = np.argwhere(number_count == np.argmin(number_count)) # selectMax = len(left_over) - 1 # next_number = left_over[random.randint(0, selectMax)] # self.flash_sequence = seq def find_replacement(self, nextelem, seq): length = CONCURRENT_ELEMENTS if len( self.flash_sequence) > CONCURRENT_ELEMENTS - 1 else len( self.flash_sequence) for i in range(1, length + 1): prev_seq = self.flash_sequence[-i:][0] for j in range(len(prev_seq)): previous_seq_elem = self.flash_sequence[-i:][0][j] prev_seq_without_prevelem = [ x for x in prev_seq if x != previous_seq_elem ] nl_prev = self.get_neighbors(prev_seq_without_prevelem) nl_current = self.get_neighbors(seq) if (nextelem not in prev_seq and #a nextelem not in nl_prev and #b nextelem not in self.flash_sequence[-i - 1:][0] and #c1 left consecutive nextelem not in self.flash_sequence[-i + 1:][ 0] #c2 right consecutive and previous_seq_elem not in seq and #a previous_seq_elem not in nl_current and #b previous_seq_elem not in self.flash_sequence[-1:][0]): #c self.flash_sequence[-i:][0][j] = nextelem seq.append(previous_seq_elem) return seq print("Can't swap..Restarting everything...") create_flash_sequence() def get_neighbors(self, seq): right = [x + 1 for x in seq] left = [x - 1 for x in seq] top = [x - 6 for x in seq] bottom = [x + 6 for x in seq] neighbors = list(chain(right, left, top, bottom)) return neighbors def create_flash_sequence(self): num_rows = self.number_of_rows num_cols = self.number_of_columns total = num_rows * num_cols num_sequence = self.num_sequence li = list(range(total)) while len(self.flash_sequence) < num_sequence: seq = [] failcount = 0 if not li: #if li is exhausted li = list(range(36)) while len(seq) < CONCURRENT_ELEMENTS: nextelem = random.choice(li) if len(seq) > 0: nl = self.get_neighbors(seq) #prevent stuck if failcount > 20: seq = self.find_replacement(nextelem, seq) li.remove(nextelem) failcount = 0 elif nextelem not in nl and nextelem not in seq: if len(self.flash_sequence): if nextelem not in self.flash_sequence[-1:][ 0]: #0 remove the outer list [[]] becomes [] seq.append(nextelem) li.remove(nextelem) else: failcount += 1 else: seq.append(nextelem) li.remove(nextelem) else: failcount += 1 else: #first element, just insert if len(self.flash_sequence): if nextelem not in self.flash_sequence[-1:][0]: seq.append(nextelem) li.remove(nextelem) else: seq.append(nextelem) li.remove(nextelem) self.flash_sequence.append(seq) self.flash_sequence = list(chain(*self.flash_sequence)) def start(self): if not (TEST_UI): self.read_lsl_marker() self.running = 1 letter = self.word[0] image_index = string.ascii_lowercase.index(letter) self.highlight_image(image_index) self.start_btn.configure(state='disabled') self.pause_btn.configure(state='normal') # def pause(self): # self.running = 0 # self.start_btn_text.set('Resume') # self.start_btn.configure(state='normal') # self.pause_btn.configure(state='disabled') # def check_pause(self): # if self.running == 0: # print('Flashing paused at sequence number ' + str(self.sequence_number)) # return def check_sequence_end(self): if self.sequence_number == len( self.flash_sequence ): #stop flashing if all generated sequence number runs out print('All elements had flashed - run out of juice') self.running = 0 self.sequence_number = 0 return def get_marker_result(self): result = self.marker_result() if (result): print("Marker received: ", result[0][0]) receive = result[0][0] else: receive = 0 return receive def output_letter(self, receive, image_index): # receive = [9,10,13] if not receive: if FLASH_CONCURRENT: self.master.after(self.break_duration, self.start_concurrent_flashing) else: self.master.after(self.break_duration, self.start_flashing) elif isinstance(receive, int): if (image_index + 1) == receive: self.output.insert("end", self.pos_to_char(receive), "green") else: self.output.insert("end", self.pos_to_char(receive), "red") self.letter_idx += 1 if self.letter_idx == len(self.word): return letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) self.master.after(self.break_duration, self.highlight_target, image_index) else: if FLASH_CONCURRENT: self.candidate_flash_sequence(receive) self.master.after(self.break_duration, self.start_concurrent_flashing) else: self.master.after(self.break_duration, self.start_flashing) def candidate_flash_sequence(self, candidates): print("got candidate") num_rows = self.number_of_rows num_col = self.number_of_columns current_elements = self.flash_sequence[self.sequence_number:self. sequence_number + CONCURRENT_ELEMENTS] next_elements = self.flash_sequence[self.sequence_number + CONCURRENT_ELEMENTS:self. sequence_number + CONCURRENT_ELEMENTS * 2] # Step 1 check if the candidate is included in the next list # Step 2 check if there is multiple candidate in the next list ~ else just continue count_contain_candidate = 0 for target_candidate in candidates: if target_candidate in next_elements: count_contain_candidate += 1 if count_contain_candidate > 1 or count_contain_candidate == 0: # Step 3 check previous i length of sequence to know lease used candidate where i = candidate length start_check_index = 0 previous_flashes_count = [] if CONCURRENT_ELEMENTS * len(candidates) > self.sequence_number: start_check_index = self.sequence_number - CONCURRENT_ELEMENTS * len( candidates) - 1 for target_candidate in candidates: previous_flashes = self.flash_sequence[start_check_index:self. sequence_number - 1] previous_flashes_count.append([ target_candidate, previous_flashes.count(target_candidate) ]) least_index = np.argmin(previous_flashes_count, axis=0)[1] choosen_candidate = previous_flashes_count[least_index][0] # generate new flash_sequence new_sequence = [choosen_candidate] li = list(range(num_rows * num_col)) print("new_sequence1 :", new_sequence) #generate sequence while len(new_sequence) < 6: nextelem = random.choice(li) print("nextelem :", nextelem) nl = self.get_neighbors(new_sequence) #random until get the flasshes sequence while (nextelem in nl or nextelem in current_elements or nextelem in next_elements): nextelem = random.choice(li) print("nextelem :", nextelem) new_sequence.append(nextelem) print("new_sequence2: ", new_sequence) print("self.sequence_number: ", self.sequence_number) # update flashing sequences print(self.sequence_number) self.flash_sequence = self.flash_sequence[ 0:self.sequence_number + CONCURRENT_ELEMENTS] + new_sequence + self.flash_sequence[ self.sequence_number + CONCURRENT_ELEMENTS:] # Step 4 generate new list where # 1 no duplicate # 2 no multiple candidate # 3 no neighbors # 4 no previous list if possible (maybe all candidate is in the previous) # 5 no next list if possible (maybe all candidate is in the next) # 6 use the least use candidate def start_concurrent_flashing(self): self.check_sequence_end() # self.check_pause() receive = self.get_marker_result() element_to_flash = self.flash_sequence[self.sequence_number:self. sequence_number + CONCURRENT_ELEMENTS] letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) #pushed markers to LSL stream print("Letter: ", image_index, " Element flash: ", [x + 1 for x in element_to_flash]) for e in element_to_flash: self.lsl_output.push_sample([e + 1 ]) # add 1 to prevent 0 in markers self.flash_multiple_elements(element_to_flash) self.output_letter(receive, image_index) self.sequence_number = self.sequence_number + CONCURRENT_ELEMENTS #change flash position def start_flashing(self): self.check_sequence_end() # self.check_pause() receive = self.get_marker_result() element_to_flash = self.flash_sequence[self.sequence_number] letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) #pushed markers to LSL stream print("Letter: ", image_index, " Element flash: ", [element_to_flash + 1]) self.lsl_output.push_sample([element_to_flash + 1 ]) # add 1 to prevent 0 in markers self.flash_single_element(element_to_flash) self.output_letter(receive, image_index) self.sequence_number = self.sequence_number + 1 #change flash position def pos_to_char(self, pos): return chr(pos + 97) def highlight_target(self, image_index): self.show_highlight_letter(self.letter_idx) self.highlight_image(image_index) def change_image(self, label, img): label.configure(image=img) label.image = img def highlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.highlight_letter_images[element_no]) self.master.after(self.delay, self.unhighlight_image, element_no) def unhighlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) if (FLASH_CONCURRENT): self.master.after(self.flash_duration, self.start_concurrent_flashing) else: self.master.after(self.flash_duration, self.start_flashing) def show_highlight_letter(self, pos): fontStyle = tkFont.Font(family="Courier", size=40) fontStyleBold = tkFont.Font(family="Courier bold", size=40) text = Text(root, height=1, font=fontStyle) text.tag_configure("bold", font=fontStyleBold) text.tag_configure("center", justify='center') for i in range(0, len(self.word)): if (i != pos): text.insert("end", self.word[i]) else: text.insert("end", self.word[i], "bold") text.configure(state="disabled", width=10) text.tag_add("center", "1.0", "end") text.grid(row=self.number_of_rows + 1, column=self.number_of_columns - 4) def flash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.flash_image) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.flash_image) self.master.after(self.flash_duration, self.unflash_row_or_col, rc_number) def unflash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) def flash_multiple_elements(self, element_array): for element_no in element_array: self.change_image(self.image_labels[element_no], self.flash_image) self.master.after(self.flash_duration, self.unflash_multiple_elements, element_array) def unflash_multiple_elements(self, element_array): for element_no in element_array: self.change_image(self.image_labels[element_no], self.usable_images[element_no]) def flash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.flash_image) self.master.after(self.flash_duration, self.unflash_single_element, element_no) def unflash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) def marker_result(self): if not (TEST_UI): marker, timestamp = self.inlet_marker.pull_chunk() return marker else: return 0 def read_lsl_marker(self): print("looking for a Markers stream...") marker_streams = resolve_byprop('name', 'ResultMarkerStream') if marker_streams: self.inlet_marker = StreamInlet(marker_streams[0]) marker_time_correction = self.inlet_marker.time_correction() print("Found Markers stream")
"""Example program to show how to read a multi-channel time series from LSL.""" from pylsl import StreamInlet, resolve_stream import time # first resolve an EEG stream on the lab network print("looking for an EEG stream...") # streams = resolve_stream('type', 'EEG') streams = resolve_stream() print(streams[0].source_id()) # print(streams[1].source_id()) # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) i=0 while True: # get a new sample (you can also omit the timestamp part if you're not # interested in it) sample, timestamp = inlet.pull_sample() timeCorr = inlet.time_correction() print(timestamp, '\n',sample, '\n', timeCorr, '\n') # if i==10: # break # break i = i + 1 time.sleep(0.01)
from sklearn import preprocessing import math import stimuli as st import threading import pygame import maze as maze from sklearn.cross_decomposition import CCA import serial import util as ul # first resolve an EEG stream on the lab network print("looking for an EEG stream...") streams = resolve_stream('type', 'EEG') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) Fs = inlet.info().nominal_srate() runing = True def first_buffer(): chunk = read_data() chunk = np.array(chunk).transpose() if len(chunk) == 0: return False, None bufferdata = [] result = False if (any(chunk[11, :])): result = True arr = np.array(chunk) index = np.where(arr > 0)
from pylsl import StreamInlet, resolve_stream import matplotlib.pyplot as plt import matplotlib.animation as anmt import time # first resolve an EEG stream on the lab network print("looking for an EEG stream...") streams = resolve_stream('type', 'EEG') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) counter = 0 fig = plt.figure() ax1 = fig.add_subplot(1, 1, 1) plt.xlabel('Time') plt.ylabel('Micro Volts') plt.title('EEG Channel 1') data = [] time = [] print("pull samples...") while True: # get a new sample (you can also omit the timestamp part if you're not # interested in it) sample = inlet.pull_sample() counter += 1 data.append(sample[0][0]) time.append(counter)
default=[0, 1, 2, 3], help= 'channel number to use. If not specified, all the channels are used') args = parser.parse_args() """CONNECT TO EEG STREAM """ # Search for active LSL stream print('Looking for an EEG stream...') streams = resolve_byprop('type', 'EEG', timeout=2) if len(streams) == 0: raise RuntimeError('Can\'t find EEG stream.') # Set active EEG stream to inlet and apply time correction print("Start acquiring data") inlet = StreamInlet(streams[0], max_chunklen=12) eeg_time_correction = inlet.time_correction() # Get the stream info, description, sampling frequency, number of channels info = inlet.info() description = info.desc() fs = int(info.nominal_srate()) n_channels = info.channel_count() # Get names of all channels ch = description.child('channels').first_child() ch_names = [ch.child_value('label')] for i in range(1, n_channels): ch = ch.next_sibling() ch_names.append(ch.child_value('label'))
default=default_fname, # 命令行参数 文件名称 help="Name of the recording file.") # dejitter timestamps dejitter = False (options, args) = parser.parse_args() print("looking for an EEG stream...") streams = resolve_byprop('type', 'EEG', timeout=2) if len(streams) == 0: raise (RuntimeError, "Cant find EEG stream") print("Start aquiring data") inlet = StreamInlet(streams[0], max_chunklen=12) eeg_time_correction = inlet.time_correction( ) # Retrieve an estimated time correction offset for the given stream print("looking for a Markers stream...") marker_streams = resolve_byprop('type', 'Markers', timeout=2) if marker_streams: inlet_marker = StreamInlet(marker_streams[0]) marker_time_correction = inlet_marker.time_correction() else: inlet_marker = False print("Cant find Markers stream") info = inlet.info() description = info.desc()
def __init__(self, incoming, outgoing, sparseOutput=None, config={}, device_source='Muse', software_source='muselsl', debug_outputs=True, verbose=False): self.incoming = incoming self.outgoing = outgoing self.sparseOutput = sparseOutput self.device_source = device_source self.software_source = software_source self.debug_outputs = debug_outputs self.verbose = verbose self.eeg_chunk_length = 12 # 1. Initialize inlet if isinstance(self.incoming, str): # LSL inlet print('Looking for the {} stream...'.format(incoming)) self._stream = resolve_byprop('type', incoming, timeout=2) if len(self._stream) == 0: raise (RuntimeError('Can\'t find {} stream.'.format(incoming))) print('Aquiring data from the \'{}\' stream...'.format(incoming)) self._inlet = StreamInlet(self._stream[0], max_chunklen=self.eeg_chunk_length) self._info_in = self._inlet.info() else: # OSC port if USE_LIBLO: self._osc_server = ServerThread(incoming['port']) print('OSC server initialized at port {}.'.format( incoming['port'])) else: self._dispatcher = dispatcher.Dispatcher() print('python-osc dispatcher initialized.') # 2. Initialize outlets if not isinstance(self.outgoing, tuple): self.outgoing = [self.outgoing] self._output_threads = [] for out in self.outgoing: if isinstance(out, str): # LSL outlet raise NotImplementedError elif isinstance(out, dict): # OSC port if USE_LIBLO: self._output_threads.append( Address(out['address'], out['port'])) else: raise NotImplementedError # self._client = udp_client.SimpleUDPClient( # outgoing['address'], outgoing['port']) print('OSC client initialized at {}:{}.'.format( out['address'], out['port'])) if (self.sparseOutput != None): if not isinstance(self.sparseOutput, tuple): self.sparseOutput = [self.sparseOutput] self._sparseOutput_threads = [] for out in self.sparseOutput: if isinstance(out, str): # LSL outlet raise NotImplementedError elif isinstance(out, dict): # OSC port if USE_LIBLO: self._sparseOutput_threads.append( Address(out['address'], out['port'])) else: raise NotImplementedError print('OSC sparse output client initialized at {}:{}.'. format(out['address'], out['port'])) # 3. Initialize internal buffers and variables self._init_processing(config)
# -*- coding: utf-8 -*- """Example program to demonstrate how to read a multi-channel time-series from LSL in a chunk-by-chunk manner (which is more efficient).""" from pylsl import StreamInlet, resolve_stream """EEG""" # first resolve an EEG stream on the lab network print("looking for an EEG stream...") streams = resolve_stream('type', 'EEG') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) """MARKERS""" # first resolve a marker stream on the lab network print("looking for a marker stream...") streamsMARKER = resolve_stream('type', 'Markers') # create a new inlet to read from the stream inletMARKER = StreamInlet(streamsMARKER[0]) while True: # get a new sample (you can also omit the timestamp part if you're not # interested in it) chunk, timestamps = inlet.pull_chunk() if timestamps: print('**********************INLET_DATA*******************') print('SAMPLE_STAMP:' + str(timestamps)) print('SAMPLE:' + str(chunk)) marker, timestampM = inletMARKER.pull_sample() if timestampM:
class FFTServer(): """Server to receive EEG data and stream classifier outputs. Attributes: See args. Args: incoming (str or dict): incoming data stream. If provided as a string, look for an LSL stream with the corresponding type. If provided as dict with fields `address` and `port`, open an OSC port at that address and port. outgoing (str or dict): outgoing data stream. If provided as a string, stream to an LSL stream with the corresponding type. If provided as dict with fields `address` and `port`, stream to an OSC port at that address and port. Keyword Args: config (dict): dictionary containing the configuration and preprocessing parameters, e.g. (these are the default values): config = {'fs': 256., 'n_channels': 4, 'raw_buffer_len': 3 * fs, 'filt_buffer_len': 3 * fs, 'window_len': fs, 'step': int(fs / 10), 'filter': ([1], [1]), 'psd_window_len': 256., 'psd_buffer_len': 10} device_source (str): Device from which the data is coming from. 'muse' or 'vive' streaming_source (str): Software source of the data stream: 'muselsl' 'musedirect' 'musemonitor' debug_outputs (bool): if True, send debug outputs (not used by VR experience) verbose (bool): if True, print status whenever new data is received or sent. """ def __init__(self, incoming, outgoing, sparseOutput=None, config={}, device_source='Muse', software_source='muselsl', debug_outputs=True, verbose=False): self.incoming = incoming self.outgoing = outgoing self.sparseOutput = sparseOutput self.device_source = device_source self.software_source = software_source self.debug_outputs = debug_outputs self.verbose = verbose self.eeg_chunk_length = 12 # 1. Initialize inlet if isinstance(self.incoming, str): # LSL inlet print('Looking for the {} stream...'.format(incoming)) self._stream = resolve_byprop('type', incoming, timeout=2) if len(self._stream) == 0: raise (RuntimeError('Can\'t find {} stream.'.format(incoming))) print('Aquiring data from the \'{}\' stream...'.format(incoming)) self._inlet = StreamInlet(self._stream[0], max_chunklen=self.eeg_chunk_length) self._info_in = self._inlet.info() else: # OSC port if USE_LIBLO: self._osc_server = ServerThread(incoming['port']) print('OSC server initialized at port {}.'.format( incoming['port'])) else: self._dispatcher = dispatcher.Dispatcher() print('python-osc dispatcher initialized.') # 2. Initialize outlets if not isinstance(self.outgoing, tuple): self.outgoing = [self.outgoing] self._output_threads = [] for out in self.outgoing: if isinstance(out, str): # LSL outlet raise NotImplementedError elif isinstance(out, dict): # OSC port if USE_LIBLO: self._output_threads.append( Address(out['address'], out['port'])) else: raise NotImplementedError # self._client = udp_client.SimpleUDPClient( # outgoing['address'], outgoing['port']) print('OSC client initialized at {}:{}.'.format( out['address'], out['port'])) if (self.sparseOutput != None): if not isinstance(self.sparseOutput, tuple): self.sparseOutput = [self.sparseOutput] self._sparseOutput_threads = [] for out in self.sparseOutput: if isinstance(out, str): # LSL outlet raise NotImplementedError elif isinstance(out, dict): # OSC port if USE_LIBLO: self._sparseOutput_threads.append( Address(out['address'], out['port'])) else: raise NotImplementedError print('OSC sparse output client initialized at {}:{}.'. format(out['address'], out['port'])) # 3. Initialize internal buffers and variables self._init_processing(config) def _init_processing(self, config): """Initialize internal buffers and variables for EEG processing. Args: config (dict): dictionary containing various parameters. See DEFAULT_CONFIG below for default values. fs (float): sampling frequency n_channels (int): number of channels raw_buffer_len (int): raw data buffer length filt_buffer_len (int): filtered data buffer length window_len (int): processing window length step (int): number of samples between two consecutive windows to process filter (tuple or dict): filtering parameters. If provided as a tuple, the first and second elements should be the `b` and `a` coefficients of a filter. If provided as a dictionary, the fields `order`, `l_freq`, `h_freq` and `method` are required; the function pre.get_filter_coeff() will then be used to compute the coefficients. If None, don't use a filter (windows will be extracted from the raw buffer). psd_window_len (int): length of the window to use for PSD psd_buffer_len (int): PSD buffer length """ DEFAULT_CONFIG = { 'fs': 256., 'n_channels': 5, 'raw_buffer_len': 3 * 256, 'filt_buffer_len': 3 * 256, 'window_len': 256, 'step': int(256 / 10), 'filter': ([1], [1]), 'filter_bank': {}, 'psd_window_len': 256., 'psd_buffer_len': 10 } config = {**DEFAULT_CONFIG, **config} self.fs = config['fs'] self.n_channels = config['n_channels'] # Initialize EEG channel remapping parameters self.eeg_ch_remap = None if self.device_source.lower() == 'vive': self.eeg_ch_remap = [3, 1, 2, 3, 4] self.n_channels = 5 if self.software_source.lower() == 'musedirect': self.eeg_ch_remap[-1] = 5 self.n_channels = 5 if self.device_source.lower() == 'leroy': self.eeg_ch_remap = None self.n_channels = 4 if self.device_source.lower() == 'muse': self.eeg_ch_remap = None self.n_channels = 4 if self.device_source.lower() == 'vivehr': self.eeg_ch_remap = [3, 1, 2, 3, 0] self.n_channels = 5 # Initialize the EEG buffers raw_buffer_len = int(config['raw_buffer_len']) filt_buffer_len = int(config['filt_buffer_len']) self.eeg_buffer = ut.NanBuffer(raw_buffer_len, self.n_channels) self.filt_eeg_buffer = ut.CircularBuffer(filt_buffer_len, self.n_channels) self.hpfilt_eeg_buffer = ut.CircularBuffer(filt_buffer_len, self.n_channels) self.smooth_eeg_buffer = ut.CircularBuffer(filt_buffer_len, self.n_channels) self.eyeH_buffer = ut.CircularBuffer(100, 1) # Initialize the EEG filter if config['filter']: if isinstance(config['filter'], tuple): b = config['filter'][0] a = config['filter'][1] elif isinstance(config['filter'], dict): b, a = ut.get_filter_coeff(self.fs, **config['filter']) zi = np.tile(signal.lfilter_zi(b, a), (self.n_channels, 1)).T self.bandpass_filt = {'b': b, 'a': a, 'zi': zi} if config['hpfilter']: b = config['hpfilter'][0] a = config['hpfilter'][1] zi = np.tile(signal.lfilter_zi(b, a), (self.n_channels, 1)).T self.hp_filt = {'b': b, 'a': a, 'zi': zi} if config['lpfilter']: b = config['lpfilter'][0] a = config['lpfilter'][1] zi = np.tile(signal.lfilter_zi(b, a), (self.n_channels, 1)).T self.lp_filt = {'b': b, 'a': a, 'zi': zi} # Initialize the filter bank if config['filter_bank']: self.filter_bank = {} for name, coeff in config['filter_bank'].items(): zi = np.tile(signal.lfilter_zi(coeff[0], coeff[1]), (self.n_channels, 1)).T self.filter_bank[name] = { 'b': coeff[0], 'a': coeff[1], 'zi': zi } # Initialize processing parameters self.window_len = int(config['window_len']) self.step = int(config['step']) # Initialize processing buffers psd_buffer_len = int(config['psd_buffer_len']) self.psd_buffer = ut.CircularBuffer(psd_buffer_len, 129, self.n_channels) # Initialize scoring histograms decayRate = 0.997 self.hists = { 'delta': ut.Histogram(1000, self.n_channels, bounds=(0, 50), min_count=80, decay=decayRate), 'theta': ut.Histogram(1000, self.n_channels, bounds=(0, 30), min_count=80, decay=decayRate), 'alpha': ut.Histogram(1000, self.n_channels, bounds=(0, 20), min_count=80, decay=decayRate), 'beta': ut.Histogram(1000, self.n_channels, bounds=(0, 10), min_count=80, decay=decayRate), 'gamma': ut.Histogram(1000, self.n_channels, bounds=(0, 10), min_count=80, decay=decayRate) } self.eyeH_hist = ut.Histogram(500, 1, bounds=(0, 10000), min_count=80, decay=decayRate) self.emg_hist = ut.Histogram(500, 1, bounds=(0, 10), min_count=80, decay=decayRate) self.blinkwait = 0 self.blink = 0 self.firstWindowProc = True self.band_names = 0 self.band_powers = 0 self.ratio_powers = 0 self.ratio_names = 0 # Used for calm score self.slow_calm_score = 0 self.slow_alpha_score = 0 self.eye_mov_percent_buffer = ut.CircularBuffer(256, 1) self.slow_calm_score_buffer = ut.CircularBuffer(512, 1) self.increments_buffer = ut.CircularBuffer(512, 1) self.low_freq_chs_buffer = ut.CircularBuffer(150, 2) self.low_freq_chs_std = 1 ###################################################################### # BODY Motion Processing, Accelerometer, Gyro raw_buffer_len = 150 filt_buffer_len = 150 self.acc_window_len = 50 self.acc_buffer = ut.NanBuffer(raw_buffer_len, 3) self.filt0_buffer = ut.CircularBuffer(filt_buffer_len, 3) self.heart_buffer = ut.CircularBuffer(150, 1) self.breath_buffer = ut.CircularBuffer(500, 1) # Initialize the Body Filters if config['filter0']: b = config['filter0'][0] a = config['filter0'][1] zi = np.tile(signal.lfilter_zi(b, a), (3, 1)).T self.filter0 = {'b': b, 'a': a, 'zi': zi} if config['filter1']: b = config['filter1'][0] a = config['filter1'][1] zi = np.tile(signal.lfilter_zi(b, a), (3, 1)).T self.filter1 = {'b': b, 'a': a, 'zi': zi} if config['filter2']: b = config['filter2'][0] a = config['filter2'][1] zi = signal.lfilter_zi(b, a) self.filter2 = {'b': b, 'a': a, 'zi': zi} if config['filter3']: b = config['filter3'][0] a = config['filter3'][1] zi = np.tile(signal.lfilter_zi(b, a), (3, 1)).T self.filter3 = {'b': b, 'a': a, 'zi': zi} if config['filter4']: b = config['filter4'][0] a = config['filter4'][1] zi = signal.lfilter_zi(b, a) self.filter4 = {'b': b, 'a': a, 'zi': zi} if config['filter5']: b = config['filter5'][0] a = config['filter5'][1] zi = signal.lfilter_zi(b, a) self.filter5 = {'b': b, 'a': a, 'zi': zi} if config['filter6']: b = config['filter6'][0] a = config['filter6'][1] zi = signal.lfilter_zi(b, a) self.filter6 = {'b': b, 'a': a, 'zi': zi} if config['filter7']: b = config['filter7'][0] a = config['filter7'][1] zi = signal.lfilter_zi(b, a) self.filter7 = {'b': b, 'a': a, 'zi': zi} def _update_eeg_liblo_osc(self, path, args): """Collect new EEG data point(s) from pyliblo OSC and process. Args: path (str): OSC path listened to args (list): received values """ if self.verbose: print('Receiving OSC packet!') sample = np.array(args).reshape(1, -1) self._process_eeg(sample[:, :self.n_channels], 0) def _update_eeg_python_osc(self, unused_addr, args, *chs): """Collect new EEG data point(s) from python-osc and process. Args: path (str): OSC path listened to args (list): received values """ if self.verbose: print('Receiving OSC packet!') sample = np.array(chs).reshape(1, -1) self._process_eeg(sample[:, :self.n_channels], 0) def _update_acc_liblo_osc(self, path, args): if self.verbose: print('Receiving ACC packet!') sample = np.array(args).reshape(1, -1) self._process_acc(sample[:, :3], 0) def _update_gyro_liblo_osc(self, path, args): if self.verbose: print('Receiving GYRO packet!') sample = np.array(args).reshape(1, -1) self._process_gyro(sample[:, :3], 0) def _process_eeg(self, samples, timestamp): """Process EEG. Process EEG. Includes buffering, filtering, windowing and pipeline. Args: samples (numpy.ndarray): new EEG samples to process timestamp (float): timestamp Returns: output (scalar): output of the pipeline """ # Re-map if self.eeg_ch_remap: samples = samples[:, self.eeg_ch_remap] self.eeg_buffer.update(samples) self._send_outputs(samples, timestamp, 'raw_eeg') # Apply filtes filt_samples = samples if config['filter']: filt_samples, self.bandpass_filt['zi'] = signal.lfilter( self.bandpass_filt['b'], self.bandpass_filt['a'], samples, axis=0, zi=self.bandpass_filt['zi']) # self._send_filtered_eeg(filt_samples, timestamp) self.filt_eeg_buffer.update(filt_samples) if config['hpfilter']: filt_samples, self.hp_filt['zi'] = signal.lfilter( self.hp_filt['b'], self.hp_filt['a'], filt_samples, axis=0, zi=self.hp_filt['zi']) self.hpfilt_eeg_buffer.update(filt_samples) if config['lpfilter']: smooth_eeg_samples, self.lp_filt['zi'] = signal.lfilter( self.lp_filt['b'], self.lp_filt['a'], filt_samples, axis=0, zi=self.lp_filt['zi']) if self.debug_outputs: self._send_output_vec(smooth_eeg_samples, timestamp, 'smooth_eeg') else: smooth_eeg_samples = filt_samples self.smooth_eeg_buffer.update(smooth_eeg_samples) if config['filter_bank']: filter_bank_samples = {} for name, filt_dict in self.filter_bank.items(): filter_bank_samples[name], self.filter_bank[name]['zi'] = \ signal.lfilter(filt_dict['b'], filt_dict['a'], filt_samples, axis=0, zi=self.filter_bank[name]['zi']) low_freq_chs = filter_bank_samples['delta'][0, [ 0, 2 ]] #+ filter_bank_samples['theta'][0, [0, 1]] window = self.smooth_eeg_buffer.extract(self.window_len) eegEarWindow = window[:, 3] #data from right ear Channel #eye movement computed from the difference between two frontal channels eyewindow = self.smooth_eeg_buffer.extract(200) eegFLWindow = eyewindow[:, 1] eegFRWindow = eyewindow[:, 2] # norm_diff_eyes = eegFLWindow[-1] - eegFRWindow[-1]*np.nanstd(eegFLWindow, axis=0)/np.nanstd(eegFRWindow, axis=0) # eyeH = np.reshape([np.square(norm_diff_eyes)], (1, 1)) #find blinks in the left eegEarWindow blinkVal = ut.blink_template_match(eegEarWindow) if (blinkVal > 100000 and self.blink == 0): self.blink = 50 self.blinkwait = 350 else: if (self.blinkwait > 0): self.blinkwait -= 1 if (self.blink > 0): self.blink -= 1 # LONGER-TERM CALM SCORE based on Saccadic Eye Movement eye_mov_percent = np.reshape( np.percentile(eegFLWindow - eegFRWindow, 90), (1, 1)) self.eye_mov_percent_buffer.update(eye_mov_percent) remap_eye_mov_percent = ut.sigmoid( self.eye_mov_percent_buffer.extract().mean(), 0.5, -10, 0) max_value = 1 incr_decr = remap_eye_mov_percent < 0.2 inc = self.increments_buffer.extract().mean() dpoints_per_second = 0.0005 if incr_decr: self.slow_calm_score += dpoints_per_second * inc # 1/max([max_value - self.slow_calm_score, 1]) else: self.slow_calm_score -= dpoints_per_second * inc * 4 #0.7 # (self.slow_calm_score)/1280 self.increments_buffer.update(np.reshape(incr_decr, (1, 1))) if self.slow_calm_score > max_value: self.slow_calm_score = max_value elif self.slow_calm_score < 0: self.slow_calm_score = 0 self.slow_calm_score_buffer.update( np.reshape(self.slow_calm_score, (1, 1))) # Send outputs at a reduced sampling rate if self.smooth_eeg_buffer.pts % 3 == 0: self._send_output_vec(smooth_eeg_samples, timestamp, 'muse/eeg') if (self.blink > 0): self._send_output(np.array([[1]]), timestamp, 'blink') else: self._send_output(np.array([[0]]), timestamp, 'blink') self._send_output(blinkVal / 300000, timestamp, 'blinkVal') self._send_output(remap_eye_mov_percent, timestamp, 'saccad') self._send_output( np.reshape(self.slow_calm_score_buffer.extract().mean(), (1, 1)), timestamp, 'calm') # slow_calm_score self._send_output(low_freq_chs / self.low_freq_chs_std + 0.5, timestamp, 'low_freq_chs') # process and send output at every step. usually about every 1/10s if self.eeg_buffer.pts > self.step: self.eeg_buffer.pts = 0 # Get filtered EEG window if config['lpfilter']: window = self.smooth_eeg_buffer.extract(self.window_len) else: window = self.eeg_buffer.extract(self.window_len) psd_raw_buffer = self.eeg_buffer.extract(self.window_len) # Get average PSD psd, f = ut.fft_continuous(psd_raw_buffer, n=int(self.fs), psd=True, log='psd', fs=self.fs, window='hamming') self.psd_buffer.update(np.expand_dims(psd, axis=0)) mean_psd = np.nanmean(self.psd_buffer.extract(), axis=0) # find variance of eegWindow for Bad Signal detact eegVar = np.nanvar(window, axis=0) self._send_output_vec(eegVar.reshape(1, self.n_channels), timestamp, 'hsi') if (self.sparseOutput != None): #send channel varience for signal quality indication at source Raspberry Pi #send(Address('10.0.0.14','1234'), "/hsi", eegVar[0],eegVar[1],eegVar[2],eegVar[3]) self._send_sparseOutput_vec(eegVar.reshape(1, self.n_channels), timestamp, 'hsi') # Get band powers and ratios bandPowers, bandNames = ut.compute_band_powers(mean_psd, f, relative=False) ratioPowers, ratioNames = ut.compute_band_ratios(bandPowers) if (self.firstWindowProc): self.band_powers = bandPowers self.band_names = bandNames self.ratio_powers = ratioPowers self.ratio_names = ratioNames self.scores = np.zeros((len(self.band_names), self.n_channels)) self.firstWindowProc = False if (eegVar.mean() < 300 and self.blinkwait == 0): #threshold for good data for i, (name, hist) in enumerate(self.hists.items()): self.band_powers = bandPowers self.ratio_powers = ratioPowers #send good data indicator based on mean eegWindow variance and blinkwait self._send_output(np.array([[1]]), timestamp, 'goodData') #good data else: self._send_output(np.array([[0]]), timestamp, 'goodData') #good data self._send_outputs(self.band_powers, timestamp, 'bands') self._send_outputs(self.ratio_powers, timestamp, 'ratios') mask = ((f >= 30) & (f < 50)) self.low_freq_chs_buffer.update(np.reshape(low_freq_chs, (1, -1))) self.low_freq_chs_std = self.low_freq_chs_buffer.extract().std( axis=0) emg_power = np.mean(mean_psd[mask, 0], axis=0) #HF power of right ear self._send_output(np.array([np.sqrt(emg_power) / 2]), timestamp, 'emg') def _process_acc(self, samples, timestamp): self._send_output_vec(samples, 0, 'muse/acc') self.acc_buffer.update(samples) window = self.acc_buffer.extract(self.acc_window_len) timestamps = np.linspace(0, 1 / 50 * self.acc_window_len, self.acc_window_len) new_fs = 250 timestamps_upsampled = np.arange(timestamps[0], timestamps[-1], 1 / new_fs) f = interpolate.interp1d(timestamps, window, kind='cubic', axis=0, fill_value=np.nan, assume_sorted=True) window_upsampled = f(timestamps_upsampled) for t in range(timestamps_upsampled.size - 5, timestamps_upsampled.size): if self.debug_outputs: self._send_output(window_upsampled[t], 0, 'upsamp') upsample = np.array(window_upsampled[t]).reshape(1, 3) filt_samples, self.filter0['zi'] = signal.lfilter( self.filter0['b'], self.filter0['a'], upsample, axis=0, zi=self.filter0['zi']) self.filt0_buffer.update(filt_samples) if self.debug_outputs: self._send_outputs(filt_samples, 0, 'filter0') filt_samples, self.filter1['zi'] = signal.lfilter( self.filter1['b'], self.filter1['a'], filt_samples, axis=0, zi=self.filter1['zi']) if self.debug_outputs: self._send_outputs(filt_samples, 0, 'filter1') filt_samples = np.sqrt(np.sum(filt_samples**2, axis=1)) if self.debug_outputs: self._send_output(filt_samples, 0, 'filter1L2') heart_samples, self.filter2['zi'] = signal.lfilter( self.filter2['b'], self.filter2['a'], filt_samples, axis=0, zi=self.filter2['zi']) if self.debug_outputs: self._send_output(heart_samples, 0, 'filter2') breathfilt_samples, self.filter3['zi'] = signal.lfilter( self.filter3['b'], self.filter3['a'], upsample, axis=0, zi=self.filter3['zi']) if self.debug_outputs: self._send_outputs(breathfilt_samples, 0, 'filter3') self.heart_buffer.update(heart_samples.reshape(1, 1)) heartbuf = self.heart_buffer.extract(150) heartbufMin = heartbuf.min() heartbufMax = heartbuf.max() heart = np.reshape( (heartbuf[-1] - heartbufMin) / (heartbufMax - heartbufMin), (1, 1)) self._send_output(heart, 0, 'heart') breathSmooth = breathfilt_samples[0, 2].reshape(1, ) if self.debug_outputs: self._send_output(breathSmooth, 0, 'breathRaw') breathSmooth, self.filter4['zi'] = signal.lfilter( self.filter4['b'], self.filter4['a'], breathSmooth, axis=0, zi=self.filter4['zi']) if self.debug_outputs: self._send_output(breathSmooth, 0, 'breathSmooth') breathNorm, self.filter5['zi'] = signal.lfilter(self.filter5['b'], self.filter5['a'], breathSmooth, axis=0, zi=self.filter5['zi']) if self.debug_outputs: self._send_output(breathNorm, 0, 'breathNorm') breathFast, self.filter6['zi'] = signal.lfilter(self.filter6['b'], self.filter6['a'], breathSmooth, axis=0, zi=self.filter6['zi']) if self.debug_outputs: self._send_output(breathFast, 0, 'breathFast') breathLow, self.filter7['zi'] = signal.lfilter(self.filter7['b'], self.filter7['a'], breathSmooth, axis=0, zi=self.filter7['zi']) if self.debug_outputs: self._send_output(breathLow, 0, 'breathLow') self.breath_buffer.update(breathLow.reshape(1, 1)) breathbuf = self.breath_buffer.extract(1000) breathbufMin = breathbuf.min() breathbufMax = breathbuf.max() breath = np.reshape( (breathbuf[-1] - breathbufMin) / (breathbufMax - breathbufMin), (1, 1)) self._send_output(breath, 0, 'breath') def _process_gyro(self, samples, timestamp): self._send_output_vec(samples, 0, 'muse/gyro') def _send_outputs(self, output, timestamp, name): """Send pipeline outputs through the LSL or OSC stream. Args: output (scalar): output of the pipeline timestamp (float): timestamp """ for out in self._output_threads: if isinstance(out, str): # LSL outlet self._outlet.push_sample([output], timestamp=timestamp) else: # OSC output stream if USE_LIBLO: for c in range(self.n_channels): new_output = [('f', x) for x in output[:, c]] message = Message('/{}{}'.format(name, c), *new_output) #send(out, Bundle(timestamp, message)) send(out, message) else: for c in range(self.n_channels): self._client.send_message('/{}{}'.format(name, c), output[:, c]) if self.verbose: print('Output: {}'.format(output)) def _send_output_vec(self, output, timestamp, name): """Send pipeline outputs through the LSL or OSC stream. Args: output (scalar): output of the pipeline timestamp (float): timestamp """ for out in self._output_threads: if isinstance(out, str): # LSL outlet self._outlet.push_sample([output], timestamp=timestamp) else: # OSC output stream if USE_LIBLO: new_output = [('f', x) for x in output[0, :]] message = Message('/{}'.format(name), *new_output) # send(out, Bundle(timestamp, message)) send(out, message) if self.verbose: print('Output: {}'.format(output)) def _send_sparseOutput_vec(self, output, timestamp, name): """Send pipeline outputs through the LSL or OSC stream. Args: output (scalar): output of the pipeline timestamp (float): timestamp """ for out in self._sparseOutput_threads: if isinstance(out, str): # LSL outlet self._outlet.push_sample([output], timestamp=timestamp) else: # OSC output stream if USE_LIBLO: new_output = [('f', x) for x in output[0, :]] message = Message('/{}'.format(name), *new_output) #send(out, Bundle(timestamp, message)) send(out, message) if self.verbose: print('sparseOutput: {}'.format(output)) def _send_output(self, output, timestamp, name): """Send pipeline outputs through the LSL or OSC stream. NOT PER CHANNEL Args: output (scalar): output of the pipeline timestamp (float): timestamp """ for out in self._output_threads: if isinstance(out, str): # LSL outlet raise NotImplementedError # self._outlet.push_sample([output], timestamp=timestamp) else: # OSC output stream if USE_LIBLO: if (np.array(output).size == 1): new_output = [('f', np.asscalar(output))] message = Message('/{}'.format(name), *new_output) else: new_output = [('f', x) for x in output[:]] message = Message('/{}'.format(name), *new_output) # send(out, Bundle(timestamp, message)) send(out, message) else: raise NotImplementedError # self._client.send_message(}{}'.format(name),output[:]) if self.verbose: print('Output: {}'.format(output)) def _send_sparseOutput(self, output, timestamp, name): for out in self._sparseOutput_threads: if isinstance(out, str): # LSL outlet raise NotImplementedError else: # OSC output stream if USE_LIBLO: if (np.array(output).size == 1): new_output = [('f', np.asscalar(output))] message = Message('/{}'.format(name), *new_output) else: new_output = [('f', x) for x in output[:]] message = Message('/{}'.format(name), *new_output) #send(out, Bundle(timestamp, message)) send(out, message) else: raise NotImplementedError if self.verbose: print('spareOutput: {}'.format(output)) def start(self): """Start receiving and processing EEG data. """ self.started = True if isinstance(self.incoming, str): # LSL inlet self.eeg_thread = Thread(target=self._update_eeg_lsl) self.eeg_thread.daemon = True self.eeg_thread.start() else: # OSC input stream if USE_LIBLO: self._osc_server.add_method('/muse/eeg', None, self._update_eeg_liblo_osc) self._osc_server.add_method('/muse/acc', None, self._update_acc_liblo_osc) self._osc_server.add_method('/muse/gyro', None, self._update_gyro_liblo_osc) self._osc_server.start() else: self._dispatcher.map('Person2/eeg', self._update_eeg_python_osc, 'EEG') self._osc_server = osc_server.ThreadingOSCUDPServer( ('127.0.0.1', self.incoming['port']), self._dispatcher) print('OSC server initialized at port {}.'.format( self.incoming['port'])) self._server_thread = Thread( target=self._osc_server.serve_forever) self._server_thread.start() def stop(self): """ """ self.started = False if isinstance(self.incoming, dict): if not USE_LIBLO: self._server_thread.shutdown()
default=default_fname, help="Name of the recording file.") # dejitter timestamps dejitter = True (options, args) = parser.parse_args() print("looking for an EEG stream...") streams = resolve_byprop('type', 'EEG', timeout=2) if len(streams) == 0: raise (RuntimeError, "Cant find EEG stream") print("Start aquiring data") inlet = StreamInlet(streams[0], max_chunklen=12) # eeg_time_correction = inlet.time_correction() print("looking for a Markers stream...") marker_streams = resolve_byprop('type', 'Markers', timeout=2) if marker_streams: inlet_marker = StreamInlet(marker_streams[0]) # marker_time_correction = inlet_marker.time_correction() else: inlet_marker = False print("Cant find Markers stream") print("after marker") info = inlet.info() description = info.desc()
"""Code modified from the example program to show how to read a multi-channel time series from LSL at https://github.com/OpenBCI/OpenBCI_GUI/blob/master/Networking-Test-Kit/LSL/lslStreamTest.py.""" from pylsl import StreamInlet, resolve_stream import time import serial # set up Arduino serial port - replace with the one you are using ser = serial.Serial('COM4', 9600) # resolve an EMG stream on the lab network and notify the user print("Looking for an EMG stream...") streams = resolve_stream('type', 'EEG') inlet = StreamInlet(streams[0]) #inlet_ch2 = StreamInlet(streams[1]) print("EMG stream found!") # initialize time threshold and variables for storing time time_thres = 500 prev_time = 0 flex_thres = 0.7 while True: samples, timestamp = inlet.pull_sample( ) # get EMG data sample and its timestamp curr_time = int(round(time.time() * 1000)) # get current time in milliseconds if ((samples[0] >= flex_thres) & (curr_time - time_thres > prev_time) ): # if an EMG spike is detected from the cheek muscles send 'G'
def run(self): streams = resolve_bypred("*", timeout=5) self.streams = [StreamInlet(stream).info() for stream in streams] self.taskFinished.emit()
#predefined variables last_report = 0 #define variable for printing of ET data #participant id variable print("This script looks for LSL Eye Tracking data...") #participant_id = raw_input('Enter participant ID:') participant_id = sys.argv[1] #takes the name from script argument filename = str(participant_id) + '_ETdata.csv' # first resolve an EEG stream on the lab network print("looking for EyeTracking LSL stream...") streams = resolve_stream('name', 'Tobii') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) print("...found. Pulling data:") #create CSV file and write received data to it os.chdir(os.getcwd() + "\data") #change cd to data folder with open( filename, 'wb' ) as csvfile: #mode needs to be "wb" in python 2 to remove blank rows csvwriter = csv.writer(csvfile) #main loop while True: ETdata, timestamp = inlet.pull_sample() ETdata.append( timestamp * 1000) #change timestamp to ms format and add to ET data list
def __init__(self, stream, fig, axes, window, scale, dejitter=True): """Init""" self.stream = stream self.window = window self.scale = scale self.dejitter = dejitter self.inlet = StreamInlet(stream, max_chunklen=buf) self.filt = True info = self.inlet.info() description = info.desc() self.sfreq = info.nominal_srate() self.n_samples = int(self.sfreq * self.window) self.n_chan = info.channel_count() ch = description.child('channels').first_child() ch_names = [ch.child_value('label')] for i in range(self.n_chan): ch = ch.next_sibling() ch_names.append(ch.child_value('label')) self.ch_names = ch_names fig.canvas.mpl_connect('key_press_event', self.OnKeypress) fig.canvas.mpl_connect('button_press_event', self.onclick) self.fig = fig self.axes = axes sns.despine(left=True) self.data = np.zeros((self.n_samples, self.n_chan)) self.times = np.arange(-self.window, 0, 1. / self.sfreq) impedances = np.std(self.data, axis=0) lines = [] for ii in range(self.n_chan): line, = axes.plot(self.times[::subsample], self.data[::subsample, ii] - ii, lw=1) lines.append(line) self.lines = lines axes.set_ylim(-self.n_chan + 0.5, 0.5) ticks = np.arange(0, -self.n_chan, -1) axes.set_xlabel('Time (s)') axes.xaxis.grid(False) axes.set_yticks(ticks) ticks_labels = [ '%s - %.1f' % (ch_names[ii], impedances[ii]) for ii in range(self.n_chan) ] axes.set_yticklabels(ticks_labels) self.display_every = int(0.2 / (12 / self.sfreq)) # self.bf, self.af = butter(4, np.array([1, 40])/(self.sfreq/2.), # 'bandpass') self.bf = firwin(32, np.array([1, 40]) / (self.sfreq / 2.), width=0.05, pass_zero=False) self.af = [1.0] zi = lfilter_zi(self.bf, self.af) self.filt_state = np.tile(zi, (self.n_chan, 1)).transpose() self.data_f = np.zeros((self.n_samples, self.n_chan))
from bokeh.io import show, output_file from bokeh.models import ColumnDataSource, FactorRange from bokeh.plotting import figure, curdoc from bokeh.transform import factor_cmap from bokeh.palettes import Spectral6 import random from pylsl import StreamInlet, resolve_stream, StreamInfo # first resolve an EEG stream on the lab network print("looking for an FreqBand stream...") streams = resolve_stream('type', 'FreqBand') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) info = inlet.info() print(info.as_xml()) print("The channel labels are as follows:") ch = info.desc().child("channels").child("channel") for k in range(info.channel_count()): print(" " + ch.child_value("label")) ch = ch.next_sibling() #electrodes = ['F1', 'F2', 'O1', 'O2'] electrodes = [ "AF3", "F7", "F3", "FC5", "T7", "P7", "O1", "O2", "P8", "T8", "FC6", "F4", "F8", "AF4" ] bands = ['Theta', 'Alpha', 'LowBeta', 'HighBeta', 'Gamma'] data = {
"""Example program to show how to read a marker time series from LSL.""" import sys sys.path.append('./pylsl') # help python find pylsl relative to this example program from pylsl import StreamInlet, resolve_stream # first resolve an EEG stream on the lab network print("looking for an BatteryStatus stream...") streams = resolve_stream('name', 'BatteryStatus') streamsFound = len(streams) if (streamsFound > 0): print 'found ' + str(streamsFound) else: print 'found none' # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) hostName = inlet.info().hostname() while True: sample, timestamp = inlet.pull_sample() if(sample): print(str(timestamp) + ' Battery Status of ' + hostName + ' ' + str(sample[0]) +'%')
# Index of the channel(s) (electrodes) to be used # 0 = left ear, 1 = left forehead, 2 = right forehead, 3 = right ear INDEX_CHANNEL = [0] if __name__ == "__main__": """ 1. CONNECT TO EEG STREAM """ # Search for active LSL streams print('Looking for an EEG stream...') streams = resolve_byprop('type', 'EEG', timeout=2) if len(streams) == 0: raise RuntimeError('Can\'t find EEG stream.') # Set active EEG stream to inlet and apply time correction print("Start acquiring data") inlet = StreamInlet(streams[0], max_chunklen=12) eeg_time_correction = inlet.time_correction() # Get the stream info and description info = inlet.info() description = info.desc() # Get the sampling frequency # This is an important value that represents how many EEG data points are # collected in a second. This influences our frequency band calculation. # for the Muse 2016, this should always be 256 fs = int(info.nominal_srate()) """ 2. INITIALIZE BUFFERS """ # Initialize raw EEG data buffer eeg_buffer = np.zeros((int(fs * BUFFER_LENGTH), 1))
import sys; sys.path.append('..') # help python find pylsl relative to this example program from pylsl import StreamInlet, resolve_stream # first resolve an EEG stream on the lab network print("looking for an EEG stream...") streams = resolve_stream('type','EEG') # create a new inlet to read from the stream inlet = StreamInlet(streams[0]) while True: # get a new sample (you can also omit the timestamp part if you're not interested in it) sample,timestamp = inlet.pull_sample() print(timestamp, sample)
class Renderer(app.Canvas): def __init__(self): app.Canvas.__init__(self, title='Use your wheel to zoom!', keys='interactive') # first resolve an EEG stream on the lab network print("looking for an EEG stream...") streams = resolve_stream('name', 'RandomSpehricalData') streamInfo = streams[0] # create a new inlet to read from the stream self.inlet = StreamInlet(streamInfo) # Number of cols and rows in the table. self.nrows = streamInfo.channel_count() n = streamInfo.nominal_srate() ncols = 1 # Number of signals. m = self.nrows*ncols # Various signal amplitudes. amplitudes = .1 + .2 * np.random.rand(m, 1).astype(np.float32) # Generate the signals as a (m, n) array. self.y = amplitudes * np.random.randn(m, n).astype(np.float32) color = np.repeat(np.random.uniform(size=(m, 3), low=.5, high=.9), n, axis=0).astype(np.float32) # Signal 2D index of each vertex (row and col) and x-index (sample index # within each signal). index = np.c_[np.repeat(np.repeat(np.arange(ncols), self.nrows), n), np.repeat(np.tile(np.arange(self.nrows), ncols), n), np.tile(np.arange(n), m)].astype(np.float32) self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) self.program['a_position'] = self.y.reshape(-1, 1) self.program['a_color'] = color self.program['a_index'] = index self.program['u_scale'] = (1., 1.) self.program['u_size'] = (self.nrows, ncols) self.program['u_n'] = n gloo.set_viewport(0, 0, *self.physical_size) self._timer = app.Timer('auto', connect=self.on_timer, start=True) gloo.set_state(clear_color='black', blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self.sampleFromLSL = None self.show() def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) def on_mouse_wheel(self, event): dx = np.sign(event.delta[1]) * .05 scale_x, scale_y = self.program['u_scale'] scale_x_new, scale_y_new = (scale_x * math.exp(2.5*dx), scale_y * math.exp(0.0*dx)) self.program['u_scale'] = (max(1, scale_x_new), max(1, scale_y_new)) self.update() def on_timer(self, event): """Add some data at the end of each signal (real-time signals).""" k = 0 # need to become the count of samples available on this timer call sampleSet = None sample, timestamp = self.inlet.pull_sample(0.0) sample = np.array([sample]) while sample.any(): k = k + 1 sample, timestamp = self.inlet.pull_sample(0.0) sample = np.array([sample]) sampleSet = np.c_[ sampleSet, sample ] if k > 0: self.y[:, :-k] = self.y[:, k:] #y[:, -k:] = amplitudes * np.random.randn(m, k) self.y[:, -k:] = sampleSet self.program['a_position'].set_data(self.y.ravel().astype(np.float32)) self.update() def on_draw(self, event): gloo.clear() self.program.draw('line_strip')