2017-08-30 168 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 –