Esempio n. 1
0
def check_wheel_move_before_feedback(data, **_):
    """ Check that the wheel does move within 100ms of the feedback onset (error sound or valve).

    Metric: M = (w_t - 0.05) - (w_t + 0.05), where t = feedback_times
    Criterion: M != 0
    Units: radians

    :param data: dict of trial data with keys ('wheel_timestamps', 'wheel_position', 'choice',
    'intervals', 'feedback_times')
    """
    # Get tuple of wheel times and positions within 100ms of feedback
    traces = traces_by_trial(
        data["wheel_timestamps"],
        data["wheel_position"],
        start=data["feedback_times"] - 0.05,
        end=data["feedback_times"] + 0.05,
    )
    metric = np.zeros_like(data["feedback_times"])
    # For each trial find the displacement
    for i, trial in enumerate(traces):
        pos = trial[1]
        if pos.size > 1:
            metric[i] = pos[-1] - pos[0]

    # except no-go trials
    metric[data["choice"] == 0] = np.nan
    nans = np.isnan(metric)
    passed = np.zeros_like(metric) * np.nan

    passed[~nans] = (metric[~nans] != 0).astype(float)
    assert data["intervals"].shape[0] == len(metric) == len(passed)
    return metric, passed
Esempio n. 2
0
def load_wheel_move_before_feedback(trial_data, wheel_data):
    """ Wheel should move within 100ms of feedback
    Variable name: wheel_move_before_feedback
    Metric: (w_t - 0.05) - (w_t + 0.05) where t = feedback_time
    Criterion: != 0 for 99% of non-NoGo trials
    """
    # Get tuple of wheel times and positions within 100ms of feedback
    traces = traces_by_trial(
        wheel_data["re_ts"],
        wheel_data["re_pos"],
        start=trial_data["feedback_times"] - 0.05,
        end=trial_data["feedback_times"] + 0.05,
    )
    metric = np.zeros_like(trial_data["feedback_times"])
    # For each trial find the displacement
    for i, trial in enumerate(traces):
        pos = trial[1]
        if pos.size > 1:
            metric[i] = pos[-1] - pos[0]

    # except no-go trials
    metric[trial_data["choice"] == 0] = np.nan
    nans = np.isnan(metric)
    passed = np.zeros_like(metric) * np.nan

    passed[~nans] = (metric[~nans] != 0).astype(np.float)
    assert len(trial_data["intervals_0"]) == len(metric) == len(passed)
    return metric, passed
Esempio n. 3
0
def _wheel_move_during_closed_loop(re_ts,
                                   re_pos,
                                   data,
                                   wheel_gain=None,
                                   tol=1,
                                   **_):
    """ Check that the wheel moves by approximately 35 degrees during the closed-loop period
    on trials where a feedback (error sound or valve) is delivered.

    Metric: M = abs(w_resp - w_t0) - threshold_displacement, where w_resp = position at response
        time, w_t0 = position at go cue time, threshold_displacement = displacement required to
        move 35 visual degrees
    Criterion: displacement < tol visual degree
    Units: degrees angle of wheel turn

    :param re_ts: extarcted wheel timestamps in seconds
    :param re_pos: extracted wheel positions in radians
    :param data: a dict with the keys (goCueTrigger_times, response_times, feedback_times,
    position, choice, intervals)
    :param wheel_gain: the 'STIM_GAIN' task setting
    :param tol: the criterion in visual degrees
    """
    if wheel_gain is None:
        _log.warning("No wheel_gain input in function call, returning None")
        return None, None

    # Get tuple of wheel times and positions over each trial's closed-loop period
    traces = traces_by_trial(re_ts,
                             re_pos,
                             start=data["goCueTrigger_times"],
                             end=data["response_times"])

    metric = np.zeros_like(data["feedback_times"])
    # For each trial find the absolute displacement
    for i, trial in enumerate(traces):
        t, pos = trial
        if pos.size != 0:
            # Find the position of the preceding sample and subtract it
            idx = np.abs(re_ts - t[0]).argmin() - 1
            origin = re_pos[idx]
            metric[i] = np.abs(pos - origin).max()

    # Load wheel_gain and thresholds for each trial
    wheel_gain = np.array([wheel_gain] * len(data["position"]))
    thresh = data["position"]
    # abs displacement, s, in mm required to move 35 visual degrees
    s_mm = np.abs(thresh / wheel_gain)  # don't care about direction
    criterion = cm_to_rad(
        s_mm *
        1e-1)  # convert abs displacement to radians (wheel pos is in rad)
    metric = metric - criterion  # difference should be close to 0
    rad_per_deg = cm_to_rad(1 / wheel_gain * 1e-1)
    passed = (np.abs(metric) < rad_per_deg * tol).astype(
        float)  # less than 1 visual degree off
    metric[data["choice"] == 0] = passed[data["choice"] ==
                                         0] = np.nan  # except no-go trials
    assert data["intervals"].shape[0] == len(metric) == len(passed)
    return metric, passed
