In [1]:
from emotiv import epoc
import multiprocessing as mp
import tables, time
from path import path
import pandas
from IPython.display import clear_output, display
from matplotlib.pylab import *
from lockfile import FileLock

In [17]:
class EEGTick(tables.IsDescription):
    state = tables.StringCol(16)
    packet = tables.UInt64Col()
    tick = tables.UInt8Col()
    tick_time = tables.UInt64Col()
    packets_skipped = tables.UInt8Col()
    battery = tables.UInt8Col()
    gyroX = tables.UInt16Col()
    gyroY = tables.UInt16Col()
    F3 = tables.Float32Col()
    FC5 = tables.Float32Col()
    AF3 = tables.Float32Col()
    F7 = tables.Float32Col()
    T7 = tables.Float32Col()
    P7 = tables.Float32Col()
    O1 = tables.Float32Col()
    O2 = tables.Float32Col()
    P8 = tables.Float32Col()
    T8 = tables.Float32Col()
    F8 = tables.Float32Col()
    AF4 = tables.Float32Col()
    FC6 = tables.Float32Col()
    F4 = tables.Float32Col()
    F3_QUAL = tables.Float32Col()
    FC5_QUAL = tables.Float32Col()
    AF3_QUAL = tables.Float32Col()
    F7_QUAL = tables.Float32Col()
    T7_QUAL = tables.Float32Col()
    P7_QUAL = tables.Float32Col()
    O1_QUAL = tables.Float32Col()
    O2_QUAL = tables.Float32Col()
    P8_QUAL = tables.Float32Col()
    T8_QUAL = tables.Float32Col()
    F8_QUAL = tables.Float32Col()
    AF4_QUAL = tables.Float32Col()
    FC6_QUAL = tables.Float32Col()
    F4_QUAL = tables.Float32Col()

In [6]:
CHANNELS = ("F3", "FC5", "AF3", "F7", "T7", "P7", "O1", "O2", "P8",  "T8",  "F8", "AF4", "FC6", "F4")
SAMPLING_RATE = 128

In [3]:
def data_collector(state_queue, file_name, dummy):
    import tables, time, numpy
    from matplotlib.dates import date2num
    from emotiv import epoc
    from path import path
    from itertools import izip
    from datetime import datetime
    from lockfile import FileLock
    
#    lock = FileLock(file_name)
    if path(file_name).exists():
        raise Exception('Recording "{}" already exists'.format(file_name))
#         h5file = tables.open_file(file_name, mode="a", title='[Title] EEG Signal')
#         h5table = h5file.root.eeg.signal
    else:
        h5file = tables.open_file(file_name, mode="w", title='[Title] EEG Signal')
        h5group = h5file.create_group("/", 'eeg', '[Group] EEG signal')
        h5table = h5file.create_table(h5group, 'signal', EEGTick, "[Table] EEG Signal")
    row = h5table.row
    
    e = epoc.EPOC(method='dummy' if dummy else 'libusb')
    try:
        cycle, last_tick, last_packet = -1, -1, 0
        current_state = 'neutral'
        while True:
            if dummy:
                time.sleep(0.0077)
            sample, tick, tick_time = e.get_sample(), e.counter, numpy.datetime64(datetime.utcnow()).astype(numpy.uint64)
            if not state_queue.empty():
                current_state = state_queue.get_nowait()
            if current_state == 'quit':
                break
            if tick < last_tick and last_tick != -1:
                cycle += 1
            last_tick = tick
            packet = cycle * (SAMPLING_RATE + 1) + tick
            packets_skipped = packet - last_packet - 1
            last_packet = packet
            if tick == 128 or cycle==-1:
                continue
#             with lock:
            row['state'], row['packet'], row['tick'], row['tick_time'] = current_state, packet, tick, tick_time
            row['packets_skipped'], row['battery'] = packets_skipped, e.battery
            row['gyroX'], row['gyroY'] = e.gyroX, e.gyroY
            for channel, value in izip(CHANNELS, sample):
                row[channel], row[channel + '_QUAL'] = value, e.quality[channel]
            row.append()
            h5table.flush()
    except KeyboardInterrupt:
        pass
    finally:
        e.disconnect()
#         with lock:
        h5table.flush()

In [1]:
class EEGRecorder(object):
    def __init__(self, file_name='recordings/recording.h5', dummy=False, overwrite=False):
        self.dummy = dummy
        self.file_name = file_name
        if overwrite and path(file_name).exists():
            path(file_name).remove()
        self._setup_process()

    def _setup_process(self):
        self.__state_queue = mp.Queue(maxsize=5)
        self.__collector = mp.Process(name='block', target=data_collector,
                                      args=(self.__state_queue, self.file_name, self.dummy))
        self.__collector.daemon = True
        
    def start(self):
        if self.is_recording():
            return
        self.__collector.start()
    
    def stop(self):
        if not self.is_recording():
            return
        self.__state_queue.put('quit')
        self.__collector.join()
        self._setup_process()

    def is_recording(self):
        return self.__collector.is_alive()
    
    def tag(self, tag=None):
        self.__state_queue.put(tag or 'neutral')
    
    def neutral(self):
        self.tag()
    
    def get_df(self):
        return pandas.read_hdf(self.file_name, '/eeg/signal')
    
    def sensor_monitor(self, sensor, duration=5):
        
        show_rows = SAMPLING_RATE * duration
#         lock = FileLock(self.file_name)
        f = figure(figsize=(15,5))
        try:
            while True:
#                 with lock:
                h5 = tables.open_file(self.file_name)
                tbl = h5.root.eeg.signal
                rows = tbl[-show_rows:]
                h5.close()
                tick_times = [row['tick_time'] for row in rows]
                signals = [row[sensor] for row in rows]
#                 qualities = [row[sensor + '_QUAL'] for row in rows]
                plot(tick_times, signals, color='b')
                xlim(tick_times[0], tick_times[-1])
                clear_output()
                display(f)
                time.sleep(1./SAMPLING_RATE*2)
        except KeyboardInterrupt:
            pass