I am trying to create a simple RT experiment in MATLAB R2014b, using Psychtoolbox. Participants will have to categorize faces, pressing one of two buttons as fast as they can. I created the paradigm on one computer, and it is working fine on that, but when I moved it to another (the one I want to be testing on), there was a weird error: even though the program seemed to be logging keypresses on the majority of trials, sometimes it wouldn't respond, and I had to press the key a number of times before it proceeded to the next trial. I am unsure what's going in, but I assume there could be something wrong with the computer itself (what might that be?), or with this particular bit of the code:
Screen('Flip', mainwin);
timeStart = GetSecs;keyIsDown=0; correct=0; rt=0;
while 1
bf = 0; %this variable is irrelevant here, I use it later to break
out of a loop
while (GetSecs - timeStart) < 0.2 %faces are presented briefly, but
%I'm recording responses here anyway, just in case there are some
%fast anticipatory responses - after this loop is over, I keep
%recording RT and button press the exact same way, but with no
%stimulus present
[keyIsDown, secs, keyCode] = KbCheck;
FlushEvents('keyDown');
if keyIsDown
nKeys = sum(keyCode);
if nKeys==1
if keyCode(Key1)||keyCode(Key2)
rt = 1000.*(GetSecs-timeStart);
keypressed=find(keyCode);
Screen('Flip', mainwin);
type = 'T';
bf = 1;
if keyCode(Key1) & targ_pic == 1
correct = 1;
elseif keyCode(Key2) & targ_pic == 0
correct = 1;
end
break;
elseif keyCode(escKey)
ShowCursor; fclose(outfile); Screen('CloseAll');
return
end
keyIsDown=0; keyCode=0;
end
else
keypressed = 0;
end
end
Can anyone maybe spot something that may be wrong with this?
Incidentally: is this the correct way to get RT out of PTB? I found that bit of code online, but I'm a little uncertain about why the "secs" variable is not used.
Both computers run Windows 10.
A few suggestions:
The Flip command will return the estimate of stimulus onset time, currently you are calling GetSecs after the Flip command, which is not necessary and will always return a value that is slightly later than the actual screen Flip. Likewise, you can use the time of key press returned by KbCheck, rather than calling GetSecs after identifying a key press.
I don't think you need the FlushEvents command, and it might be causing some timing variability.
It is also sometimes useful to pause for a short amount of time (for example 1 millisecond) between KbCheck events.
Below is a version of your code snippet with a few of these changes. It may also be more concise to have a single response checking loop, in which you Flip off the stimulus after 200 ms, rather than separate pre 200 ms and post 200 ms response checking loops, though I haven't made that change here.
keyIsDown=0; correct=0; rt=0;
[~, timeStart] = Screen('Flip', mainwin);
while 1
bf = 0; %this variable is irrelevant here, I use it later to break
%out of a loop
while (GetSecs - timeStart) < 0.2 %faces are presented briefly, but
%I'm recording responses here anyway, just in case there are some
%fast anticipatory responses - after this loop is over, I keep
%recording RT and button press the exact same way, but with no
%stimulus present
[keyIsDown, secs, keyCode] = KbCheck;
if keyIsDown
nKeys = sum(keyCode);
if nKeys==1
if keyCode(Key1)||keyCode(Key2)
rt = 1000.*(secs-timeStart);
keypressed=find(keyCode);
Screen('Flip', mainwin);
type = 'T';
bf = 1;
if keyCode(Key1) & targ_pic == 1
correct = 1;
elseif keyCode(Key2) & targ_pic == 0
correct = 1;
end
break;
elseif keyCode(escKey)
ShowCursor; fclose(outfile); Screen('CloseAll');
return
end
keyIsDown=0; keyCode=0;
end
else
keypressed = 0;
end
WaitSecs(.001);
end
end
Related
I created a MATLAB code using Psychtoolbox to make an experiment.
Everything works as I intended but it seems the initial loading of the experiment takes too long. The task is a simple yes/no response task whether the target word('probe') appeared in the previous set of word stimuli.
I put basic intro text as an image and then wait for any keypress to start the experiment but it will take about 40 seconds to actually begin the first trial after any keystroke. I want to make it work without any delay. It should start its first trial immediately after any keystroke.
I checked the timestops with GetSecs() on numerous positions in the code and it was not anything to do with loading stimuli or initial setting of the experiment before the for loop I attached below.
To make things look simpler, I changed some of the variables into actual numbers I used. I can gurantee that it is not due to large stimuli size since it is only 1500 words. Once the for loop starts, it goes smoothly but it takes 40 seconds to actually start the first trial so I think it is something to do with a specific function in the for loop or the way I built it.
Please let me know if anything is too vague or unclear. I will do my best to make things read better.
Edit: I minimalized the code leaving only the function names used in Psychtoolbox. I left the functions I used in between loops to let you know if they could cause any delay. It will not be possible to run this without Psychtoolbox installed so I guess you can briefly examine the structure of the code.
for trial = 1:250
for i = 1:6
DrawFormattedText();
Screen();
WaitSecs(0.5);
end
DrawFormattedText();
flipTime = Screen();
WaitSecs(0.5);
DrawFormattedText();
flipTime = Screen();
rt = 0;
resp = 0;
while GetSecs - flipTime < 3
clear keyCode;
RestrictKeysForKbCheck();
[keyIsDown,secs,keyCode] = KbCheck;
respTime = GetSecs;
pressedKeys = find(keyCode);
% ESC key quits the experiment
if keyCode(KbName('ESCAPE')) == 1
clear all
close all
sca
return
end
% Check for response keys
if ~isempty(pressedKeys)
for i = 1:2
if KbName(i) == pressedKeys(1)
resp = i;
rt = respTime - flipTime;
end
end
end
% Exit loop once a response is recorded
if rt > 0
break;
end
end
if rt == 0 || rt > 3 % 3 second limit for subjects to react to the probe stimuli
DrawFormattedText();
Screen();
WaitSecs(1);
end
Screen();
vbl = Screen();
WaitSecs(1);
% Record the trial data into output data matrix
respMat{1, trial} = trial;
respMat{2, trial} = resp;
respMat{3, trial} = rt;
end
I get users to do the following to adjust the luminance of grey squares through Psychtoolbox (allowing both big and small changes and registering these values).
while exitDemo == false
[keyIsDown,secs, keyCode] = KbCheck;
if keyCode(escapeKey)
exitDemo = true;
elseif keyCode(more_lum_small)
rectColor = rectColor + smallcolorchange;
elseif keyCode(less_lum_small)
rectColor = rectColor - smallcolorchange;
elseif keyCode(more_lum_large)
rectColor = rectColor + bigcolorchange;
elseif keyCode(less_lum_large)
rectColor = rectColor - bigcolorchange;
end
if keyCode(more_lum_small)
colorcounter = colorcounter + 0.001;
elseif keyCode(less_lum_small)
colorcounter = colorcounter - 0.001;
elseif keyCode(less_lum_large)
colorcounter = colorcounter - 0.1;
elseif keyCode(more_lum_large)
colorcounter = colorcounter + 0.1;
end
centeredRect = CenterRectOnPointd(baseRect, squareX, squareY);
centeredRect2 = CenterRectOnPointd(baseRect2, square2X, square2Y);
banner_break = CenterRectOnPointd(banner, bannerX, bannerY);
% Draw the rect to the screen
Screen('FillRect', window, rectColor, centeredRect);
Screen('FillRect', window, rect2Color, centeredRect2);
Screen('FillRect', window, bannerColor, banner_break);
% Flip to the screen
vbl = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi);
end
I now would like to put this in a for loop. Ideally, the user would move to the next iteration by pressing a key or the mouse button.
I am somehow stuck. Should I use a continue function?
If I understand you right, you want to run this type of test on your users multiple times in a for-loop. I don’t want to install Psychtoolbox just to answer one question so I thought I'll mock up my own example with 3 question quiz. You can stop said quiz by answering q (quit) to any of the questions. I presume that this would be your application.
%% Initialise questions and answers
prompts = {...
'What instrument did Sherlock Holmes play?';
'How do we get rid of the pigeons form the roof?';
'What did you bring me this time minion!?!'};
answers = {...
'trumpet';
'bazooka';
'window'};
no_responses = {...
'Hmm, interesting... "%s" you say...?\n';
'Madness! "%s" will never work!\n';
'Yes! Now that the "%s" is complete, people will tremble at my masters plan!\n'};
yes_responses = {...
'Splendid! That''s correct\n';
'Yes... This might work\n';
'Nooo...!!! The light! It burns!\n'};
completion_message = 'Level up!';
%% Ask questions
exitDemo = false;
for j = 1:numel(no_responses)
fprintf('--- Question %d ---\n',j);
response = no_responses{j};
prompt = sprintf('%s\n>',prompts{j});
% Loop while has not gotten a correct answer yet
gotCorrectAnswer = false;
while ~gotCorrectAnswer
answer = input(prompt,'s');
answer = lower(answer);
if strcmp(answer,'q') % Check for exit condition
exitDemo = true;
break
elseif strcmp(answer,answers{j}) % Check for the correct answer
fprintf(yes_responses{j});
gotCorrectAnswer = true;
else
fprintf(no_responses{j},answer);
end
end
% Check whether broke out of the for loop due to exit condition
if exitDemo
break
end
end
if ~exitDemo
fprintf(completion_message);
end
Note that you could achieve the same effect by pressing Ctrl+C on your keyboard while the code is executing. You can e.g. stop code like while true; pause(1); end with this. You don't need to program in any explicit stop conditions. Having said that, that is a bit of a hacky solution and it will abort every part of code which is running. The explicit exit condition allows you to handle exit a bit more gracefully (e.q. close up files, write to log, display a message etc.). Also; you should be aware that your users could possibly do that if they're malicious (unless Psychtoolbox protects against that which I doubt).
answer for keypress:
https://stackoverflow.com/a/9311250/5841680; based on input()
answer for mouseclick:
https://de.mathworks.com/help/matlab/ref/waitforbuttonpress.html; based on waitforbuttonpress
you don't need a for loop, works in a while loop all the same.
hope I did understand the question properly...
I am delivering a psychology experiment in Matlab, where screens with questions will be presented to subject. The screen will also collect and display the subjects' responses. For example: Screen displays '2+3' and also displays what participant types (say, 99999), until they press enter.
GOAL: get it to stop displaying the question after 16 seconds if the participant has not yet pressed enter. (That is, stop displaying the screen if time=16sec OR if subject presses Enter.)
The problem revolves around the following code:
While CurrentTime<TimeOut
respond=GetChar() <-(Waits till user press enter)
end
So whatever the statements we add before/after capturing respond statements is not executed.
Any help on how to work around this issue would be greatly appreciated! Thanks.
Here is an example, I presented an oval as an example, you can obviously replace that with whatever your stimuli are. Enter and Return are separate keys, I wasn't sure which you're looking for, so in the example the loop looks for either.
%% include at top of experiment / block
waitForResponseSeconds = 16; % number of second to wait for a response
enterKey = KbName('enter'); % numeric code for enter key
returnKeys = KbName('return'); % numeric code for return key(s)
responseKeys = [enterKey returnKeys];
wPtr = Screen('OpenWindow', 0, 0, [0 0 400 400]);
%% within the trial loop:
hasResponded = 0;
% present the stimulus (here the window pointer is called wPtr, you may
% need to adjust this depending on what you named the window pointer.
Screen('FillOval', wPtr, [100 0 100], [0 0 400 400]);
[~, Onset] = Screen('Flip', wPtr);
while ~hasResponded && ((GetSecs - Onset) <= waitForResponseSeconds)
[keyIsDown, secs, keyCode] = KbCheck;
if any(keyCode(responseKeys))
rt = 1000.*(secs-Onset); % get response time
hasResponded = 1;
end
% Wait 1 ms before checking again to prevent
% overload of the machine at elevated priority
WaitSecs(0.001);
end
%% end of exp
sca;
I created a Stroop-like reaction time task in MATLAB and looking at pilot results suggests there might be something wrong with my code (the congruency effect is much bigger than expected). I suspect I may be recording RTs wrong, so could anyone help me out with whether the following setup is okay?
On any given trial, two events happen (following a fixation cross): first, the target stimulus is presented for a max of 3 seconds (or until response), then the participant has to press a button to start the next trial. RT for both buttonpresses (target and trial-start button) is recorded. Here's my code:
Screen('DrawTexture', mainwin, Target);
Screen('Flip', mainwin);
timeStart = GetSecs;keyIsDown=0; correct=0; rt=0;
while 1 & (GetSecs - timeStart) < 3
[keyIsDown, secs, keyCode] = KbCheck;
FlushEvents('keyDown');
if keyIsDown
nKeys = sum(keyCode);
if nKeys==1
if keyCode(Left)||keyCode(Right)||keyCode(Down)||keyCode(Up)
rt = 1000.*(GetSecs-timeStart);
keypressed=find(keyCode);
Screen('Flip', mainwin);
if ... [I removed some irrelevant ERROR feedback related code here]...
elseif keyCode(escKey)
ShowCursor; fclose(outfile); Screen('CloseAll'); return
end
keyIsDown=0; keyCode=0;
end
else
keypressed = 0; %the resp column in output is 0 if no button is pressed
end
end
if keypressed == 0 %indicates timeout errors
DrawFormattedText(mainwin, 'TOO SLOW', 'center', 'center', errorcol);
Screen('Flip', mainwin);
WaitSecs(1);
end
Screen('DrawTexture', mainwin, press5);
Screen('Flip', mainwin);
keyIsDown=0; timeSt = GetSecs;
while 1
[keyIsDown, secs, keyCode] = KbCheck;
if keyIsDown
if keyCode(MoveOn)
pause_rt = 1000.*(secs - timeSt);
break ;
elseif keyCode(escKey)
ShowCursor;
fclose(outfile);
Screen('CloseAll');
return;
end
end
end
My questions: the command GetSecs gets the time whenever it's called, right? So GetSecs - timeStart is an okay way of calculating RT - but so is secs - timeSt (as seen for the second stimulus), as secs is the time KbCheck returns for the buttonpress. The two methods are largely equivalent (with GetSecs - timeStart maybe slightly overestimating RT), is that correct?
My worry here is that RT estimates for the target on the NEXT trial might be influenced by the second button press RT of the PREVIOUS trial. Do you see any evidence of that?
You are correct that GetSecs returns the time whenever it is called. But, although not the cause of your error, calling GetSecs after each window Flip isn't necessary, because the Flip function returns the estimate of stimulus onset, as the second output of the function. So for example, instead of:
Screen('Flip', mainwin);
timeStart = GetSecs;
You can just use
[~, timeStart] = Screen('Flip', mainwin);
I don't notice anything obviously wrong in the code, when you say the RTs seem incorrect, do they seem too fast, or too slow? Personally I would use KbReleaseWait after collecting RT to wait until the Key has been released, but perhaps FlushEvents is doing a similar thing here.
I finished my assignment and it runs the way the descriptions in the prompts say it should.
This is more of a question of sheer curiosity.
Sometimes to find out where my code might not be working I'll remove the ';' at the end of statements in the script and let it output as much as it can before it hits the error. For small scripts I'm finding this useful. I haven't learned about using proper debugging features just yet.
Anyway, I have some code for an assignment
function endValue = tradeStock(initInvest, price, buy, sell)
% given an initial investment, vector of price fluctuations (price), vector
% of lows (buy), and a vector of peaks (sell), this function will return
% the final value of said initial investment. Assumed in the process is a
% per transaction cost of $12.95
% vectorization not possible, use loops
format bank
tcost = 12.95;
purse = initInvest;
endValue = 0;
if max(size(buy))~=max(size(sell))
disp('Buy and sell vectors must be the same size')
return
end
for i = 1:max(size(buy))
stock = floor(purse/price(buy(i)));
purch = stock*price(buy(i)) + tcost;
while purse < purch
stock = stock - 1;
purch = stock*price(buy(i)) + tcost;
end
if purch <= 0
disp('You have run out of money before the market closed')
endValue = initInvest;
return
end
purse = purse - purch; % paid for stock
purse = purse + stock*price(sell(i)) - tcost; % sell stock
end
endValue = purse;
If you start out with an initial Investment that is too small, the code should straight away enter the while loop, and then wind up in the following if then statement returning you from the function and giving you back your initial Investment. And this indeed happens if you enter in an initInvest that is too small.
However, if I remove the ';' from the code enclosed by the while loop, and run it with an initInvest value that is too small, no extra values are output from within the while loop upon execution.....
Do while loops just automatically suppress output from any enclosed code?