Beispiel #1
0
def align_with_bpod(session_path):
    """
    Reads in trials.intervals ALF dataset from bpod and fpga.
    Asserts consistency between datasets and compute the median time difference

    :param session_path:
    :return: dt: median time difference of trial start times (fpga - bpod)
    """
    ITI_DURATION = 0.5
    # check consistency
    output_path = Path(session_path) / 'alf'
    trials = alf.io.load_object(output_path, '_ibl_trials')
    if alf.io.check_dimensions(trials) != 0:
        # patching things up if the bpod and FPGA don't have the same recording span
        _logger.warning(
            "BPOD/FPGA synchronization: Bpod and FPGA don't have the same amount of"
            " trial start events. Patching alf files.")
        _, _, ibpod, ifpga = raw.sync_trials_robust(
            trials['intervals_bpod'][:, 0],
            trials['intervals'][:, 0],
            return_index=True)
        if ibpod.size == 0:
            raise err.SyncBpodFpgaException(
                'Can not sync BPOD and FPGA - no matching sync pulses '
                'found.')
        for k in trials:
            if 'bpod' in k:
                trials[k] = trials[k][ibpod]
            else:
                trials[k] = trials[k][ibpod]
        alf.io.save_object_npy(output_path, trials, '_ibl_trials')
    assert (alf.io.check_dimensions(trials) == 0)
    tlen = (np.diff(trials['intervals_bpod']) -
            np.diff(trials['intervals']))[:-1] - ITI_DURATION
    assert (np.all(np.abs(tlen[np.invert(np.isnan(tlen))])[:-1] < 5 * 1e-3))
    # dt is the delta to apply to bpod times in order to be on the ephys clock
    dt = trials['intervals'][:, 0] - trials['intervals_bpod'][:, 0]
    # compute the clock drift bpod versus dt
    ppm = np.polyfit(trials['intervals'][:, 0], dt, 1)[0] * 1e6
    if ppm > BPOD_FPGA_DRIFT_THRESHOLD_PPM:
        _logger.warning(
            'BPOD/FPGA synchronization shows values greater than 150 ppm')
        # plt.plot(trials['intervals'][:, 0], dt, '*')
    # so far 2 datasets concerned: goCueTrigger_times_bpod  and response_times_bpod
    for k in trials:
        if not k.endswith('_times_bpod'):
            continue
        np.save(output_path.joinpath(f'_ibl_trials.{k[:-5]}.npy'),
                trials[k] + dt)
    return interpolate.interp1d(trials['intervals_bpod'][:, 0],
                                trials['intervals'][:, 0],
                                fill_value="extrapolate")
Beispiel #2
0
def bpod_fpga_sync(bpod_intervals=None,
                   ephys_intervals=None,
                   iti_duration=None):
    """
    Computes synchronization function from bpod to fpga
    :param bpod_intervals
    :param ephys_intervals
    :return: interpolation function
    """
    if iti_duration is None:
        iti_duration = 0.5
    # check consistency
    if bpod_intervals.size != ephys_intervals.size:
        # patching things up if the bpod and FPGA don't have the same recording span
        _logger.warning(
            "BPOD/FPGA synchronization: Bpod and FPGA don't have the same amount of"
            " trial start events. Patching alf files.")
        _, _, ibpod, ifpga = raw_data_loaders.sync_trials_robust(
            bpod_intervals[:, 0], ephys_intervals[:, 0], return_index=True)
        if ibpod.size == 0:
            raise err.SyncBpodFpgaException(
                'Can not sync BPOD and FPGA - no matching sync pulses '
                'found.')
        bpod_intervals = bpod_intervals[ibpod, :]
        ephys_intervals = ephys_intervals[ifpga, :]
    else:
        ibpod, ifpga = [
            np.arange(bpod_intervals.shape[0]) for _ in np.arange(2)
        ]
    tlen = (np.diff(bpod_intervals) -
            np.diff(ephys_intervals))[:-1] - iti_duration
    assert np.all(np.abs(tlen[np.invert(np.isnan(tlen))])[:-1] < 5 * 1e-3)
    # dt is the delta to apply to bpod times in order to be on the ephys clock
    dt = bpod_intervals[:, 0] - ephys_intervals[:, 0]
    # compute the clock drift bpod versus dt
    ppm = np.polyfit(bpod_intervals[:, 0], dt, 1)[0] * 1e6
    if ppm > BPOD_FPGA_DRIFT_THRESHOLD_PPM:
        _logger.warning(
            'BPOD/FPGA synchronization shows values greater than %i ppm',
            BPOD_FPGA_DRIFT_THRESHOLD_PPM)
        # plt.plot(trials['intervals'][:, 0], dt, '*')
    # so far 2 datasets concerned: goCueTrigger_times_bpod  and response_times_bpod
    fcn_bpod2fpga = interpolate.interp1d(bpod_intervals[:, 0],
                                         ephys_intervals[:, 0],
                                         fill_value="extrapolate")
    return ibpod, ifpga, fcn_bpod2fpga
