示例#1
0
def run_leave_one_out_cv(features, labels, classifier=LinearDiscriminantAnalysis()):
    """
    Runs leave one out CV.
    :param features: Features shape(epoch, feature)
    :param labels: list of lables of length num epochs
    :param classifier: Sklearn classifier (Defaults to LDA)
    :return: A list of cross validation scores.  Use np.average on the result to find the average score.
    """
    loo = LeaveOneOut()
    scores = []
    for train_indexes, test_indexes in loo.split(features, labels):

        # Assert our split maintains the same number of features
        CCDLAssert.assert_equal(len(train_indexes) + len(test_indexes), features.shape[0])

        # Assert we have the same number of features

        X_train, X_test = features[train_indexes, :], features[test_indexes, :]
        Y_train, Y_test = np.asarray(labels)[train_indexes], np.asarray(labels)[test_indexes]

        # Assert our X_train and X_test have the same number of features
        CCDLAssert.assert_equal(X_train.shape[1], X_test.shape[1])

        # Fit our classifier to our
        classifier.fit(X_train, Y_train)

        score = classifier.score(X_test, Y_test)
        scores.append(score)

    return scores
示例#2
0
def run_leave_one_out_cv(features,
                         labels,
                         classifier=LinearDiscriminantAnalysis()):
    """
    Runs leave one out CV.
    :param features: Features shape(epoch, feature)
    :param labels: list of lables of length num epochs
    :param classifier: Sklearn classifier (Defaults to LDA)
    :return: A list of cross validation scores.  Use np.average on the result to find the average score.
    """
    loo = LeaveOneOut()
    scores = []
    for train_indexes, test_indexes in loo.split(features, labels):

        # Assert our split maintains the same number of features
        CCDLAssert.assert_equal(
            len(train_indexes) + len(test_indexes), features.shape[0])

        # Assert we have the same number of features

        X_train, X_test = features[train_indexes, :], features[test_indexes, :]
        Y_train, Y_test = np.asarray(labels)[train_indexes], np.asarray(
            labels)[test_indexes]

        # Assert our X_train and X_test have the same number of features
        CCDLAssert.assert_equal(X_train.shape[1], X_test.shape[1])

        # Fit our classifier to our
        classifier.fit(X_train, Y_train)

        score = classifier.score(X_test, Y_test)
        scores.append(score)

    return scores
示例#3
0
def extract_min_difference_between_lists(lst_large, lst_small):
    """
    Takes a list and extracts the minimum difference between two lists.
    For example
        [3, 5, 125, 23543]
        [1, 4, 120, 2354]
    Would return 1
    """
    AV.assert_equal(len(lst_large), len(lst_small))
    return min([a_i - b_i for a_i, b_i in zip(lst_large, lst_small)])
示例#4
0
def convert_ununiform_start_stop_lists_to_uniform_start_stop_lists(start_lst, stop_lst):
    """
    Takes a list of start indexes and a list of end indexes a returns a new end index list such that trial_dur = stop_lst[i] - start_lst[i] and trial dur is the same for
    each list (set to the minimum trial dur contained in the list)
    :param start_lst: list of floats denoting starts of trials
    :param stop_lst: list of floats denoting end of trials
    :return: new stop list
    """
    AV.assert_equal(len(start_lst), len(stop_lst))
    dur = extract_min_difference_between_lists(stop_lst, start_lst)
    return [start_lst_val + dur for start_lst_val in start_lst]
示例#5
0
def fire(high_flag, bgm, tms, high_intensity, low_intensity):
    """
    Fire the TMS at the high intensity if high_flag, else we'll fire it at the low intensity.
    """
    Assert.assert_less(low_intensity, high_intensity)
    # flash red crosshair
    fire_time = time.time()
    bgm.flash_red()
    if high_flag:
        tms.tms_fire(i=high_intensity)
    else:
        tms.tms_fire(i=low_intensity)
    return fire_time
