Esempio n. 1
0
def sync_probe_front_times(t, tref, sr, display=False):
    """
    From 2 timestamps vectors of equivalent length, output timestamps array to be used for
    linear interpolation
    :param t: time-serie to be synchronized
    :param tref: time-serie of the reference
    :param sr: sampling rate of the slave probe
    :return: a 2 columns by n-sync points array where each row corresponds
    to a sync point: sample_index (0 based), tref
    """
    COMPUTE_RESIDUAL = True
    # the main drift is computed through linear regression. A further step compute a smoothed
    # version of the residual to add to the linear drift. The precision is enforced
    # by ensuring that each point lies less than one sampling rate away from the predicted.
    pol = np.polyfit(t, tref, 1)  # higher order terms first: slope / int for linear
    residual = (tref - np.polyval(pol, t))
    if COMPUTE_RESIDUAL:
        # the interp function from camera fronts is not smooth due to the locking of detections
        # to the sampling rate of digital channels. The residual is fit using frequency domain
        # smoothing
        import ibllib.dsp as dsp
        CAMERA_UPSAMPLING_RATE_HZ = 300
        PAD_LENGTH_SECS = 60
        STAT_LENGTH_SECS = 30  # median length to compute padding value
        SYNC_SAMPLING_RATE_SECS = 20
        t_upsamp = np.arange(tref[0], tref[-1], 1 / CAMERA_UPSAMPLING_RATE_HZ)
        res_upsamp = np.interp(t_upsamp, tref, residual)
        # padding needs extra care as the function oscillates
        lpad = int(sr * PAD_LENGTH_SECS)
        res_filt = np.pad(res_upsamp, lpad, mode='median', stat_length=sr * STAT_LENGTH_SECS)
        fbounds = 1 / SYNC_SAMPLING_RATE_SECS * np.array([2, 4])
        res_filt = dsp.lp(res_filt, 1 / sr, fbounds)[lpad:-lpad]
        tout = np.arange(0, np.max(tref) + SYNC_SAMPLING_RATE_SECS, 20)
        sync_points = np.c_[tout * sr, np.polyval(pol, tout) - np.interp(tout, t_upsamp, res_filt)]
        if display:
            plt.plot(tref, residual * sr)
            plt.plot(t_upsamp, res_filt * sr)
            plt.plot(tout, np.interp(tout, t_upsamp, res_filt) * sr, '*')
            plt.ylabel('Residual drift (samples @ 30kHz)')
            plt.xlabel('time (sec)')
    else:
        sync_points = np.c_[np.array([0, sr]), np.polyval(pol, np.array([0, 1]))]
        if display:
            plt.plot(tref, residual * sr)
            plt.ylabel('Residual drift (samples @ 30kHz)')
            plt.xlabel('time (sec)')
    return sync_points
Esempio n. 2
0
def sync_probe_front_times(t, tref, sr, display=False, type='smooth', tol=2.0):
    """
    From 2 timestamps vectors of equivalent length, output timestamps array to be used for
    linear interpolation
    :param t: time-serie to be synchronized
    :param tref: time-serie of the reference
    :param sr: sampling rate of the slave probe
    :return: a 2 columns by n-sync points array where each row corresponds
    to a sync point: sample_index (0 based), tref
    :return: quality Bool. False if tolerance is exceeded
    """
    qc = True
    """
    the main drift is computed through linear regression. A further step compute a smoothed
    version of the residual to add to the linear drift. The precision is enforced
    by ensuring that each point lies less than one sampling rate away from the predicted.
    """
    pol = np.polyfit(t, tref,
                     1)  # higher order terms first: slope / int for linear
    residual = tref - np.polyval(pol, t)
    if type == 'smooth':
        """
        the interp function from camera fronts is not smooth due to the locking of detections
        to the sampling rate of digital channels. The residual is fit using frequency domain
        smoothing
        """
        import ibllib.dsp as dsp
        CAMERA_UPSAMPLING_RATE_HZ = 300
        PAD_LENGTH_SECS = 60
        STAT_LENGTH_SECS = 30  # median length to compute padding value
        SYNC_SAMPLING_RATE_SECS = 20
        t_upsamp = np.arange(tref[0], tref[-1], 1 / CAMERA_UPSAMPLING_RATE_HZ)
        res_upsamp = np.interp(t_upsamp, tref, residual)
        # padding needs extra care as the function oscillates and numpy fft performance is
        # abysmal for non prime sample sizes
        nech = res_upsamp.size + (CAMERA_UPSAMPLING_RATE_HZ * PAD_LENGTH_SECS)
        lpad = 2**np.ceil(np.log2(nech)) - res_upsamp.size
        lpad = [int(np.floor(lpad / 2) + lpad % 2), int(np.floor(lpad / 2))]
        res_filt = np.pad(res_upsamp,
                          lpad,
                          mode='median',
                          stat_length=CAMERA_UPSAMPLING_RATE_HZ *
                          STAT_LENGTH_SECS)
        fbounds = [0.001, 0.002]
        res_filt = dsp.lp(res_filt, 1 / CAMERA_UPSAMPLING_RATE_HZ,
                          fbounds)[lpad[0]:-lpad[1]]
        tout = np.arange(0, np.max(tref) + SYNC_SAMPLING_RATE_SECS, 20)
        sync_points = np.c_[tout,
                            np.polyval(pol, tout) +
                            np.interp(tout, t_upsamp, res_filt)]
        if display:
            if isinstance(display, matplotlib.axes.Axes):
                ax = display
            else:
                ax = plt.axes()
            ax.plot(tref, residual * sr, label='residual')
            ax.plot(t_upsamp, res_filt * sr, label='smoothed residual')
            ax.plot(tout,
                    np.interp(tout, t_upsamp, res_filt) * sr,
                    '*',
                    label='interp timestamps')
            ax.legend()
            ax.set_xlabel('time (sec)')
            ax.set_ylabel('Residual drift (samples @ 30kHz)')
    elif type == 'exact':
        sync_points = np.c_[t, tref]
        if display:
            plt.plot(tref, residual * sr, label='residual')
            plt.ylabel('Residual drift (samples @ 30kHz)')
            plt.xlabel('time (sec)')
            pass
    elif type == 'linear':
        sync_points = np.c_[np.array([0., 1.]),
                            np.polyval(pol, np.array([0., 1.]))]
        if display:
            plt.plot(tref, residual * sr)
            plt.ylabel('Residual drift (samples @ 30kHz)')
            plt.xlabel('time (sec)')
    # test that the interp is within tol sample
    fcn = interp1d(sync_points[:, 0],
                   sync_points[:, 1],
                   fill_value='extrapolate')
    if np.any(np.abs((tref - fcn(t)) * sr) > (tol)):
        _logger.error(
            f'Synchronization check exceeds tolerance of {tol} samples. Check !!'
        )
        qc = False
        # plt.plot((tref - fcn(t)) * sr)
        # plt.plot( (sync_points[:, 0] - fcn(sync_points[:, 1])) * sr)
    return sync_points, qc