Beispiel #3
0
def extract_behaviour_sync(sync,
                           chmap=None,
                           display=False,
                           bpod_trials=None,
                           tmax=np.inf):
    """
    Extract wheel positions and times from sync fronts dictionary

    :param sync: dictionary 'times', 'polarities' of fronts detected on sync trace for all 16 chans
    :param chmap: dictionary containing channel index. Default to constant.
        chmap = {'bpod': 7, 'frame2ttl': 12, 'audio': 15}
    :param display: bool or matplotlib axes: show the full session sync pulses display
    defaults to False
    :return: trials dictionary
    """
    bpod = _get_sync_fronts(sync, chmap['bpod'], tmax=tmax)
    if bpod.times.size == 0:
        raise err.SyncBpodFpgaException(
            'No Bpod event found in FPGA. No behaviour extraction. '
            'Check channel maps.')
    frame2ttl = _get_sync_fronts(sync, chmap['frame2ttl'], tmax=tmax)
    frame2ttl = _clean_frame2ttl(frame2ttl)
    audio = _get_sync_fronts(sync, chmap['audio'], tmax=tmax)
    # extract events from the fronts for each trace
    t_trial_start, t_valve_open, t_iti_in = _assign_events_bpod(
        bpod['times'], bpod['polarities'])
    # one issue is that sometimes bpod pulses may not have been detected, in this case
    # perform the sync bpod/FPGA, and add the start that have not been detected
    if bpod_trials:
        bpod_start = bpod_trials['intervals_bpod'][:, 0]
        fcn, drift, ibpod, ifpga = dsp.utils.sync_timestamps(
            bpod_start, t_trial_start, return_indices=True)
        # if it's drifting too much
        if drift > 200 and bpod_start.size != t_trial_start.size:
            raise err.SyncBpodFpgaException("sync cluster f*ck")
        missing_bpod = fcn(bpod_start[np.setxor1d(ibpod,
                                                  np.arange(len(bpod_start)))])
        t_trial_start = np.sort(np.r_[t_trial_start, missing_bpod])
    else:
        _logger.warning(
            "Deprecation Warning: calling FPGA trials extraction without a bpod trials"
            " dictionary will result in an error.")
    t_ready_tone_in, t_error_tone_in = _assign_events_audio(
        audio['times'], audio['polarities'])
    trials = Bunch({
        'goCue_times':
        _assign_events_to_trial(t_trial_start, t_ready_tone_in, take='first'),
        'errorCue_times':
        _assign_events_to_trial(t_trial_start, t_error_tone_in),
        'valveOpen_times':
        _assign_events_to_trial(t_trial_start, t_valve_open),
        'stimFreeze_times':
        _assign_events_to_trial(t_trial_start, frame2ttl['times'], take=-2),
        'stimOn_times':
        _assign_events_to_trial(t_trial_start,
                                frame2ttl['times'],
                                take='first'),
        'stimOff_times':
        _assign_events_to_trial(t_trial_start, frame2ttl['times']),
        'itiIn_times':
        _assign_events_to_trial(t_trial_start, t_iti_in)
    })
    # feedback times are valve open on good trials and error tone in on error trials
    trials['feedback_times'] = np.copy(trials['valveOpen_times'])
    ind_err = np.isnan(trials['valveOpen_times'])
    trials['feedback_times'][ind_err] = trials['errorCue_times'][ind_err]
    trials['intervals'] = np.c_[t_trial_start, trials['itiIn_times']]

    if display:
        width = 0.5
        ymax = 5
        if isinstance(display, bool):
            plt.figure("Ephys FPGA Sync")
            ax = plt.gca()
        else:
            ax = display
        r0 = _get_sync_fronts(sync, chmap['rotary_encoder_0'])
        plots.squares(bpod['times'],
                      bpod['polarities'] * 0.4 + 1,
                      ax=ax,
                      color='k')
        plots.squares(frame2ttl['times'],
                      frame2ttl['polarities'] * 0.4 + 2,
                      ax=ax,
                      color='k')
        plots.squares(audio['times'],
                      audio['polarities'] * 0.4 + 3,
                      ax=ax,
                      color='k')
        plots.squares(r0['times'],
                      r0['polarities'] * 0.4 + 4,
                      ax=ax,
                      color='k')
        plots.vertical_lines(t_ready_tone_in,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='goCue_times',
                             color='b',
                             linewidth=width)
        plots.vertical_lines(t_trial_start,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='start_trial',
                             color='m',
                             linewidth=width)
        plots.vertical_lines(t_error_tone_in,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='error tone',
                             color='r',
                             linewidth=width)
        plots.vertical_lines(t_valve_open,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='valveOpen_times',
                             color='g',
                             linewidth=width)
        plots.vertical_lines(trials['stimFreeze_times'],
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='stimFreeze_times',
                             color='y',
                             linewidth=width)
        plots.vertical_lines(trials['stimOff_times'],
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='stim off',
                             color='c',
                             linewidth=width)
        plots.vertical_lines(trials['stimOn_times'],
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='stimOn_times',
                             color='tab:orange',
                             linewidth=width)
        c = _get_sync_fronts(sync, chmap['left_camera'])
        plots.squares(c['times'], c['polarities'] * 0.4 + 5, ax=ax, color='k')
        c = _get_sync_fronts(sync, chmap['right_camera'])
        plots.squares(c['times'], c['polarities'] * 0.4 + 6, ax=ax, color='k')
        c = _get_sync_fronts(sync, chmap['body_camera'])
        plots.squares(c['times'], c['polarities'] * 0.4 + 7, ax=ax, color='k')
        ax.legend()
        ax.set_yticklabels(['', 'bpod', 'f2ttl', 'audio', 're_0', ''])
        ax.set_yticks([0, 1, 2, 3, 4, 5])
        ax.set_ylim([0, 5])

    return trials