示例#6
0
def epoch_data(eeg_indexes, raw_data, trial_starts, trial_stops, trim=False):
    """
    Takes raw data of shape [sample, channel] and returns epoched data of shape [epoch, sample channel].
    The epoches are taken according to the indexing of the start and stop values in the eeg indexes

    :param eeg_indexes: epoch index for each sample in raw data (eeg index or time)
    :param raw_data: data shape [sample, channel]
    :param trial_starts: lst of trial start values (eeg index or time)
    :param trial_stops: lst of trial start values (eeg index or time)
    :param trim: if trim, we will cut the end of one axis to make the concat work.
    :return:
    """
    AV.assert_equal(len(eeg_indexes), raw_data.shape[0])
    AV.assert_equal(len(trial_starts), len(trial_stops))
    epoched_data = None
    for start_stop_index in xrange(len(trial_starts)):
        start_packet_index = bisect.bisect_right(eeg_indexes, trial_starts[start_stop_index])
        end_packet_index = bisect.bisect_left(eeg_indexes, trial_stops[start_stop_index])
        AV.assert_less(start_packet_index, end_packet_index)
        trial_epoched_data = np.expand_dims(raw_data[start_packet_index:end_packet_index], axis=0)
        try:
            epoched_data = trial_epoched_data if epoched_data is None else np.concatenate((epoched_data, trial_epoched_data), axis=0)
        except ValueError:
            if trim:
                min_shape = min(epoched_data.shape[1], trial_epoched_data.shape[1])
                epoched_data = epoched_data[:, :min_shape, :]
                trial_epoched_data = trial_epoched_data[:, :min_shape, :]
                epoched_data = np.concatenate((epoched_data, trial_epoched_data), axis=0)
            else:
                raise ValueError('Epoched data shape', epoched_data.shape, 'Trial Epoched data shape', trial_epoched_data.shape)
    AV.assert_equal(len(trial_stops), epoched_data.shape[0])
    return epoched_data
示例#7
0
def convert_start_end_index_lists_to_single_duration_trials(start_trial_index, end_trial_index):
    """
    Takes two lists of start and end indexes and returns a new end trial index list that ensures
    that all epochs will be the same duration. The duration used is the minimum duration between
    corresponding entries in the lists
    
    Example:
        start_trial_index = [0, 50, 100] 
        end_trial_index = [10, 60, 109]
        
        returns -> [9, 59, 109]
    
    :param start_trial_index: Indexes marking the start of trials
    :param end_trial_index: Indexes marking the end of trials.
    :return: List of new end trial indexes that is equal to the start index list + the minimum duration
    """
    AV.assert_equal(len(start_trial_index), len(end_trial_index))
    return CCDLArrayParser.convert_ununiform_start_stop_lists_to_uniform_start_stop_lists(start_lst=start_trial_index, stop_lst=end_trial_index)
示例#8
0
def trim_freqs(freqs, density, high=None, low=None):
    """
    Takes freqs and density and trims them according to the high and low values.
    
    if freqs = [ 10.  11.  12.  13.  14. 15. 16]
    and trim freqs is called on this list with high=15 and low=10,
    the result would be [ 10.  11.  12.  13.  14.]
    
    :param freqs: freqs (numpy array)
    :param density: density -- Shape: (epoch, sample, channel) OR (epoch, sample)
    :param high: removes all freqs above and equal to this val . Cast to int if passed a float.
    :param low: removes all freqs below this val.  Cast to int if passed a float.
    :return: freqs, density
            Both elements are modified. Lenght of freqs is equal to the size of the first axis of density (samples).
    """
    original_num_samples = density.shape[1]
    if high is None and low is None:
        raise ValueError('High or low must be an int')

    if high is not None:
        high = int(high)

        index_of_high = bisect.bisect_left(a=freqs, x=high)
        freqs = freqs[:index_of_high]
        try:
            density = density[:, :index_of_high, :]
        except IndexError:
            density = density[:, :index_of_high]
    if low is not None:
        low = int(low)
        index_of_low = bisect.bisect_left(a=freqs, x=low)
        freqs = freqs[index_of_low:]
        try:
            density = density[:, index_of_low:, :]
        except IndexError:
            density = density[:, index_of_low:]
    # Assure we trimmed something
    AV.assert_not_equal(original_num_samples, density.shape[1])
    # Ensure each density pos has a corresponding freq.
    AV.assert_equal(len(freqs), density.shape[1])

    # return the trimmed freqs and trimmed density.
    return freqs, density
