Beispiel #1
0
 def update_session_data(self, data, epoch, save=True):
     """Update the session data and optionally save."""
     data['total_time_spent'] = self.experiment_clock.getTime()
     data['total_number_epochs'] = epoch.epoch_counter
     data['epochs'] = epoch.data
     if save:
         _save_session_related_data(self.session_save_location, data)
Beispiel #2
0
    def init_session_data(self, save: bool = True):
        """Initializes the session data; saved to session.json file.
        Parameters:
        -----------
            save - whether to save the data to disk.
        Returns:
        --------
            data - map with initial session data.
        """
        data = {
            'session': self.file_save,
            'session_type': self.session_description,
            'paradigm': 'RSVP',
            'epochs': {},
            'total_time_spent': self.experiment_clock.getTime(),
            'total_number_epochs': 0,
        }

        if save:
            _save_session_related_data(self.session_save_location, data)
        return data
Beispiel #3
0
    def execute(self):
        self.logger.debug('Starting Copy Phrase Task!')
        text_task = str(self.copy_phrase[0:self.spelled_letters_count])
        task_list = [(str(self.copy_phrase),
                      str(self.copy_phrase[0:self.spelled_letters_count]))]

        # Try Initializing Copy Phrase Wrapper:
        copy_phrase_task = CopyPhraseWrapper(self.min_num_seq,
                                             self.max_seq_length,
                                             signal_model=self.signal_model,
                                             fs=self.daq.device_info.fs,
                                             k=2, alp=self.alp,
                                             task_list=task_list,
                                             lmodel=self.language_model,
                                             is_txt_sti=self.is_txt_sti,
                                             device_name=self.daq.device_info.name,
                                             device_channels=self.daq.device_info.channels,
                                             stimuli_timing=[self.time_cross, self.time_flash])

        # Set new epoch (whether to present a new epoch),
        #   run (whether to cont. session),
        #   sequence counter (how many seq have occured).
        #   epoch counter and index (what epoch, and how many sequences within it)
        new_epoch = True
        run = True
        seq_counter = 0
        epoch_counter = 0
        epoch_index = 0

        # Init session data and save before beginning
        data = {
            'session': self.file_save,
            'session_type': 'Copy Phrase',
            'paradigm': 'RSVP',
            'epochs': {},
            'total_time_spent': self.experiment_clock.getTime(),
            'total_number_epochs': 0,
        }

        # Save session data
        _save_session_related_data(self.session_save_location, data)

        # check user input to make sure we should be going
        if not get_user_input(self.rsvp, self.wait_screen_message,
                              self.wait_screen_message_color,
                              first_run=True):
            run = False

        # Start the Session!
        while run:

            # check user input to make sure we should be going
            if not get_user_input(self.rsvp, self.wait_screen_message,
                                  self.wait_screen_message_color):
                break

            if self.copy_phrase[0:len(text_task)] == text_task:
                target_letter = self.copy_phrase[len(text_task)]
            else:
                target_letter = '<'

            # Get sequence information
            if new_epoch:

                # Init an epoch, getting initial stimuli
                new_epoch, sti = copy_phrase_task.initialize_epoch()
                ele_sti = sti[0]
                timing_sti = sti[1]
                color_sti = sti[2]

                # Increase epoch number and reset epoch index
                epoch_counter += 1
                data['epochs'][epoch_counter] = {}
                epoch_index = 0
            else:
                epoch_index += 1

            # Update task state and reset the static
            self.rsvp.update_task_state(text=text_task, color_list=['white'])
            self.rsvp.draw_static()
            self.window.flip()

            # Setup the new Stimuli
            self.rsvp.stim_sequence = ele_sti[0]
            if self.is_txt_sti:
                self.rsvp.color_list_sti = color_sti[0]
            self.rsvp.time_list_sti = timing_sti[0]

            # Pause for a time
            core.wait(self.buffer_val)

            # Do the self.RSVP sequence!
            sequence_timing = self.rsvp.do_sequence()

            self.first_stim_time = self.rsvp.first_stim_time

            # Write triggers to file
            _write_triggers_from_sequence_copy_phrase(
                sequence_timing,
                self.trigger_file,
                self.copy_phrase,
                text_task)

            core.wait(self.buffer_val)

            if seq_counter == 0:
                del sequence_timing[0]

            # reshape the data and triggers as needed for later modules
            raw_data, triggers, target_info = \
                process_data_for_decision(
                    sequence_timing,
                    self.daq,
                    self.window,
                    self.parameters,
                    self.first_stim_time,
                    self.static_offset)

            # Uncomment this to turn off fake decisions, but use fake data.
            # self.fake = False
            if self.fake:
                # Construct Data Record
                data['epochs'][epoch_counter][epoch_index] = {
                    'stimuli': ele_sti,
                    'eeg_len': len(raw_data),
                    'timing_sti': timing_sti,
                    'triggers': triggers,
                    'target_info': target_info,
                    'target_letter': target_letter,
                    'current_text': text_task,
                    'copy_phrase': self.copy_phrase}

                # Evaluate this sequence
                (target_letter, text_task, run) = \
                    fake_copy_phrase_decision(self.copy_phrase,
                                              target_letter,
                                              text_task)

                # here we assume, in fake mode, all sequences result in a selection.
                last_selection = text_task[-1]
                new_epoch = True
                # Update next state for this record
                data['epochs'][
                    epoch_counter][
                    epoch_index][
                    'next_display_state'] = \
                    text_task

            else:
                # Evaluate this sequence, returning whether to gen a new
                #  epoch (seq) or stimuli to present
                new_epoch, sti = \
                    copy_phrase_task.evaluate_sequence(raw_data, triggers,
                                                       target_info, self.collection_window_len)

                # Construct Data Record
                data['epochs'][epoch_counter][epoch_index] = {
                    'stimuli': ele_sti,
                    'eeg_len': len(raw_data),
                    'timing_sti': timing_sti,
                    'triggers': triggers,
                    'target_info': target_info,
                    'current_text': text_task,
                    'copy_phrase': self.copy_phrase,
                    'next_display_state':
                        copy_phrase_task.decision_maker.displayed_state,
                    'lm_evidence': copy_phrase_task
                        .conjugator
                        .evidence_history['LM'][0]
                        .tolist(),
                    'eeg_evidence': copy_phrase_task
                        .conjugator
                        .evidence_history['ERP'][-1]
                        .tolist(),
                    'likelihood': copy_phrase_task
                        .conjugator.likelihood.tolist()
                }

                # If new_epoch is False, get the stimuli info returned
                if not new_epoch:
                    ele_sti = sti[0]
                    timing_sti = sti[1]
                    color_sti = sti[2]

                # Get the current task text from the decision maker
                text_task = copy_phrase_task.decision_maker.displayed_state
                last_selection = copy_phrase_task.decision_maker.last_selection

            # if a letter was selected and feedback enabled, show the chosen letter
            if new_epoch and self.show_feedback:
                self.feedback.administer(last_selection, message='Selected:')

            # Update time spent and save data
            data['total_time_spent'] = self.experiment_clock.getTime()
            data['total_number_epochs'] = epoch_counter
            _save_session_related_data(self.session_save_location, data)

            # Decide whether to keep the task going
            max_tries_exceeded = seq_counter >= self.max_seq_length
            max_time_exceeded = data['total_time_spent'] >= self.max_seconds
            if (text_task == self.copy_phrase or max_tries_exceeded or
                    max_time_exceeded):
                if max_tries_exceeded:
                    logging.debug("Max tries exceeded: to allow for more tries"
                                  " adjust the Maximum Sequence Length "
                                  "(max_seq_len) parameter.")
                if max_time_exceeded:
                    logging.debug("Max time exceeded. To allow for more time "
                                  "adjust the max_minutes parameter.")
                run = False

            # Increment sequence counter
            seq_counter += 1

        # Update task state and reset the static
        self.rsvp.update_task_state(text=text_task, color_list=['white'])

        # Say Goodbye!
        self.rsvp.text = trial_complete_message(self.window, self.parameters)
        self.rsvp.draw_static()
        self.window.flip()

        # Give the system time to process
        core.wait(self.buffer_val)

        if self.daq.is_calibrated:
            _write_triggers_from_sequence_copy_phrase(
                ['offset', self.daq.offset], self.trigger_file,
                self.copy_phrase, text_task, offset=True)

        # Close the trigger file for this session
        self.trigger_file.close()

        # Wait some time before exiting so there is trailing eeg data saved
        core.wait(self.eeg_buffer)

        return self.file_save
