def bci_main(parameter_location: str, user: str, task: TaskType, experiment: str = DEFAULT_EXPERIMENT_ID) -> bool: """BCI Main. The BCI main function will initialize a save folder, construct needed information and execute the task. This is the main connection between any UI and running the app. It may also be invoked via tha command line. Ex. `python bci_main.py` this will default parameters, mode, user, and type. You can pass it those attributes with flags, if desired. Ex. `python bci_main.py --user "bci_user" --task "RSVP Calibration" --experiment "default" Input: parameter_location (str): location of parameters file to use user (str): name of the user task (TaskType): registered bcipy TaskType experiment_id (str): Name of the experiment. Default name is DEFAULT_EXPERIMENT_ID. """ validate_experiment(experiment) # Load parameters parameters = load_json_parameters(parameter_location, value_cast=True) # Update property to reflect the parameter source parameters['parameter_location'] = parameter_location if parameter_location != DEFAULT_PARAMETERS_PATH: parameters.save() default_params = load_json_parameters(DEFAULT_PARAMETERS_PATH, value_cast=True) if parameters.add_missing_items(default_params): raise Exception('Parameters file out of date.') # update our parameters file with system related information sys_info = get_system_info() # Initialize Save Folder save_folder = init_save_data_structure( parameters['data_save_loc'], user, parameter_location, task=task.label, experiment_id=experiment) # configure bcipy session logging configure_logger(save_folder, log_name=parameters['log_name'], version=sys_info['bcipy_version']) logging.getLogger(__name__).info(sys_info) # Collect experiment field data collect_experiment_field_data(experiment, save_folder) return execute_task(task, parameters, save_folder)
def init_display_window(parameters): """ Init Display Window. Function to Initialize main display window needed for all later stimuli presentation. See Psychopy official documentation for more information and working demos: http://www.psychopy.org/api/visual/window.html """ # Check is full_screen mode is set and get necessary values if parameters['full_screen']: # get relevant info about the system info = get_system_info() # set window attributes based on resolution window_height = info['resolution'][1] window_width = info['resolution'][0] # set full screen mode to true (removes os dock, explorer etc.) full_screen = True # otherwise, get user defined window attributes else: # set window attributes directly from parameters file window_height = parameters['window_height'] window_width = parameters['window_width'] # make sure full screen is set to false full_screen = False # Initialize PsychoPy Window for Main Display of Stimuli display_window = visual.Window(size=[window_width, window_height], screen=int(parameters['stim_screen']), allowGUI=False, useFBO=False, fullscr=full_screen, allowStencil=False, monitor='mainMonitor', winType='pyglet', units='norm', waitBlanking=False, color=parameters['background_color']) # Return display window to caller return display_window
def bci_main(parameters: dict, user: str, exp_type: int, mode: str) -> bool: """BCI Main. The BCI main function will initialize a save folder, construct needed information and execute the task. This is the main connection between any UI and running the app. It may also be invoked via tha command line. Ex. `python bci_main.py` this will default parameters, mode, user, and type. You can pass it those attributes with flags, if desired. Ex. `python bci_main.py --user "bci_user" --mode "SHUFFLE"` Input: parameters (dict): parameter dictionary user (str): name of the user exp_type (int): type of experiment. Ex. 1 = calibration mode (str): BCI mode. Ex. RSVP, SHUFFLE, MATRIX """ # Define the parameter and data save location parameter_location = parameters['parameter_location'] data_save_location = parameters['data_save_loc'] # Initialize Save Folder save_folder = init_save_data_structure(data_save_location, user, parameter_location, mode, exp_type) # Register Task Type task_type = {'mode': mode, 'exp_type': exp_type} # update our parameters file with system related information parameters.update(get_system_info()) # configure bcipy session logging configure_logger(save_folder, log_name=parameters['log_name'], version=parameters['bcipy_version']) return execute_task(task_type, parameters, save_folder)
def _generate_sequence(self): """Generate Sequence. Generate stimuli for next RSVP sequence. """ stim_info = [] for idx in range(len(self.stimuli_sequence)): current_stim = {} # turn ms timing into frames! Much more accurate! current_stim['time_to_present'] = int(self.stimuli_timing[idx] * self.refresh_rate) # check if stimulus needs to use a non-default size if self.size_list_sti: this_stimuli_size = self.size_list_sti[idx] else: this_stimuli_size = self.stimuli_height # Set the Stimuli attrs if self.stimuli_sequence[idx].endswith('.png'): current_stim['sti'] = self.create_stimulus( mode='image', height_int=this_stimuli_size) current_stim['sti'].image = self.stimuli_sequence[idx] current_stim['sti'].size = resize_image( current_stim['sti'].image, current_stim['sti'].win.size, this_stimuli_size) current_stim['sti_label'] = path.splitext( path.basename(self.stimuli_sequence[idx]))[0] else: # text stimulus current_stim['sti'] = self.create_stimulus( mode='text', height_int=this_stimuli_size) txt = self.stimuli_sequence[idx] # customize presentation of space char. current_stim[ 'sti'].text = txt if txt != SPACE_CHAR else self.space_char current_stim['sti'].color = self.stimuli_colors[idx] current_stim['sti_label'] = txt # test whether the word will be too big for the screen text_width = current_stim['sti'].boundingBox[0] if text_width > self.window.size[0]: info = get_system_info() text_height = current_stim['sti'].boundingBox[1] # If we are in full-screen, text size in Psychopy norm units # is monitor width/monitor height if self.window.size[0] == info['RESOLUTION'][0]: new_text_width = info['RESOLUTION'][0] / \ info['RESOLUTION'][1] else: # If not, text width is calculated relative to both # monitor size and window size new_text_width = ( self.window.size[1] / info['RESOLUTION'][1]) * ( info['RESOLUTION'][0] / info['RESOLUTION'][1]) new_text_height = (text_height * new_text_width) / text_width current_stim['sti'].height = new_text_height stim_info.append(current_stim) return stim_info
def do_sequence(self): """Do Sequence. Animates a sequence of flashing letters to achieve RSVP. """ # init an array for timing information timing = [] if self.first_run: # play a sequence start sound to help orient triggers first_stim_timing = _calibration_trigger( self.experiment_clock, trigger_type=self.trigger_type, display=self.window, on_trigger=self.marker_writer.push_marker) timing.append(first_stim_timing) self.first_stim_time = first_stim_timing[-1] self.first_run = False # do the sequence for idx in range(len(self.stimuli_sequence)): # set a static period to do all our stim setting. # will warn if ISI value is violated. self.staticPeriod.start(self.static_time) # turn ms timing into frames! Much more accurate! self.time_to_present = int(self.stimuli_timing[idx] * self.refresh_rate) # check if stimulus needs to use a non-default size if self.size_list_sti: this_stimuli_size = self.size_list_sti[idx] else: this_stimuli_size = self.stimuli_height # Set the Stimuli attrs if self.stimuli_sequence[idx].endswith('.png'): self.sti = self.create_stimulus(mode='image', height_int=this_stimuli_size) self.sti.image = self.stimuli_sequence[idx] self.sti.size = resize_image(self.sti.image, self.sti.win.size, this_stimuli_size) sti_label = path.splitext( path.basename(self.stimuli_sequence[idx]))[0] else: # text stimulus self.sti = self.create_stimulus(mode='text', height_int=this_stimuli_size) txt = self.stimuli_sequence[idx] # customize presentation of space char. self.sti.text = txt if txt != SPACE_CHAR else self.space_char self.sti.color = self.stimuli_colors[idx] sti_label = txt # test whether the word will be too big for the screen text_width = self.sti.boundingBox[0] if text_width > self.window.size[0]: info = get_system_info() text_height = self.sti.boundingBox[1] # If we are in full-screen, text size in Psychopy norm units # is monitor width/monitor height if self.window.size[0] == info['RESOLUTION'][0]: new_text_width = info['RESOLUTION'][0] / info['RESOLUTION'][1] else: # If not, text width is calculated relative to both # monitor size and window size new_text_width = ( self.window.size[1] / info['RESOLUTION'][1]) * ( info['RESOLUTION'][0] / info['RESOLUTION'][1]) new_text_height = (text_height * new_text_width) / text_width self.sti.height = new_text_height # End static period self.staticPeriod.complete() # Reset the timing clock to start presenting self.window.callOnFlip(self.trigger_callback.callback, self.experiment_clock, sti_label) self.window.callOnFlip(self.marker_writer.push_marker, sti_label) if idx == 0 and callable(self.first_stim_callback): self.first_stim_callback(self.sti) # Draw stimulus for n frames for _n_frames in range(self.time_to_present): self.sti.draw() self.draw_static() self.window.flip() # append timing information if self.is_txt_stim: timing.append(self.trigger_callback.timing) else: timing.append(self.trigger_callback.timing) self.trigger_callback.reset() # draw in static and flip once more self.draw_static() self.window.flip() return timing