2017-08-30 173 views
0

我正在使用PsychToolbox在fMRI掃描儀上運行實驗。我想收集掃描儀發出的脈衝時間點。如果您對此主題不熟悉:掃描儀發出一個信號 - 相當於按定義的時間間隔按鍵盤數字「5」。第一個這樣的信號啓動代碼,使得掃描器的測量和代碼的開始時間同步。我的代碼是順序的,如下圖所示。我怎麼能實現一個循環,在任何時候檢查這些輸入的「5」,即使我的主代碼運行在「for」循環中?使用KbQueueCheck連續檢查設備輸入

我的代碼:

% here I wait for the scanner to input a "5", then my code will start 
KbQueueCreate; 
KbQueueStart; 
Trigger = KbName('5%'); 

keyboard = -1; 
AllowedKeys = zeros(256,1); %Disable all keys 
AllowedKeys([LeftPress RightPress MiddlePress]) = 1; %Also allow escape always. 


while 1 

     [pressed, firstPress] = KbQueueCheck(); 

    if firstPress(Trigger) 
     startExperiment = GetSecs; 
break 
    end 
end 


KbQueueStop; 
KbQueueRelease; 

% In the foolowing loop, the main experiment runs. here I show a screen, 
% signal etc. 

for i = 1:nrTrials 


% here I would like to have code checking for scanner inputs 

end 

KbQueueCheck應檢查是否有任何鍵盤輸入從最近的呼叫KbQueueStart啓動功能。 任何輸入是非常歡迎。

+0

在多遠?你能否詳細說明一下?我真的想解決這個問題。謝謝 – Pegah

+0

那麼,你有甚麼努力去做到這一點?我看到的只是「我寫了我需要的代碼的前半部分,請爲我寫第二部分」。既然SO不是代碼寫作服務,那麼我認爲目前狀態下的這個問題就是這樣的話題。 – Adriaan

+2

是的,我知道,如果你看看我的其他問題,這不是我如何使用SO。但在這種情況下,我真的不知道從哪裏開始 - 這並不是很好,但在這種情況下是這樣。我知道,例如我可以創建while循環代碼的特定部分,但是我又錯過了一些觸發輸入。順便說一句,我也沒有要求我寫的代碼,但也像「只使用parfor」一樣的評論是一般,沒有那麼有用。 – Pegah

回答

1

我們用我們的掃描儀做類似的事情,所以我提供了我使用的功能。我通常只檢測第一個觸發器來啓動刺激,並且不會讀取以後的觸發器。但我確實在後臺註冊了所有觸發器以及其他響應,以便在出現任何問題時檢查事件。

函數KbQueue.m可以做幾乎所有相關的響應收集,包括後臺密鑰寄存器。請閱讀有關如何使用不同功能的幫助。代碼的某些部分(如不同的鍵盤索引)對於MAC和Linux沒有很好的測試。