示例#9
0
def trim_freqs(freqs, density, high=None, low=None):
    """
    Takes freqs and density and trims them according to the high and low values.
    
    if freqs = [ 10.  11.  12.  13.  14. 15. 16]
    and trim freqs is called on this list with high=15 and low=10,
    the result would be [ 10.  11.  12.  13.  14.]
    
    :param freqs: freqs (numpy array)
    :param density: density -- Shape: (epoch, sample, channel) OR (epoch, sample)
    :param high: removes all freqs above and equal to this val . Cast to int if passed a float.
    :param low: removes all freqs below this val.  Cast to int if passed a float.
    :return: freqs, density
            Both elements are modified. Lenght of freqs is equal to the size of the first axis of density (samples).
    """
    original_num_samples = density.shape[1]
    if high is None and low is None:
        raise ValueError('High or low must be an int')

    if high is not None:
        high = int(high)

        index_of_high = bisect.bisect_left(a=freqs, x=high)
        freqs = freqs[:index_of_high]
        try:
            density = density[:, :index_of_high, :]
        except IndexError:
            density = density[:, :index_of_high]
    if low is not None:
        low = int(low)
        index_of_low = bisect.bisect_left(a=freqs, x=low)
        freqs = freqs[index_of_low:]
        try:
            density = density[:, index_of_low:, :]
        except IndexError:
            density = density[:, index_of_low:]
    # Assure we trimmed something
    AV.assert_not_equal(original_num_samples, density.shape[1])
    # Ensure each density pos has a corresponding freq.
    AV.assert_equal(len(freqs), density.shape[1])

    # return the trimmed freqs and trimmed density.
    return freqs, density
示例#10
0
def fire_twice(c1_turn, c2_turn, bgm, tms, high_intensity, low_intensity, attempt):
    """
    Fires the TMS twice if fire_tms_flag.  If not fire_tms_flag, we'll just flash the crosshairs red.
    
    Fires according to:
        TMS- High threshold if cX_turn is True (we need to rotate the piece)

    Fire High if Rotate.  Fire low if Not Rotate.

    :param c1_turn: True if we need to rotate the piece as according to c1, false otherwise
    :param c2_turn: True if we need to rotate the piece as according to c2, false otherwise
    :param bgm: Block game manager (so we can flash the cross hairs)
    :param tms: the TMS object
    :param high_intensity: the high intensity to fire TMS
    :param low_intensity: the low intensity to fire TMS
    :param attempt: show the round index (1 or 2)
    """
    # set up
    Assert.assert_less(low_intensity, high_intensity)
    verbose_info(VERBOSE, "Firing TMS: %s, %s" % ('High' if c1_turn else "Low", 'High' if c2_turn else "Low"))
    fire_times = []
    for i, cx_turn in enumerate([c1_turn, c2_turn]):
        # show prompt
        prompt_screen(bgm, attempt + 1, i + 1)
        # fire
        if FIRE_TMS:
            # Set our intensity now so we can fire sooner later.
            if cx_turn:
                tms.set_intensity(intensity=high_intensity)
            else:
                tms.set_intensity(intensity=low_intensity)

        if FIRE_TMS:
            fire_times.append(fire(cx_turn, bgm=bgm, tms=tms, high_intensity=high_intensity, low_intensity=low_intensity))
        # Only sleep after c1_turn
        if i == 0:  # We need to sleep between firings for safety reasons.
            time.sleep(Constants.SLEEP_BETWEEN_FIRINGS)

    if not FIRE_TMS:
        fire_times = [None, None]

    return fire_times
示例#11
0
def convert_start_end_index_lists_to_single_duration_trials(
        start_trial_index, end_trial_index):
    """
    Takes two lists of start and end indexes and returns a new end trial index list that ensures
    that all epochs will be the same duration. The duration used is the minimum duration between
    corresponding entries in the lists
    
    Example:
        start_trial_index = [0, 50, 100] 
        end_trial_index = [10, 60, 109]
        
        returns -> [9, 59, 109]
    
    :param start_trial_index: Indexes marking the start of trials
    :param end_trial_index: Indexes marking the end of trials.
    :return: List of new end trial indexes that is equal to the start index list + the minimum duration
    """
    AV.assert_equal(len(start_trial_index), len(end_trial_index))
    return CCDLArrayParser.convert_ununiform_start_stop_lists_to_uniform_start_stop_lists(
        start_lst=start_trial_index, stop_lst=end_trial_index)
示例#12
0
def average_density_over_epochs(density):
    """
    Takes our densioty of shape (epoch, sample, channel) and averages over all trials.

    :param density: Spectral density of the form (epoch, sample, channel)
    :return: New density of the form (1, sample, channel), where the sample dimension is averaged over epoch
    """

    # Assert density is of the form (epoch, sample, channel)
    assert len(density.shape) == 3

    num_samples = density.shape[1]
    num_channles = density.shape[2]
    averaged = np.average(density, axis=0)

    # restore our epoch dim.
    averaged = np.expand_dims(averaged, axis=0)

    # Ensure we didn't change any unwanted dims.  Only num_epochs (dim 0) should change.
    AV.assert_equal(num_samples, averaged.shape[1])
    AV.assert_equal(num_channles, averaged.shape[2])
    return averaged
