def run_trial( self, trial_frame: pd.DataFrame) -> (TrialResult, Optional[OrderedDict]): self.othread.reset_data_buffer() self.pthread.reset_data_buffer() self.athread.reset_command_timestamps() # set up variables for one trial left_to_right = trial_frame['left_to_right'] shift = trial_frame['shift'] if left_to_right else -trial_frame['shift'] amplitude = trial_frame[ 'amplitude'] if left_to_right else -trial_frame['amplitude'] fixation_led = trial_frame[ 'fixation_led'] if left_to_right else 254 - trial_frame[ 'fixation_led'] target_led = fixation_led + amplitude shifted_target_led = target_led + shift fixation_threshold = trial_frame['fixation_threshold'] fixation_head_velocity_threshold = trial_frame[ 'fixation_head_velocity_threshold'] saccade_threshold = trial_frame['saccade_threshold'] landing_fixation_threshold = trial_frame['landing_fixation_threshold'] pupil_min_confidence = trial_frame['pupil_min_confidence'] before_fixation_color = trial_frame['before_fixation_color'] during_fixation_color = trial_frame['during_fixation_color'] before_response_target_color = trial_frame[ 'before_response_target_color'] during_response_target_color = trial_frame[ 'during_response_target_color'] fixation_duration = trial_frame['fixation_duration'] blanking_duration = trial_frame['blanking_duration'] maximum_target_reaching_duration = trial_frame[ 'maximum_target_reaching_duration'] maximum_saccade_latency = trial_frame['maximum_saccade_latency'] after_landing_fixation_duration = trial_frame[ 'after_landing_fixation_duration'] inter_trial_interval = trial_frame['inter_trial_interval'] t_started_fixating = None i_started_fixating = None t_target_appeared = None i_target_appeared = None t_saccade_started = None i_saccade_started = None t_saccade_landed = None i_saccade_landed = None t_blanking_ended = None i_blanking_ended = None i_led_shift_done = None i_target_turned_off = None response = None # turn off all leds self.athread.write_uint8(255, 0, 0, 0) # do the inter trial interval here, too many exit points time.sleep(inter_trial_interval) # show the fixation led self.athread.write_uint8(fixation_led, *before_fixation_color) phase = Phase.BEFORE_FIXATION t_trial_started = time.monotonic() last_i = None R_head_world = np.full((3, 3), np.nan) # this loop runs during data collection in the trial # if trial_successful is true when you break out of it, the trial's parameters and timings are saved trial_successful = False while True: # do calibration if escape was pressed escape_pressed, backspace_pressed, kp_enter_pressed = fh.was_key_pressed( pygame.K_ESCAPE, pygame.K_BACKSPACE, pygame.K_KP_ENTER) if escape_pressed: return TrialResult.CALIBRATE, None if kp_enter_pressed: return TrialResult.EYE_CALIBRATE, None if backspace_pressed: self.athread.write_uint8(255, 128, 0, 0) key = fh.wait_for_keypress(pygame.K_ESCAPE, pygame.K_SPACE) if key == pygame.K_ESCAPE: return TrialResult.FAILED, None else: self.athread.write_uint8(255, 0, 128, 0) time.sleep(0.5) self.athread.write_uint8(255, 0, 0, 0) return TrialResult.QUIT_EXPERIMENT, None # check that a new pupil sample is available current_i = self.pthread.i_current_sample if current_i == last_i: time.sleep(0.0005) continue last_i = current_i pdata = self.pthread.current_sample.copy() gaze_normals = pdata[NORMALS] confidence = pdata[CONFIDENCE] odata = self.othread.current_sample.copy() helmet_leds = odata[HELMET].reshape((4, 3)) last_R_head_world = R_head_world R_head_world, helmet_ref_points = self.helmet.solve(helmet_leds) T_eye_world = helmet_ref_points[I_EYE, :] # if helmet rigidbody couldn't be solved or pupil data is bad if fh.anynan(R_head_world) or fh.anynan(gaze_normals) or ( confidence < pupil_min_confidence and phase != Phase.DURING_SACCADE): if phase == Phase.BEFORE_FIXATION: continue elif phase == Phase.DURING_FIXATION: if fh.anynan(R_head_world): print('head was not visible during fixation') if fh.anynan(gaze_normals): print('nan values in gaze normals during fixation') if confidence < pupil_min_confidence: print('pupil confidence was too low during fixation') phase = Phase.BEFORE_FIXATION self.athread.write_uint8(fixation_led, *before_fixation_color) continue else: if fh.anynan(R_head_world): print('head was not visible') if fh.anynan(gaze_normals): print('nan values in gaze normals') if confidence < pupil_min_confidence: print('pupil confidence was too low') break current_head_angular_velocity = 0 if np.allclose( last_R_head_world, R_head_world ) else np.rad2deg( np.arccos( (np.trace(last_R_head_world @ R_head_world.T) - 1) / 2) ) * self.othread.server_config['optotrak']['collection_frequency'] def is_eye_within_led_threshold(led, threshold): eye_to_led = fh.to_unit(self.rig_leds[led, :] - T_eye_world) gaze_normals_world = R_head_world @ fh.normals_nonlinear_angular_transform( self.R_eye_head @ gaze_normals, self.nonlinear_parameters) eye_to_led_azim_elev = np.rad2deg( np.abs( fh.to_azim_elev(gaze_normals_world) - fh.to_azim_elev(eye_to_led))) # threshold is only horizontal right now because of increased vertical angle noise and spikes is_within_threshold = eye_to_led_azim_elev[0] <= threshold return is_within_threshold if phase == Phase.BEFORE_FIXATION: is_fixating = is_eye_within_led_threshold( fixation_led, fixation_threshold) is_holding_still = current_head_angular_velocity <= fixation_head_velocity_threshold if is_fixating and is_holding_still: self.athread.write_uint8(fixation_led, *during_fixation_color) t_started_fixating = time.monotonic() i_started_fixating = current_i phase = Phase.DURING_FIXATION elif phase == Phase.DURING_FIXATION: is_fixating = is_eye_within_led_threshold( fixation_led, fixation_threshold) is_holding_still = current_head_angular_velocity <= fixation_head_velocity_threshold if not (is_fixating and is_holding_still): self.athread.write_uint8(fixation_led, *before_fixation_color) phase = Phase.BEFORE_FIXATION # if fixation is lost here, don't start a completely new trial, that would be wasteful because # the target wasn't even shown else: if time.monotonic( ) - t_started_fixating >= fixation_duration: i_target_appeared = current_i t_target_appeared = time.monotonic() self.athread.write_uint8(target_led, *before_response_target_color) phase = Phase.BEFORE_SACCADE elif phase == Phase.BEFORE_SACCADE: if time.monotonic( ) - t_target_appeared > maximum_saccade_latency: # abort trial because saccade latency was too long print('maximum saccade latency exceeded') break has_started_saccade = not is_eye_within_led_threshold( fixation_led, saccade_threshold) if has_started_saccade: i_saccade_started = current_i t_saccade_started = time.monotonic() if blanking_duration == 0: i_led_shift_done = self.athread.write_uint8( shifted_target_led, *before_response_target_color) else: i_target_turned_off = self.athread.write_uint8( 255, 0, 0, 0) phase = Phase.DURING_SACCADE elif phase == Phase.DURING_SACCADE: if time.monotonic() - t_saccade_started >= blanking_duration: if t_blanking_ended is None: t_blanking_ended = time.monotonic() i_blanking_ended = current_i i_led_shift_done = self.athread.write_uint8( shifted_target_led, *before_response_target_color) if time.monotonic( ) - t_blanking_ended > maximum_target_reaching_duration: print('maximum target reaching duration was exceeded') break is_fixating_target = is_eye_within_led_threshold( shifted_target_led, landing_fixation_threshold) if is_fixating_target: i_saccade_landed = current_i t_saccade_landed = time.monotonic() phase = Phase.AFTER_LANDING elif phase == Phase.AFTER_LANDING: if time.monotonic( ) - t_saccade_landed > after_landing_fixation_duration: self.athread.write_uint8(shifted_target_led, *during_response_target_color) response_key = fh.wait_for_keypress( pygame.K_LEFT, pygame.K_RIGHT) response = 'left' if response_key == pygame.K_LEFT else 'right' trial_successful = True break # sampling loop over if trial_successful: trial_data = OrderedDict([ # arrays need to be wrapped in a list so pandas doesn't try to make them long columns ('o_data', [self.othread.get_shortened_data()]), ('p_data', [self.pthread.get_shortened_data()]), ('helmet', self.helmet), ('nonlinear_parameters', [self.nonlinear_parameters]), ('R_eye_head', [self.R_eye_head]), ('t_trial_started', t_trial_started), ('i_started_fixating', i_started_fixating), ('t_started_fixating', t_started_fixating), ('i_target_appeared', i_target_appeared), ('t_target_appeared', t_target_appeared), ('t_saccade_started', t_saccade_started), ('i_saccade_started', i_saccade_started), ('t_saccade_landed', t_saccade_landed), ('i_saccade_landed', i_saccade_landed), ('i_blanking_ended', i_blanking_ended), ('t_blanking_ended', t_blanking_ended), ('t_led_shift_done', self.athread.command_timestamps[i_led_shift_done]), ('t_target_turned_off', self.athread.command_timestamps[i_target_turned_off] if blanking_duration > 0 else None), ('response', response), ]) return TrialResult.COMPLETED, trial_data else: return TrialResult.FAILED, None
athread = fh.ArduinoThread() athread.start() athread.started_running.wait() probe = fh.FourMarkerProbe() n_samples = 5 led_positions = np.full((n_samples, 3), np.nan) rig_calibration_indices = np.round(np.linspace(0, 254, n_samples)).astype(np.int) for i, ci in enumerate(rig_calibration_indices): athread.write_uint8(ci, 100, 0, 0) while True: fh.wait_for_keypress(pygame.K_SPACE) probe_rotation, probe_tip = probe.solve(othread.current_sample.copy()[15:27].reshape(4,3)) if fh.anynan(probe_rotation): continue led_positions[i, :] = probe_tip[:] break athread.write_uint8(255, 0, 0, 0) rig = fh.LedRig(rig_calibration_indices) rotation, rig_led_positions = rig.solve(led_positions) from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt fig, axes = plt.subplots(3, 1) axes[0].plot(rig_led_positions[:, 0], rig_led_positions[:, 1]) axes[1].plot(rig_led_positions[:, 0], rig_led_positions[:, 2])
results = {} for c in corners: clist = [] for i in range(n_measurements): print(c, 'measurement', i) measurement = {} measurement['dist'] = float(input('measured distance: ')) print('measure position') while True: input('press enter') r_probe, t_probe = probe.solve( othread.current_sample[15:27].reshape((4, 3))) if fh.anynan(r_probe): print('try again') continue measurement['pos'] = t_probe break clist.append(measurement) results[c] = (clist) print('done') othread.should_stop.set() othread.join() #%% with open('screen_corners_triangulation_data.pickle', 'wb') as file: pickle.dump(results, file)