Esempio n. 4
0
    def test_traces_by_trial(self):
        t, pos = self.test_data[0][0]
        start = self.trials['stimOn_times']
        end = self.trials['feedback_times']
        traces = wheel.traces_by_trial(t, pos, start=start, end=end)
        # Check correct number of tuples returned
        self.assertEqual(len(traces), start.size)
        expected_ids = ([144, 60143], [74944, 84943], [99944, 102943],
                        [119944, 129943], [163944, 187943])

        for trace, ind in zip(traces, expected_ids):
            trace_t, trace_pos = trace
            np.testing.assert_array_equal(trace_t[[0, -1]], t[ind])
            np.testing.assert_array_equal(trace_pos[[0, -1]], pos[ind])
Esempio n. 5
0
def load_wheel_move_during_closed_loop(trial_data, wheel_data, wheel_gain):
    """ Wheel should move a sufficient amount during the closed-loop period
    Variable name: wheel_move_during_closed_loop
    Metric: abs(w_resp - w_t0) - threshold_displacement, where w_resp = position at response
        time, w_t0 = position at go cue time, threshold_displacement = displacement required to
        move 35 visual degrees
    Criterion: displacement < 1 visual degree for 99% of non-NoGo trials
    """
    if wheel_gain is None:
        log.warning("No wheel_gain input in function call, retruning None")
        return None

    # Get tuple of wheel times and positions over each trial's closed-loop period
    traces = traces_by_trial(
        wheel_data["re_ts"],
        wheel_data["re_pos"],
        start=trial_data["goCueTrigger_times"],
        end=trial_data["response_times"],
    )

    metric = np.zeros_like(trial_data["feedback_times"])
    # For each trial find the absolute displacement
    for i, trial in enumerate(traces):
        t, pos = trial
        if pos.size == 0:
            metric[i] = np.nan
        else:
            # Find the position of the preceding sample and subtract it
            origin = wheel_data["re_pos"][wheel_data["re_ts"] <= t[0]][-1]
            metric[i] = np.abs(pos - origin).max()

    # Load wheel_gain and thresholds for each trial
    wheel_gain = np.array([wheel_gain] * len(trial_data["position"]))
    thresh = trial_data["position"]
    # abs displacement, s, in mm required to move 35 visual degrees
    s_mm = np.abs(thresh / wheel_gain)  # don't care about direction
    criterion = cm_to_rad(
        s_mm *
        1e-1)  # convert abs displacement to radians (wheel pos is in rad)
    metric = metric - criterion  # difference should be close to 0
    rad_per_deg = cm_to_rad(1 / wheel_gain * 1e-1)
    passed = (np.abs(metric) < rad_per_deg).astype(
        np.float)  # less than 1 visual degree off
    metric[trial_data["choice"] == 0] = np.nan  # except no-go trials
    passed[trial_data["choice"] == 0] = np.nan  # except no-go trials
    assert len(trial_data["intervals_0"]) == len(metric) == len(passed)
    return metric, passed
