Esse commit está contido em:
Pierre Karashchuk
2017-04-11 16:53:24 -07:00
commit cc6bd88883
10 arquivos alterados com 827 adições e 0 exclusões
+19
Ver Arquivo
@@ -0,0 +1,19 @@
## code by Alexandre Barachant
## adapted by Pierre Karashchuk for compatibility with Python3
from pylsl import StreamInfo, StreamOutlet
import numpy as np
try:
input = raw_input
except NameError:
pass
info = StreamInfo('Ganglion_EEG', 'EEG', 4, 200, 'float32',
'Ganglion_123456789')
outlet = StreamOutlet(info)
while True:
strSample = input().split(': ', 1)
sample = 1e6*np.array(list(map(float, strSample[1].split(' '))))
stamp = float(strSample[0])*1e-3
outlet.push_sample(sample, stamp)
# print('Pushed Sample At: ' + strSample[0])
+27
Ver Arquivo
@@ -0,0 +1,27 @@
# Lab 9: Measuring brain response to different smells
### Introduction
In this lab, we will record the different brain responses to various smells.
### Setup
First, install the libraries (there are new python dependencies this time!):
``` bash
npm install
pip install -r requirements.txt
```
(If you don't have `npm`, you can install by running `brew install node`. You can get `brew` from https://brew.sh/)
### Stimulus Presentation + Recording
- Attach electrodes to participant's head, 2 on the frontal cortex (on forehead) and 2 on temporal lobe (right above the ears).
- Connect to the ganglion and stream data: `node ganglion-lsl.js`
- Run lsl-viewer to check connections and stream: `python lsl-viewer.py`
- Record data by opening the `record_data.ipynb` notebook
### Analysis
- Open `analyze_data.ipynb` notebook
- Replace the filenames at the beginning with your filenames
- Run it and see the effects!
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Arquivo executável
+78
Ver Arquivo
@@ -0,0 +1,78 @@
#!/usr/bin/env python
import numpy as np
import pandas as pd
from time import time, strftime, gmtime
from pylsl import StreamInlet, resolve_byprop
def get_channel_names(inlet):
"""Get channel names for an LSL inlet"""
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'))
if np.all(np.array(ch_names) == ""):
ch_names = ['C{}'.format(x) for x in range(1,len(ch_names)+1)]
return ch_names
def collect_eeg(inlet, duration=10, tag=None):
"""Collect EEG data from stream.
Returns a pandas object with the data
Arguments:
duration -- length of recording in seconds.
tag -- optionally tag the data
"""
### ignore any data stored
data = [0]
while len(data) != 0:
data, timestamp = inlet.pull_chunk(max_samples=1024)
res = []
timestamps = []
t_init = time()
print('Start recording at time t={:.3f}'.format(t_init))
last = t_init
while (last - t_init) < duration:
try:
data, timestamp = inlet.pull_chunk(timeout=1.0,
max_samples=24)
if timestamp:
res.append(data)
timestamps.extend(timestamp)
last = timestamp[-1]
except KeyboardInterrupt:
break
t_done = time()
print('Finished recording at time {:.3f} ({:.3f} seconds)'.format(t_done, t_done - t_init))
res = np.concatenate(res, axis=0)
timestamps = np.array(timestamps)
res = np.c_[timestamps, res]
ch_names = get_channel_names(inlet)
columns = ['timestamps'] + ch_names
if tag is not None:
columns += ['tag']
tagc = np.repeat(tag, res.shape[0])[:, np.newaxis]
res = np.hstack([res, tagc])
data = pd.DataFrame(data=res, columns=columns)
return data
Ver Arquivo
+29
Ver Arquivo
@@ -0,0 +1,29 @@
// code by Alexandre Barachant
const Ganglion = require('openbci-ganglion').Ganglion;
const ganglion = new Ganglion();
// Construct LSL Handoff Python Shell
var PythonShell = require('python-shell');
var lsloutlet = new PythonShell('LSLHandoff.py');
lsloutlet.on('message', function(message){
console.log('LslOutlet: ' + message);
});
console.log('Python Shell Created for LSLHandoff');
ganglion.once('ganglionFound', (peripheral) => {
// Stop searching for BLE devices once a ganglion is found.
ganglion.searchStop();
ganglion.on('sample', (sample) => {
/** Work with sample */
st = sample.channelData.join(' ');
var s = ''+ sample.timeStamp + ': '+ st
lsloutlet.send(s)
});
ganglion.once('ready', () => {
ganglion.streamStart();
});
ganglion.connect(peripheral);
});
// Start scanning for BLE devices
ganglion.searchStart();
Arquivo executável
+191
Ver Arquivo
@@ -0,0 +1,191 @@
#!/usr/bin/env python
## code by Alexandre Barachant
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import butter, filtfilt
from time import time, sleep
from pylsl import StreamInlet, resolve_byprop
import seaborn as sns
from threading import Thread
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.")
filt = True
subsample = 2
buf = 12
(options, args) = parser.parse_args()
window = options.window
scale = options.scale
figsize = np.int16(options.figure.split('x'))
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")
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')
def update_plot(self):
k = 0
while self.started:
samples, timestamps = self.inlet.pull_chunk(timeout=1.0,
max_samples=12)
if timestamps:
self.data = np.vstack([self.data, samples])
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.data = self.data[-self.n_samples:]
self.times = self.times[-self.n_samples:]
k += 1
if k == self.display_every:
if self.filt:
data_f = filtfilt(self.bf, self.af, self.data, axis=0)
else:
data_f = self.data
data_f -= data_f.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(data_f[::subsample, ii] /
self.scale - ii)
impedances = np.std(data_f, 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
fig, axes = plt.subplots(1, 1, figsize=figsize, sharex=True)
lslv = LSLViewer(streams[0], fig, axes, window, scale)
help_str = """
toggle filter : d
toogle full screen : f
zoom out : /
zoom in : *
increase time scale : -
decrease time scale : +
"""
print(help_str)
lslv.start()
plt.show()
lslv.stop()
+16
Ver Arquivo
@@ -0,0 +1,16 @@
{
"name": "lab3",
"version": "1.0.0",
"description": "",
"main": "ganglion-lsl.js",
"dependencies": {
"openbci-ganglion": "^0.4.3",
"python-shell": "^0.4.0"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "GPL-3.0"
}
+152
Ver Arquivo
@@ -0,0 +1,152 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from scipy import signal, stats\n",
"import numpy as np\n",
"import pandas as pd\n",
"from time import time, strftime, gmtime\n",
"from pylsl import StreamInlet, resolve_byprop\n",
"from matplotlib.pyplot import *\n",
"\n",
"from collect_tools import collect_eeg\n",
"\n",
"%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"looking for an EEG stream...\n",
"Found stream!\n"
]
}
],
"source": [
"print(\"looking for an EEG stream...\")\n",
"streams = resolve_byprop('type', 'EEG', timeout=2)\n",
"\n",
"if len(streams) == 0:\n",
" print('No streams found! Are you streaming data?')\n",
"else:\n",
" print('Found stream!')"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [],
"source": [
"inlet = StreamInlet(streams[0], max_chunklen=24)"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"baseline: just stay still and don't smell too much\n",
"\n",
"Start recording at time t=1491953530.525\n",
"Finished recording at time 1491953540.611 (10.086 seconds)\n"
]
}
],
"source": [
"print(\"baseline: just stay still and don't smell too much\\n\")\n",
"baseline = collect_eeg(inlet,duration=10, tag='baseline')"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"smell: try to smell the object as much as possible\n",
"\n",
"Start recording at time t=1491953540.627\n",
"Finished recording at time 1491953550.697 (10.069 seconds)\n"
]
}
],
"source": [
"print(\"smell: try to smell the object as much as possible\\n\")\n",
"smell = collect_eeg(inlet,duration=10, tag='smell')"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"data saved in:\n",
"data/smell_1491953550.csv\n"
]
}
],
"source": [
"data = pd.concat([baseline, smell])\n",
"last = np.array(data.timestamps)[-1]\n",
"last = int(float(last))\n",
"fname = 'data/smell_{}.csv'.format(last)\n",
"data.to_csv(fname, index=False)\n",
"print('data saved in:\\n{}'.format(fname))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
+5
Ver Arquivo
@@ -0,0 +1,5 @@
numpy
scipy
matplotlib
pandas
pylsl