示例#13
0
def average_density_over_epochs(density):
    """
    Takes our densioty of shape (epoch, sample, channel) and averages over all trials.

    :param density: Spectral density of the form (epoch, sample, channel)
    :return: New density of the form (1, sample, channel), where the sample dimension is averaged over epoch
    """

    # Assert density is of the form (epoch, sample, channel)
    assert len(density.shape) == 3

    num_samples = density.shape[1]
    num_channles = density.shape[2]
    averaged = np.average(density, axis=0)

    # restore our epoch dim.
    averaged = np.expand_dims(averaged, axis=0)

    # Ensure we didn't change any unwanted dims.  Only num_epochs (dim 0) should change.
    AV.assert_equal(num_samples, averaged.shape[1])
    AV.assert_equal(num_channles, averaged.shape[2])
    return averaged
示例#14
0
def main(data_folder):
    # Set up the TMS Machine
    tms = None
    if FIRE_TMS:
        tms = TMS.TMS()
        tms.tms_arm()
    # sampling rate
    fs = SystemsInfo.get_eeg_sampling_rate(EEGConstants.NAME)
    # manage data storage / Start EEG threads
    subject_num, subject_data_folder_path = FileParser.manage_storage(data_storage_location=data_folder,
                                                                      take_init=TAKE_INIT)
    condition, experiment_num = 0, 0
    # for BrainAmp
    live_channels = ['Oz']

    if TAKE_INIT:
        experiment_num = int(raw_input('TMS Group Experiment Number Tracker:'))
        condition = int(raw_input('Enter condition (int):'))
        tms_low = int(raw_input('Enter TMS Low intensity (integer between 1 and 100):'))
        tms_high = int(raw_input('Enter TMS High intensity (integer between 1 and 100):'))
        # check intensity range
        Assert.assert_less(tms_low, tms_high)
        Assert.assert_less(tms_high, 100)
        Assert.assert_greater(tms_low, 0)
    else:
        tms_high, tms_low = 80, 50

    # Create Arduino
    if RUN_ARDUINO:
        ard = Arduino(com_port=ARDUINO_COMPORT)
    else:
        ard = None
    # Load our reference list
    ref_list = FileParser.load_yaml_file('ref/Condition%d.yaml' % condition)
    verbose_info(VERBOSE, ref_list)
    # Set up our EEG system
    if RUN_EEG:
        eeg = EEG.start_eeg(EEGConstants.NAME, live_channels, subject_data_folder_path, subject_num)
    else:
        eeg, data_save_queue = None, Queue.Queue()
    # out buffer queue
    out_buffer_queue = eeg.out_buffer_queue if eeg is not None else None
    # Start our Logging
    log_file_save_location = subject_data_folder_path + 'Subject%s_log.txt' % subject_num
    logger = Log.Log(subject_log_file_path=log_file_save_location)
    verbose_info(VERBOSE, "Saving Log File: " + log_file_save_location)

    # graphics
    bgm = start_graphics()
    # Meta contains all the meta info about the experiment, such as the condition number and the subject number.
    meta = {'condition': condition, 'subject_id': subject_num, 'high_tms': tms_high, 'low_tms': tms_low,
            'experiment_num': experiment_num, 'subject_num': subject_num, 'data_path': subject_data_folder_path}
    logger.info(str(meta))  # Save our meta info to our log file.

    # Start
    run_main_logic(ref_list=ref_list, bgm=bgm, ard=ard, logger=logger, eeg=eeg, fs=fs,
                   out_buffer_queue=out_buffer_queue, tms=tms, tms_low=tms_low, tms_high=tms_high)