Esempio n. 6
0
def load_wheel_freeze_during_quiescence(trial_data, wheel_data):
    """ Wheel should not move more than 2 ticks each direction for at least 0.2 + 0.2-0.6
    amount of time (quiescent period; exact value in bpod['quiescence']) before go cue
    Variable name: wheel_freeze_during_quiescence
    Metric: abs(min(W - w_t0), max(W - w_t0)) where W is wheel pos over interval
    np.max(Metric) to get highest displaceente in any direction
    interval = [goCueTrigger_time-quiescent_duration,goCueTrigger_time]
    Criterion: <2 degrees for 99% of trials
    """
    assert np.all(np.diff(wheel_data["re_ts"]) > 0)
    assert trial_data["quiescence"].size == trial_data[
        "goCueTrigger_times"].size
    # Get tuple of wheel times and positions over each trial's quiescence period
    qevt_start_times = trial_data["goCueTrigger_times"] - trial_data[
        "quiescence"]
    traces = traces_by_trial(
        wheel_data["re_ts"],
        wheel_data["re_pos"],
        start=qevt_start_times,
        end=trial_data["goCueTrigger_times"],
    )

    # metric = np.zeros_like(trial_data['quiescence'])
    # for i, trial in enumerate(traces):
    #     pos = trial[1]
    #     if pos.size > 1:
    #         metric[i] = np.abs(pos.max() - pos.min())
    # -OR-
    metric = np.zeros(
        (len(trial_data["quiescence"]), 2))  # (n_trials, n_directions)
    for i, trial in enumerate(traces):
        t, pos = trial
        # Get the last position before the period began
        if pos.size > 1:
            # Find the position of the preceding sample and subtract it
            origin = wheel_data["re_pos"][wheel_data["re_ts"] < t[0]][-1]
            # Find the absolute min and max relative to the last sample
            metric[i, :] = np.abs([np.min(pos - origin), np.max(pos - origin)])
    # Reduce to the largest displacement found in any direction
    metric = np.max(metric, axis=1)
    metric = 180 * metric / np.pi  # convert to degrees from radians
    criterion = 2  # Position shouldn't change more than 2 in either direction
    passed = (metric < criterion).astype(np.float)
    assert len(trial_data["intervals_0"]) == len(metric) == len(passed)
    return metric, passed
Esempio n. 7
0
def check_wheel_freeze_during_quiescence(data, **_):
    """ Check that the wheel does not move more than 2 ticks each direction for at least 0.2 + 0.2-0.6
    amount of time (quiescent period; exact value in bpod['quiescence']) before the go cue tone.

    Metric: M = |max(W) - min(W)| where W is wheel pos over quiescence interval
    interval = [goCueTrigger_time - quiescent_duration, goCueTrigger_time]
    Criterion: M < 2 degrees
    Units: degrees angle of wheel turn

    :param data: dict of trial data with keys ('wheel_timestamps', 'wheel_position', 'quiescence',
    'intervals', 'stimOnTrigger_times')
    """
    assert np.all(np.diff(data["wheel_timestamps"]) > 0)
    assert data["quiescence"].size == data["stimOnTrigger_times"].size
    # Get tuple of wheel times and positions over each trial's quiescence period
    qevt_start_times = data["stimOnTrigger_times"] - data["quiescence"]
    traces = traces_by_trial(
        data["wheel_timestamps"],
        data["wheel_position"],
        start=qevt_start_times,
        end=data["stimOnTrigger_times"]
    )

    metric = np.zeros((len(data["quiescence"]), 2))  # (n_trials, n_directions)
    for i, trial in enumerate(traces):
        t, pos = trial
        # Get the last position before the period began
        if pos.size > 0:
            # Find the position of the preceding sample and subtract it
            idx = np.abs(data["wheel_timestamps"] - t[0]).argmin() - 1
            origin = data["wheel_position"][idx if idx != -1 else 0]
            # Find the absolute min and max relative to the last sample
            metric[i, :] = np.abs([np.min(pos - origin), np.max(pos - origin)])
    # Reduce to the largest displacement found in any direction
    metric = np.max(metric, axis=1)
    metric = 180 * metric / np.pi  # convert to degrees from radians
    criterion = 2  # Position shouldn't change more than 2 in either direction
    passed = metric < criterion
    assert data["intervals"].shape[0] == len(metric) == len(passed)
    return metric, passed
Esempio n. 8
0
fig, axs = plt.subplots(1, n_trials, figsize=(8.5, 2.5))
plt.tight_layout()

# Plot go cue and response times
goCues = trial_data['goCue_times'][trial_ids]
responses = trial_data['response_times'][trial_ids]

# Plot traces between trial intervals
starts = trial_data['intervals'][trial_ids, 0]
ends = trial_data['intervals'][trial_ids, 1]
# Cut up the wheel vectors
Fs = 1000
pos, t = wh.interpolate_position(wheel.timestamps, wheel.position, freq=Fs)
vel, acc = wh.velocity_smoothed(pos, Fs)

traces = wh.traces_by_trial(t, pos, start=starts, end=ends)
zipped = zip(traces, axs, goCues, responses, trial_ids)

for (trace, ax, go, resp, n) in zipped:
    ax.plot(trace[0], trace[1], 'k-')
    ax.axvline(x=go, color='g', label='go cue', linestyle=':')
    ax.axvline(x=resp, color='r', label='threshold', linestyle=':')
    ax.set_title('Trial #%s' % n)

    # Turn off tick labels
    ax.set_yticklabels([])
    ax.set_xticklabels([])

# Add labels to first
axs[0].set_xlabel('time / sec')
axs[0].set_ylabel('position / rad')