Beispiel #4
0
def extract_behaviour_sync(sync,
                           output_path=None,
                           save=False,
                           chmap=None,
                           display=False,
                           tmax=np.inf):
    """
    Extract wheel positions and times from sync fronts dictionary

    :param sync: dictionary 'times', 'polarities' of fronts detected on sync trace for all 16 chans
    :param output_path: where to save the data
    :param save: True/False
    :param chmap: dictionary containing channel index. Default to constant.
        chmap = {'bpod': 7, 'frame2ttl': 12, 'audio': 15}
    :param display: bool or matplotlib axes: show the full session sync pulses display
    defaults to False
    :return: trials dictionary
    """
    bpod = _get_sync_fronts(sync, chmap['bpod'], tmax=tmax)
    if bpod.times.size == 0:
        raise err.SyncBpodFpgaException(
            'No Bpod event found in FPGA. No behaviour extraction. '
            'Check channel maps.')
    frame2ttl = _get_sync_fronts(sync, chmap['frame2ttl'], tmax=tmax)
    audio = _get_sync_fronts(sync, chmap['audio'], tmax=tmax)
    # extract events from the fronts for each trace
    t_trial_start, t_valve_open, t_iti_in = _bpod_events_extraction(
        bpod['times'], bpod['polarities'])
    t_ready_tone_in, t_error_tone_in = _audio_events_extraction(
        audio['times'], audio['polarities'])
    # stim off time is the first frame2ttl rise/fall after the trial start
    # does not apply for 1st trial
    ind = np.searchsorted(frame2ttl['times'], t_iti_in, side='left')
    t_stim_off = frame2ttl['times'][np.minimum(ind, frame2ttl.times.size - 1)]
    t_stim_freeze = frame2ttl['times'][np.maximum(ind - 1, 0)]
    # stimOn_times: first fram2ttl change after trial start
    trials = Bunch({
        'ready_tone_in':
        _assign_events_to_trial(t_trial_start, t_ready_tone_in, take='first'),
        'error_tone_in':
        _assign_events_to_trial(t_trial_start, t_error_tone_in),
        'valve_open':
        _assign_events_to_trial(t_trial_start, t_valve_open),
        'stim_freeze':
        _assign_events_to_trial(t_trial_start, t_stim_freeze),
        'stimOn_times':
        _assign_events_to_trial(t_trial_start,
                                frame2ttl['times'],
                                take='first'),
        'stimOff_times':
        _assign_events_to_trial(t_trial_start, t_stim_off),
        'iti_in':
        _assign_events_to_trial(t_trial_start, t_iti_in)
    })
    # goCue_times corresponds to the tone_in event
    trials['goCue_times'] = np.copy(trials['ready_tone_in'])
    # feedback times are valve open on good trials and error tone in on error trials
    trials['feedback_times'] = np.copy(trials['valve_open'])
    ind_err = np.isnan(trials['valve_open'])
    trials['feedback_times'][ind_err] = trials['error_tone_in'][ind_err]
    trials['intervals'] = np.c_[t_trial_start, trials['iti_in']]

    if display:
        width = 0.5
        ymax = 5
        if isinstance(display, bool):
            plt.figure("Ephys FPGA Sync")
            ax = plt.gca()
        else:
            ax = display
        r0 = _get_sync_fronts(sync, chmap['rotary_encoder_0'])
        plots.squares(bpod['times'],
                      bpod['polarities'] * 0.4 + 1,
                      ax=ax,
                      color='k')
        plots.squares(frame2ttl['times'],
                      frame2ttl['polarities'] * 0.4 + 2,
                      ax=ax,
                      color='k')
        plots.squares(audio['times'],
                      audio['polarities'] * 0.4 + 3,
                      ax=ax,
                      color='k')
        plots.squares(r0['times'],
                      r0['polarities'] * 0.4 + 4,
                      ax=ax,
                      color='k')
        plots.vertical_lines(t_ready_tone_in,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='ready tone in',
                             color='b',
                             linewidth=width)
        plots.vertical_lines(t_trial_start,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='start_trial',
                             color='m',
                             linewidth=width)
        plots.vertical_lines(t_error_tone_in,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='error tone',
                             color='r',
                             linewidth=width)
        plots.vertical_lines(t_valve_open,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='valve open',
                             color='g',
                             linewidth=width)
        plots.vertical_lines(t_stim_freeze,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='stim freeze',
                             color='y',
                             linewidth=width)
        plots.vertical_lines(t_stim_off,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='stim off',
                             color='c',
                             linewidth=width)
        plots.vertical_lines(trials['stimOn_times'],
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='stim on',
                             color='tab:orange',
                             linewidth=width)
        ax.legend()
        ax.set_yticklabels(['', 'bpod', 'f2ttl', 'audio', 're_0', ''])
        ax.set_ylim([0, 5])

    if save and output_path:
        output_path = Path(output_path)
        np.save(output_path / '_ibl_trials.goCue_times.npy',
                trials['goCue_times'])
        np.save(output_path / '_ibl_trials.stimOn_times.npy',
                trials['stimOn_times'])
        np.save(output_path / '_ibl_trials.intervals.npy', trials['intervals'])
        np.save(output_path / '_ibl_trials.feedback_times.npy',
                trials['feedback_times'])
    return trials