function varargout = KbQueue(cmd, param) 
% KbQueueXXX series functions have some good features: 
% 1. detect short key event, like those from fOPR KbCheck may miss; 
% 2. detect only interested keys; 
% 3. buffer key event while code is running. 
% 
% But the syntax of KbQueueXXX is a little inconvenient due to the flexibility. 
% 
% This code tries to provide a convenient wrapper for practical purpose, by 
% sacrificing some flexibility, like deviceIndex etc. By default, KbQueue 
% detects events for the first device (this doesn't seem the case for Windows, 
% where it detects all devices). If you have more than one keyboard, and like to 
% detect a different one, you have to add following in your code: 
% 
% global KbQueueDevice; KbQueueDevice = 2; 
% 
% where 2 means the second device. You need to find this index for your target 
% keyboard. 
% 
% KbQueue('start', keys); 
% - Create a queue and start it. The second input specify the keys to be queued. 
% The default are numbers 1~5, both keyboard and keypad numbers. The key names 
% adopt those with KbName('UnifyKeyNames'). The 'start' command can be called 
% more than once to update new keys. If it is not called, other subfunctions 
% will call it with the default keys automatically. 
% 
% Example: 
% KbQueue('start', {'LeftArrow' 'RightArrow'}); % start to queue 2 arrows 
% 
% The input keys can also be 256-element keyCode, such as that returned by 
% KbCheck etc. It can also be the index in the keyCode, such as that returned by 
% KbQueue('wait') etc. 
% 
% nEvents = KbQueue('nEvents' [, 'type']); 
% - Return number of events in the queue. The default event type is 'press', 
% which returns the number of keypress. It can be 'release' or 'all', which will 
% return number of release events or all events. 
% 
% [pressTime, pressCode] = KbQueue('wait' [, secs or keys]); 
% - Wait for a new event, and return key press time and keycode. During the 
% wait, pressing Escape will abort code execution, unless 'Escape' is included 
% in defined keys. 
% 
% If there is no second input, this will wait forever till a defined key by 
% previous 'start' command is detected. 
% 
% If the second input is numeric, it will be interpreted as the seconds to wait, 
% and wait for a key defined by previous 'start' command is pressed, or the 
% seconds has elapsed, whichever is earlier. If there is no any event during the 
% time window, both output will be empty. For example: 
% 
% [t, key] = KbQueue('wait', 1); % wait for 1 sec or a key is detected 
% 
% If the second input is a string or cellstr, it will be interpreted as the 
% key(s) to detect. The provided keys here affects only this call, i.e. it has 
% no effect on the queue keys defined by previous 'start' command. For example: 
% 
% t = KbQueue('wait', {'5%' '5'}); % wait till 5 is pressed 
% 
% [pressCodeTime, releaseCodeTime] = KbQueue('check' [, t0]); 
% - Return first and last press keycode and times for each queued key, and 
% optionally release keycode and times. The output will be empty if there is no 
% buffered response. Both output are two row vector, with the first row for the 
% keycode and second row for times. If the second input t0, default 0, is 
% provided, the returned times will be relative to t0. For example: 
% 
% press = KbQueue('check'); % return 1st and last key press in the queue 
% pressedKey = KbName(press(1, :); % convert keycode into key names 
% 
% KbQueue('flush'); 
% - Flush events in the current queue. 
% 
% t = KbQueue('until', whenSecs); 
% - This is the same as WaitSecs('UntilTime', whenSecs), but allows to exit by 
% pressing ESC. If whenSecs is not provided or is already passed, this still 
% checks ESC, so allows use to quit. For example: 
% KbQueue('until', GetSecs+5); % wait for 5 secs from now 
% KbQueue('until'); % only check ESC exit 
% 
% [pressCodeTime, releaseCodeTime] = KbQueue('stop' [, t0]); 
% - This is similar to 'check' command, but it returns all queued events since 
% last 'flush', or since the queue was started. It also stops and releases the 
% queue. This provides a way to check response in the end of a session. For 
% example: 
% KbQueue('start', {'5%' '5'}); % start to queue 5 at beginning of a session 
% KbQueue('flush'); % optionally remove unwanted events at a time point 
% t0 = GetSecs; % the start time of your experiment 
% % run your experiment 
% pressCodeTime = KbQueue('stop', t0); % get all keycode and time 

% 10/2012 wrote it, [email protected] 
% 12/2012 try to release queue from GetChar etc, add nEvents 
% 11/2014 try to use response device for OSX and Linux 

persistent kCode started evts; 
global KbQueueDevice; % allow to change in user code 
if isempty(started), started = false; end 
if nargin<1 || isempty(cmd), cmd = 'start'; end 
if any(cmd=='?'), subFuncHelp('KbQueue', cmd); return; end 

if strcmpi(cmd, 'start') 
    if started, BufferEvents; end 
    if nargin<2 
     param = {'1' '2' '3' '4' '5' '1!' '[email protected]' '3#' '4$' '5%'}; 
    end 
    KbName('UnifyKeyNames'); 
    if ischar(param) || iscellstr(param) % key names 
     kCode = zeros(256, 1); 
     kCode(KbName(param)) = 1; 
    elseif length(param)==256 % full keycode 
     kCode = param; 
    else 
     kCode = zeros(256, 1); 
     kCode(param) = 1;   
    end 
    if isempty(KbQueueDevice), KbQueueDevice = responseDevice; end 
    try KbQueueReserve(2, 1, KbQueueDevice); end %#ok 
    KbQueueCreate(KbQueueDevice, kCode); 
    KbQueueStart(KbQueueDevice); 
    started = true; 
    return; 
end 

if ~started, KbQueue('start'); end 

if strcmpi(cmd, 'nEvents') 
    BufferEvents; 
    n = length(evts); 
    if n, nPress = sum([evts.Pressed] == 1); 
    else nPress = 0; 
    end 
    if nargin<2, param = 'press'; end 
    if strncmpi(param, 'press', 5) 
     varargout{1} = nPress; 
    elseif strncmpi(param, 'release', 7) 
     varargout{1} = n - nPress; 
    else 
     varargout{1} = n; 
    end 
elseif strcmpi(cmd, 'check') 
    [down, p1, r1, p2, r2] = KbQueueCheck(KbQueueDevice); 
    if ~down 
     varargout = repmat({[]}, 1, nargout); 
     return; 
    end 
    if nargin<2, param = 0; end 
    i1 = find(p1); i2 = find(p2); 
    varargout{1} = [i1 i2; [p1(i1) p2(i2)]-param]; 
    if nargout>1 
     i1 = find(r1); i2 = find(r2); 
     varargout{2} = [i1 i2; [r1(i1) r2(i2)]-param]; 
    end 
elseif strcmpi(cmd, 'wait') 
    endSecs = GetSecs; 
    secs = inf; % wait forever unless secs provided 
    newCode = kCode; % use old keys unless new keys provided 
    if nargin>1 % new keys or secs provided 
     if isempty(param), param = inf; end 
     if isnumeric(param) % input is secs 
      secs = param; 
     else % input is keys 
      newCode = zeros(256, 1); 
      newCode(KbName(param)) = 1; 
     end 
    end 
    esc = KbName('Escape'); 
    escExit = ~newCode(esc); 
    newCode(esc) = 1; 
    changed = any(newCode~=kCode); 
    if changed % change it so we detect new keys 
     BufferEvents; 
     KbQueueCreate(KbQueueDevice, newCode); 
     KbQueueStart(KbQueueDevice); % Create and Start are twins here :) 
    else 
     KbQueueFlush(KbQueueDevice, 1); % flush KbQueueCheck buffer 
    end 
    endSecs = endSecs+secs; 
    while 1 
     [down, p1] = KbQueueCheck(KbQueueDevice); 
     if down || GetSecs>endSecs, break; end 
     WaitSecs('YieldSecs', 0.005); 
    end 
    if changed % restore original keys if it is changed 
     BufferEvents; 
     KbQueueCreate(KbQueueDevice, kCode); 
     KbQueueStart(KbQueueDevice); 
    end 
    if isempty(p1) 
     varargout = repmat({[]}, 1, nargout); 
     return; 
    end 
    ind = find(p1); 
    if escExit && any(ind==esc) 
     error('User pressed ESC. Exiting ...'); 
    end 
    varargout = {p1(ind) ind}; 