Beispiel #4
0
    def execute(self):
        self.logger.debug('Starting Icon to Icon Task!')
        image_array, timing_array = generate_icon_match_images(
            self.len_sti, self.image_path, self.num_sti, self.timing,
            self.is_word)

        # Get all png images in image path
        alp_image_array = glob.glob(self.image_path + '*.png')

        # Remove plus image from array
        for image in alp_image_array:
            if image.endswith('PLUS.png'):
                alp_image_array.remove(image)

        if self.is_word:
            image_name_array = glob.glob(self.image_path + '*.png')
            for image in image_name_array:
                image_name_array[image_name_array.index(
                    image)] = path.basename(image)
            alp_image_array.extend(image_name_array)

        for image in alp_image_array:
            alp_image_array[alp_image_array.index(image)] = image.split(
                '/')[-1].split('.')[0]

        self.alp = alp_image_array

        # Try Initializing Copy Phrase Wrapper:
        #       (sig_pro, decision maker, signal_model)
        try:
            copy_phrase_task = CopyPhraseWrapper(
                self.min_num_seq,
                self.max_seq_length,
                signal_model=self.signal_model,
                fs=self.daq.device_info.fs,
                k=2,
                alp=self.alp,
                task_list=['unnecessary_string', 'unnecessary_string'],
                lmodel=self.language_model,
                is_txt_sti=self.is_txt_sti,
                device_name=self.daq.device_info.name,
                device_channels=self.daq.device_info.channels)
        except Exception as e:
            self.logger.debug(f'Error Initializing Icon to Icon Task! {e}')
            raise e

        run = True
        new_epoch = True
        epoch_index = 0
        correct_trials = 0

        # Init session data and save before beginning
        data = {
            'session': self.file_save,
            'session_type': 'Icon to Icon Matching',
            'paradigm': 'RSVP',
            'epochs': {},
            'total_time_spent': self.experiment_clock.getTime(),
            'total_number_epochs': 0,
        }

        # Save session data
        _save_session_related_data(self.session_save_location, data)

        # Check user input to make sure we should be going
        if not get_user_input(self.rsvp,
                              self.wait_screen_message,
                              self.wait_screen_message_color,
                              first_run=True):
            run = False

        current_trial = 0
        while run:
            # check user input to make sure we should be going
            if not get_user_input(self.rsvp, self.wait_screen_message,
                                  self.wait_screen_message_color):
                break

            if new_epoch:
                # Init an epoch, getting initial stimuli
                new_epoch, sti = copy_phrase_task.initialize_epoch()

                # If correct decisions are being faked, make sure that we always
                # are starting a new epoch
                if self.fake:
                    new_epoch = True

                # Increase epoch number and reset epoch index
                data['epochs'][current_trial] = {}
                epoch_index = 0
            else:
                epoch_index += 1

            data['epochs'][current_trial][epoch_index] = {}

            if current_trial < len(image_array) or not new_epoch:
                self.rsvp.sti.height = self.stimuli_height

                self.rsvp.stim_sequence = image_array[current_trial]
                self.rsvp.time_list_sti = timing_array
                # Change size of target word if we are in word matching mode
                if self.is_word:
                    # Generate list whose length is the length of the stimuli sequence, filled with the stimuli height
                    self.rsvp.size_list_sti = list(
                        repeat(self.stimuli_height,
                               len(self.rsvp.stim_sequence) + 1))
                    # Set the target word font size to the font size defined in parameters
                    self.rsvp.size_list_sti[0] = self.word_matching_text_size

                core.wait(self.buffer_val)

                self.rsvp.update_task_state(self.rsvp.stim_sequence[0],
                                            self.task_height, 'yellow',
                                            self.rsvp.win.size, self.is_word)

                # Do the sequence
                sequence_timing = self.rsvp.do_sequence()

                self.first_stim_time = self.rsvp.first_stim_time

                # Write triggers to file
                _write_triggers_from_sequence_calibration(
                    sequence_timing, self.trigger_file)

                # Wait for a time
                core.wait(self.buffer_val)

                # reshape the data and triggers as needed for later modules
                raw_data, triggers, target_info = \
                    process_data_for_decision(sequence_timing, self.daq, self.window,
                                              self.parameters, self.first_stim_time)

                # self.fake = False

                display_stimulus = self.rsvp.stim_sequence[0]

                display_message = False
                if self.fake:
                    # Construct Data Record
                    data['epochs'][current_trial][epoch_index] = {
                        'stimuli': image_array[current_trial],
                        'eeg_len': len(raw_data),
                        'timing_sti': timing_array,
                        'triggers': triggers,
                        'target_info': target_info,
                        'target_letter': display_stimulus
                    }
                    correct_decision = True
                    display_message = True
                    message_color = 'green'
                    current_trial += 1
                    correct_trials += 1
                    new_epoch = True
                    if self.is_word:
                        display_stimulus = self.image_path + \
                            self.rsvp.stim_sequence[0] + '.png'
                else:
                    new_epoch, sti = \
                        copy_phrase_task.evaluate_sequence(raw_data, triggers,
                                                           target_info, self.collection_window_len)

                    # Construct Data Record
                    data['epochs'][current_trial][epoch_index] = {
                        'stimuli':
                        image_array[current_trial],
                        'eeg_len':
                        len(raw_data),
                        'timing_sti':
                        timing_array,
                        'triggers':
                        triggers,
                        'target_info':
                        target_info,
                        'lm_evidence':
                        copy_phrase_task.conjugator.evidence_history['LM']
                        [0].tolist(),
                        'eeg_evidence':
                        copy_phrase_task.conjugator.evidence_history['ERP']
                        [0].tolist(),
                        'likelihood':
                        copy_phrase_task.conjugator.likelihood.tolist()
                    }

                    # Test whether to display feedback message, and what color
                    # the message should be
                    if new_epoch:
                        if self.is_word:
                            decide_image_path = self.image_path + \
                                copy_phrase_task.decision_maker.last_selection + '.png'
                        else:
                            decide_image_path = copy_phrase_task.decision_maker.last_selection + '.png'
                        correct_decision = decide_image_path == self.rsvp.stim_sequence[
                            0]
                        display_stimulus = decide_image_path
                        current_trial += 1
                        if correct_decision:
                            message_color = 'green'
                            correct_trials += 1
                        else:
                            message_color = 'red'
                        display_message = True
                    else:
                        display_message = False

                if display_message:
                    # Display feedback about whether decision was correct
                    visual_feedback = VisualFeedback(
                        display=self.window,
                        parameters=self.parameters,
                        clock=self.experiment_clock)
                    stimulus = display_stimulus
                    visual_feedback.message_color = message_color
                    visual_feedback.administer(stimulus,
                                               compare_assertion=None,
                                               message='Decision:')

                # Update time spent and save data
                data['total_time_spent'] = self.experiment_clock.getTime()
                data['total_number_epochs'] = current_trial
                _save_session_related_data(self.session_save_location, data)

                # Decide whether to keep the task going
                max_tries_exceeded = current_trial >= self.max_seq_length
                max_time_exceeded = data['total_time_spent'] >= self.max_seconds
                if (max_tries_exceeded or max_time_exceeded):
                    if max_tries_exceeded:
                        logging.debug(
                            "Max tries exceeded: to allow for more tries"
                            " adjust the Maximum Sequence Length "
                            "(max_seq_len) parameter.")
                    if max_time_exceeded:
                        logging.debug(
                            "Max time exceeded. To allow for more time "
                            "adjust the max_minutes parameter.")
                    run = False

            else:
                run = False

        # Write trial data to icon_data.csv in file save location
        with open(f"{self.file_save}/icon_data.csv", 'w+') as icon_output_csv:
            icon_output_writer = csv.writer(icon_output_csv, delimiter=',')
            icon_output_writer.writerow([
                'Participant ID',
                dirname(self.file_save).replace(self.data_save_path, '')
            ])
            icon_output_writer.writerow(['Date/Time', datetime.datetime.now()])
            if self.auc_filename:
                icon_output_writer.writerow([
                    'Calibration AUC',
                    basename(self.auc_filename).replace('.pkl', '')
                ])
            temp_epoch_index = 1 if epoch_index == 0 else epoch_index
            temp_current_trial = 1 if current_trial == 0 else current_trial
            icon_output_writer.writerow([
                'Percentage of correctly selected icons',
                (correct_trials / (temp_current_trial * temp_epoch_index)) *
                100
            ])
            icon_output_writer.writerow([
                'Task type',
                ('Icon to word' if self.is_word else 'Icon to icon')
            ])

        # Say Goodbye!
        self.rsvp.text = trial_complete_message(self.window, self.parameters)
        self.rsvp.draw_static()
        self.window.flip()

        # Give the system time to process
        core.wait(self.buffer_val)

        # Close this sessions trigger file and return some data
        self.trigger_file.close()

        # Wait some time before exiting so there is trailing eeg data saved
        core.wait(self.eeg_buffer)

        return self.file_save
