def batch_run(record_dir=None, amp_name=None, amp_serial=None): # configure LSL server name and device serial if available if not record_dir: record_dir = '%s/records' % os.getcwd() if not amp_name: amp_name, amp_serial = pu.search_lsl(ignore_markers=True) run(record_dir, amp_name=amp_name, amp_serial=amp_serial)
def find_lsl_stream(state, amp_name=None, amp_serial=None): """ Find the amplifier name and its serial number to connect to cfg = config file state = GUI sharing variable """ if any(v is None for v in (amp_name, amp_serial)): amp_name, amp_serial = pu.search_lsl(state, ignore_markers=True) return amp_name, amp_serial
def find_lsl_stream(cfg, state): """ Find the amplifier name and its serial number to connect to cfg = config file state = GUI sharing variable """ if cfg.AMP_NAME is None and cfg.AMP_SERIAL is None: amp_name, amp_serial = pu.search_lsl(state, ignore_markers=True) else: amp_name = cfg.AMP_NAME amp_serial = cfg.AMP_SERIAL return amp_name, amp_serial
def run_gui(recordState, protocolState, record_dir, recordLogger=logger, amp_name=None, amp_serial=None, eeg_only=False, queue=None): redirect_stdout_to_queue(recordLogger, queue, 'INFO') # configure LSL server name and device serial if available if not amp_name: amp_name, amp_serial = pu.search_lsl(recordState, recordLogger, ignore_markers=True) recordLogger.info('\nOutput directory: %s' % (record_dir)) # spawn the recorder as a child process recordLogger.info('\n>> Recording started.') proc = mp.Process(target=record, args=[ recordState, amp_name, amp_serial, record_dir, eeg_only, recordLogger, queue ]) proc.start() while not recordState.value: pass # Launching the protocol (shared variable) with protocolState.get_lock(): protocolState.value = 1 # Continue recording until the shared variable changes to 0. while recordState.value: time.sleep(1) recordLogger.info('(main) Waiting for recorder process to finish.') proc.join(10) if proc.is_alive(): recordLogger.error( 'Recorder process not finishing. Are you running from Spyder?') recordLogger.error('Dropping into a shell') qc.shell() sys.stdout.flush() recordLogger.info('Recording finished.')
exit() ''' # leeq if (self.ui.pushButton_stoprec.isEnabled()): subprocess.Popen(["cl_rpc", "closexdf"], close_fds=True) with self.state.get_lock(): self.state.value = 0 # ---------------------------------------------------------------------------------------------------- # END OF EVENT HANDLERS # ---------------------------------------------------------------------------------------------------- if __name__ == '__main__': if len(sys.argv) == 2: amp_name = sys.argv[1] amp_serial = None elif len(sys.argv) == 3: amp_name, amp_serial = sys.argv[1:3] else: amp_name, amp_serial = pu.search_lsl() if amp_name == 'None': amp_name = None logger.info('Connecting to a server %s (Serial %s).' % (amp_name, amp_serial)) app = QApplication(sys.argv) ex = Scope(amp_name, amp_serial) sys.exit(app.exec_())
def test_receiver(): import mne import os CH_INDEX = [1] # channel to monitor TIME_INDEX = None # integer or None. None = average of raw values of the current window SHOW_PSD = False mne.set_log_level('ERROR') os.environ['OMP_NUM_THREADS'] = '1' # actually improves performance for multitaper # connect to LSL server amp_name, amp_serial = pu.search_lsl() sr = StreamReceiver(window_size=1, buffer_size=1, amp_serial=amp_serial, eeg_only=False, amp_name=amp_name) sfreq = sr.get_sample_rate() trg_ch = sr.get_trigger_channel() logger.info('Trigger channel = %d' % trg_ch) # PSD init if SHOW_PSD: psde = mne.decoding.PSDEstimator(sfreq=sfreq, fmin=1, fmax=50, bandwidth=None, \ adaptive=False, low_bias=True, n_jobs=1, normalization='length', verbose=None) watchdog = qc.Timer() tm = qc.Timer(autoreset=True) last_ts = 0 while True: sr.acquire() window, tslist = sr.get_window() # window = [samples x channels] window = window.T # chanel x samples qc.print_c('LSL Diff = %.3f' % (pylsl.local_clock() - tslist[-1]), 'G') # print event values tsnew = np.where(np.array(tslist) > last_ts)[0] if len(tsnew) == 0: logger.warning('There seems to be delay in receiving data.') time.sleep(1) continue trigger = np.unique(window[trg_ch, tsnew[0]:]) # for Biosemi # if sr.amp_name=='BioSemi': # trigger= set( [255 & int(x-1) for x in trigger ] ) if len(trigger) > 0: logger.info('Triggers: %s' % np.array(trigger)) logger.info('[%.1f] Receiving data...' % watchdog.sec()) if TIME_INDEX is None: datatxt = qc.list2string(np.mean(window[CH_INDEX, :], axis=1), '%-15.6f') print('[%.3f : %.3f]' % (tslist[0], tslist[-1]) + ' data: %s' % datatxt) else: datatxt = qc.list2string(window[CH_INDEX, TIME_INDEX], '%-15.6f') print('[%.3f]' % tslist[TIME_INDEX] + ' data: %s' % datatxt) # show PSD if SHOW_PSD: psd = psde.transform(window.reshape((1, window.shape[0], window.shape[1]))) psd = psd.reshape((psd.shape[1], psd.shape[2])) psdmean = np.mean(psd, axis=1) for p in psdmean: print('%.1f' % p, end=' ') last_ts = tslist[-1] tm.sleep_atleast(0.05)
def run(cfg, state=mp.Value('i', 1), queue=None): redirect_stdout_to_queue(logger, queue, 'INFO') # Wait the recording to start (GUI) while state.value == 2: # 0: stop, 1:start, 2:wait pass # Protocol runs if state equals to 1 if not state.value: sys.exit(-1) if cfg.FAKE_CLS is None: # chooose amp if cfg.AMP_NAME is None and cfg.AMP_SERIAL is None: amp_name, amp_serial = pu.search_lsl(state, ignore_markers=True) else: amp_name = cfg.AMP_NAME amp_serial = cfg.AMP_SERIAL fake_dirs = None else: amp_name = None amp_serial = None fake_dirs = [v for (k, v) in cfg.DIRECTIONS] # events and triggers tdef = trigger_def(cfg.TRIGGER_FILE) #if cfg.TRIGGER_DEVICE is None: # input('\n** Warning: No trigger device set. Press Ctrl+C to stop or Enter to continue.') trigger = pyLptControl.Trigger(state, cfg.TRIGGER_DEVICE) if trigger.init(50) == False: logger.error( 'Cannot connect to USB2LPT device. Use a mock trigger instead?') input('Press Ctrl+C to stop or Enter to continue.') trigger = pyLptControl.MockTrigger() trigger.init(50) # init classification decoder = BCIDecoderDaemon( cfg.DECODER_FILE, buffer_size=1.0, fake=(cfg.FAKE_CLS is not None), amp_name=amp_name, amp_serial=amp_serial, fake_dirs=fake_dirs, parallel=cfg.PARALLEL_DECODING[cfg.PARALLEL_DECODING['selected']], alpha_new=cfg.PROB_ALPHA_NEW) # OLD: requires trigger values to be always defined #labels = [tdef.by_value[x] for x in decoder.get_labels()] # NEW: events can be mapped into integers: labels = [] dirdata = set([d[1] for d in cfg.DIRECTIONS]) for x in decoder.get_labels(): if x not in dirdata: labels.append(tdef.by_value[x]) else: labels.append(x) # map class labels to bar directions bar_def = {label: str(dir) for dir, label in cfg.DIRECTIONS} bar_dirs = [bar_def[l] for l in labels] dir_seq = [] for x in range(cfg.TRIALS_EACH): dir_seq.extend(bar_dirs) if cfg.TRIALS_RANDOMIZE: random.shuffle(dir_seq) else: dir_seq = [d[0] for d in cfg.DIRECTIONS] * cfg.TRIALS_EACH num_trials = len(dir_seq) logger.info('Initializing decoder.') while decoder.is_running() is 0: time.sleep(0.01) # bar visual object if cfg.FEEDBACK_TYPE == 'BAR': from neurodecode.protocols.viz_bars import BarVisual visual = BarVisual(cfg.GLASS_USE, screen_pos=cfg.SCREEN_POS, screen_size=cfg.SCREEN_SIZE) if cfg.FEEDBACK_TYPE == 'COLORS': from neurodecode.protocols.viz_colors import ColorVisual visual = ColorVisual(cfg.GLASS_USE, screen_pos=cfg.SCREEN_POS, screen_size=cfg.SCREEN_SIZE) elif cfg.FEEDBACK_TYPE == 'BODY': assert hasattr(cfg, 'FEEDBACK_IMAGE_PATH' ), 'FEEDBACK_IMAGE_PATH is undefined in your config.' from neurodecode.protocols.viz_human import BodyVisual visual = BodyVisual(cfg.FEEDBACK_IMAGE_PATH, use_glass=cfg.GLASS_USE, screen_pos=cfg.SCREEN_POS, screen_size=cfg.SCREEN_SIZE) visual.put_text('Waiting to start') if cfg.LOG_PROBS: logdir = qc.parse_path_list(cfg.DECODER_FILE)[0] probs_logfile = time.strftime(logdir + "probs-%Y%m%d-%H%M%S.txt", time.localtime()) else: probs_logfile = None feedback = Feedback(cfg, state, visual, tdef, trigger, probs_logfile) # start trial = 1 dir_detected = [] prob_history = {c: [] for c in bar_dirs} while trial <= num_trials: if cfg.SHOW_TRIALS: title_text = 'Trial %d / %d' % (trial, num_trials) else: title_text = 'Ready' true_label = dir_seq[trial - 1] # profiling feedback #import cProfile #pr = cProfile.Profile() #pr.enable() result = feedback.classify(decoder, true_label, title_text, bar_dirs, prob_history=prob_history) #pr.disable() #pr.print_stats(sort='time') if result is None: break else: pred_label = result dir_detected.append(pred_label) if cfg.WITH_REX is True and pred_label == true_label: # if cfg.WITH_REX is True: if pred_label == 'U': rex_dir = 'N' elif pred_label == 'L': rex_dir = 'W' elif pred_label == 'R': rex_dir = 'E' elif pred_label == 'D': rex_dir = 'S' else: logger.warning('Rex cannot execute undefined action %s' % pred_label) rex_dir = None if rex_dir is not None: visual.move(pred_label, 100, overlay=False, barcolor='B') visual.update() logger.info('Executing Rex action %s' % rex_dir) os.system('%s/Rex/RexControlSimple.exe %s %s' % (pycnbi.ROOT, cfg.REX_COMPORT, rex_dir)) time.sleep(8) if true_label == pred_label: msg = 'Correct' else: msg = 'Wrong' if cfg.TRIALS_RETRY is False or true_label == pred_label: logger.info('Trial %d: %s (%s -> %s)' % (trial, msg, true_label, pred_label)) trial += 1 if len(dir_detected) > 0: # write performance and log results fdir, _, _ = qc.parse_path_list(cfg.DECODER_FILE) logfile = time.strftime(fdir + "/online-%Y%m%d-%H%M%S.txt", time.localtime()) with open(logfile, 'w') as fout: fout.write('Ground-truth,Prediction\n') for gt, dt in zip(dir_seq, dir_detected): fout.write('%s,%s\n' % (gt, dt)) cfmat, acc = qc.confusion_matrix(dir_seq, dir_detected) fout.write('\nAccuracy %.3f\nConfusion matrix\n' % acc) fout.write(cfmat) logger.info('Log exported to %s' % logfile) print('\nAccuracy %.3f\nConfusion matrix\n' % acc) print(cfmat) visual.finish() with state.get_lock(): state.value = 0 if decoder: decoder.stop() ''' # automatic thresholding if prob_history and len(bar_dirs) == 2: total = sum(len(prob_history[c]) for c in prob_history) fout = open(probs_logfile, 'a') msg = 'Automatic threshold optimization.\n' max_acc = 0 max_bias = 0 for bias in np.arange(-0.99, 1.00, 0.01): corrects = 0 for p in prob_history[bar_dirs[0]]: p_biased = (p + bias) / (bias + 1) # new sum = (p+bias) + (1-p) = bias+1 if p_biased >= 0.5: corrects += 1 for p in prob_history[bar_dirs[1]]: p_biased = (p + bias) / (bias + 1) # new sum = (p+bias) + (1-p) = bias+1 if p_biased < 0.5: corrects += 1 acc = corrects / total msg += '%s%.2f: %.3f\n' % (bar_dirs[0], bias, acc) if acc > max_acc: max_acc = acc max_bias = bias msg += 'Max acc = %.3f at bias %.2f\n' % (max_acc, max_bias) fout.write(msg) fout.close() print(msg) ''' logger.info('Finished.')
def __init__(self, state=mp.Value('i', 1), lpttype='USB2LPT', portaddr=None, verbose=True, check_lsl_offset=False): self.evefile = None self.lpttype = lpttype self.verbose = verbose if self.lpttype in ['USB2LPT', 'DESKTOP']: if self.lpttype == 'USB2LPT': if ctypes.sizeof(ctypes.c_voidp) == 4: dllname = 'LptControl_USB2LPT32.dll' # 32 bit else: dllname = 'LptControl_USB2LPT64.dll' # 64 bit if portaddr not in [0x278, 0x378]: logger.warning('LPT port address %d is unusual.' % portaddr) elif self.lpttype == 'DESKTOP': if ctypes.sizeof(ctypes.c_voidp) == 4: dllname = 'LptControl_Desktop32.dll' # 32 bit else: dllname = 'LptControl_Desktop64.dll' # 64 bit if portaddr not in [0x278, 0x378]: logger.warning('LPT port address %d is unusual.' % portaddr) self.portaddr = portaddr search = [] search.append(os.path.dirname(__file__) + '/' + dllname) search.append(os.path.dirname(__file__) + '/libs/' + dllname) search.append(os.getcwd() + '/' + dllname) search.append(os.getcwd() + '/libs/' + dllname) for f in search: if os.path.exists(f): dllpath = f break else: logger.error('Cannot find the required library %s' % dllname) raise RuntimeError logger.info('Loading %s' % dllpath) self.lpt = ctypes.cdll.LoadLibrary(dllpath) elif self.lpttype == 'ARDUINO': import serial, serial.tools.list_ports BAUD_RATE = 115200 # portaddr should be None or in the form of 'COM1', 'COM2', etc. if portaddr is None: arduinos = [x for x in serial.tools.list_ports.grep('Arduino')] if len(arduinos) == 0: logger.error('No Arduino found. Stop.') sys.exit() for i, a in enumerate(arduinos): logger.info('Found %s' % a[0]) try: com_port = arduinos[0].device except AttributeError: # depends on Python distribution com_port = arduinos[0][0] else: com_port = portaddr self.ser = serial.Serial(com_port, BAUD_RATE) time.sleep(1) # doesn't work without this delay. why? logger.info('Connected to %s.' % com_port) elif self.lpttype == 'SOFTWARE': from neurodecode.stream_receiver.stream_receiver import StreamReceiver logger.info('Using software trigger') # get data file location LSL_SERVER = 'StreamRecorderInfo' inlet = cnbi_lsl.start_client(LSL_SERVER, state) evefile = inlet.info().source_id() eveoffset_file = evefile[:-4] + '-offset.txt' logger.info('Event file is: %s' % evefile) self.evefile = open(evefile, 'a') if check_lsl_offset: # check server LSL time server integrity logger.info( "Checking LSL server's timestamp integrity for logging software triggers." ) amp_name, amp_serial = pu.search_lsl() sr = StreamReceiver(window_size=1, buffer_size=1, amp_serial=amp_serial, eeg_only=False, amp_name=amp_name) local_time = pylsl.local_clock() server_time = sr.get_window_list()[1][-1] lsl_time_offset = local_time - server_time with open(eveoffset_file, 'a') as f: f.write( 'Local time: %.6f, Server time: %.6f, Offset: %.6f\n' % (local_time, server_time, lsl_time_offset)) logger.info('LSL timestamp offset (%.3f) saved to %s' % (lsl_time_offset, eveoffset_file)) elif self.lpttype == 'FAKE' or self.lpttype is None or self.lpttype is False: logger.warning('Using a fake trigger.') self.lpttype = 'FAKE' self.lpt = None else: logger.error('Unrecognized lpttype device name %s' % lpttype) sys.exit(-1)