def test_load_encoder_events(self): raw.load_encoder_events(self.training_lt5['path'], settings={'IBLRIG_VERSION_TAG': '4.9.9'}) raw.load_encoder_events(self.training_ge5['path']) raw.load_encoder_events(self.biased_lt5['path'], settings={'IBLRIG_VERSION_TAG': '4.9.9'}) raw.load_encoder_events(self.biased_ge5['path'])
def sync_rotary_encoder(session_path, bpod_data=None, re_events=None): if not bpod_data: bpod_data = raw.load_data(session_path) evt = re_events or raw.load_encoder_events(session_path) # we work with stim_on (2) and closed_loop (3) states for the synchronization with bpod tre = evt.re_ts.values / 1e6 # convert to seconds # the first trial on the rotary encoder is a dud rote = {'stim_on': tre[evt.sm_ev == 2][:-1], 'closed_loop': tre[evt.sm_ev == 3][:-1]} bpod = { 'stim_on': np.array([tr['behavior_data']['States timestamps'] ['stim_on'][0][0] for tr in bpod_data]), 'closed_loop': np.array([tr['behavior_data']['States timestamps'] ['closed_loop'][0][0] for tr in bpod_data]), } if rote['closed_loop'].size <= 1: raise err.SyncBpodWheelException("Not enough Rotary Encoder events to perform wheel" " synchronization. Wheel data not extracted") # bpod bug that spits out events in ms instead of us if np.diff(bpod['closed_loop'][[-1, 0]])[0] / np.diff(rote['closed_loop'][[-1, 0]])[0] > 900: _logger.error("Rotary encoder stores values in ms instead of us. Wheel timing inaccurate") rote['stim_on'] *= 1e3 rote['closed_loop'] *= 1e3 # just use the closed loop for synchronization # handle different sizes in synchronization: sz = min(rote['closed_loop'].size, bpod['closed_loop'].size) # if all the sample are contiguous and first samples match diff_first_match = np.diff(rote['closed_loop'][:sz]) - np.diff(bpod['closed_loop'][:sz]) # if all the sample are contiguous and last samples match diff_last_match = np.diff(rote['closed_loop'][-sz:]) - np.diff(bpod['closed_loop'][-sz:]) # 99% of the pulses match for a first sample lock DIFF_THRESHOLD = 0.005 if np.mean(np.abs(diff_first_match) < DIFF_THRESHOLD) > 0.99: re = rote['closed_loop'][:sz] bp = bpod['closed_loop'][:sz] indko = np.where(np.abs(diff_first_match) >= DIFF_THRESHOLD)[0] # 99% of the pulses match for a last sample lock elif np.mean(np.abs(diff_last_match) < DIFF_THRESHOLD) > 0.99: re = rote['closed_loop'][-sz:] bp = bpod['closed_loop'][-sz:] indko = np.where(np.abs(diff_last_match) >= DIFF_THRESHOLD)[0] # last resort is to use ad-hoc sync function else: bp, re = raw.sync_trials_robust(bpod['closed_loop'], rote['closed_loop'], diff_threshold=DIFF_THRESHOLD, max_shift=5) indko = np.array([]) # raise ValueError("Can't sync bpod and rotary encoder: non-contiguous sync pulses") # remove faulty indices due to missing or bad syncs indko = np.int32(np.unique(np.r_[indko + 1, indko])) re = np.delete(re, indko) bp = np.delete(bp, indko) # check the linear drift assert bp.size > 1 poly = np.polyfit(bp, re, 1) assert np.all(np.abs(np.polyval(poly, bp) - re) < 0.002) return interpolate.interp1d(re, bp, fill_value="extrapolate")
def get_trial_start_times_re(session_path, evt=None): if not evt: evt = raw.load_encoder_events(session_path) trial_start_times_re = evt.re_ts[evt.sm_ev[evt.sm_ev == 1].index].values / 1e6 return trial_start_times_re[:-1]
def test_encoder_events_duds(self): dy = loaders.load_encoder_events(self.session_path) self.assertEqual(dy.bns_ts.dtype.name, 'object') self.assertTrue(dy.shape[0] == 7)