Example: A simple reaction time task
In this guide, we’ve covered the basics of registers, markers and schedules and our centralized I/O hub architecture. Now we will turn to a simple example of a reaction time task.
A target is flashed and participants must press a button, which is monitored on digital input channel 1. We will use the Digital Input Log, a schedule that records changes in the digital inputs.
We start our digital input log and set a marker on a register write tied to the next video frame (RegWrVSync). This starts our recording and simultaneously saves a timestamp of when our target appears.
Once the target appears, we begin routine register updates to check the button press occurred. We will compare the time of a press to the timestamp we recorded to measure reaction time. If the reaction time is more than 5 s after the target onset, we reject the trial.
%%
%SETUP
Datapixx('Open');
%Next, we will set up a DInLog, using the default buffer address. This will record any changes in
%the digital input, which we will treat as a button press. For more on how to interpret our digital
%input channels, see our RPx demos
Datapixx('SetDinLog');
Datapixx('EnableDinDebounce'); %reduces jitter
Datapixx('RegWr');
numberOfTrials = 5;
responseTimeCutoff = 5;
targetWidth=100;
targetColour=[50, 50, 50];
%%
%START
[windowPtr, rect] = Screen('OpenWindow', 2, [0,0,0]);
for k = 1:numberOfTrials
isValid = 0;
while ~isValid
%Add a variable delay to keep participants attentive
WaitSecs(randi(4));
%Generate a random location for top left corner of target, and draw
targetLoc = [randi(rect(3)-targetWidth), randi(rect(4)-targetWidth)];
Screen('FillRect', ...
windowPtr, ...
targetColour,...
[targetLoc(1), targetLoc(2), targetLoc(1)+targetWidth, targetLoc(2)+targetWidth]);
%Start our log and marker, implemented on next video frame, i.e., when target appears
Datapixx('StartDinLog');
Datapixx('SetMarker');
Datapixx('RegWrVideoSync');
Screen('Flip', windowPtr);
%collect our marker
Datapixx('RegWrRd');
startTime = Datapixx('GetMarker');
press = 0;
%%
%READ
%Let's create an inner loop to check the log. The first thing in the log buffer should be our
%press; if the buffer is empty, then we haven't recorded anything yet.
while ~press
WaitSecs(0.25);
Datapixx('RegWrRd');
status=Datapixx('GetDinStatus');
if status.newLogFrames > 0
%%
%STOP
Datapixx('StopDinLog');
Datapixx('RegWr');
Screen('Flip', windowPtr);
press = 1;
end
end
%Let's get the timestamp of the first event in our log, which we assume is the button press.
%This timestamp is on the same clock as our marker.
Datapixx('RegWrRd');
[~, logTimetags, ~] = Datapixx('ReadDinLog');
if (logTimetags(1)-startTime) <= responseTimeCutoff
isValid = 1;
fprintf("Success! Your response time was %.2d seconds.n", (logTimetags(1)-startTime));
else
fprintf("Too slow! Your response time was %.2d seconds.n", (logTimetags(1)-startTime));
end
end
end
Datapixx('Close');
Screen('Closeall');
##
#SETUP
from pypixxlib import _libdpx as dp
from psychopy import visual, core
import random
dp.DPxOpen()
#Next, we will set up a DInLog, using the default buffer address. This will record any changes in
#the digital input, which we will treat as a button press. For more on how to interpret our digital
#input channels, see our RPx demos: https://www.vpixx.com/manuals/python/html/basicdemo.html
myDinLog = dp.DPxSetDinLog()
dp.DPxEnableDinDebounce() #reduces jitter
dp.DPxWriteRegCache()
numberOfTrials = 5
responseTimeCutoff = 5
targetWidth=100
targetColour=(50, 50, 50)
##
#START
win = visual.Window(screen = 0,
monitor = None,
size = [1920,1080],
fullscr=True,
pos = [0,0],
color='black',
units = "pix")
for k in range(numberOfTrials):
isValid = 0
while not isValid:
#Add a variable delay to keep participants attentive
core.wait(random.randint(1, 4))
#Generate a random location for center point of target, and draw
targetLoc = [random.randint(int((-(1920/2))+(targetWidth/2)), int((1920/2)-(targetWidth/2))),
random.randint(int((-(1080/2))+(targetWidth/2)), int((1080/2)-(targetWidth/2)))];
myRect = visual.Rect(win,
width=targetWidth,
height=targetWidth,
pos=(targetLoc[0], targetLoc[1]),
fillColorSpace = 'rgb255',
fillColor=targetColour)
myRect.draw()
#start our log and marker, implemented on the next video frame
dp.DPxStartDinLog()
dp.DPxSetMarker()
dp.DPxWriteRegCacheAfterVideoSync()
win.flip()
dp.DPxUpdateRegCache()
startTime = dp.DPxGetMarker()
press = 0
##
#READ
#Let's create an inner loop to check the log. The first thing in the buffer should be our
#press; if the buffer is empty, then we haven't recorded anything yet.
while not press:
core.wait(0.25)
dp.DPxUpdateRegCache()
dp.DPxGetDinStatus(myDinLog)
if myDinLog['newLogFrames'] > 0:
dp.DPxStopDinLog()
dp.DPxWriteRegCache()
win.flip()
press = 1
#Let's get the timestamp of the first event in our log, which we assume is the button press.
#This timestamp is on the same clock as our marker.
dp.DPxUpdateRegCache()
bufferContent = dp.DPxReadDinLog(myDinLog, int(myDinLog['newLogFrames']))
if (bufferContent[0][0]-startTime) <= responseTimeCutoff:
isValid = 1
print("Succes! Your response time was ", (bufferContent[0][0]-startTime), " seconds.")
else:
print("Too slow! Your response time was ", (bufferContent[0][0]-startTime), " seconds.")
dp.DPxClose()
win.close()
##
#SETUP
from pypixxlib.viewpixx import VIEWPixx
from pypixxlib import _libdpx as dp
from psychopy import visual, core
import random
device = VIEWPixx()
device.open()
#Next, we will set up a DInLog, using the default buffer address. This will record any changes in
#the digital input, which we will treat as a button press. For more on how to interpret our digital
#input channels, see our RPx demos: https://www.vpixx.com/manuals/python/html/basicdemo.html
myDinLog = device.din.setDinLog()
device.din.setDebounce(True) #reduce jitter
device.writeRegisterCache()
numberOfTrials = 5
responseTimeCutoff = 5
targetWidth=100
targetColour=(50, 50, 50)
##
#START
win = visual.Window(screen = 0,
monitor = None,
size = [1920,1080],
fullscr=True,
pos = [0,0],
color='black',
units = "pix")
for k in range(numberOfTrials):
isValid = 0
while not isValid:
#Add a variable delay to keep participants attentive
core.wait(random.randint(1, 4))
#Generate a random location for center point of target, and draw
targetLoc = [random.randint(int((-(1920/2))+(targetWidth/2)), int((1920/2)-(targetWidth/2))),
random.randint(int((-(1080/2))+(targetWidth/2)), int((1080/2)-(targetWidth/2)))];
myRect = visual.Rect(win,
width=targetWidth,
height=targetWidth,
pos=(targetLoc[0], targetLoc[1]),
fillColorSpace = 'rgb255',
fillColor=targetColour)
myRect.draw()
#start our log and marker, implemented on the next video frame
device.din.startDinLog()
dp.DPxSetMarker() #This function is from libdpx; an OOP version will be created eventually.
device.writeRegCacheAfterVideoSync()
win.flip()
device.updateRegisterCache()
startTime = dp.DPxGetMarker() #This function is from libdpx; an OOP version will be created eventually.
press = 0
##
#READ
#Let's create an inner loop to check the log. The first thing in the buffer should be our
#press; if the buffer is empty, then we haven't recorded anything yet.
while not press:
core.wait(0.25)
device.updateRegisterCache()
device.din.getDinLogStatus(myDinLog)
if myDinLog['newLogFrames'] > 0:
device.din.stopDinLog()
device.writeRegisterCache()
win.flip()
press = 1
#Let's get the timestamp of the first event in our log, which we assume is the button press.
#This timestamp is on the same clock as our marker.
device.updateRegisterCache()
bufferContent = device.din.readDinLog(myDinLog, int(myDinLog['newLogFrames']))
if (bufferContent[0][0]-startTime) <= responseTimeCutoff:
isValid = 1
print("Succes! Your response time was ", (bufferContent[0][0]-startTime), " seconds.")
else:
print("Too slow! Your response time was ", (bufferContent[0][0]-startTime), " seconds.")
device.close()
win.close()