def test_displayNewRound(self): next_label = visual.TextStim(window, units='norm', text=u'New Round', pos=[0, 0], height=0.1, color='red', colorSpace='rgb', alignHoriz='center', alignVert='center') res = displayNewRound(window, next_label, keyboard, False) self.assertEqual(res, 0)
def trial(self, clock, window, shapes, keys, text_color, centered, wait_time, warning_time, exp, count, ser): """ Main motor type function :param clock: clock used for standardized timing; initialized in the main experimental loop :param window: display window :param shapes: array of shape objects to be used (not already randomized) :param keys: keyboard device :param text_color: color for text :param centered: true if blocks are to be centered, false otherwise :param wait_time: max seconds for trial to wait before continuing if trial is not completed :param warning_time: num of seconds left to begin countdown :param exp: experiment object for adding trial data :param count: number of motor trials during this experiment for file naming :param ser: serial port that links to XBee for syncing :return: status of trial where 0 = completed but incorrect; 1 = completed and correct; 2 = incomplete """ # Default Value Set Up for Timing # global stimulus_beg_time stimulus_beg_time = -1 global in_between_time in_between_time = -1 global total_stimuli_time total_stimuli_time = -1 # Position Tracking File Set Up # text_file = open("keys_exp_%d.txt" % count, "w") text_file.write("Time\tKey Event\tKey Type\n") # Text Values # count_label = visual.TextStim(window, units='norm', text=u'', pos=[-0.5,-0.5], height=0.2, color=text_color, colorSpace='rgb255',alignHoriz='center', alignVert='center') second_label = visual.TextStim(window, units='norm', text=u'Press on keyboard.', pos = [0,0], height=0.1, color=text_color, colorSpace='rgb255',alignHoriz='center', alignVert='center') next_label = visual.TextStim(window, units='norm', text=u'Motor Round', pos=[0,0], height=0.1, color=text_color, colorSpace='rgb',alignHoriz='center', alignVert='center') # Display round name helper.displayNewRound(window, next_label) # Set up default values for tracking mouse click timing key_times = [-1, -1, -1] key_counter = [0, 0, 0] length = len(shapes) event.clearEvents() # block sequence display # print "%f BEGIN BLOCK SEQUENCE" %(clock.getTime()) global in_between_time in_between_time = helper.drawSequence(window, shapes, clock) print "%f END BLOCK SEQUENCE" %(clock.getTime()) # instructions are displayed # self.hub.clearEvents('all') for nFrames in range(200): second_label.draw() window.flip() # for block interaction # beg_time = clock.getTime() curr_time = clock.getTime() self.hub.clearEvents() # changes location of shapes if centered, so that they don't overlap if centered and length > 1: helper.adjustShapeLoc(shapes) # Map the blocks in each corner to the respective keyboard events keyMap = mapKeys(shapes) # store time right when interactive stimuli is presented for reference window.callOnFlip(track_time, clock) # draw the interactive stimuli [s.draw() for s in shapes] window.flip() temp_time = -1 key_char = '' no_release = False # loop until trial finished or timed out while curr_time - beg_time < wait_time: # redraw the stimuli every window flip [s.draw() for s in shapes] count_label.draw() window.flip() # If the shape is pressed long enough to set it to 0 opacity, then the timing of that setting is recorded. events = keys.getKeys() # iohub device keyboard info events2 = event.getKeys(timeStamped=clock) # event keyboard with event timing based on clock input for kbe in events2: # get accurate timing key_char = kbe[0] temp_time = kbe[1] print kbe for kbe in events: # use for recording keystrokes and durations text_file.write(str(clock.getTime())) text_file.write("\t") text_file.write(kbe.type) text_file.write("\t") text_file.write(kbe.char) text_file.write("\n") if kbe.type == 'KEYBOARD_RELEASE': no_release = False elif kbe.type == 'KEYBOARD_PRESS': no_release = True if no_release is True: if key_char == keyMap[0]: key_counter[0] += 1 if 0 < key_counter[0] < CHNG_INTERVAL: shapes[0].setOpacity(shapes[0].opacity - ADJ_INTERVAL) elif key_counter[0] == CHNG_INTERVAL: shapes[0].setOpacity(OPACITY_THRES) elif key_counter[0] == REQUIRED_FRAMES: shapes[0].setOpacity(0.0) key_times[0] = temp_time elif key_char == keyMap[1]: key_counter[1] += 1 if 0 < key_counter[1] < CHNG_INTERVAL: shapes[1].setOpacity(shapes[1].opacity - ADJ_INTERVAL) elif key_counter[1] == CHNG_INTERVAL: shapes[1].setOpacity(OPACITY_THRES) elif key_counter[1] == REQUIRED_FRAMES: shapes[1].setOpacity(0.0) key_times[1] = temp_time elif key_char == keyMap[2]: key_counter[2] += 1 if 0 < key_counter[2] < CHNG_INTERVAL: shapes[2].setOpacity(shapes[2].opacity - ADJ_INTERVAL) elif key_counter[2] == CHNG_INTERVAL: shapes[2].setOpacity(OPACITY_THRES) elif key_counter[2] == REQUIRED_FRAMES: shapes[2].setOpacity(0.0) key_times[2] = temp_time # once the round is finished, use previous counters to calculate total time spent and individual click times if helper.checkOpacity(shapes): finish_time = clock.getTime() total_stimuli_time = finish_time - stimulus_beg_time print "\n%f\t%f\t%f" %(key_times[0], key_times[1], key_times[2]) print "%f TOTAL TIME TO FINISH ROUND" %(total_stimuli_time) break # adjust countdown value, to be displayed with the next flip curr_time = clock.getTime() if (curr_time - beg_time) >= (wait_time - warning_time - 0.1): count_label.setText(int(round(wait_time - (curr_time - beg_time))), 0) event.clearEvents() # save data in the experiment file global stimulus_beg_time exp.addData("stimulus_begin_time", stimulus_beg_time) exp.addData("in_between_time", in_between_time) exp.addData("total_stimuli_time", total_stimuli_time) exp.addData("time1", key_times[0]) exp.addData("time2", key_times[1]) exp.addData("time3", key_times[2]) # return status code based on correctness of sequence if (curr_time-beg_time) > wait_time: return 2 if length == 1: return 1 elif length == 2: if key_times[1] > key_times[0]: return 1 # correct else: return 0 # not correct elif length == 3: if key_times[0] < key_times[1] < key_times[2]: return 1 # correct else: return 0 # not correct return -1
def trial(self, clock, window, shapes, mouse, text_color, centered, wait_time, warning_time, exp, count, ser): """ Main motor type function :param clock: clock used for standardized timing; initialized in the main experimental loop :param window: display window :param shapes: array of shape objects to be used (not already randomized) :param mouse: mouse device :param text_color: color for text :param centered: true if blocks are to be centered, false otherwise :param wait_time: max seconds for trial to wait before continuing if trial is not completed :param warning_time: num of seconds left to begin countdown :param exp: experiment object for adding trial data :param count: number of motor trials during this experiment for file naming :param ser: serial port that links to XBee for syncing :return: status of trial where 0 = completed but incorrect; 1 = completed and correct; 2 = incomplete """ # Default Value Set Up for Timing # global stimulus_beg_time stimulus_beg_time = -1 global in_between_time in_between_time = -1 global total_stimuli_time total_stimuli_time = -1 # Position Tracking File Set Up # text_file = open("motor_exp_%d.txt" % count, "w") text_file.write("Time\tX\tY\tRandom\n") # Text Values # count_label = visual.TextStim(window, units='norm', text=u'', pos=[-0.5, -0.5], height=0.2, color=text_color, colorSpace='rgb255', alignHoriz='center', alignVert='center') second_label = visual.TextStim(window, units='norm', text=u'Click on blocks', pos=[0, 0], height=0.1, color=text_color, colorSpace='rgb255', alignHoriz='center', alignVert='center') next_label = visual.TextStim(window, units='norm', text=u'Motor Round', pos=[0, 0], height=0.1, color=text_color, colorSpace='rgb', alignHoriz='center', alignVert='center') # Display round name helper.displayNewRound(window, next_label) # Set up default values for tracking mouse click timing mouse_times = [-1, -1, -1] mouse_counter = [0, 0, 0] length = len(shapes) event.clearEvents() # block sequence display # print "%f BEGIN BLOCK SEQUENCE" % (clock.getTime()) ser.write("Begin Sequence") global in_between_time in_between_time = helper.drawSequence(window, shapes, clock) ser.write("End Sequence") print "%f END BLOCK SEQUENCE" % (clock.getTime()) # instructions are displayed # self.hub.clearEvents('all') for nFrames in range(200): second_label.draw() window.flip() # for block interaction # beg_time = clock.getTime() curr_time = clock.getTime() self.hub.clearEvents() # changes location of shapes if centered, so that they don't overlap if centered and length > 1: helper.adjustShapeLoc(shapes) # store time right when interactive stimuli is presented for reference window.callOnFlip(track_time, clock, mouse) # draw the interactive stimuli [s.draw() for s in shapes] window.flip() # set up the initial mouse position mouse.getPos() # loop until trial finished or timed out while curr_time - beg_time < wait_time: # redraw the stimuli every window flip [s.draw() for s in shapes] count_label.draw() window.flip() # Check for mouse clicks and location; even if not all present, goes off location. # First checks if mouse is within a block, then fades that block out according to the specified # constant values. # If the shape is pressed long enough to set it to 0 opacity, then the timing of that setting is recorded. buttons = mouse.getPressed() if buttons[0] == 1: if shapes[0].contains(mouse): mouse_counter[0] += 1 if 0 < mouse_counter[0] < CHNG_INTERVAL: shapes[0].setOpacity(shapes[0].opacity - ADJ_INTERVAL) elif mouse_counter[0] == CHNG_INTERVAL: shapes[0].setOpacity(OPACITY_THRES) elif mouse_counter[0] == REQUIRED_FRAMES: shapes[0].setOpacity(0.0) mouse_times[0] = clock.getTime() elif shapes[1].contains(mouse): mouse_counter[1] += 1 if 0 < mouse_counter[1] < CHNG_INTERVAL: shapes[1].setOpacity(shapes[1].opacity - ADJ_INTERVAL) elif mouse_counter[1] == CHNG_INTERVAL: shapes[1].setOpacity(OPACITY_THRES) elif mouse_counter[1] == REQUIRED_FRAMES: shapes[1].setOpacity(0.0) mouse_times[1] = clock.getTime() elif shapes[2].contains(mouse): mouse_counter[2] += 1 if 0 < mouse_counter[2] < CHNG_INTERVAL: shapes[2].setOpacity(shapes[2].opacity - ADJ_INTERVAL) elif mouse_counter[2] == CHNG_INTERVAL: shapes[2].setOpacity(OPACITY_THRES) elif mouse_counter[2] == REQUIRED_FRAMES: shapes[2].setOpacity(0.0) mouse_times[2] = clock.getTime() # once the round is finished, use previous counters to calculate total time spent and individual click times if helper.checkOpacity(shapes): finish_time = clock.getTime() total_stimuli_time = finish_time - stimulus_beg_time print "\n%f\t%f\t%f" % (mouse_times[0], mouse_times[1], mouse_times[2]) print "%f TOTAL TIME TO FINISH ROUND" % (total_stimuli_time) break num = random.randint(0, 10) ser.write(num) # write random number to Zigbee for syncing # gets and saves the mouse position and time A mouse_position_time(clock, mouse, text_file, num) # adjust countdown value, to be displayed with the next flip curr_time = clock.getTime() if (curr_time - beg_time) >= (wait_time - warning_time - 0.1): count_label.setText(int(round(wait_time - (curr_time - beg_time))), 0) # save data in the experiment file global stimulus_beg_time exp.addData("stimulus_begin_time", stimulus_beg_time) exp.addData("in_between_time", in_between_time) exp.addData("total_stimuli_time", total_stimuli_time) exp.addData("time1", mouse_times[0]) exp.addData("time2", mouse_times[1]) exp.addData("time3", mouse_times[2]) # closes the position tracking file at the end A text_file.close() # return status code based on correctness of sequence if (curr_time - beg_time) > wait_time: return 2 if length == 1: return 1 elif length == 2: if mouse_times[1] > mouse_times[0]: return 1 # correct else: return 0 # not correct elif length == 3: if mouse_times[0] < mouse_times[1] < mouse_times[2]: return 1 # correct else: return 0 # not correct return -1
def trial(self, clock, window, shapes, text_color, centered, wait_time, warning_time, exp, count, ser): """ Main eye tracking type function :param clock: clock used for standardized timing; initialized in the main experimental loop :param window: display window :param shapes: array of shape objects to be used (not already randomized) :param text_color: color for text :param centered: true if blocks are to be centered, false otherwise :param wait_time: max seconds for trial to wait before continuing if trial is not completed :param warning_time: num of seconds left to begin countdown :param exp: experiment object for adding trial data :param count: number of eye tracking trials during this experiment for file naming :param ser: serial port that links to XBee for syncing :return: status of trial where 0 = completed but incorrect; 1 = completed and correct; 2 = incomplete """ # Default Value Set Up for Timing # global stimulus_beg_time stimulus_beg_time = -1 global in_between_time in_between_time = -1 global total_stimuli_time total_stimuli_time = -1 # Position Tracking File Set Up # text_file = open("eye_exp_%d.txt" % count, "w") text_file.write("Time\tX\tY\tRandom\n") # Eye tracker set up tracker=self.hub.devices.tracker tracker.runSetupProcedure() tracker.setConnectionState(True) tracker.setRecordingState(True) # Check if eye tracker is returning any data over a short range of time. # Quits the trial if the eye tracker if it is not connected or cannot detect eyes. connection_counter = 0 for i in range(100): gpos=tracker.getLastGazePosition() if not isinstance(gpos,(tuple,list)): connection_counter += 1 time.sleep(0.01) if connection_counter > 97: print "no connection" return # Text Values # gaze_dot = visual.GratingStim(window,tex=None, mask="gauss", pos=(0,0 ),size=(0.1,0.1),color='green', units='norm') instructions_text_stim = visual.TextStim(window, units='norm', text=u'Look at blocks', pos=[0,0.2], height=0.1, color=text_color, colorSpace='rgb',alignHoriz='center', alignVert='center') count_label = visual.TextStim(window, units='norm', text=u'', pos=[-0.5,-0.5], height=0.2, color=text_color, colorSpace='rgb255',alignHoriz='center', alignVert='center') next_label = visual.TextStim(window, units='norm', text=u'Eye Round', pos=[0,0], height=0.1, color=text_color, colorSpace='rgb',alignHoriz='center', alignVert='center') # Display round name helper.displayNewRound(window, next_label) # Set up default values # self.hub.clearEvents('all') init_time_array = [-1, -1, -1] if len(shapes) == 3: init_time_array[0] = 0 init_time_array[1] = 0 init_time_array[2] = 0 elif len(shapes) == 2: init_time_array[0] = 0 init_time_array[1] = 0 elif len(shapes) == 1: init_time_array[0] = 0 time_diff_array = [-1, -1, -1] length = len(shapes) # block sequence display # print "%f BEGIN BLOCK SEQUENCE" %(clock.getTime()) ser.write("Begin Sequence") global in_between_time in_between_time = helper.drawSequence(window, shapes, clock) ser.write("End Sequence") print "%f END BLOCK SEQUENCE" %(clock.getTime()) # instructions are displayed # self.hub.clearEvents('all') for nFrames in range(200): instructions_text_stim.draw() window.flip() # for block interaction # beg_time = clock.getTime() curr_time = clock.getTime() self.hub.clearEvents() # changes location of shapes if centered, so that they don't overlap # if centered and length > 1: helper.adjustShapeLoc(shapes) # store time right when clicking stimuli is presented for reference window.callOnFlip(track_time, clock) # draw the interactive stimuli [s.draw() for s in shapes] window.flip() # initialize the block vertices shapes_verts = [] for num in range(len(shapes)): shapes_verts.append(helper.pix_conv(window.size[0], window.size[1], shapes[num].width, shapes[num].height, shapes[num].pos[0], shapes[num].pos[1])) print shapes_verts # loop until trial finished or timed out while curr_time - beg_time < wait_time: [s.draw() for s in shapes] count_label.draw() # Get the latest gaze position gpos = tracker.getLastGazePosition() if not isinstance(gpos,(tuple,list)): window.flip() continue # Check if eye position is within each block. # If so, then the time is recorded and the opacity is changed accordingly. for num in range(length): s = shapes[num] if s.opacity == 0.0: continue if isinstance(gpos,(tuple,list)): if shapes_verts[num][0] < gpos[0] < shapes_verts[num][1] and shapes_verts[num][3] < gpos[1] < shapes_verts[num][2]: init_time_array[num] += 1 time_diff_array[num] = clock.getTime() if init_time_array[num] == REQUIRED_FRAMES/2: s.setOpacity(0.5) if init_time_array[num] > REQUIRED_FRAMES: s.setOpacity(0.0) init_time_array[num] = clock.getTime() break # if position is within one block, it could not be in any of the other blocks # reset the opacity of the block if the eye looks away for a specified amount time # implemented to prevent registering blocks when the eye is just passing over the screen elif clock.getTime() - time_diff_array[num] > THRES_TIME_AWAY: init_time_array[num] = 0 time_diff_array[num] = -1 s.setOpacity(1.0) ## EYE TRACKING DISPLAY DOT; NOT NECESSARY FOR ACTUAL IMPLEMENTATION ## # if isinstance(gpos,(tuple,list)): # # Adjusting eye tracking values to match norm units to display on the screen # gpos0_adj = (gpos[0]/window.size[1])*2 # gpos1_adj = (gpos[1]/window.size[0])*2 # gaze_dot.setPos([gpos0_adj, gpos1_adj]) # gaze_dot.draw() # once the round is finished, use previous counters to calculate total time spent and individual position times if helper.checkOpacity(shapes): finish_time = clock.getTime() total_stimuli_time = finish_time - stimulus_beg_time print "\n%f\t%f\t%f" %(init_time_array[0], init_time_array[1], init_time_array[2]) print "%f TOTAL TIME TO FINISH ROUND" %(total_stimuli_time) break num = random.randint(0, 10) ser.write(num) # write random number to Zigbee for syncing # gets and saves the eye position and time eye_position_time(clock, gpos, text_file) # adjust count_down, to be displayed with the next flip curr_time = clock.getTime() if (curr_time - beg_time) >= (wait_time - warning_time - 0.1): count_label.setText(int(round(wait_time - (curr_time - beg_time))), 0) window.flip() # turn off eye tracker self.hub.clearEvents('all') tracker.setRecordingState(False) tracker.setConnectionState(False) # save data in the experiment file exp.addData("stimulus_begin_time", stimulus_beg_time) exp.addData("in_between_time", in_between_time) exp.addData("total_stimuli_time", total_stimuli_time) exp.addData("time1", init_time_array[0]) exp.addData("time2", init_time_array[1]) exp.addData("time3", init_time_array[2]) # closes the position tracking file at the end A text_file.close() # return status code based on correctness of sequence if (curr_time-beg_time) > wait_time: return 2 if length == 1: return 1 elif length == 2: if init_time_array[1] > init_time_array[0]: return 1 # correct else: return 0 # not correct elif length == 3: if init_time_array[0] < init_time_array[1] < init_time_array[2]: return 1 # correct else: return 0 # not correct
def trial(self, clock, window, shapes, mouse, keys, text_color, wait_time, warning_time, exp, count, ser): """ Main speech type function :param clock: clock used for standardized timing; initialized in the main experimental loop :param window: display window :param shapes: array of shape objects to be used (not already randomized) :param mouse: mouse device :param text_color: color for text :param wait_time: max seconds for trial to wait before continuing if trial is not completed :param warning_time: num of seconds left to begin countdown :param exp: experiment object for adding trial data :param count: number of speech trials during this experiment for file naming :param ser: serial port that links to XBee for syncing :return: status of trial where 0 = completed but incorrect; 1 = completed and correct; 2 = incomplete """ # Default Value Set Up for Timing # global stimulus_beg_time stimulus_beg_time = -1 global in_between_time in_between_time = -1 global total_stimuli_time total_stimuli_time = -1 # Text values count_label = visual.TextStim(window, units='norm', text=u'', pos = [0, -0.6], height=0.2, color=text_color, colorSpace='rgb255',alignHoriz='center', alignVert='center') second_label = visual.TextStim(window, units='norm', text=u'Speak color of blocks', pos=[0,0.3], height=0.1, color=text_color, colorSpace='rgb255',alignHoriz='center', alignVert='center') done_label = visual.TextStim(window, units='norm', text=u'Done', pos=[0,-0.25], height=0.1, color=text_color, colorSpace='rgb255',alignHoriz='center', alignVert='center') done_button = visual.Rect(window, width=0.5, height=0.25, lineColor=(0, 0, 0), lineWidth=2, lineColorSpace='rgb', pos=(0, -0.25)) next_label = visual.TextStim(window, units='norm', text=u'Speech Round', pos=[0,0], height=0.1, color=text_color, colorSpace='rgb',alignHoriz='center', alignVert='center') BLOCK_LIST = [second_label, done_button, done_label] # Display round name helper.displayNewRound(window, next_label) # Microphone Set Up # microphone.switchOn(sampleRate=16000) name = "speech_exp_%d.wav" %count mic = microphone.AdvAudioCapture(filename=name) # todo: can edit marker to output as sync signal; played when recording starts # marker currently set to not output any sound on onset of recording mic.setMarker(tone=5000, secs=0.015, volume=0.0) # Block Sequence Display # print "%f BEGIN BLOCK SEQUENCE" %(clock.getTime()) ser.write("Begin Sequence") global in_between_time in_between_time = helper.drawSequence(window, shapes, clock) ser.write("End Sequence") print "%f END BLOCK SEQUENCE" %(clock.getTime()) # for block interaction # self.hub.clearEvents() start_time = clock.getTime() timeout_counter = 0 self.hub.clearEvents() # store time right when clicking stimuli is presented for reference window.callOnFlip(track_speech_time, clock, mouse) window.flip() # records for length of specified wait time mic.record(wait_time, block=False) ser.write("Start") while mic.recorder.running: [s.draw() for s in BLOCK_LIST] count_label.draw() window.flip() timeout_counter += 1 ## FOR MOUSE-CLICK END: ## # buttons, times = mouse.getPressed(getTime=True) # if mouse.isPressedIn(done_button, buttons=[0]): # break ## FOR KEYBOARD END: ## events = keys.getKeys() if len(events) != 0: break # adjust countdown value, to be displayed with the next flip if timeout_counter >= ((wait_time - warning_time)*60) and timeout_counter % 60 == 0: count_label.setText(((wait_time*60)-timeout_counter)/60) # turn off microphone and saves the audio file automatically ser.write("Finish") microphone.switchOff() finish_time = clock.getTime() # once the round is finished, use previous counters to calculate total time spent and individual click times total_stimuli_time = finish_time - speech_beg_time print "\n%f" %(finish_time) print "%f TOTAL TIME TO FINISH ROUND" %(total_stimuli_time) # save data in the experiment file exp.addData("stimulus_begin_time", speech_beg_time) exp.addData("in_between_time", in_between_time) exp.addData("total_stimuli_time", total_stimuli_time) exp.addData("time1", start_time) exp.addData("time2", finish_time) # return status code based on correctness of sequence if timeout_counter == wait_time*60: return 2 if timeout_counter < wait_time*60: # assume finished normally by clicking button return 0 return -1
def test_displayNewRound(self): next_label = visual.TextStim(window, units='norm', text=u'New Round', pos=[0,0], height=0.1, color='red', colorSpace='rgb',alignHoriz='center', alignVert='center') res= displayNewRound(window,next_label,keyboard,False) self.assertEqual(res,0)
def trial(self, clock, window, shapes, mouse, keys, text_color, wait_time, warning_time, exp, count, ser): """ Main speech type function :param clock: clock used for standardized timing; initialized in the main experimental loop :param window: display window :param shapes: array of shape objects to be used (not already randomized) :param mouse: mouse device :param text_color: color for text :param wait_time: max seconds for trial to wait before continuing if trial is not completed :param warning_time: num of seconds left to begin countdown :param exp: experiment object for adding trial data :param count: number of speech trials during this experiment for file naming :param ser: serial port that links to XBee for syncing :return: status of trial where 0 = completed but incorrect; 1 = completed and correct; 2 = incomplete """ # Default Value Set Up for Timing # global stimulus_beg_time stimulus_beg_time = -1 global in_between_time in_between_time = -1 global total_stimuli_time total_stimuli_time = -1 # Text values count_label = visual.TextStim(window, units='norm', text=u'', pos=[0, -0.6], height=0.2, color=text_color, colorSpace='rgb255', alignHoriz='center', alignVert='center') second_label = visual.TextStim(window, units='norm', text=u'Speak color of blocks', pos=[0, 0.3], height=0.1, color=text_color, colorSpace='rgb255', alignHoriz='center', alignVert='center') done_label = visual.TextStim(window, units='norm', text=u'Done', pos=[0, -0.25], height=0.1, color=text_color, colorSpace='rgb255', alignHoriz='center', alignVert='center') done_button = visual.Rect(window, width=0.5, height=0.25, lineColor=(0, 0, 0), lineWidth=2, lineColorSpace='rgb', pos=(0, -0.25)) next_label = visual.TextStim(window, units='norm', text=u'Speech Round', pos=[0, 0], height=0.1, color=text_color, colorSpace='rgb', alignHoriz='center', alignVert='center') BLOCK_LIST = [second_label, done_button, done_label] # Display round name helper.displayNewRound(window, next_label) # Microphone Set Up # microphone.switchOn(sampleRate=16000) name = "speech_exp_%d.wav" % count mic = microphone.AdvAudioCapture(filename=name) # todo: can edit marker to output as sync signal; played when recording starts # marker currently set to not output any sound on onset of recording mic.setMarker(tone=5000, secs=0.015, volume=0.0) # Block Sequence Display # print "%f BEGIN BLOCK SEQUENCE" % (clock.getTime()) ser.write("Begin Sequence") global in_between_time in_between_time = helper.drawSequence(window, shapes, clock) ser.write("End Sequence") print "%f END BLOCK SEQUENCE" % (clock.getTime()) # for block interaction # self.hub.clearEvents() start_time = clock.getTime() timeout_counter = 0 self.hub.clearEvents() # store time right when clicking stimuli is presented for reference window.callOnFlip(track_speech_time, clock, mouse) window.flip() # records for length of specified wait time mic.record(wait_time, block=False) ser.write("Start") while mic.recorder.running: [s.draw() for s in BLOCK_LIST] count_label.draw() window.flip() timeout_counter += 1 ## FOR MOUSE-CLICK END: ## # buttons, times = mouse.getPressed(getTime=True) # if mouse.isPressedIn(done_button, buttons=[0]): # break ## FOR KEYBOARD END: ## events = keys.getKeys() if len(events) != 0: break # adjust countdown value, to be displayed with the next flip if timeout_counter >= ( (wait_time - warning_time) * 60) and timeout_counter % 60 == 0: count_label.setText(((wait_time * 60) - timeout_counter) / 60) # turn off microphone and saves the audio file automatically ser.write("Finish") microphone.switchOff() finish_time = clock.getTime() # once the round is finished, use previous counters to calculate total time spent and individual click times total_stimuli_time = finish_time - speech_beg_time print "\n%f" % (finish_time) print "%f TOTAL TIME TO FINISH ROUND" % (total_stimuli_time) # save data in the experiment file exp.addData("stimulus_begin_time", speech_beg_time) exp.addData("in_between_time", in_between_time) exp.addData("total_stimuli_time", total_stimuli_time) exp.addData("time1", start_time) exp.addData("time2", finish_time) # return status code based on correctness of sequence if timeout_counter == wait_time * 60: return 2 if timeout_counter < wait_time * 60: # assume finished normally by clicking button return 0 return -1
def trial(self, clock, window, shapes, text_color, centered, wait_time, warning_time, exp, count, ser): """ Main eye tracking type function :param clock: clock used for standardized timing; initialized in the main experimental loop :param window: display window :param shapes: array of shape objects to be used (not already randomized) :param text_color: color for text :param centered: true if blocks are to be centered, false otherwise :param wait_time: max seconds for trial to wait before continuing if trial is not completed :param warning_time: num of seconds left to begin countdown :param exp: experiment object for adding trial data :param count: number of eye tracking trials during this experiment for file naming :param ser: serial port that links to XBee for syncing :return: status of trial where 0 = completed but incorrect; 1 = completed and correct; 2 = incomplete """ # Default Value Set Up for Timing # global stimulus_beg_time stimulus_beg_time = -1 global in_between_time in_between_time = -1 global total_stimuli_time total_stimuli_time = -1 # Position Tracking File Set Up # text_file = open("eye_exp_%d.txt" % count, "w") text_file.write("Time\tX\tY\tRandom\n") # Eye tracker set up tracker = self.hub.devices.tracker tracker.runSetupProcedure() tracker.setConnectionState(True) tracker.setRecordingState(True) # Check if eye tracker is returning any data over a short range of time. # Quits the trial if the eye tracker if it is not connected or cannot detect eyes. connection_counter = 0 for i in range(100): gpos = tracker.getLastGazePosition() if not isinstance(gpos, (tuple, list)): connection_counter += 1 time.sleep(0.01) if connection_counter > 97: print "no connection" return # Text Values # gaze_dot = visual.GratingStim(window, tex=None, mask="gauss", pos=(0, 0), size=(0.1, 0.1), color='green', units='norm') instructions_text_stim = visual.TextStim(window, units='norm', text=u'Look at blocks', pos=[0, 0.2], height=0.1, color=text_color, colorSpace='rgb', alignHoriz='center', alignVert='center') count_label = visual.TextStim(window, units='norm', text=u'', pos=[-0.5, -0.5], height=0.2, color=text_color, colorSpace='rgb255', alignHoriz='center', alignVert='center') next_label = visual.TextStim(window, units='norm', text=u'Eye Round', pos=[0, 0], height=0.1, color=text_color, colorSpace='rgb', alignHoriz='center', alignVert='center') # Display round name helper.displayNewRound(window, next_label) # Set up default values # self.hub.clearEvents('all') init_time_array = [-1, -1, -1] if len(shapes) == 3: init_time_array[0] = 0 init_time_array[1] = 0 init_time_array[2] = 0 elif len(shapes) == 2: init_time_array[0] = 0 init_time_array[1] = 0 elif len(shapes) == 1: init_time_array[0] = 0 time_diff_array = [-1, -1, -1] length = len(shapes) # block sequence display # print "%f BEGIN BLOCK SEQUENCE" % (clock.getTime()) ser.write("Begin Sequence") global in_between_time in_between_time = helper.drawSequence(window, shapes, clock) ser.write("End Sequence") print "%f END BLOCK SEQUENCE" % (clock.getTime()) # instructions are displayed # self.hub.clearEvents('all') for nFrames in range(200): instructions_text_stim.draw() window.flip() # for block interaction # beg_time = clock.getTime() curr_time = clock.getTime() self.hub.clearEvents() # changes location of shapes if centered, so that they don't overlap # if centered and length > 1: helper.adjustShapeLoc(shapes) # store time right when clicking stimuli is presented for reference window.callOnFlip(track_time, clock) # draw the interactive stimuli [s.draw() for s in shapes] window.flip() # initialize the block vertices shapes_verts = [] for num in range(len(shapes)): shapes_verts.append( helper.pix_conv(window.size[0], window.size[1], shapes[num].width, shapes[num].height, shapes[num].pos[0], shapes[num].pos[1])) print shapes_verts # loop until trial finished or timed out while curr_time - beg_time < wait_time: [s.draw() for s in shapes] count_label.draw() # Get the latest gaze position gpos = tracker.getLastGazePosition() if not isinstance(gpos, (tuple, list)): window.flip() continue # Check if eye position is within each block. # If so, then the time is recorded and the opacity is changed accordingly. for num in range(length): s = shapes[num] if s.opacity == 0.0: continue if isinstance(gpos, (tuple, list)): if shapes_verts[num][0] < gpos[0] < shapes_verts[num][ 1] and shapes_verts[num][3] < gpos[1] < shapes_verts[ num][2]: init_time_array[num] += 1 time_diff_array[num] = clock.getTime() if init_time_array[num] == REQUIRED_FRAMES / 2: s.setOpacity(0.5) if init_time_array[num] > REQUIRED_FRAMES: s.setOpacity(0.0) init_time_array[num] = clock.getTime() break # if position is within one block, it could not be in any of the other blocks # reset the opacity of the block if the eye looks away for a specified amount time # implemented to prevent registering blocks when the eye is just passing over the screen elif clock.getTime() - time_diff_array[num] > THRES_TIME_AWAY: init_time_array[num] = 0 time_diff_array[num] = -1 s.setOpacity(1.0) ## EYE TRACKING DISPLAY DOT; NOT NECESSARY FOR ACTUAL IMPLEMENTATION ## # if isinstance(gpos,(tuple,list)): # # Adjusting eye tracking values to match norm units to display on the screen # gpos0_adj = (gpos[0]/window.size[1])*2 # gpos1_adj = (gpos[1]/window.size[0])*2 # gaze_dot.setPos([gpos0_adj, gpos1_adj]) # gaze_dot.draw() # once the round is finished, use previous counters to calculate total time spent and individual position times if helper.checkOpacity(shapes): finish_time = clock.getTime() total_stimuli_time = finish_time - stimulus_beg_time print "\n%f\t%f\t%f" % (init_time_array[0], init_time_array[1], init_time_array[2]) print "%f TOTAL TIME TO FINISH ROUND" % (total_stimuli_time) break num = random.randint(0, 10) ser.write(num) # write random number to Zigbee for syncing # gets and saves the eye position and time eye_position_time(clock, gpos, text_file) # adjust count_down, to be displayed with the next flip curr_time = clock.getTime() if (curr_time - beg_time) >= (wait_time - warning_time - 0.1): count_label.setText(int(round(wait_time - (curr_time - beg_time))), 0) window.flip() # turn off eye tracker self.hub.clearEvents('all') tracker.setRecordingState(False) tracker.setConnectionState(False) # save data in the experiment file exp.addData("stimulus_begin_time", stimulus_beg_time) exp.addData("in_between_time", in_between_time) exp.addData("total_stimuli_time", total_stimuli_time) exp.addData("time1", init_time_array[0]) exp.addData("time2", init_time_array[1]) exp.addData("time3", init_time_array[2]) # closes the position tracking file at the end A text_file.close() # return status code based on correctness of sequence if (curr_time - beg_time) > wait_time: return 2 if length == 1: return 1 elif length == 2: if init_time_array[1] > init_time_array[0]: return 1 # correct else: return 0 # not correct elif length == 3: if init_time_array[0] < init_time_array[1] < init_time_array[2]: return 1 # correct else: return 0 # not correct
def trial(self, clock, window, shapes, keys, text_color, centered, wait_time, warning_time, exp, count, ser): """ Main motor type function :param clock: clock used for standardized timing; initialized in the main experimental loop :param window: display window :param shapes: array of shape objects to be used (not already randomized) :param keys: keyboard device :param text_color: color for text :param centered: true if blocks are to be centered, false otherwise :param wait_time: max seconds for trial to wait before continuing if trial is not completed :param warning_time: num of seconds left to begin countdown :param exp: experiment object for adding trial data :param count: number of motor trials during this experiment for file naming :param ser: serial port that links to XBee for syncing :return: status of trial where 0 = completed but incorrect; 1 = completed and correct; 2 = incomplete """ # Default Value Set Up for Timing # global stimulus_beg_time stimulus_beg_time = -1 global in_between_time in_between_time = -1 global total_stimuli_time total_stimuli_time = -1 # Position Tracking File Set Up # text_file = open("keys_exp_%d.txt" % count, "w") text_file.write("Time\tKey Event\tKey Type\n") # Text Values # count_label = visual.TextStim(window, units='norm', text=u'', pos=[-0.5, -0.5], height=0.2, color=text_color, colorSpace='rgb255', alignHoriz='center', alignVert='center') second_label = visual.TextStim(window, units='norm', text=u'Press on keyboard.', pos=[0, 0], height=0.1, color=text_color, colorSpace='rgb255', alignHoriz='center', alignVert='center') next_label = visual.TextStim(window, units='norm', text=u'Motor Round', pos=[0, 0], height=0.1, color=text_color, colorSpace='rgb', alignHoriz='center', alignVert='center') # Display round name helper.displayNewRound(window, next_label) # Set up default values for tracking mouse click timing key_times = [-1, -1, -1] key_counter = [0, 0, 0] length = len(shapes) event.clearEvents() # block sequence display # print "%f BEGIN BLOCK SEQUENCE" % (clock.getTime()) global in_between_time in_between_time = helper.drawSequence(window, shapes, clock) print "%f END BLOCK SEQUENCE" % (clock.getTime()) # instructions are displayed # self.hub.clearEvents('all') for nFrames in range(200): second_label.draw() window.flip() # for block interaction # beg_time = clock.getTime() curr_time = clock.getTime() self.hub.clearEvents() # changes location of shapes if centered, so that they don't overlap if centered and length > 1: helper.adjustShapeLoc(shapes) # Map the blocks in each corner to the respective keyboard events keyMap = mapKeys(shapes) # store time right when interactive stimuli is presented for reference window.callOnFlip(track_time, clock) # draw the interactive stimuli [s.draw() for s in shapes] window.flip() temp_time = -1 key_char = '' no_release = False # loop until trial finished or timed out while curr_time - beg_time < wait_time: # redraw the stimuli every window flip [s.draw() for s in shapes] count_label.draw() window.flip() # If the shape is pressed long enough to set it to 0 opacity, then the timing of that setting is recorded. events = keys.getKeys() # iohub device keyboard info events2 = event.getKeys( timeStamped=clock ) # event keyboard with event timing based on clock input for kbe in events2: # get accurate timing key_char = kbe[0] temp_time = kbe[1] print kbe for kbe in events: # use for recording keystrokes and durations text_file.write(str(clock.getTime())) text_file.write("\t") text_file.write(kbe.type) text_file.write("\t") text_file.write(kbe.char) text_file.write("\n") if kbe.type == 'KEYBOARD_RELEASE': no_release = False elif kbe.type == 'KEYBOARD_PRESS': no_release = True if no_release is True: if key_char == keyMap[0]: key_counter[0] += 1 if 0 < key_counter[0] < CHNG_INTERVAL: shapes[0].setOpacity(shapes[0].opacity - ADJ_INTERVAL) elif key_counter[0] == CHNG_INTERVAL: shapes[0].setOpacity(OPACITY_THRES) elif key_counter[0] == REQUIRED_FRAMES: shapes[0].setOpacity(0.0) key_times[0] = temp_time elif key_char == keyMap[1]: key_counter[1] += 1 if 0 < key_counter[1] < CHNG_INTERVAL: shapes[1].setOpacity(shapes[1].opacity - ADJ_INTERVAL) elif key_counter[1] == CHNG_INTERVAL: shapes[1].setOpacity(OPACITY_THRES) elif key_counter[1] == REQUIRED_FRAMES: shapes[1].setOpacity(0.0) key_times[1] = temp_time elif key_char == keyMap[2]: key_counter[2] += 1 if 0 < key_counter[2] < CHNG_INTERVAL: shapes[2].setOpacity(shapes[2].opacity - ADJ_INTERVAL) elif key_counter[2] == CHNG_INTERVAL: shapes[2].setOpacity(OPACITY_THRES) elif key_counter[2] == REQUIRED_FRAMES: shapes[2].setOpacity(0.0) key_times[2] = temp_time # once the round is finished, use previous counters to calculate total time spent and individual click times if helper.checkOpacity(shapes): finish_time = clock.getTime() total_stimuli_time = finish_time - stimulus_beg_time print "\n%f\t%f\t%f" % (key_times[0], key_times[1], key_times[2]) print "%f TOTAL TIME TO FINISH ROUND" % (total_stimuli_time) break # adjust countdown value, to be displayed with the next flip curr_time = clock.getTime() if (curr_time - beg_time) >= (wait_time - warning_time - 0.1): count_label.setText(int(round(wait_time - (curr_time - beg_time))), 0) event.clearEvents() # save data in the experiment file global stimulus_beg_time exp.addData("stimulus_begin_time", stimulus_beg_time) exp.addData("in_between_time", in_between_time) exp.addData("total_stimuli_time", total_stimuli_time) exp.addData("time1", key_times[0]) exp.addData("time2", key_times[1]) exp.addData("time3", key_times[2]) # return status code based on correctness of sequence if (curr_time - beg_time) > wait_time: return 2 if length == 1: return 1 elif length == 2: if key_times[1] > key_times[0]: return 1 # correct else: return 0 # not correct elif length == 3: if key_times[0] < key_times[1] < key_times[2]: return 1 # correct else: return 0 # not correct return -1