Dear Community,
Info:
Psychtoolbox-3 (3.0.19)
MATLAB platform (2021b)
ubuntu (22.04)
I have implemented an easy finger tapping reaction time task using Psychtoolbox.
First, the participants are presented with a white circle which means “prepare for the movement”. Then they see a green circle which means “press the button ASAP”.
I’ve set up the following timing for these stimuli:
white circle - 1-1.5 sec (random)
green circle - 1 sec.
But then I run the task it seems that these timed do not work as expected. I have triple checked the script, but I am fairly new to PTB and Matlab. Could you please suggest what can be the issue?
I am also running a more complex task using PTB which seem to have a similar timing problems.
Please find the script for Finger tapping task below.
Best,
Kat
%% FINGER TAPPING TASK
% Clear the workspace and the screen
sca;
close all;
clear;
% Here we call some default settings for setting up Psychtoolbox
PsychDefaultSetup(2);
% Participant details
subjID = 's1_pac_sub05'; % s1_pac_sub00; old - 's1_sub00_NT'
taskID = '_BL';
outFileName = [subjID, taskID];
home_dir = '~/Desktop/Delayed cursor rotation task/data';
subjFoldPath = fullfile(home_dir, subjID);
% Check if the subject folder exists
if ~exist(subjFoldPath, 'dir')
mkdir(subjFoldPath);
end
cd(subjFoldPath);
% Check if the file exists in the current folder to prevent overwriting
if exist([outFileName, '.mat'], 'file')
error(sprintf('File %s exists. Please rename the file!', outFileName));
end
% Debugging mode
% PsychDebugWindowConfiguration;
% Get the screen numbers
screens = Screen('Screens');
% Draw we select the maximum of these numbers. So in a situation where we
% have two screens attached to our monitor we will draw to the external screen
screenNumber = max(screens);
% Define screen color and circle properties
colorScreen = 0; % 0 for black, 0.5 for gray
circleSize = 100;
whiteColor = [255 255 255]; % White
greenColor = [0 255 0]; % Green
textSize = 36;
% Define timing
prepareDuration = 1 + rand(1) * 0.5; % 0.5-1 sec
stimulusDuration = 1; % in seconds
photodiodeDuration = 0.1;
% Number of trials
totalTrials = 120; % 120
breakAfterTrials = 40; % 40
% Set up serial port
portParams = 'BaudRate=115200 DTR=1 Terminator=10';
com = '/dev/ttyACM0'; %COM port for serial connection
SerPort = IOPort('OpenSerialPort', com, portParams); % Get handle for serial port
% 100 - launch, 101 - st_instr, 102 - break, 103 - fin_instr, 10 - new trial, 11 - prepare, 12 - isi1, 13 - go, 14 - isi2
IOPort('Write', SerPort, ['WRITE 100 1000 0' char(10)]);
% Open an on screen window and color it black.
[window, windowRect] = PsychImaging('OpenWindow', screenNumber, colorScreen);
% set priority level
topPriorityLevel = MaxPriority(window);
Priority(topPriorityLevel);
% Get the size of the on screen window in pixels.
[screenXpixels, screenYpixels] = Screen('WindowSize', window);
SetMouse(screenXpixels, 0, window);
% Get the centre coordinate of the window in pixels
[xCenter, yCenter] = RectCenter(windowRect);
% Enable alpha blending for anti-aliasing
% ???????????????????
Screen('BlendFunction', window, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
% rect settings for protodiode
photRect = [0 0 30 30]; %size of rectangle to display for photodiode
% note and save start time
start_time = GetSecs; % NOT LOGGED
%outVar.start_time = (start_time);
% INSTRUCTIONS
% Present instructions
Screen('TextSize', window, 36);
DrawFormattedText(window, ['Welcome to our experiment!' ...
'\n\nPlease follow the instructions below:' ...
'\n\n\nwhen you see a WHITE circle - prepare to move' ...
'\n\nwhen you see a GREEN circle - PRESS <ENTER> WITH YOUR INDEX FINGER' ...
'\n\n\n<< Press ANY KEY to START >>'], ...
'center', 'center', [255 255 255]);
Screen('Flip', window);
% 100 - launch, 101 - st_instr, 102 - break, 103 - fin_instr, 10 - new trial, 11 - prepare, 12 - isi1, 13 - go, 14 - isi2
IOPort('Write', SerPort, ['WRITE 101 1000 0' char(10)]);
KbWait;
% Loop through trials
for trial = 1:totalTrials
% Check for ESC key press to exit
[~, ~, keyCode] = KbCheck;
if keyCode(KbName('ESCAPE'))
break; % Exit the loop if ESC key is pressed
end
% NEW TRIAL
% 100 - launch, 101 - st_instr, 102 - break, 103 - fin_instr, 10 - new trial, 11 - prepare, 12 - isi1, 13 - go, 14 - isi2
IOPort('Write', SerPort, ['WRITE 10 1000 0' char(10)]);
trial_start = GetSecs;
% PREPARE
% Photodiode loop
for iter = 1:2
% Present dot and rectangle simultaneously for 0.1 seconds in iteration 1
if iter == 1
% Present white circle and rect
Screen('DrawDots', window, [xCenter yCenter], circleSize, whiteColor, [], 2);
% Trigger for photodiode
Screen('FillRect', window, whiteColor, photRect);
% Flip to the screen
Screen('Flip', window);
% 100 - launch, 101 - st_instr, 102 - break, 103 - fin_instr, 10 - new trial, 11 - prepare, 12 - isi1, 13 - go, 14 - isi2
IOPort('Write', SerPort, ['WRITE 11 1000 0' char(10)]);
% get times
prepare_sig = GetSecs - trial_start;
WaitSecs(photodiodeDuration);
% Present only white circle
else
% Present white circle
Screen('DrawDots', window, [xCenter yCenter], circleSize, whiteColor, [], 2);
% Flip to the screen
Screen('Flip', window);
WaitSecs(prepareDuration - photodiodeDuration);
end
end
% ISI1
% Present empty screen for 100 ms
Screen('Flip', window);
% 100 - launch, 101 - st_instr, 102 - break, 103 - fin_instr, 10 - new trial, 11 - prepare, 12 - isi1, 13 - go, 14 - isi2
IOPort('Write', SerPort, ['WRITE 12 1000 0' char(10)]);
isi_sig1 = GetSecs - trial_start;
WaitSecs(0.1); % 500 milliseconds
% GO
% Photodiode loop
for iter = 1:2
% Present dot and rectangle simultaneously for 0.1 seconds in iteration 1
if iter == 1
% Present green circle and rect
Screen('DrawDots', window, [xCenter yCenter], circleSize, greenColor, [], 2);
% Trigger for photodiode
Screen('FillRect', window, whiteColor, photRect);
% Flip to the screen
Screen('Flip', window);
% 100 - launch, 101 - st_instr, 102 - break, 103 - fin_instr, 10 - new trial, 11 - prepare, 12 - isi1, 13 - go, 14 - isi2
IOPort('Write', SerPort, ['WRITE 13 1000 0' char(10)]);
% get times
go_sig = GetSecs - trial_start;
% Wait 0.1 sec for ENTER key press
KbWait([], 2, GetSecs + (photodiodeDuration));
key_press_fast = GetSecs - trial_start - go_sig;
if key_press_fast < 0.1
% Send a keypress trigger 15 to the EEG
IOPort('Write', SerPort, ['WRITE 15 1000 0' char(10)]);
disp(['Reaction time: ', num2str(key_press_fast)]);
else
key_press_fast = nan;
disp('No key was pressed within 0.1 sec.');
end
% Present only green circle
else
% Present green circle
Screen('DrawDots', window, [xCenter yCenter], circleSize, greenColor, [], 2);
% Flip to the screen
Screen('Flip', window);
% Wait 1 sec for ENTER key press
KbWait([], 2, GetSecs + (stimulusDuration - photodiodeDuration));
key_press_slow = GetSecs - trial_start - go_sig;
if key_press_slow < 0.9
% Send a keypress trigger 15 to the EEG
IOPort('Write', SerPort, ['WRITE 15 1000 0' char(10)]);
disp(['Reaction time: ', num2str(key_press_slow)]);
else
key_press_slow = nan;
disp('No key was pressed between 0.1 and 1 sec.');
end
end
end
% ISI2
% Present empty screen for 500 ms
Screen('Flip', window);
% 100 - launch, 101 - st_instr, 102 - break, 103 - fin_instr, 10 - new trial, 11 - prepare, 12 - isi1, 13 - go, 14 - isi2
IOPort('Write', SerPort, ['WRITE 14 1000 0' char(10)]);
isi_sig2 = GetSecs - trial_start;
WaitSecs(0.5); % 500 milliseconds
% SAVING THE DATA
%! outVar.timing(trial,:) = ([trial_start, prepare_sig, isi_sig1, go_sig, isi_sig2]);
outVar.trial_start(trial,:) = trial_start;
outVar.prepare_sig(trial,:) = prepare_sig;
outVar.isi_sig1(trial,:) = isi_sig1;
outVar.go_sig(trial,:) = go_sig;
outVar.key_press_fast(trial,:) = key_press_fast;
outVar.key_press_slow(trial,:) = key_press_slow;
outVar.isi_sig2(trial,:) = isi_sig2;
% BREAK
% Check for break
if mod(trial, breakAfterTrials) == 0 && trial ~= totalTrials
Screen('TextSize', window, 36);
DrawFormattedText(window, 'BREAK \n\n<< Press ANY KEY to continue >>', 'center', 'center', [255 255 255]);
Screen('Flip', window);
% 100 - launch, 101 - st_instr, 102 - break, 103 - fin_instr, 10 - new trial, 11 - prepare, 12 - isi1, 13 - go, 14 - isi2
IOPort('Write', SerPort, ['WRITE 102 1000 0' char(10)]);
KbWait;
end
end
% FINISH
% End of experiment
Screen('TextSize', window, 36);
DrawFormattedText(window, ['Experiment finished. \n\nThank you for participation!' ...
'\n\n<< Press ANY KEY to exit >>'], 'center', 'center', [255 255 255]);
Screen('Flip', window);
% 0 - launch, 1 - st_instr, 2 - break, 103 - fin_instr, 10 - new trial, 11 - prepare, 12 - isi1, 13 - go, 14 - isi2
IOPort('Write', SerPort, ['WRITE 103 1000 0' char(10)]);
KbWait;
sca;
% close port
IOPort('Close', SerPort);
% SAVING THE DATA
% save times
save([outFileName '.mat'],'outVar');
% Convert structure to table
outTable = struct2table(outVar);
% Add column names
outTable.Properties.VariableNames = {'trial_start', 'prepare_sig', 'isi_sig1', ...
'go_sig', 'key_press_fast', 'key_press_slow', 'isi_sig2'};
% Write table to CSV file
csvFileName = [outFileName '.csv'];
writetable(outTable, csvFileName);