示例#15
0
def epoch_data(eeg_indexes, raw_data, trial_starts, trial_stops, trim=False):
    """
    Takes raw data of shape [sample, channel] and returns epoched data of shape [epoch, sample channel].
    The epoches are taken according to the indexing of the start and stop values in the eeg indexes

    :param eeg_indexes: epoch index for each sample in raw data (eeg index or time)
    :param raw_data: data shape [sample, channel]
    :param trial_starts: lst of trial start values (eeg index or time)
    :param trial_stops: lst of trial start values (eeg index or time)
    :param trim: if trim, we will cut the end of one axis to make the concat work.
    :return:
    """
    AV.assert_equal(len(eeg_indexes), raw_data.shape[0])
    AV.assert_equal(len(trial_starts), len(trial_stops))
    epoched_data = None
    for start_stop_index in xrange(len(trial_starts)):
        start_packet_index = bisect.bisect_right(
            eeg_indexes, trial_starts[start_stop_index])
        end_packet_index = bisect.bisect_left(eeg_indexes,
                                              trial_stops[start_stop_index])
        AV.assert_less(start_packet_index, end_packet_index)
        trial_epoched_data = np.expand_dims(
            raw_data[start_packet_index:end_packet_index], axis=0)
        try:
            epoched_data = trial_epoched_data if epoched_data is None else np.concatenate(
                (epoched_data, trial_epoched_data), axis=0)
        except ValueError:
            if trim:
                min_shape = min(epoched_data.shape[1],
                                trial_epoched_data.shape[1])
                epoched_data = epoched_data[:, :min_shape, :]
                trial_epoched_data = trial_epoched_data[:, :min_shape, :]
                epoched_data = np.concatenate(
                    (epoched_data, trial_epoched_data), axis=0)
            else:
                raise ValueError('Epoched data shape', epoched_data.shape,
                                 'Trial Epoched data shape',
                                 trial_epoched_data.shape)
    AV.assert_equal(len(trial_stops), epoched_data.shape[0])
    return epoched_data
示例#16
0
    def start_buffer(self):
        """
         Starts the buffer, reading from buffer_queue and writing to out_buffer_queue
         once the buffer reaches moving_window_size.  Once the buffer reaches moving_window_size and is placed on the
         queue, the buffer is cleared.
        """
        # This is our lead and trailing storage indexes.  This means the data in our buffer (as seen by the client)
            # is self.buffer[trail_buffer_storage_index:lead_buffer_storage_index, :]
        lead_buffer_storage_index = -1  # Set to -1 so first update will make it 0. This avoids a fense-post problem.
        trail_buffer_storage_index = lead_buffer_storage_index - self.moving_window_size
        sample_index = 0
        while True:
            # We follow the algorithm
                # 1. Get new sample and update indexes
                # 2. Check if we need to adjust the buffer bounds
                # 3. Add sample to the buffer at sample_index
                # 4. Check if we need to put our buffer on the sample queue
                    # The buffer should be formatted so this can be done easily

            ###############################################
            #  Step 1 - Get new sample and update indexes #
            ###############################################
            sample_arr = self.buffer_queue.get()  # A blocking call
            if sample_arr == 'stop':
                # Reset our indexes.
                lead_buffer_storage_index = -1  # Set to -1 so first update will make it 0. This avoids a fense-post problem.
                trail_buffer_storage_index = lead_buffer_storage_index - self.moving_window_size
                sample_index = 0
                self.handle_stop()


            # This one we just collected is our nth sample.
            # Samples are zero based indexed
            sample_index += 1
            lead_buffer_storage_index += 1
            trail_buffer_storage_index += 1


            ############################################################
            #  Step 2  - Check if we need to adjust the buffer bounds  #
            ############################################################
            # Check if we're going to go over the buffer limit.
            if lead_buffer_storage_index == self.internal_buffer_size:
                # Fix buffer by coping over data.  We use self.moving_window_size - 1 because we have not yet
                # inserted the new data.
                self.buffer[0:self.moving_window_size - 1, :] = self.buffer[trail_buffer_storage_index: lead_buffer_storage_index - 1]
                # Reset our indexes.
                lead_buffer_storage_index = self.moving_window_size
                trail_buffer_storage_index = 0

            # Run some assertions
            AV.assert_less(lead_buffer_storage_index, self.internal_buffer_size)
            AV.assert_equal(lead_buffer_storage_index - trail_buffer_storage_index, self.moving_window_size)

            ############################################
            #  Step 3  - Add Add sample to the buffer  #
            ############################################
            self.buffer[lead_buffer_storage_index, :] = sample_arr


            #####################################################################
            #  Step 4 - Check if we need to put our buffer on the sample queue  #
            #####################################################################
            # Check if we need to put the buffer on the queue.
            if sample_index == self.update_interval:
                sub_buffer_to_send = self.buffer[trail_buffer_storage_index:lead_buffer_storage_index, :]
                AV.assert_equal(sub_buffer_to_send.shape[0], self.moving_window_size, message="Internal Error - buffer is incorrectly formatted")
                # Add our buffer to the queue.
                self.out_queue.put(sub_buffer_to_send)
                # Reset our sample index
                sample_index = 0