Beispiel #5
0
def extract_behaviour_sync(sync, chmap=None, display=False, tmax=np.inf):
    """
    Extract wheel positions and times from sync fronts dictionary

    :param sync: dictionary 'times', 'polarities' of fronts detected on sync trace for all 16 chans
    :param chmap: dictionary containing channel index. Default to constant.
        chmap = {'bpod': 7, 'frame2ttl': 12, 'audio': 15}
    :param display: bool or matplotlib axes: show the full session sync pulses display
    defaults to False
    :return: trials dictionary
    """
    bpod = _get_sync_fronts(sync, chmap['bpod'], tmax=tmax)
    if bpod.times.size == 0:
        raise err.SyncBpodFpgaException(
            'No Bpod event found in FPGA. No behaviour extraction. '
            'Check channel maps.')
    frame2ttl = _get_sync_fronts(sync, chmap['frame2ttl'], tmax=tmax)
    audio = _get_sync_fronts(sync, chmap['audio'], tmax=tmax)
    # extract events from the fronts for each trace
    t_trial_start, t_valve_open, t_iti_in = _assign_events_bpod(
        bpod['times'], bpod['polarities'])
    t_ready_tone_in, t_error_tone_in = _assign_events_audio(
        audio['times'], audio['polarities'])
    trials = Bunch({
        'goCue_times':
        _assign_events_to_trial(t_trial_start, t_ready_tone_in, take='first'),
        'errorCue_times':
        _assign_events_to_trial(t_trial_start, t_error_tone_in),
        'valveOpen_times':
        _assign_events_to_trial(t_trial_start, t_valve_open),
        'stimFreeze_times':
        _assign_events_to_trial(t_trial_start, frame2ttl['times'], take=-2),
        'stimOn_times':
        _assign_events_to_trial(t_trial_start,
                                frame2ttl['times'],
                                take='first'),
        'stimOff_times':
        _assign_events_to_trial(t_trial_start, frame2ttl['times']),
        'itiIn_times':
        _assign_events_to_trial(t_trial_start, t_iti_in)
    })
    # feedback times are valve open on good trials and error tone in on error trials
    trials['feedback_times'] = np.copy(trials['valveOpen_times'])
    ind_err = np.isnan(trials['valveOpen_times'])
    trials['feedback_times'][ind_err] = trials['errorCue_times'][ind_err]
    trials['intervals'] = np.c_[t_trial_start, trials['itiIn_times']]

    if display:
        width = 0.5
        ymax = 5
        if isinstance(display, bool):
            plt.figure("Ephys FPGA Sync")
            ax = plt.gca()
        else:
            ax = display
        r0 = _get_sync_fronts(sync, chmap['rotary_encoder_0'])
        plots.squares(bpod['times'],
                      bpod['polarities'] * 0.4 + 1,
                      ax=ax,
                      color='k')
        plots.squares(frame2ttl['times'],
                      frame2ttl['polarities'] * 0.4 + 2,
                      ax=ax,
                      color='k')
        plots.squares(audio['times'],
                      audio['polarities'] * 0.4 + 3,
                      ax=ax,
                      color='k')
        plots.squares(r0['times'],
                      r0['polarities'] * 0.4 + 4,
                      ax=ax,
                      color='k')
        plots.vertical_lines(t_ready_tone_in,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='goCue_times',
                             color='b',
                             linewidth=width)
        plots.vertical_lines(t_trial_start,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='start_trial',
                             color='m',
                             linewidth=width)
        plots.vertical_lines(t_error_tone_in,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='error tone',
                             color='r',
                             linewidth=width)
        plots.vertical_lines(t_valve_open,
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='valveOpen_times',
                             color='g',
                             linewidth=width)
        plots.vertical_lines(trials['stimFreeze_times'],
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='stimFreeze_times',
                             color='y',
                             linewidth=width)
        plots.vertical_lines(trials['stimOff_times'],
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='stim off',
                             color='c',
                             linewidth=width)
        plots.vertical_lines(trials['stimOn_times'],
                             ymin=0,
                             ymax=ymax,
                             ax=ax,
                             label='stimOn_times',
                             color='tab:orange',
                             linewidth=width)
        c = _get_sync_fronts(sync, chmap['left_camera'])
        plots.squares(c['times'], c['polarities'] * 0.4 + 5, ax=ax, color='k')
        c = _get_sync_fronts(sync, chmap['right_camera'])
        plots.squares(c['times'], c['polarities'] * 0.4 + 6, ax=ax, color='k')
        c = _get_sync_fronts(sync, chmap['body_camera'])
        plots.squares(c['times'], c['polarities'] * 0.4 + 7, ax=ax, color='k')
        ax.legend()
        ax.set_yticklabels(['', 'bpod', 'f2ttl', 'audio', 're_0', ''])
        ax.set_yticks([0, 1, 2, 3, 4, 5])
        ax.set_ylim([0, 5])

    return trials