elseif strcmpi(cmd, 'flush') 
    KbQueueFlush(KbQueueDevice, 3); % flush both buffers 
    evts = []; 
elseif strcmpi(cmd, 'until') 
    if nargin<2 || isempty(param), param = 0; end 
    while 1 
     [down, t, kc] = KbCheck(-1); 
     if down && kc(KbName('Escape')) 
      error('User pressed ESC. Exiting ...'); 
     end 
     if t>=param, break; end 
     WaitSecs('YieldSecs', 0.005); 
    end 
    if nargout, varargout = {t}; end 
elseif strcmpi(cmd, 'stop') 
    KbQueueStop(KbQueueDevice); 
    started = false; 
    if nargout 
     BufferEvents; 
     if isempty(evts) 
      varargout = repmat({[]}, 1, nargout); 
      return; 
     end 

     isPress = [evts.Pressed] == 1; 
     if nargin<2, param = 0; end 
     varargout{1} = [[evts(isPress).Keycode] 
         [evts(isPress).Time]-param]; 
     if nargout>1 
      varargout{2} = [[evts(~isPress).Keycode] 
          [evts(~isPress).Time]-param]; 
     end 
    end 
    KbQueueRelease(KbQueueDevice); 
else 
    error('Unknown command: %s.', cmd); 
