/////////////////////////////////////////////////////////////////////////////// // // Eye tracker // /////////////////////////////////////////////////////////////////////////////// folder 'Eye Tracker' { var eye_h_raw = 0 var eye_v_raw = 0 var pupil_size_r = 0 } eyelink eye_tracker ( tracker_ip = '100.1.1.1' tracking_dist = 1024 data_interval = 1ms pupil_rx = eye_h_raw pupil_ry = eye_v_raw autostart = true pupil_size_r = pupil_size_r ) /////////////////////////////////////////////////////////////////////////////// // // Eye monitoring // /////////////////////////////////////////////////////////////////////////////// folder 'Eye Tracker' { var eye_h_calibrated = 0 var eye_v_calibrated = 0 } linear_eye_calibrator eye_calibrator ( eyeh_raw = eye_h_raw eyev_raw = eye_v_raw eyeh_calibrated = eye_h_calibrated eyev_calibrated = eye_v_calibrated ) folder 'Eye Tracker' { var eye_h = 0 var eye_v = 0 } boxcar_filter_1d ( in1 = eye_h_calibrated out1 = eye_h width_samples = 5 ) boxcar_filter_1d ( in1 = eye_v_calibrated out1 = eye_v width_samples = 5 ) folder 'Fixation' { var fixation_window_size = 2 var fixation_color_r = 1 var fixation_color_g = 1 var fixation_color_b = 1 var fixation_point_size_min = 0.2 var fixation_point_size_max = 5 var fixation_point_visible = true var fixation_pulse_period = 1s var fixation_pulse_start_time = 0 var fixation_pos_x = 0 var fixation_pos_y = 0 var eye_in_window = false } %define fixation_pulse_amplitude = (fixation_point_size_max - fixation_point_size_min) / 2 %define fixation_pulse_offset = (fixation_point_size_min + fixation_point_size_max) / 2 %define fixation_pulse_delta_t = now() - fixation_pulse_start_time %define fixation_pulse_phase = 2*pi() / fixation_pulse_period * fixation_pulse_delta_t %define fixation_point_size_dynamic = -fixation_pulse_amplitude * cos(fixation_pulse_phase) + fixation_pulse_offset %define fixation_point_size_static = fixation_point_size_min %define fixation_point_size = [fixation_point_size_static, fixation_point_size_dynamic][fixation_pulse_delta_t <= fixation_pulse_period] frame_list dynamic_fixation_point ( stimulus_group = dynamic_fixation_point_frames loop = true autoplay = true ) stimulus_group dynamic_fixation_point_frames { circular_fixation_point fixation_point ( trigger_width = fixation_window_size trigger_watch_x = eye_h trigger_watch_y = eye_v trigger_flag = eye_in_window color = fixation_color_r, fixation_color_g, fixation_color_b x_size = fixation_point_size x_position = fixation_pos_x y_position = fixation_pos_y alpha_multiplier = fixation_point_visible ) } folder 'Fixation' { var saccade = false } basic_eye_monitor ( eyeh_calibrated = eye_h eyev_calibrated = eye_v eye_state = saccade width_samples = 5 saccade_entry_speed = 60 saccade_exit_speed = 20 ) /////////////////////////////////////////////////////////////////////////////// // // Photodiode // /////////////////////////////////////////////////////////////////////////////// rectangle photodiode_image ( x_size = 2 x_position = display_bounds('right') - 4.2 y_position = display_bounds('top') - 2.8 ) /////////////////////////////////////////////////////////////////////////////// // // Background Color // /////////////////////////////////////////////////////////////////////////////// stimulus_display ( background_color = 0.5, 0.5, 0.5 ) /////////////////////////////////////////////////////////////////////////////// // // DAQ // /////////////////////////////////////////////////////////////////////////////// var experiment_state_line = false var trial_start_line = false var stim_start_line = false var reward_line = false //var photodiode_voltage = 0 nidaq daq ( name = Dev1 update_interval = 3ms analog_input_data_interval = 1ms analog_output_data_interval = 1ms autostart = true ) { nidaq_digital_output ( port_number = 2 num_lines_in_port = 8 line4 = reward_line line2 = experiment_state_line line0= stim_start_line ) //nidaq_analog_input_voltage ( // channel_number = 0 // range_min = -10 // range_max = 10 // voltage = photodiode_voltage // ) } %define send_ttl_pulse (line) assignment (variable = line; value = true) assignment (variable = line; value = false) %end %define send_ttl_start (line) assignment (variable = line; value = true) %end %define send_ttl_end (line) assignment (variable = line; value = false) %end folder 'Performance' { var reward (0) { if (reward > 0) { play_sound (reward_sound) reward_line = true wait (reward) reward_line = false } } } /////////////////////////////////////////////////////////////////////////////// // // Mouse // /////////////////////////////////////////////////////////////////////////////// var mouse_x = 0 var mouse_y = 0 var mouse_button_pressed (false) { if (mouse_button_pressed) { fixation_pos_x = mouse_x fixation_pos_y = mouse_y } } mouse_input mouse ( mouse_position_x = mouse_x mouse_position_y = mouse_y mouse_down = mouse_button_pressed use_mirror_window = true ) /////////////////////////////////////////////////////////////////////////////// // // Keyboard // /////////////////////////////////////////////////////////////////////////////// var key_x_pressed = false // Allow hot keys only if "x" kept pressed var key_p_pressed (false) { if (key_p_pressed and key_x_pressed) { fixation_pulse_start_time = now() } } var key_r_pressed (false) { if (key_r_pressed and key_x_pressed) { reward = reward_duration } } var key_spacebar_pressed (false) { if (key_spacebar_pressed and key_x_pressed) { take_calibration_sample ( calibrator = eye_calibrator calibratable_object = fixation_point ) } } var key_c_pressed (false) { if (key_c_pressed and key_x_pressed) { update_calibration (eye_calibrator) } } usbhid_generic keyboard ( usage_page = 1 usage = 6 //log_all_input_values = true ) { usbhid_generic_input_channel ( usage_page = 7 usage = 19 value = key_p_pressed ) usbhid_generic_input_channel ( usage_page = 7 usage = 21 value = key_r_pressed ) usbhid_generic_input_channel ( usage_page = 7 usage = 44 value = key_spacebar_pressed ) usbhid_generic_input_channel ( usage_page = 7 usage = 6 value = key_c_pressed ) usbhid_generic_input_channel ( usage_page = 7 usage = 27 value = key_x_pressed ) } /////////////////////////////////////////////////////////////////////////////// // // Stimuli // /////////////////////////////////////////////////////////////////////////////// %define stimulus_set_size = 3 folder 'Stimuli' { var stimulus_size = 8 var stimulus_pos_x = 0 var stimulus_pos_y = 0 } stimulus_group RSVP_stimuli { range_replicator ( variable = RSVP_stim_counter from = 1 to = stimulus_set_size step = 1 ) { image_file Normalizer_${RSVP_stim_counter} ( path = 'images/Normalizer_${RSVP_stim_counter}.png' x_size = stimulus_size x_position = stimulus_pos_x y_position = stimulus_pos_y ) } } selection RSVP_test_stim_index ( values = 0 : stimulus_set_size - 1, 0 : stimulus_set_size - 1 selection = random_without_replacement ) /////////////////////////////////////////////////////////////////////////////// // // Sounds // /////////////////////////////////////////////////////////////////////////////// wav_file stim_on_sound ('/Library/Application Support/MWorks/Examples/RSVPDemo/sounds/stm_on.wav') wav_file acquire_sound ('/Library/Application Support/MWorks/Examples/RSVPDemo/sounds/acquire.wav') wav_file reward_sound ('/Library/Application Support/MWorks/Examples/RSVPDemo/sounds/reward.wav') wav_file failure_sound ('/Library/Application Support/MWorks/Examples/RSVPDemo/sounds/failure.wav') wav_file complete_sound ('/Library/Application Support/MWorks/Examples/RSVPDemo/sounds/complete.wav') /////////////////////////////////////////////////////////////////////////////// // // RSVP protocol // /////////////////////////////////////////////////////////////////////////////// folder 'RSVP' { var inter_trial_interval = 500ms var ignore_time = 5s var stim_on_delay = 300ms var stim_on_time = 100ms var stim_off_time = 100ms var stimulus_set_repetitions = 2 var stimulus_set_repeat_count = 0 var stimuli_per_trial = 8 var stimuli_shown = 0 var correct_fixation = -1 var stimulus_presented = -1 var correct_fixation_list = [] var stimulus_presented_list = [] } folder 'Reward' { var reward_duration = 50ms } folder 'Performance' { var num_stims_shown = 0 var miss_count = 0 var success = 0 var failure = 0 var ignore = 0 } var sync = 0 // For MATLAB window protocol 'RSVP' { start_io_device (keyboard) fixation_pos_x = 0 fixation_pos_y = 0 reset_selection (RSVP_test_stim_index) stimuli_shown = 0 stimulus_set_repeat_count = 0 correct_fixation = -1 stimulus_presented = -1 correct_fixation_list = [] stimulus_presented_list = [] send_ttl_start (experiment_state_line) task { state 'RSVP start' { wait (inter_trial_interval) send_ttl_pulse (trial_start_line) sync = 1 report ('********** RSVP Trial Started **********') play_sound (stim_on_sound) num_stims_shown = 0 live_queue_stimulus (fixation_point) update_display () start_timer ( timer = rsvp_timer duration = ignore_time ) goto ( target = 'RSVP wait' when = eye_in_window and (not saccade) ) goto ( target = 'RSVP ignore' when = timer_expired(rsvp_timer) ) } state 'RSVP wait' { play_sound (acquire_sound) if (stim_on_delay > 0) { start_timer ( timer = rsvp_timer duration = stim_on_delay ) } goto ( target = 'RSVP failure' when = (not eye_in_window) and (not saccade) ) goto ( target = 'RSVP stim on' when = (stim_on_delay == 0) or timer_expired(rsvp_timer) ) } state 'RSVP stim on' { queue_stimulus (photodiode_image) queue_stimulus (RSVP_stimuli[RSVP_test_stim_index]) dequeue_stimulus (fixation_point) update_display () start_timer ( timer = rsvp_timer duration = stim_on_time ) send_ttl_pulse (stim_start_line) stimulus_presented = RSVP_test_stim_index + 1 stimulus_presented_list += [RSVP_test_stim_index + 1] goto ( target = 'RSVP stim reject' when = (not eye_in_window) and (not saccade) ) goto ( target = 'RSVP stim off' when = timer_expired(rsvp_timer) ) } state 'RSVP stim off' { dequeue_stimulus (photodiode_image) dequeue_stimulus (RSVP_stimuli[RSVP_test_stim_index]) queue_stimulus (fixation_point) update_display () start_timer ( timer = rsvp_timer duration = stim_off_time ) goto ( target = 'RSVP stim reject' when = (not eye_in_window) and (not saccade) ) goto ( target = 'RSVP stim accept' when = timer_expired(rsvp_timer) ) } state 'RSVP stim accept' { accept_selections (RSVP_test_stim_index) correct_fixation = 1 correct_fixation_list += [1] num_stims_shown += 1 stimuli_shown += 1 choose { when (stimuli_shown < stimulus_set_size * stimulus_set_repetitions) { next_selection (RSVP_test_stim_index) } otherwise { reset_selection (RSVP_test_stim_index) stimulus_set_repeat_count += stimulus_set_repetitions stimuli_shown = 0 } } goto ( target = 'RSVP success' when = num_stims_shown >= stimuli_per_trial ) goto ('RSVP stim on') } state 'RSVP stim reject' { dequeue_stimulus (photodiode_image) dequeue_stimulus (RSVP_stimuli[RSVP_test_stim_index]) queue_stimulus (fixation_point) correct_fixation = 0 correct_fixation_list += [0] update_display () reject_selections (RSVP_test_stim_index) goto ('RSVP failure') } state 'RSVP success' { wait (200ms) report ('*********** RSVP Success!!!! ***********') reward = reward_duration success += 1 miss_count = 0 goto ('RSVP pause') } state 'RSVP failure' { report ('*********** RSVP Failure!!!! ***********') play_sound (failure_sound) failure += 1 miss_count += 1 goto ('RSVP pause') } state 'RSVP ignore' { report ('*********** RSVP Ignored!!!! ***********') ignore += 1 miss_count += 1 goto ('RSVP pause') } state 'RSVP pause' { dequeue_stimulus (fixation_point) update_display () start_timer ( timer = rsvp_timer duration = 500ms ) goto ( target = 'RSVP break' when = miss_count == 5 ) goto ( target = 'RSVP complete' when = timer_expired(rsvp_timer) ) } state 'RSVP break' { play_sound (failure_sound) miss_count = 0 wait(2500ms) goto ('RSVP complete') } state 'RSVP complete' { report ('********* RSVP Trial Completed *********') sync = 0 goto ( target = 'RSVP start' when = stimulus_set_repeat_count < stimulus_set_repetitions ) yield () } } stimulus_presented = -1 correct_fixation = -1 } /////////////////////////////////////////////////////////////////////////////// // // Eye calibration protocol // /////////////////////////////////////////////////////////////////////////////// folder 'Calibration' { var cal_fixation_duration = 800ms var cal_fix_pos_x (scope = local; default_value = 0) var cal_fix_pos_y (scope = local; default_value = 0) } protocol 'Eye Calibration' { report ('******** STARTING CALIBRATION ********') clear_calibration (eye_calibrator) num_stims_shown = 0 miss_count = 0 success = 0 ignore = 0 failure = 0 list calibration_list (selection = random_without_replacement) { range_replicator ( variable = cal_fix_pos_x from = -5 to = 5 step = 5 ) { range_replicator ( variable = cal_fix_pos_y from = -5 to = 5 step = 5 ) { trial { task { state 'cal wait' { wait (1000ms) fixation_pos_x = cal_fix_pos_x fixation_pos_y = cal_fix_pos_y fixation_point_visible = false live_queue_stimulus (fixation_point) update_display () goto ( target = 'cal prefixation' when = not eye_in_window ) } state 'cal prefixation' { play_sound (stim_on_sound) fixation_point_visible = true update_display () start_timer ( timer = cal_timer duration = 1500ms ) goto ( target = 'cal ignore' when = timer_expired(cal_timer) ) goto ( target = 'cal acquire' when = eye_in_window and (not saccade) ) } state 'cal acquire' { play_sound (acquire_sound) start_timer ( timer = cal_timer duration = cal_fixation_duration ) goto ('cal fixation') } state 'cal fixation' { begin_calibration_average (eye_calibrator) goto ( target = 'cal failure' when = not eye_in_window ) goto ( target = 'cal fixation monitor' when = saccade ) goto ( target = 'cal pre reward' when = timer_expired(cal_timer) ) } state 'cal fixation monitor' { end_calibration_average_and_ignore (eye_calibrator) goto ( target = 'cal fixation' when = eye_in_window and (not saccade) ) goto ( target = 'cal failure' when = (not eye_in_window) and (not saccade) ) } state 'cal pre reward' { start_timer ( timer = cal_timer duration = 30ms ) goto ( target = 'cal success' when = saccade or timer_expired(cal_timer) ) } state 'cal success' { report ('************** HIT!!!!! **************') end_calibration_average_and_take_sample ( calibrator = eye_calibrator calibratable_object = fixation_point ) success += 1 dequeue_stimulus (fixation_point) update_display () reward = reward_duration num_stims_shown += 1 miss_count = 0 accept_selections (calibration_list) yield () } state 'cal failure' { report ('************** MISS!!!! **************') end_calibration_average_and_ignore (eye_calibrator) play_sound (failure_sound) wait (500ms) dequeue_stimulus (fixation_point) update_display () failure += 1 miss_count += 1 reject_selections (calibration_list) goto ( target = 'cal break' when = miss_count == 7 ) yield () } state 'cal ignore' { report ('************* IGNORE!!!! *************') dequeue_stimulus (fixation_point) update_display () ignore += 1 miss_count += 1 reject_selections (calibration_list) goto ( target = 'cal break' when = miss_count == 7 ) yield () } state 'cal break' { miss_count = 0 wait (5s) yield () } } } } } } report ('******** CALIBRATION FINISHED ********') play_sound (complete_sound) update_calibration (eye_calibrator) wait (3s) // Let completion sound finish playing } /////////////////////////////////////////////////////////////////////////////// // // Manual eye calibration protocol // /////////////////////////////////////////////////////////////////////////////// protocol 'Eye Calibration (Manual)' { start_io_device (mouse) start_io_device (keyboard) clear_calibration (eye_calibrator) live_queue_stimulus (dynamic_fixation_point) update_display () wait (600s) }