From b8b1922d68b6fda131489a276322b5273a3b74d8 Mon Sep 17 00:00:00 2001 From: Alexandre Barachant Date: Fri, 27 Jan 2017 14:03:53 +0100 Subject: [PATCH] add examples and stuff --- lsl-record.py | 88 ++++++++++++++++++++++++++++ lsl-viewer.py | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ muse_lsl.py | 40 +++++++++++++ muse_record.py | 34 +++++++++++ 4 files changed, 314 insertions(+) create mode 100755 lsl-record.py create mode 100644 lsl-viewer.py create mode 100644 muse_lsl.py create mode 100644 muse_record.py diff --git a/lsl-record.py b/lsl-record.py new file mode 100755 index 0000000..2ca3c81 --- /dev/null +++ b/lsl-record.py @@ -0,0 +1,88 @@ +"""Test.""" +import numpy as np +import pandas as pd +from time import time, strftime, gmtime +from optparse import OptionParser +from pylsl import StreamInlet, resolve_byprop + +default_fname = ("data_%s.csv" % strftime("%Y-%m-%d-%H:%M:%S", gmtime())) +parser = OptionParser() +parser.add_option("-d", "--duration", + dest="duration", type='int', default=60, + help="duration of the recording in seconds.") +parser.add_option("-f", "--filename", + dest="filename", type='str', default=default_fname, + help="Name of the recording file.") + +(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") + +info = inlet.info() +description = info.desc() + +freq = info.nominal_srate() +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() +print('Start recording at time t=%.3f' % t_init) +while (time() - t_init) < options.duration: + try: + data, timestamp = inlet.pull_chunk(timeout=1.0, + max_samples=12) + 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 + +res = np.concatenate(res, axis=0) +timestamps = np.array(timestamps) +res = np.c_[timestamps, res] +print res.shape +data = pd.DataFrame(data=res, columns=['timestamps'] + ch_names) + +data['Marker'] = 0 +# process markers: +for marker in markers: + # find index of margers + ix = np.argmin(np.abs(marker[1] - timestamps)) + val = timestamps[ix] + data.loc[ix, 'Marker'] = marker[0][0] + + +data.to_csv(options.filename, float_format='%.3f', index=False) + +print('Done !') diff --git a/lsl-viewer.py b/lsl-viewer.py new file mode 100644 index 0000000..60c5048 --- /dev/null +++ b/lsl-viewer.py @@ -0,0 +1,152 @@ +"""Test.""" +import numpy as np +import matplotlib.pyplot as plt +from scipy.signal import butter, filtfilt +from time import time +from pylsl import StreamInlet, resolve_byprop +import seaborn as sns +sns.set(style="whitegrid") + +from optparse import OptionParser + +parser = OptionParser() + +parser.add_option("-w", "--window", + dest="window", type='float', default=5., + help="window lenght to display in seconds.") +parser.add_option("-s", "--scale", + dest="scale", type='float', default=100, + help="scale in uV") +parser.add_option("-r", "--refresh", + dest="refresh", type='float', default=0.2, + help="refresh rate in seconds.") +parser.add_option("-f", "--figure", + dest="figure", type='string', default="15x6", + help="window size.") +parser.add_option("-a", "--avgref", + dest="avgref", action="store_false", default=False, + help="Activate average reference.") + +filt = True +subsample = 2 +buf = 12 + +(options, args) = parser.parse_args() + +figsize = np.int16(options.figure.split('x')) +print figsize + +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=buf) + +info = inlet.info() +description = info.desc() + +freq = info.nominal_srate() +Nchan = info.channel_count() + +ch = description.child('channels').first_child() +ch_names = [ch.child_value('label')] +for i in range(Nchan): + ch = ch.next_sibling() + ch_names.append(ch.child_value('label')) + +picks = range(Nchan) +# create a new inlet to read from the stream + +frs = np.fft.fftfreq(n=128, d=1.0/freq) + +ix_noise = (frs > 55) & (frs < 65) +ix_signal = (frs > 25) & (frs < 35) + +to_read = int(options.window * (freq / buf)) +Nchan_plot = len(ch_names) + +res = [] +t_init = time() +k = 0 +while k < to_read: + data, timestamps = inlet.pull_chunk(timeout=1.0, max_samples=buf) + if timestamps: + res.append(data) + k += 1 +dur = time() - t_init +data = np.concatenate(res, axis=0) +print data.shape +if options.avgref: + data -= np.atleast_2d(data.mean(1)).T +ffts = np.abs(np.fft.fft(data[:, 0:], n=128, axis=0)) +dur = data[-1, 0] - data[0, 0] + +bf, af = butter(4, np.array([1, 40])/(freq/2.), 'bandpass') + +# You probably won't need this if you're embedding things in a tkinter plot... +plt.ion() + +fig, axes = plt.subplots(1, 1, figsize=figsize, + sharex=True) +sns.despine(left=True) + + +time = np.arange(len(data))/freq + +lines = [] +impedances = np.log(ffts[ix_noise].mean(0)) / np.log(ffts[ix_signal].mean(0)) +print impedances +for i, ix in enumerate(picks): + line, = axes.plot(time[::subsample], + data[::subsample, ix] - (i * options.scale * 2), + lw=1) + lines.append(line) +vmin = 0 +vmax = 0 +axes.set_ylim(-len(picks) * options.scale * 2, + 2 * options.scale) +ticks = np.arange(0, -Nchan * options.scale * 2, -options.scale * 2) + +axes.set_yticks(ticks) +axes.get_xaxis().set_visible(False) + +ticks_labels = ['%s - %.1f' % (ch_names[i], impedances[i]) + for i in picks] +axes.set_yticklabels(ticks_labels) +plt.show() + +display_every = int(options.refresh / (buf/freq)) +k = 0 +while 1: + try: + data, timestamps = inlet.pull_chunk(timeout=1.0, max_samples=buf) + if timestamps: + res.append(data) + res.pop(0) + k += 1 + if k == display_every: + data = np.concatenate(res, axis=0) + if options.avgref: + data -= np.atleast_2d(data.mean(1)).T + #ffts = np.abs(np.fft.fft(data[:, 1:], n=128, axis=0)) + + if filt: + data = filtfilt(bf, af, data, axis=0) + for i, ix in enumerate(picks): + lines[i].set_ydata(data[::subsample, ix] - + (i * options.scale * 2)) + # axes.relim() + # axes.autoscale_view() + #impedances = (np.log(ffts[ix_noise].mean(0)) / + # np.log(ffts[ix_signal].mean(0))) + impedances = np.std(data[:, 0:], 0) + ticks_labels = ['%s - %.2f' % (ch_names[i], impedances[i]) + for i in picks] + axes.set_yticklabels(ticks_labels) + fig.canvas.draw() + k = 0 + except KeyboardInterrupt: + break diff --git a/muse_lsl.py b/muse_lsl.py new file mode 100644 index 0000000..cd15fbb --- /dev/null +++ b/muse_lsl.py @@ -0,0 +1,40 @@ +from muse import Muse +from time import sleep +from pylsl import StreamInfo, StreamOutlet + +YOUR_DEVICE_ADDRESS = "00:55:DA:B0:06:D6" + +info = info = StreamInfo('Muse', 'EEG', 5, 256, 'float32', + 'Muse%s' % YOUR_DEVICE_ADDRESS) + +info.desc().append_child_value("manufacturer", "Muse") +channels = info.desc().append_child("channels") + +for c in ['TP9', 'AF7', 'AF8', 'TP10', 'Right AUX']: + channels.append_child("channel") \ + .append_child_value("label", c) \ + .append_child_value("unit", "microvolts") \ + .append_child_value("type", "EEG") +outlet = StreamOutlet(info, 12, 360) + + +def process(data, timestamps): + for ii in range(12): + outlet.push_sample(data[:, ii], timestamps[ii]) + +muse = Muse(address=YOUR_DEVICE_ADDRESS, callback=process) + +muse.connect() +print('Connected') +muse.start() +print('Streaming') + +while 1: + try: + sleep(1) + except: + break + +muse.stop() +muse.disconnect() +print('Disonnected') diff --git a/muse_record.py b/muse_record.py new file mode 100644 index 0000000..4bedb00 --- /dev/null +++ b/muse_record.py @@ -0,0 +1,34 @@ +from muse import Muse +from time import time, sleep +import numpy as np +import pandas as pd +YOUR_DEVICE_ADDRESS = "00:55:DA:B0:06:D6" + +full_time = [] +full_data = [] + +def process(data, timestamps): + full_time.append(timestamps) + full_data.append(data) + +muse = Muse(YOUR_DEVICE_ADDRESS, process) + +muse.connect() +muse.start() + +while 1: + try: + sleep(1) + except: + break + +muse.stop() +muse.disconnect() + +full_time = np.concatenate(full_time) +full_data = np.concatenate(full_data, 1).T +res = pd.DataFrame(data=full_data, + columns=['TP9', 'AF7', 'AF8', 'TP10', 'Right AUX']) + +res['timestamps'] = full_time +res.to_csv('dump.csv', float_format='%.3f')