diff --git a/rds_task.psyexp b/rds_task.psyexp index 60c83e0c41f1ddc6dc338f5f639135163d9dbd13..41cefdacda4e8fcecad4d24168ea156e6edf663d 100644 --- a/rds_task.psyexp +++ b/rds_task.psyexp @@ -8,7 +8,7 @@ <Param name="Enable Escape" updates="None" val="True" valType="bool"/> <Param name="Experiment info" updates="None" val="{'participant': '', 'session': '001'}" valType="code"/> <Param name="Force stereo" updates="None" val="True" valType="bool"/> - <Param name="Full-screen window" updates="None" val="True" valType="bool"/> + <Param name="Full-screen window" updates="None" val="False" valType="bool"/> <Param name="HTML path" updates="None" val="html" valType="str"/> <Param name="Incomplete URL" updates="None" val="" valType="str"/> <Param name="JS libs" updates="None" val="packaged" valType="str"/> @@ -23,7 +23,7 @@ <Param name="Show mouse" updates="None" val="False" valType="bool"/> <Param name="Units" updates="None" val="deg" valType="str"/> <Param name="Use version" updates="None" val="" valType="str"/> - <Param name="Window size (pixels)" updates="None" val="[1920, 1080]" valType="code"/> + <Param name="Window size (pixels)" updates="None" val="[1600, 900]" valType="code"/> <Param name="blendMode" updates="None" val="avg" valType="str"/> <Param name="color" updates="None" val="$[0,0,0]" valType="str"/> <Param name="colorSpace" updates="None" val="rgb" valType="str"/> @@ -33,6 +33,21 @@ </Settings> <Routines> <Routine name="trial"> + <CodeComponent name="sequence"> + <Param name="Begin Experiment" updates="constant" val="MINLEVEL=0&#10;MAXLEVEL=9" valType="extendedCode"/> + <Param name="Begin JS Experiment" updates="constant" val="" valType="extendedCode"/> + <Param name="Begin JS Routine" updates="constant" val="" valType="extendedCode"/> + <Param name="Begin Routine" updates="constant" val="sequence=np.random.randint(0,10,MAXLEVEL-level+1)&#10;stair.addOtherData('digits.stimulus', sequence)" valType="extendedCode"/> + <Param name="Code Type" updates="None" val="Py" valType="str"/> + <Param name="Each Frame" updates="constant" val="" valType="extendedCode"/> + <Param name="Each JS Frame" updates="constant" val="" valType="extendedCode"/> + <Param name="End Experiment" updates="constant" val="" valType="extendedCode"/> + <Param name="End JS Experiment" updates="constant" val="" valType="extendedCode"/> + <Param name="End JS Routine" updates="constant" val="" valType="extendedCode"/> + <Param name="End Routine" updates="constant" val="resp.corr = [int(k) for k in resp.keys] == list(np.flip(sequence))&#10;stair.addResponse(resp.corr)&#10;stair.addOtherData('resp.keys', resp.keys)" valType="extendedCode"/> + <Param name="disabled" updates="None" val="False" valType="bool"/> + <Param name="name" updates="None" val="sequence" valType="code"/> + </CodeComponent> <TextComponent name="digits"> <Param name="color" updates="constant" val="white" valType="str"/> <Param name="colorSpace" updates="constant" val="rgb" valType="str"/> @@ -49,14 +64,32 @@ <Param name="saveStartStop" updates="None" val="True" valType="bool"/> <Param name="startEstim" updates="None" val="" valType="code"/> <Param name="startType" updates="None" val="time (s)" valType="str"/> - <Param name="startVal" updates="None" val="1" valType="code"/> + <Param name="startVal" updates="None" val="0" valType="code"/> <Param name="stopType" updates="None" val="duration (s)" valType="str"/> <Param name="stopVal" updates="constant" val="1.0" valType="code"/> <Param name="syncScreenRefresh" updates="None" val="True" valType="bool"/> - <Param name="text" updates="constant" val="3" valType="str"/> + <Param name="text" updates="set every repeat" val="$str(sequence)[1:-1]" valType="str"/> <Param name="units" updates="None" val="from exp settings" valType="str"/> - <Param name="wrapWidth" updates="constant" val="" valType="code"/> + <Param name="wrapWidth" updates="constant" val="30" valType="code"/> </TextComponent> + <KeyboardComponent name="resp"> + <Param name="allowedKeys" updates="constant" val="'0','1','2','3','4','5','6','7','8','9'" valType="code"/> + <Param name="correctAns" updates="constant" val="" valType="str"/> + <Param name="disabled" updates="None" val="False" valType="bool"/> + <Param name="discard previous" updates="constant" val="True" valType="bool"/> + <Param name="durationEstim" updates="None" val="" valType="code"/> + <Param name="forceEndRoutine" updates="constant" val="False" valType="bool"/> + <Param name="name" updates="None" val="resp" valType="code"/> + <Param name="saveStartStop" updates="None" val="True" valType="bool"/> + <Param name="startEstim" updates="None" val="" valType="code"/> + <Param name="startType" updates="None" val="time (s)" valType="str"/> + <Param name="startVal" updates="None" val="0" valType="code"/> + <Param name="stopType" updates="None" val="duration (s)" valType="str"/> + <Param name="stopVal" updates="constant" val="2" valType="code"/> + <Param name="store" updates="constant" val="all keys" valType="str"/> + <Param name="storeCorrect" updates="constant" val="False" valType="bool"/> + <Param name="syncScreenRefresh" updates="constant" val="True" valType="bool"/> + </KeyboardComponent> </Routine> <Routine name="Instruction"> <TextComponent name="instruction"> @@ -72,7 +105,7 @@ <Param name="opacity" updates="constant" val="1" valType="code"/> <Param name="ori" updates="constant" val="0" valType="code"/> <Param name="pos" updates="constant" val="(0, 0)" valType="code"/> - <Param name="saveStartStop" updates="None" val="True" valType="bool"/> + <Param name="saveStartStop" updates="None" val="False" valType="bool"/> <Param name="startEstim" updates="None" val="" valType="code"/> <Param name="startType" updates="None" val="time (s)" valType="str"/> <Param name="startVal" updates="None" val="0.0" valType="code"/> @@ -87,36 +120,40 @@ <Param name="allowedKeys" updates="constant" val="'space'" valType="code"/> <Param name="correctAns" updates="constant" val="" valType="str"/> <Param name="disabled" updates="None" val="False" valType="bool"/> - <Param name="discard previous" updates="constant" val="True" valType="bool"/> + <Param name="discard previous" updates="constant" val="False" valType="bool"/> <Param name="durationEstim" updates="None" val="" valType="code"/> <Param name="forceEndRoutine" updates="constant" val="True" valType="bool"/> <Param name="name" updates="None" val="key_start" valType="code"/> - <Param name="saveStartStop" updates="None" val="True" valType="bool"/> + <Param name="saveStartStop" updates="None" val="False" valType="bool"/> <Param name="startEstim" updates="None" val="" valType="code"/> <Param name="startType" updates="None" val="time (s)" valType="str"/> <Param name="startVal" updates="None" val="0.0" valType="code"/> <Param name="stopType" updates="None" val="duration (s)" valType="str"/> <Param name="stopVal" updates="constant" val="" valType="code"/> - <Param name="store" updates="constant" val="last key" valType="str"/> + <Param name="store" updates="constant" val="nothing" valType="str"/> <Param name="storeCorrect" updates="constant" val="False" valType="bool"/> - <Param name="syncScreenRefresh" updates="constant" val="True" valType="bool"/> + <Param name="syncScreenRefresh" updates="constant" val="False" valType="bool"/> </KeyboardComponent> </Routine> </Routines> <Flow> <Routine name="Instruction"/> - <LoopInitiator loopType="TrialHandler" name="trials"> - <Param name="Selected rows" updates="None" val="" valType="str"/> - <Param name="conditions" updates="None" val="None" valType="str"/> - <Param name="conditionsFile" updates="None" val="" valType="str"/> + <LoopInitiator loopType="StairHandler" name="stair"> + <Param name="N down" updates="None" val="1" valType="code"/> + <Param name="N reversals" updates="None" val="1" valType="code"/> + <Param name="N up" updates="None" val="1" valType="code"/> <Param name="endPoints" updates="None" val="[0, 1]" valType="num"/> <Param name="isTrials" updates="None" val="True" valType="bool"/> - <Param name="loopType" updates="None" val="sequential" valType="str"/> + <Param name="loopType" updates="None" val="staircase" valType="str"/> + <Param name="max value" updates="None" val="MAXLEVEL" valType="code"/> + <Param name="min value" updates="None" val="MINLEVEL" valType="code"/> <Param name="nReps" updates="None" val="20" valType="code"/> - <Param name="name" updates="None" val="trials" valType="code"/> - <Param name="random seed" updates="None" val="" valType="code"/> + <Param name="name" updates="None" val="stair" valType="code"/> + <Param name="start value" updates="None" val="MAXLEVEL-1" valType="code"/> + <Param name="step sizes" updates="None" val="[1]" valType="code"/> + <Param name="step type" updates="None" val="lin" valType="str"/> </LoopInitiator> <Routine name="trial"/> - <LoopTerminator name="trials"/> + <LoopTerminator name="stair"/> </Flow> </PsychoPy2experiment> diff --git a/rds_task.py b/rds_task.py new file mode 100644 index 0000000000000000000000000000000000000000..b55782594c23692f96efde9a4d789b56070f741e --- /dev/null +++ b/rds_task.py @@ -0,0 +1,342 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +This experiment was created using PsychoPy3 Experiment Builder (v3.2.0), + on November 13, 2019, at 18:38 +If you publish work using this script the most relevant publication is: + + Peirce J, Gray JR, Simpson S, MacAskill M, Höchenberger R, Sogo H, Kastman E, Lindeløv JK. (2019) + PsychoPy2: Experiments in behavior made easy Behav Res 51: 195. + https://doi.org/10.3758/s13428-018-01193-y + +""" + +from __future__ import absolute_import, division + +from psychopy import locale_setup +from psychopy import prefs +from psychopy import sound, gui, visual, core, data, event, logging, clock +from psychopy.constants import (NOT_STARTED, STARTED, PLAYING, PAUSED, + STOPPED, FINISHED, PRESSED, RELEASED, FOREVER) + +import numpy as np # whole numpy lib is available, prepend 'np.' +from numpy import (sin, cos, tan, log, log10, pi, average, + sqrt, std, deg2rad, rad2deg, linspace, asarray) +from numpy.random import random, randint, normal, shuffle +import os # handy system and path functions +import sys # to get file system encoding + +from psychopy.hardware import keyboard + +# Ensure that relative paths start from the same directory as this script +_thisDir = os.path.dirname(os.path.abspath(__file__)) +os.chdir(_thisDir) + +# Store info about the experiment session +psychopyVersion = '3.2.0' +expName = 'rds_task' # from the Builder filename that created this script +expInfo = {'participant': '', 'session': '001'} +dlg = gui.DlgFromDict(dictionary=expInfo, sortKeys=False, title=expName) +if dlg.OK == False: + core.quit() # user pressed cancel +expInfo['date'] = data.getDateStr() # add a simple timestamp +expInfo['expName'] = expName +expInfo['psychopyVersion'] = psychopyVersion + +# Data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc +filename = _thisDir + os.sep + u'data/%s_%s_%s' % (expInfo['participant'], expName, expInfo['date']) + +# An ExperimentHandler isn't essential but helps with data saving +thisExp = data.ExperimentHandler(name=expName, version='', + extraInfo=expInfo, runtimeInfo=None, + originPath='D:\\Projects\\reverse-digit-span-task\\rds_task.py', + savePickle=True, saveWideText=True, + dataFileName=filename) +# save a log file for detail verbose info +logFile = logging.LogFile(filename+'.log', level=logging.EXP) +logging.console.setLevel(logging.WARNING) # this outputs to the screen, not a file + +endExpNow = False # flag for 'escape' or other condition => quit the exp +frameTolerance = 0.001 # how close to onset before 'same' frame + +# Start Code - component code to be run before the window creation + +# Setup the Window +win = visual.Window( + size=[1600, 900], fullscr=False, screen=0, + winType='pyglet', allowGUI=True, allowStencil=False, + monitor='testMonitor', color=[0,0,0], colorSpace='rgb', + blendMode='avg', useFBO=True, + units='deg') +# store frame rate of monitor if we can measure it +expInfo['frameRate'] = win.getActualFrameRate() +if expInfo['frameRate'] != None: + frameDur = 1.0 / round(expInfo['frameRate']) +else: + frameDur = 1.0 / 60.0 # could not measure, so guess + +# create a default keyboard (e.g. to check for escape) +defaultKeyboard = keyboard.Keyboard() + +# Initialize components for Routine "Instruction" +InstructionClock = core.Clock() +instruction = visual.TextStim(win=win, name='instruction', + text='Press SPACE to start', + font='Arial', + pos=(0, 0), height=2.5, wrapWidth=None, ori=0, + color='white', colorSpace='rgb', opacity=1, + languageStyle='LTR', + depth=0.0); +key_start = keyboard.Keyboard() + +# Initialize components for Routine "trial" +trialClock = core.Clock() +MINLEVEL=0 +MAXLEVEL=9 +digits = visual.TextStim(win=win, name='digits', + text='default text', + font='Arial', + pos=(0, 0), height=2.5, wrapWidth=30, ori=0, + color='white', colorSpace='rgb', opacity=1, + languageStyle='LTR', + depth=-1.0); +resp = keyboard.Keyboard() + +# Create some handy timers +globalClock = core.Clock() # to track the time since experiment started +routineTimer = core.CountdownTimer() # to track time remaining of each (non-slip) routine + +# ------Prepare to start Routine "Instruction"------- +# update component parameters for each repeat +key_start.keys = [] +key_start.rt = [] +# keep track of which components have finished +InstructionComponents = [instruction, key_start] +for thisComponent in InstructionComponents: + thisComponent.tStart = None + thisComponent.tStop = None + thisComponent.tStartRefresh = None + thisComponent.tStopRefresh = None + if hasattr(thisComponent, 'status'): + thisComponent.status = NOT_STARTED +# reset timers +t = 0 +_timeToFirstFrame = win.getFutureFlipTime(clock="now") +InstructionClock.reset(-_timeToFirstFrame) # t0 is time of first possible flip +frameN = -1 +continueRoutine = True + +# -------Run Routine "Instruction"------- +while continueRoutine: + # get current time + t = InstructionClock.getTime() + tThisFlip = win.getFutureFlipTime(clock=InstructionClock) + tThisFlipGlobal = win.getFutureFlipTime(clock=None) + frameN = frameN + 1 # number of completed frames (so 0 is the first frame) + # update/draw components on each frame + + # *instruction* updates + if instruction.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance: + # keep track of start time/frame for later + instruction.frameNStart = frameN # exact frame index + instruction.tStart = t # local t and not account for scr refresh + instruction.tStartRefresh = tThisFlipGlobal # on global time + win.timeOnFlip(instruction, 'tStartRefresh') # time at next scr refresh + instruction.setAutoDraw(True) + if instruction.status == STARTED: + if bool(False): + # keep track of stop time/frame for later + instruction.tStop = t # not accounting for scr refresh + instruction.frameNStop = frameN # exact frame index + win.timeOnFlip(instruction, 'tStopRefresh') # time at next scr refresh + instruction.setAutoDraw(False) + + # *key_start* updates + if key_start.status == NOT_STARTED and t >= 0.0-frameTolerance: + # keep track of start time/frame for later + key_start.frameNStart = frameN # exact frame index + key_start.tStart = t # local t and not account for scr refresh + key_start.tStartRefresh = tThisFlipGlobal # on global time + win.timeOnFlip(key_start, 'tStartRefresh') # time at next scr refresh + key_start.status = STARTED + # keyboard checking is just starting + if key_start.status == STARTED: + theseKeys = key_start.getKeys(keyList=['space'], waitRelease=False) + if len(theseKeys): + theseKeys = theseKeys[0] # at least one key was pressed + + # check for quit: + if "escape" == theseKeys: + endExpNow = True + # a response ends the routine + continueRoutine = False + + # check for quit (typically the Esc key) + if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]): + core.quit() + + # check if all components have finished + if not continueRoutine: # a component has requested a forced-end of Routine + break + continueRoutine = False # will revert to True if at least one component still running + for thisComponent in InstructionComponents: + if hasattr(thisComponent, "status") and thisComponent.status != FINISHED: + continueRoutine = True + break # at least one component has not yet finished + + # refresh the screen + if continueRoutine: # don't flip if this routine is over or we'll get a blank screen + win.flip() + +# -------Ending Routine "Instruction"------- +for thisComponent in InstructionComponents: + if hasattr(thisComponent, "setAutoDraw"): + thisComponent.setAutoDraw(False) +# the Routine "Instruction" was not non-slip safe, so reset the non-slip timer +routineTimer.reset() + +# --------Prepare to start Staircase "stair" -------- +# set up handler to look after next chosen value etc +stair = data.StairHandler(startVal=MAXLEVEL-1, extraInfo=expInfo, + stepSizes=[1], stepType='lin', + nReversals=1, nTrials=20, + nUp=1, nDown=1, + minVal=MINLEVEL, maxVal=MAXLEVEL, + originPath=-1, name='stair') +thisExp.addLoop(stair) # add the loop to the experiment +level = thisStair = MAXLEVEL-1 # initialise some vals + +for thisStair in stair: + currentLoop = stair + level = thisStair + + # ------Prepare to start Routine "trial"------- + routineTimer.add(2.000000) + # update component parameters for each repeat + sequence=np.random.randint(0,10,MAXLEVEL-level+1) + stair.addOtherData('digits.stimulus', sequence) + digits.setText(str(sequence)[1:-1]) + resp.keys = [] + resp.rt = [] + # keep track of which components have finished + trialComponents = [digits, resp] + for thisComponent in trialComponents: + thisComponent.tStart = None + thisComponent.tStop = None + thisComponent.tStartRefresh = None + thisComponent.tStopRefresh = None + if hasattr(thisComponent, 'status'): + thisComponent.status = NOT_STARTED + # reset timers + t = 0 + _timeToFirstFrame = win.getFutureFlipTime(clock="now") + trialClock.reset(-_timeToFirstFrame) # t0 is time of first possible flip + frameN = -1 + continueRoutine = True + + # -------Run Routine "trial"------- + while continueRoutine and routineTimer.getTime() > 0: + # get current time + t = trialClock.getTime() + tThisFlip = win.getFutureFlipTime(clock=trialClock) + tThisFlipGlobal = win.getFutureFlipTime(clock=None) + frameN = frameN + 1 # number of completed frames (so 0 is the first frame) + # update/draw components on each frame + + # *digits* updates + if digits.status == NOT_STARTED and tThisFlip >= 0-frameTolerance: + # keep track of start time/frame for later + digits.frameNStart = frameN # exact frame index + digits.tStart = t # local t and not account for scr refresh + digits.tStartRefresh = tThisFlipGlobal # on global time + win.timeOnFlip(digits, 'tStartRefresh') # time at next scr refresh + digits.setAutoDraw(True) + if digits.status == STARTED: + # is it time to stop? (based on global clock, using actual start) + if tThisFlipGlobal > digits.tStartRefresh + 1.0-frameTolerance: + # keep track of stop time/frame for later + digits.tStop = t # not accounting for scr refresh + digits.frameNStop = frameN # exact frame index + win.timeOnFlip(digits, 'tStopRefresh') # time at next scr refresh + digits.setAutoDraw(False) + + # *resp* updates + waitOnFlip = False + if resp.status == NOT_STARTED and tThisFlip >= 0-frameTolerance: + # keep track of start time/frame for later + resp.frameNStart = frameN # exact frame index + resp.tStart = t # local t and not account for scr refresh + resp.tStartRefresh = tThisFlipGlobal # on global time + win.timeOnFlip(resp, 'tStartRefresh') # time at next scr refresh + resp.status = STARTED + # keyboard checking is just starting + waitOnFlip = True + win.callOnFlip(resp.clock.reset) # t=0 on next screen flip + win.callOnFlip(resp.clearEvents, eventType='keyboard') # clear events on next screen flip + if resp.status == STARTED: + # is it time to stop? (based on global clock, using actual start) + if tThisFlipGlobal > resp.tStartRefresh + 2-frameTolerance: + # keep track of stop time/frame for later + resp.tStop = t # not accounting for scr refresh + resp.frameNStop = frameN # exact frame index + win.timeOnFlip(resp, 'tStopRefresh') # time at next scr refresh + resp.status = FINISHED + if resp.status == STARTED and not waitOnFlip: + theseKeys = resp.getKeys(keyList=['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], waitRelease=False) + if len(theseKeys): + theseKeys = theseKeys[0] # at least one key was pressed + + # check for quit: + if "escape" == theseKeys: + endExpNow = True + resp.keys.append(theseKeys.name) # storing all keys + resp.rt.append(theseKeys.rt) + + # check for quit (typically the Esc key) + if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]): + core.quit() + + # check if all components have finished + if not continueRoutine: # a component has requested a forced-end of Routine + break + continueRoutine = False # will revert to True if at least one component still running + for thisComponent in trialComponents: + if hasattr(thisComponent, "status") and thisComponent.status != FINISHED: + continueRoutine = True + break # at least one component has not yet finished + + # refresh the screen + if continueRoutine: # don't flip if this routine is over or we'll get a blank screen + win.flip() + + # -------Ending Routine "trial"------- + for thisComponent in trialComponents: + if hasattr(thisComponent, "setAutoDraw"): + thisComponent.setAutoDraw(False) + resp.corr = [int(k) for k in resp.keys] == list(np.flip(sequence)) + stair.addResponse(resp.corr) + stair.addOtherData('resp.keys', resp.keys) + stair.addOtherData('digits.started', digits.tStartRefresh) + stair.addOtherData('digits.stopped', digits.tStopRefresh) + # check responses + if resp.keys in ['', [], None]: # No response was made + resp.keys = None + stair.addOtherData('resp.started', resp.tStartRefresh) + stair.addOtherData('resp.stopped', resp.tStopRefresh) + thisExp.nextEntry() + +# staircase completed + + +# Flip one final time so any remaining win.callOnFlip() +# and win.timeOnFlip() tasks get executed before quitting +win.flip() + +# these shouldn't be strictly necessary (should auto-save) +thisExp.saveAsWideText(filename+'.csv') +thisExp.saveAsPickle(filename) +logging.flush() +# make sure everything is closed down +thisExp.abort() # or data files will save again on exit +win.close() +core.quit() diff --git a/rds_task_lastrun.py b/rds_task_lastrun.py index fa539d41dbbaaec3a562cc66543a4f7ab79cdcf5..49813a75f848143c9fb6eae618c0a63218853894 100644 --- a/rds_task_lastrun.py +++ b/rds_task_lastrun.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ This experiment was created using PsychoPy3 Experiment Builder (v3.2.0), - on November 04, 2019, at 16:17 + on November 13, 2019, at 18:35 If you publish work using this script the most relevant publication is: Peirce J, Gray JR, Simpson S, MacAskill M, Höchenberger R, Sogo H, Kastman E, Lindeløv JK. (2019) @@ -63,8 +63,8 @@ frameTolerance = 0.001 # how close to onset before 'same' frame # Setup the Window win = visual.Window( - size=[1920, 1080], fullscr=True, screen=0, - winType='pyglet', allowGUI=False, allowStencil=False, + size=[1600, 900], fullscr=False, screen=0, + winType='pyglet', allowGUI=True, allowStencil=False, monitor='testMonitor', color=[0,0,0], colorSpace='rgb', blendMode='avg', useFBO=True, units='deg') @@ -91,13 +91,16 @@ key_start = keyboard.Keyboard() # Initialize components for Routine "trial" trialClock = core.Clock() +MINLEVEL=0 +MAXLEVEL=9 digits = visual.TextStim(win=win, name='digits', - text='3', + text='default text', font='Arial', - pos=(0, 0), height=2.5, wrapWidth=None, ori=0, + pos=(0, 0), height=2.5, wrapWidth=30, ori=0, color='white', colorSpace='rgb', opacity=1, languageStyle='LTR', - depth=0.0); + depth=-1.0); +resp = keyboard.Keyboard() # Create some handy timers globalClock = core.Clock() # to track the time since experiment started @@ -149,8 +152,7 @@ while continueRoutine: instruction.setAutoDraw(False) # *key_start* updates - waitOnFlip = False - if key_start.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance: + if key_start.status == NOT_STARTED and t >= 0.0-frameTolerance: # keep track of start time/frame for later key_start.frameNStart = frameN # exact frame index key_start.tStart = t # local t and not account for scr refresh @@ -158,10 +160,7 @@ while continueRoutine: win.timeOnFlip(key_start, 'tStartRefresh') # time at next scr refresh key_start.status = STARTED # keyboard checking is just starting - waitOnFlip = True - win.callOnFlip(key_start.clock.reset) # t=0 on next screen flip - win.callOnFlip(key_start.clearEvents, eventType='keyboard') # clear events on next screen flip - if key_start.status == STARTED and not waitOnFlip: + if key_start.status == STARTED: theseKeys = key_start.getKeys(keyList=['space'], waitRelease=False) if len(theseKeys): theseKeys = theseKeys[0] # at least one key was pressed @@ -169,8 +168,6 @@ while continueRoutine: # check for quit: if "escape" == theseKeys: endExpNow = True - key_start.keys = theseKeys.name # just the last key pressed - key_start.rt = theseKeys.rt # a response ends the routine continueRoutine = False @@ -195,44 +192,34 @@ while continueRoutine: for thisComponent in InstructionComponents: if hasattr(thisComponent, "setAutoDraw"): thisComponent.setAutoDraw(False) -thisExp.addData('instruction.started', instruction.tStartRefresh) -thisExp.addData('instruction.stopped', instruction.tStopRefresh) -# check responses -if key_start.keys in ['', [], None]: # No response was made - key_start.keys = None -thisExp.addData('key_start.keys',key_start.keys) -if key_start.keys != None: # we had a response - thisExp.addData('key_start.rt', key_start.rt) -thisExp.addData('key_start.started', key_start.tStartRefresh) -thisExp.addData('key_start.stopped', key_start.tStopRefresh) -thisExp.nextEntry() # the Routine "Instruction" was not non-slip safe, so reset the non-slip timer routineTimer.reset() -# set up handler to look after randomisation of conditions etc -trials = data.TrialHandler(nReps=20, method='sequential', - extraInfo=expInfo, originPath=-1, - trialList=[None], - seed=None, name='trials') -thisExp.addLoop(trials) # add the loop to the experiment -thisTrial = trials.trialList[0] # so we can initialise stimuli with some values -# abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb) -if thisTrial != None: - for paramName in thisTrial: - exec('{} = thisTrial[paramName]'.format(paramName)) +# --------Prepare to start Staircase "stair" -------- +# set up handler to look after next chosen value etc +stair = data.StairHandler(startVal=MAXLEVEL-1, extraInfo=expInfo, + stepSizes=[1], stepType='lin', + nReversals=1, nTrials=20, + nUp=1, nDown=1, + minVal=MINLEVEL, maxVal=MAXLEVEL, + originPath=-1, name='stair') +thisExp.addLoop(stair) # add the loop to the experiment +level = thisStair = MAXLEVEL-1 # initialise some vals -for thisTrial in trials: - currentLoop = trials - # abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb) - if thisTrial != None: - for paramName in thisTrial: - exec('{} = thisTrial[paramName]'.format(paramName)) +for thisStair in stair: + currentLoop = stair + level = thisStair # ------Prepare to start Routine "trial"------- routineTimer.add(2.000000) # update component parameters for each repeat + sequence=np.random.randint(0,10,MAXLEVEL-level+1) + stair.addOtherData('digits.stimulus', sequence) + digits.setText(str(sequence)[1:-1]) + resp.keys = [] + resp.rt = [] # keep track of which components have finished - trialComponents = [digits] + trialComponents = [digits, resp] for thisComponent in trialComponents: thisComponent.tStart = None thisComponent.tStop = None @@ -257,7 +244,7 @@ for thisTrial in trials: # update/draw components on each frame # *digits* updates - if digits.status == NOT_STARTED and tThisFlip >= 1-frameTolerance: + if digits.status == NOT_STARTED and tThisFlip >= 0-frameTolerance: # keep track of start time/frame for later digits.frameNStart = frameN # exact frame index digits.tStart = t # local t and not account for scr refresh @@ -273,6 +260,38 @@ for thisTrial in trials: win.timeOnFlip(digits, 'tStopRefresh') # time at next scr refresh digits.setAutoDraw(False) + # *resp* updates + waitOnFlip = False + if resp.status == NOT_STARTED and tThisFlip >= 0-frameTolerance: + # keep track of start time/frame for later + resp.frameNStart = frameN # exact frame index + resp.tStart = t # local t and not account for scr refresh + resp.tStartRefresh = tThisFlipGlobal # on global time + win.timeOnFlip(resp, 'tStartRefresh') # time at next scr refresh + resp.status = STARTED + # keyboard checking is just starting + waitOnFlip = True + win.callOnFlip(resp.clock.reset) # t=0 on next screen flip + win.callOnFlip(resp.clearEvents, eventType='keyboard') # clear events on next screen flip + if resp.status == STARTED: + # is it time to stop? (based on global clock, using actual start) + if tThisFlipGlobal > resp.tStartRefresh + 2-frameTolerance: + # keep track of stop time/frame for later + resp.tStop = t # not accounting for scr refresh + resp.frameNStop = frameN # exact frame index + win.timeOnFlip(resp, 'tStopRefresh') # time at next scr refresh + resp.status = FINISHED + if resp.status == STARTED and not waitOnFlip: + theseKeys = resp.getKeys(keyList=['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], waitRelease=False) + if len(theseKeys): + theseKeys = theseKeys[0] # at least one key was pressed + + # check for quit: + if "escape" == theseKeys: + endExpNow = True + resp.keys.append(theseKeys.name) # storing all keys + resp.rt.append(theseKeys.rt) + # check for quit (typically the Esc key) if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]): core.quit() @@ -294,11 +313,19 @@ for thisTrial in trials: for thisComponent in trialComponents: if hasattr(thisComponent, "setAutoDraw"): thisComponent.setAutoDraw(False) - trials.addData('digits.started', digits.tStartRefresh) - trials.addData('digits.stopped', digits.tStopRefresh) + resp.corr = [int(k) for k in resp.keys] == list(np.flip(sequence)) + stair.addResponse(resp.corr) + stair.addOtherData('resp.keys', resp.keys) + stair.addOtherData('digits.started', digits.tStartRefresh) + stair.addOtherData('digits.stopped', digits.tStopRefresh) + # check responses + if resp.keys in ['', [], None]: # No response was made + resp.keys = None + stair.addOtherData('resp.started', resp.tStartRefresh) + stair.addOtherData('resp.stopped', resp.tStopRefresh) thisExp.nextEntry() -# completed 20 repeats of 'trials' +# staircase completed # Flip one final time so any remaining win.callOnFlip()