diff --git a/Board.py b/Board.py index 0b8ac2b..4c40c5a 100644 --- a/Board.py +++ b/Board.py @@ -15,6 +15,10 @@ GANGLION = "Ganglion" CYTON = "Cyton" CYTON_DAISY = "Cyton-Daisy" +MUSE_2016_BLED = "Muse 2016 BLE Dongle" +MUSE_2_BLED = "Muse 2 BLE Dongle" +MUSE_S_BLED = "Muse S BLE Dongle" +MUSE_2016 = "Muse 2016" MUSE_2 = "Muse 2" MUSE_S = "Muse S" @@ -41,7 +45,7 @@ def get_serial_port(board_id): pass else: # didn't have the bad com port exeption - BoardShim.release_all_sessions() + board.release_session() return params.serial_port BoardShim.release_all_sessions() @@ -138,12 +142,14 @@ def stop(self): def get_board_id(data_type, hardware, model): - """Gets the brainflow board_id from the given arguments + """Gets the brainflow board_id from the given arguments. Note that BLED boards\ + require a BLED112 dongle. Non BLED muse hardware is untested. Args: data_type (String): A string of either "Task live" or "Task simulate" hardware (String): A string of either "Muse" or "OpenBCI" - model (String): A string of either "Muse 2", "Muse S", "Ganglion", "Cyton", or "Cyton-Daisy" + model (String): A string of either "Ganglion", "Cyton", or "Cyton-Daisy", "Muse 2016 BLE Dongle", + "Muse 2 BLE Dongle", "Muse S BLE Dongle", "Muse 2016", "Muse 2","Muse S" Returns: int: The board_id that brainflow uses internally to determine board type @@ -158,10 +164,19 @@ def get_board_id(data_type, hardware, model): elif model == CYTON_DAISY: board_id = 2 elif hardware == MUSE: - if model == MUSE_2: + if model == MUSE_S_BLED: + board_id = 21 + elif model == MUSE_2_BLED: board_id = 22 + elif model == MUSE_2016_BLED: + board_id = 42 + elif model == MUSE_2: + board_id = 38 elif model == MUSE_S: - board_id = 21 + board_id = 39 + elif model == MUSE_2016: + board_id = 41 + elif data_type == SIMULATE: board_id = -1 diff --git a/graph_window.py b/graph_window.py index b4c5774..575d751 100644 --- a/graph_window.py +++ b/graph_window.py @@ -84,9 +84,8 @@ def __init__( # save file should be an ok file name to save to with approriate ending ('.csv') self.save_file = save_file self.board_id = get_board_id(data_type, hardware, model) - self.exg_channels = BoardShim.get_exg_channels(self.board_id) - self.marker_channels = BoardShim.get_marker_channel(self.board_id) + self.sampling_rate = BoardShim.get_sampling_rate(self.board_id) self.update_speed_ms = 50 self.window_size = 5 @@ -99,8 +98,6 @@ def __init__( self.chan_num = len(self.exg_channels) self.exg_channels = np.array(self.exg_channels) - self.marker_channels = np.array(self.marker_channels) - print('board decription {}'.format(BoardShim.get_board_descr(board_id))) logger.debug('EXG channels is {}'.format(self.exg_channels)) @@ -130,7 +127,8 @@ def __init__( def _init_timeseries(self): self.plots = list() self.curves = list() - for i in range(self.chan_num+1): + num_curves = self.chan_num + for i in range(num_curves): p = self.graphWidget.addPlot(row=i, col=0) p.showAxis("left", False) p.setMenuEnabled("left", False) @@ -184,15 +182,6 @@ def update(self): FilterTypes.BUTTERWORTH.value, 0, ) - DataFilter.perform_bandpass( - data[channel], - self.sampling_rate, - 51.0, - 100.0, - 2, - FilterTypes.BUTTERWORTH.value, - 0, - ) DataFilter.perform_bandstop( data[channel], self.sampling_rate, @@ -212,8 +201,6 @@ def update(self): 0, ) self.curves[count].setData(data[channel].tolist()) - self.curves[len(self.exg_channels)].setData(data[self.marker_channels].tolist()) - logger.debug('Marker channel data was {}'.format(data[self.marker_channels].tolist())) logger.debug('Graph window finished updating (successfully got data from board and applied it to graphs)') def closeEvent(self, event): diff --git a/impedance_window.py b/impedance_window.py index d6307b8..4547e21 100644 --- a/impedance_window.py +++ b/impedance_window.py @@ -22,7 +22,22 @@ from PyQt5.QtGui import QPainter, QBrush, QPen, QPolygon import numpy as np import statistics as stats -from multiprocessing import Process, Queue +import logging +log_file = "boiler.log" +logging.basicConfig(level=logging.INFO, filemode="a") + +f = logging.Formatter( + "Logger: %(name)s: %(levelname)s at: %(asctime)s, line %(lineno)d: %(message)s" +) +stdout = logging.StreamHandler(sys.stdout) +boiler_log = logging.FileHandler(log_file) +stdout.setFormatter(f) +boiler_log.setFormatter(f) + +logger = logging.getLogger("ImpedWindow") +logger.addHandler(boiler_log) +logger.addHandler(stdout) +logger.info("Program started at {}".format(time.time())) from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds from brainflow.data_filter import DataFilter, FilterTypes @@ -48,6 +63,7 @@ def __init__( board_id=None, ): super().__init__() + logger.info('Initializing impedance window') # Ensures that the user has not provided a muse, leading to # hard to debug errors later. @@ -181,6 +197,7 @@ def init_hardware(self): # let's start eeg receiving! # self.start_data_stream() + logger.info("Initializing hardware") self.board = Board(board_id=self.board_id, serial_port=self.serial_port, manual_mode = True) print( @@ -196,6 +213,7 @@ def init_hardware(self): # This wild string puts Cyton-Daisy into impedance mode. # DO NOT CHANGE # Think Pulse + logger.info("Configuring Cyton") self.board.board.config_board( "x1040010Xx2040010Xx3040010Xx4040010Xx5040010Xx6040010Xx7040010Xx8040010XxQ040010XxW040010XxE040010XxR040010XxT040010XxY040010XxU040010XxI040010X" ) @@ -214,6 +232,7 @@ def init_hardware(self): # ganglion impedances based on # https://github.com/OpenBCI/brainflow/blob/master/tests/python/ganglion_resist.py # expected result: 5 seconds of resistance data(unknown sampling rate) after that 5 seconds of exg data + logger.info("Configuring Ganglion") self.board.board.config_board("z") print('sent board z, not yet start stream') self.board.board.start_stream(45000, None) @@ -230,31 +249,26 @@ def init_hardware(self): resistance_channels = BoardShim.get_resistance_channels (BoardIds.GANGLION_BOARD.value) print (resistance_channels) + else: + logger.error('Impedance window cannote run using this hardware (Board ID: {}). Try an OpenBCI ganglion or cyton instead.'.format(self.board_id)) def closeEvent(self, event): # this code will autorun just before the window closes # we will check whether streams are running, if they are we will close them - print("close event works") + logger.info("Closing window") self.finished = True self.parent.impedance_window_open = False self.on_end() def loop_start(self): - print("starting loop") + logger.info("starting loop") self.loop_running = True self.loop_timer.timeout.disconnect() self.loop_timer.timeout.connect(self.start_iteration) self.loop_timer.start(1000) self.update() - # def loop_end(self): - # print("ending loop") - # self.loop_running = False - # self.update() - # self.loop_timer.timeout.disconnect() - # self.loop_timer.timeout.connect(self.start_iteration) - # self.loop_timer.start(1000) def start_iteration(self): # called by hitting enter @@ -274,14 +288,14 @@ def start_iteration(self): # use stdev as proxy. chan_rms_uV = np.sqrt(np.sum(self.data ** 2)) self.impedances[count] = ((stats.sqrt( 2.0 ) * (chan_rms_uV) * 1.0e-6) / 6.0e-9 - 2200)/1000 - print(self.impedances) + logger.debug("Imedances: {}".format(self.impedances)) """ HERE """ # need to do some smoothing from the past 6 seconds to take out instantaneous self.loop_start() else: - print("exiting") + logger.info("exiting") def filter_custom(self, chan): DataFilter.perform_highpass( @@ -313,7 +327,7 @@ def display_instructions(self): def keyPressEvent(self, event): if event.key() == Qt.Qt.Key_Space: - print("received user input") + logger.info("received user input") elif event.key() == Qt.Qt.Key_Return or event.key == Qt.Qt.Key_Enter: if self.hardware_connected and not self.running_test: self.running_test = True @@ -323,7 +337,7 @@ def keyPressEvent(self, event): def paintEvent(self, event): # here is where we draw stuff on the screen # you give drawing instructions in pixels - here I'm getting pixel values based on window size - print("paint event runs") + logger.info("paint event runs") painter = QPainter(self) if self.loop_running: radius = self.geometry().width() // 18 diff --git a/main_menu.py b/main_menu.py index cd01053..1f5c36b 100644 --- a/main_menu.py +++ b/main_menu.py @@ -1,59 +1,5 @@ """ -TO DO: - Main Menu: - Add in hardware/model: - unicorn - muse (2/S) - Compartmentalize the board id grab in utils (pass in hardware/model/datatype) - - Impedence menu - Look for muse impedance check scripts - Confirm that the OpenBCI Impedence checks are working properly - - For time sync - add back in pyLSL? - - Render GA ERPs in results window from the either the baseline or the session - Choose from baseline/session - - Logisitics: - Send Paul another Muse and possibly an arduino + light? - -M todo -order of events lets you try and fauil tomopen graph wo selecting com port -one dropdown for hardware (<-eden no likey) -implement impedanece for all,not just cyton daisy - -add support for non openbci hardware -add option to not import tensorflow -in train model, has hard coded 16 channel # (fix) - - - -opens windows: -graph window - shows live timeseries --potentially make it configurable -- label on garph which line is which channel by chcking hardware -impedance window --curently hacked together, obnly cyton daiusy --implement with other -arduino --debug requires putting in 1 --preset for neuorstimduino -- need dosc for how to upload script to arduino using arduino ide, attach led -- currently provides a way to turn led on arduino on and off on command -baseline -- basically like the oddball window -- outputs eeg file in brainflow format -- new plan: use pylsl sender to constantly grab brainflow and events and send them together, so ww can be sure of times -saving -- sqlite prob overkill -- use numpy -- later maybe add sqlite to use if run for long time -remove unecessary windows -- we don't need a model window with tensorflow to train a thing. this isn't koalacademy -ADD SIMULATE AS HARDWARE OPTION -make board id happen in menu window so not passing raw srtrings between windows - +This is the main menu. It starts all the other programs as directed by the user. """ @@ -69,7 +15,8 @@ import time import os import logging -from Board import BCI, CONNECT, CYTON, CYTON_DAISY, GANGLION, MUSE, MUSE_2, MUSE_S, SIMULATE, get_board_id +from Board import BCI, CONNECT, CYTON, CYTON_DAISY, GANGLION, MUSE, MUSE_2, MUSE_S, MUSE_2016, \ + MUSE_S_BLED, MUSE_2_BLED, MUSE_2016_BLED, SIMULATE, get_board_id # Creates the global logger @@ -97,7 +44,7 @@ # results not implemented yet from graph_window import graph_win -if False: # debugging... remebeber to put the tf imports back in session_window +if False: # debugging... remember to put the tf imports back in session_window import tensorflow as tf if sys.platform == "win32": @@ -358,9 +305,7 @@ def handle_hardware_choice(self): if self.hardware_dropdown.currentText() == BCI: self.model_dropdown.addItems([GANGLION, CYTON, CYTON_DAISY]) elif self.hardware_dropdown.currentText() == MUSE: - self.model_dropdown.addItems([MUSE_2, MUSE_S]) - elif self.hardware_dropdown.currentText() == "Blueberry": - self.model_dropdown.addItem("Prototype") + self.model_dropdown.addItems([MUSE_2016_BLED, MUSE_2_BLED, MUSE_S_BLED]) def handle_model_choice(self): """Handles changes to the model dropdown""" @@ -400,7 +345,10 @@ def handle_type_choice(self): self.data_type = self.type_dropdown.currentText() self.graph_window_button.setEnabled(True) self.baseline_window_button.setEnabled(True) - self.impedance_window_button.setEnabled(True) + if self.hardware_dropdown.currentText() != MUSE: + self.impedance_window_button.setEnabled(True) + else: + logger.info('Impedance window is not available for Muse hardware. Try OpenBCI instead.') if self.data_type == CONNECT: self.title.setText("Select BCI Hardware Port") self.bci_port.setEnabled(True)