Beispiel #5
0
    def execute(self):
        self.logger.debug('Starting Copy Phrase Task!')

        # already correctly spelled letters
        text_task = str(self.copy_phrase[0:self.spelled_letters_count])
        task_list = [(str(self.copy_phrase),
                      str(self.copy_phrase[0:self.spelled_letters_count]))]

        # Try Initializing Copy Phrase Wrapper:
        copy_phrase_task = CopyPhraseWrapper(
            self.min_num_inq,
            self.max_inq_per_trial,
            signal_model=self.signal_model,
            fs=self.daq.device_info.fs,
            k=2,
            alp=self.alp,
            task_list=task_list,
            lmodel=self.language_model,
            is_txt_stim=self.is_txt_stim,
            device_name=self.daq.device_info.name,
            device_channels=self.daq.device_info.channels,
            stimuli_timing=[self.time_cross, self.time_flash],
            decision_threshold=self.parameters['decision_threshold'],
            backspace_prob=self.parameters['lm_backspace_prob'],
            backspace_always_shown=self.parameters['backspace_always_shown'],
            filter_high=self.filter_high,
            filter_low=self.filter_low,
            filter_order=self.fitler_order,
            notch_filter_frequency=self.notch_filter_frequency)

        # Set new series (whether to present a new series),
        #   run (whether to cont. session),
        #   inquiry counter (how many inquiries have occured).
        #   series counter and index
        #   (what series, and how many inquiries within it)
        new_series = True
        run = True
        inq_counter = 0
        series_counter = 0
        series_index = 0

        # Init session data and save before beginning
        data = {
            'session': self.file_save,
            'session_type': 'Copy Phrase',
            'paradigm': 'RSVP',
            'series': {},
            'total_time_spent': self.experiment_clock.getTime(),
            'total_number_series': 0,
        }

        # Save session data
        _save_session_related_data(self.session_save_location, data)

        # check user input to make sure we should be going
        if not get_user_input(self.rsvp,
                              self.wait_screen_message,
                              self.wait_screen_message_color,
                              first_run=True):
            run = False

        # Start the Session!
        while run:

            # check user input to make sure we should be going
            if not get_user_input(self.rsvp, self.wait_screen_message,
                                  self.wait_screen_message_color):
                break

            if self.copy_phrase[0:len(text_task)] == text_task:
                # if correctly spelled so far, get the next unspelled letter.
                target_letter = self.copy_phrase[len(text_task)]
            else:
                # otherwise target is the backspace char.
                target_letter = '<'

            # Get inquiry information
            if new_series:

                # Init an series, getting initial stimuli
                new_series, sti = copy_phrase_task.initialize_series()
                ele_sti = sti[0]
                timing_sti = sti[1]
                color_sti = sti[2]

                # Increase series number and reset series index
                series_counter += 1
                data['series'][series_counter] = {}
                series_index = 0
            else:
                series_index += 1

            # Update task state and reset the static
            self.rsvp.update_task_state(text=text_task, color_list=['white'])
            self.rsvp.draw_static()
            self.window.flip()

            # Setup the new Stimuli
            self.rsvp.stimuli_inquiry = ele_sti[0]
            if self.is_txt_stim:
                self.rsvp.stimuli_colors = color_sti[0]
            self.rsvp.stimuli_timing = timing_sti[0]

            # Pause for a time
            core.wait(self.buffer_val)

            # Do the self.RSVP inquiry!
            inquiry_timing = self.rsvp.do_inquiry()

            # Write triggers to file
            _write_triggers_from_inquiry_copy_phrase(inquiry_timing,
                                                     self.trigger_file,
                                                     self.copy_phrase,
                                                     text_task)

            core.wait(self.buffer_val)

            # Delete calibration
            if inq_counter == 0:
                del inquiry_timing[0]

            # reshape the data and triggers as needed for later modules
            raw_data, triggers, target_info = \
                process_data_for_decision(
                    inquiry_timing,
                    self.daq,
                    self.window,
                    self.parameters,
                    self.rsvp.first_stim_time,
                    self.static_offset)

            # Uncomment this to turn off fake decisions, but use fake data.
            # self.fake = False
            if self.fake:
                # Construct Data Record
                data['series'][series_counter][series_index] = {
                    'stimuli': ele_sti,
                    'eeg_len': len(raw_data),
                    'timing_sti': timing_sti,
                    'triggers': triggers,
                    'target_info': target_info,
                    'target_letter': target_letter,
                    'current_text': text_task,
                    'copy_phrase': self.copy_phrase
                }

                # Evaluate this inquiry
                (target_letter, text_task, run) = \
                    fake_copy_phrase_decision(self.copy_phrase,
                                              target_letter,
                                              text_task)

                # here we assume, in fake mode, all inquiries result in a
                # selection.
                last_selection = text_task[-1]
                new_series, sti = copy_phrase_task.initialize_series()
                # Update next state for this record
                data['series'][
                    series_counter][
                    series_index][
                    'next_display_state'] = \
                    text_task

            else:
                # Evaluate this inquiry, returning whether to gen a new
                #  series (inq) or stimuli to present
                new_series, sti = \
                    copy_phrase_task.evaluate_inquiry(
                        raw_data,
                        triggers,
                        target_info,
                        self.collection_window_len)

                # Construct Data Record
                data['series'][series_counter][series_index] = {
                    'stimuli':
                    ele_sti,
                    'eeg_len':
                    len(raw_data),
                    'timing_sti':
                    timing_sti,
                    'triggers':
                    triggers,
                    'target_info':
                    target_info,
                    'current_text':
                    text_task,
                    'copy_phrase':
                    self.copy_phrase,
                    'next_display_state':
                    copy_phrase_task.decision_maker.displayed_state,
                    'lm_evidence':
                    copy_phrase_task.conjugator.evidence_history['LM']
                    [0].tolist(),
                    'eeg_evidence':
                    copy_phrase_task.conjugator.evidence_history['ERP']
                    [-1].tolist(),
                    'likelihood':
                    copy_phrase_task.conjugator.likelihood.tolist()
                }

                # If new_series is False, get the stimuli info returned
                if not new_series:
                    ele_sti = sti[0]
                    timing_sti = sti[1]
                    color_sti = sti[2]

                # Get the current task text from the decision maker
                text_task = copy_phrase_task.decision_maker.displayed_state
                last_selection = copy_phrase_task.decision_maker.last_selection

            # if a letter was selected and feedback enabled, show the chosen
            # letter
            if new_series and self.show_feedback:
                self.feedback.administer(last_selection,
                                         message='Selected:',
                                         line_color=self.feedback_color,
                                         fill_color=self.feedback_color)

            # Update time spent and save data
            data['total_time_spent'] = self.experiment_clock.getTime()
            data['total_number_series'] = series_counter
            _save_session_related_data(self.session_save_location, data)

            # Decide whether to keep the task going
            max_tries_exceeded = inq_counter >= self.max_inq_length
            max_time_exceeded = data['total_time_spent'] >= self.max_seconds
            if (text_task == self.copy_phrase or max_tries_exceeded
                    or max_time_exceeded):
                if max_tries_exceeded:
                    self.logger.debug(
                        'COPYPHRASE ERROR: Max tries exceeded. To allow for more tries '
                        'adjust the max_inq_len parameter.')
                if max_time_exceeded:
                    self.logger.debug(
                        'COPYPHRASE ERROR: Max time exceeded. To allow for more time '
                        'adjust the max_minutes parameter.')
                run = False

            # Increment inquiry counter
            inq_counter += 1

        # Update task state and reset the static
        self.rsvp.update_task_state(text=text_task, color_list=['white'])

        # Say Goodbye!
        self.rsvp.text = trial_complete_message(self.window, self.parameters)
        self.rsvp.draw_static()
        self.window.flip()

        # Give the system time to process
        core.wait(self.buffer_val)

        if self.daq.is_calibrated:
            _write_triggers_from_inquiry_copy_phrase(
                ['offset', self.daq.offset],
                self.trigger_file,
                self.copy_phrase,
                text_task,
                offset=True)

        # Close the trigger file for this session
        self.trigger_file.close()

        # Wait some time before exiting so there is trailing eeg data saved
        core.wait(self.eeg_buffer)

        return self.file_save