end 

    function BufferEvents % buffer events so we don't lose them 
     n = KbEventAvail(KbQueueDevice); 
     if n<1, return; end 
     for ic = 1:n 
      foo(ic) = KbEventGet(KbQueueDevice); %#ok 
     end 
     if isempty(evts), evts = foo; 
     else evts = [evts foo]; 
     end 
    end 

end 

function idx = responseDevice 
    if IsWin, idx = []; return; end % all keyboards 

    clear PsychHID; % refresh 
    [ind, pName] = GetKeyboardIndices; 
    if IsOSX 
     idx = ind(1); % based on limited computers 
    else % Linux 
     for i = length(ind):-1:1 
      if ~isempty(strfind(pName{i}, 'HIDKeys')) || ... 
       ~isempty(strfind(pName{i}, 'fORP')) % faked, need to update 
       idx = ind(i); 
       return; 
      end 
      idx = ind(end); % based on limited computers 
     end 
    end 
end 
+2

要麼不回答,要麼不在回答之上提出這個問題不適合的消息。 – Adriaan

3

我認爲你對這一切都是錯誤的。有很多簡單的方法來捕獲和處理事件,而不涉及昂貴的循環。具體來說,matlab(和八度)通過圖形句柄實例最有效地做到這一點(即使您不一定需要圖形對象可見)。

這是一個非常簡單的例子(我在octave上創建了它,但它也應該在matlab上工作)。運行它以查看它是如何工作的,然後進行相應的研究和修改。

%% In file 'run_fMRI_experiment.m' 
% Main experiment function 
function fMRIData = run_fMRI_experiment 
    global fMRIData; 
    F = figure; 
    text(-1,0,{'Glorious Experiment, she is now runnink, yes?', 'Please to be pressink "5", I will collectink data.', '', 'Please to be pressink key of escapeness when finishedski, da?'}, 'fontsize', 14); 
    axis off; axis ([-1, 1, -1, 1]); 
    set (F, 'keypressfcn', @detectFirstKeyPress); 
    waitfor(F); 
end 

% subfunctions (i.e. in same file) acting as callback functions 
function detectFirstKeyPress(CallerHandle, KeyPressEvent) 
    if strcmp(KeyPressEvent.Key, '5') 
    set (CallerHandle, 'keypressfcn', {@detectKeyPresses, tic()}); 
    fprintf ('Experiment started at %s\n', datestr(now())); 
    end 
end 

function detectKeyPresses (CallerHandle, KeyPressEvent, StartTime) 
    if strcmp (KeyPressEvent.Key, '5'); 
    global fMRIData; fMRIData(end+1) = toc(StartTime);; 
    fprintf('"5" pressed at %d seconds.\n', fMRIData(end)); return 
    elseif strcmp (KeyPressEvent.Key, 'escape'); 
    disp ('Escape Pressed. Ending Experiment'); 
    close (CallerHandle); 
    end 
end 


PS:請注意,被認爲是按鍵事件鏈接到數字,這個數字必須是焦點。即在終端聚焦時不要按'5',這將不會被計算在內。只要啓動該功能並在出現窗口時開始按下按鈕。

PS2:順便說一句,如果你必須執行某些任務異步,MATLAB提供了batch功能這一點。還值得看看。

+0

你可以使用省略號'...' –

+0

@SardarUsama我最初做的,但它看起來更怪,哈哈。無論如何,這條線只是爲了美容效果,沒有一點支配代碼窗口。 :p –