Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
    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