示例#17
0
CURSOR_TASK_SIZE = (1920, 1080)
# the distance of steps taken in the task
STEP = 50
# results
STOP_EARLY = 'stop_early'
STOP_LATE = 'stop_late'
YES = 'yes'
NO = 'no'
# some constants
CELL_SIZE = 100  # Size of the cells
NUM_COLS = 12  # Number of columns for Tetris pieces on the board (Affects Size)
NUM_ROWS = 10  # Number of rows for Tetris pieces in the board
BOARD_WIDTH = CELL_SIZE * NUM_COLS
BOARD_HEIGHT = CELL_SIZE * NUM_ROWS
# We want our number of columns to be divisible by 3
Assert.assert_equal(NUM_COLS % 3, 0)
"""
SSVEP Related
"""
EEG_COLLECT_TIME_SECONDS = 15
WINDOW_SIZE_SECONDS = 2
RECEIVER_Q = 'Did you see a phosphene?'
SENDER_Q = 'Should the piece be turned?'
"""
Communication Related
"""
PORT = 9999
# Guthrie
# 128.95.226.122
# 69.91.185.63
# 173.250.200.83
示例#18
0
    def start_buffer(self):
        """
         Starts the buffer, reading from buffer_queue and writing to out_buffer_queue
         once the buffer reaches moving_window_size.  Once the buffer reaches moving_window_size and is placed on the
         queue, the buffer is cleared.
        """
        # This is our lead and trailing storage indexes.  This means the data in our buffer (as seen by the client)
        # is self.buffer[trail_buffer_storage_index:lead_buffer_storage_index, :]
        lead_buffer_storage_index = -1  # Set to -1 so first update will make it 0. This avoids a fense-post problem.
        trail_buffer_storage_index = lead_buffer_storage_index - self.moving_window_size
        sample_index = 0
        while True:
            # We follow the algorithm
            # 1. Get new sample and update indexes
            # 2. Check if we need to adjust the buffer bounds
            # 3. Add sample to the buffer at sample_index
            # 4. Check if we need to put our buffer on the sample queue
            # The buffer should be formatted so this can be done easily

            ###############################################
            #  Step 1 - Get new sample and update indexes #
            ###############################################
            sample_arr = self.buffer_queue.get()  # A blocking call
            if sample_arr == 'stop':
                # Reset our indexes.
                lead_buffer_storage_index = -1  # Set to -1 so first update will make it 0. This avoids a fense-post problem.
                trail_buffer_storage_index = lead_buffer_storage_index - self.moving_window_size
                sample_index = 0
                self.handle_stop()

            # This one we just collected is our nth sample.
            # Samples are zero based indexed
            sample_index += 1
            lead_buffer_storage_index += 1
            trail_buffer_storage_index += 1

            ############################################################
            #  Step 2  - Check if we need to adjust the buffer bounds  #
            ############################################################
            # Check if we're going to go over the buffer limit.
            if lead_buffer_storage_index == self.internal_buffer_size:
                # Fix buffer by coping over data.  We use self.moving_window_size - 1 because we have not yet
                # inserted the new data.
                self.buffer[0:self.moving_window_size - 1, :] = self.buffer[
                    trail_buffer_storage_index:lead_buffer_storage_index - 1]
                # Reset our indexes.
                lead_buffer_storage_index = self.moving_window_size
                trail_buffer_storage_index = 0

            # Run some assertions
            AV.assert_less(lead_buffer_storage_index,
                           self.internal_buffer_size)
            AV.assert_equal(
                lead_buffer_storage_index - trail_buffer_storage_index,
                self.moving_window_size)

            ############################################
            #  Step 3  - Add Add sample to the buffer  #
            ############################################
            self.buffer[lead_buffer_storage_index, :] = sample_arr

            #####################################################################
            #  Step 4 - Check if we need to put our buffer on the sample queue  #
            #####################################################################
            # Check if we need to put the buffer on the queue.
            if sample_index == self.update_interval:
                sub_buffer_to_send = self.buffer[
                    trail_buffer_storage_index:lead_buffer_storage_index, :]
                AV.assert_equal(
                    sub_buffer_to_send.shape[0],
                    self.moving_window_size,
                    message="Internal Error - buffer is incorrectly formatted")
                # Add our buffer to the queue.
                self.out_queue.put(sub_buffer_to_send)
                # Reset our sample index
                sample_index = 0