attachment:MEGSynchClass.m of MEGSynch - Meg Wiki
location: attachment:MEGSynchClass.m of MEGSynch

Attachment 'MEGSynchClass.m'

Download

   1 % Interface for National Instruments PCI 6503 card
   2 % Version 1.2
   3 %
   4 % DESCRIPTION
   5 %
   6 % N.B.: It does not monitor pulses in the background, so you have to make sure that you wait for any pulse before it comes!
   7 %
   8 % Properties (internal variables):
   9 % 	IsValid						= device set and operational
  10 %   Keys                        = set a cell of key names for emulation mode. For key names look for KbName.m.
  11 %                                   N.B.: Requires PTB.
  12 %                                   N.B.: Suppress passing keypresses to MATLAB during the whole experiment
  13 %
  14 % 	Clock						= interal clock (seconds past since the first scanner pulse or clock reset)
  15 %
  16 % 	Buttons						= current state of the any button
  17 %   ButtonPresses               = index/indices of each button(s) pressed since last check
  18 %   TimeOfButtonPresses         = time (according to the internal clock) of each button(s) pressed since last check
  19 % 	LastButtonPress				= index/indices of the last button(s) pressed
  20 % 	TimeOfLastButtonPress		= time (according to the internal clock) of the last button press (any)
  21 %   BBoxTimeout                 = set a non-Inf value (in seconds) to wait for button press only for a limited time
  22 %                               = set a negative value (in seconds) to wait even in case of response
  23 %
  24 % Methods (internal functions):
  25 % 	MEGSynchClass   			= constructor
  26 % 	delete						= destructor
  27 %	ResetClock					= reset internal clock
  28 %
  29 %   SendTrigger(v)              = sends 'v' (integer between 0-255) as a trigger pulse
  30 %
  31 %	SetButtonReadoutTime(t) 	= blocks individual button readout after a button press for 't' seconds (detection of other buttons is still possible)
  32 %	SetButtonBoxReadoutTime(t)	= blocks the whole button box readout after a button press for 't' seconds (detection of other buttons is also not possible)
  33 %	WaitForButtonPress			= wait until a button is pressed
  34 % 	WaitForButtonRelease		= wait until a button is released
  35 %
  36 % USAGE
  37 %
  38 % Initialise:
  39 % 	MEG = MEGSynchClass;
  40 % 	% MEG = MEGSynchClass(1); % for full emulation (no DAQ available)
  41 %   % MEG = MEGSynchClass({'B1','S4';'B2','S5'}); % specify button names and maps them to S4 and S5
  42 % Close:
  43 % 	MEG.delete;
  44 %
  45 % Example for Trigger:
  46 %   for v = 0:255
  47 %       pause(0.1)
  48 %       MEG.SendTrigger(v)
  49 %   end
  50 %
  51 % Example for buttons:
  52 % 	MEG.SetButtonReadoutTime(0.5);      % block individual buttons
  53 %	% MEG.SetButtonBoxReadoutTime(0.5); % block the whole buttonbox
  54 %   % MEG.Keys = {'f1','f2','f3','f4'}; % emulation Buttons #1-#4 with F1-F4
  55 %	n = 0;
  56 %   % MEG.BBoxTimeout = 1.5;            % Wait for button press for 1.5s
  57 %   % MEG.BBoxTimeout = -1.5;           % Wait for button press for 1.5s even in case of response
  58 %	MEG.ResetClock;
  59 %	while n ~= 10                       % polls 10 button presses
  60 %   	MEG.WaitForButtonPress;         % Wait for any button to be pressed
  61 %   	% MEG.WaitForButtonRelease;     % Wait for any button to be	released
  62 %   	% MEG.WaitForButtonPress([],2); % Wait for Button #2
  63 %   	% MEG.WaitForButtonPress(2);    % Wait for any button for 2s (overrides MEG.BBoxTimeout only for this event)
  64 %   	% MEG.WaitForButtonPress(-2);   % Wait for any button for 2s even in case of response (overrides MEG.BBoxTimeout only for this event)
  65 %   	% MEG.WaitForButtonPress(2,2);  % Wait for Button #2 for 2s (overrides MEG.BBoxTimeout only for this event)
  66 %   	% MEG.WaitForButtonPress(-2,2); % Wait for Button #2 for 2s even in case of response (overrides MEG.BBoxTimeout only for this event)
  67 %    	n = n + 1;
  68 %    	fprintf('At %2.3fs, ',MEG.Clock);
  69 %    	fprintf('Button %d ',MEG.LastButtonPress);
  70 %    	fprintf('pressed: %2.3fs\n',MEG.TimeOfLastButtonPress);
  71 %	end
  72 %_______________________________________________________________________
  73 % Copyright (C) 2015 MRC CBSU Cambridge
  74 %
  75 % Tibor Auer: tibor.auer@mrc-cbu.cam.ac.uk
  76 %_______________________________________________________________________
  77 
  78 classdef MEGSynchClass < handle
  79     
  80     
  81     properties
  82         Keys = {}
  83         BBoxTimeout = Inf % second (timeout for WaitForButtonPress)
  84     end
  85     
  86     properties (SetAccess = private)
  87         ButtonPresses
  88         TimeOfButtonPresses
  89         
  90         LastButtonPress
  91     end
  92     
  93     properties (Access = private)
  94         Map = {...
  95             'S3' [0 1];... % S3 - port0/line1
  96             'S4' [0 2];... % S4 - port0/line2
  97             'S5' [0 3];... % S5 - port0/line3
  98             'S6' [0 4];... % S6 - port0/line4
  99             'S7' [0 0];... % S7 - port0/line0
 100             };
 101         DAQ
 102         nChannels
 103         
 104         tID % internal timer
 105         
 106         Data % current data
 107         Datap % previous data
 108         TOA % Time of access 1*n
 109         ReadoutTime = 0 % sec to store data before refresh 1*n
 110         BBoxReadout = false
 111         BBoxWaitForRealease = false % wait for release instead of press
 112         
 113         BTable
 114         
 115         isDAQ
 116         isPTB
 117     end
 118     
 119     properties (Dependent)
 120         IsValid
 121         
 122         Buttons
 123         Clock
 124         
 125         TimeOfLastButtonPress
 126     end
 127     
 128     methods
 129         
 130         %% Contructor and destructor
 131         function obj = MEGSynchClass(varargin)
 132             fprintf('Initialising MEG Synch...');
 133             % Create session
 134             
 135             buttons = {...
 136                 'LY' 'S3';...
 137                 'RB' 'S4';...
 138                 'RY' 'S5';...
 139                 'RG' 'S6';...
 140                 'RR' 'S7';...
 141                 };
 142             noDAQ = false;
 143             for arg = varargin
 144                 if iscell(arg{1}), buttons = arg{1}; end
 145                 if islogical(arg{1}) || isnumeric(arg{1}), noDAQ = logical(arg{1}); end
 146             end
 147             obj.BTable = table(buttons(:,1));
 148             obj.BTable.Properties.VariableNames = {'Buttons'};
 149             obj.BTable.Properties.RowNames = buttons(:,2);
 150             
 151             if ~noDAQ
 152                 warning off daq:Session:onDemandOnlyChannelsAdded
 153                 obj.DAQ = daq.createSession('ni');
 154                 % Add channels for buttons
 155                 for b = obj.BTable.Properties.RowNames'
 156                     patch = obj.Map{strcmp(obj.Map(:,1),b{1}),2};
 157                     obj.DAQ.addDigitalChannel('Dev1', sprintf('port%d/line%d',patch(1),patch(2)), 'InputOnly');
 158                 end
 159                 obj.nChannels = numel(obj.DAQ.Channels);
 160                 
 161                 % Add channels for trigger
 162                 for l = 0:7
 163                     obj.DAQ.addDigitalChannel('Dev1', sprintf('port2/line%d',l), 'OutputOnly');
 164                 end
 165                 obj.isDAQ = 1;
 166             else
 167                 obj.isDAQ = 0;
 168                 obj.DAQ.isvalid = 1;
 169                 obj.DAQ.Channels = 1:5;
 170                 obj.nChannels = numel(obj.DAQ.Channels);
 171                 fprintf('\n');
 172                 fprintf('WARNING: DAQ card is not in use!\n');
 173                 fprintf('Emulation: ButtonBox is not available           --> ');
 174                 fprintf('You may need to set Keys!\n');
 175             end
 176             
 177             if ~obj.IsValid
 178                 warning('WARNING: MEG Synch is not open!');
 179             end
 180             
 181             obj.isPTB = exist('KbCheck','file') == 2;
 182             
 183             obj.Data = zeros(1,obj.nChannels);
 184             obj.Datap = zeros(1,obj.nChannels);
 185             obj.ReadoutTime = obj.ReadoutTime * ones(1,obj.nChannels);
 186             obj.ResetClock;
 187             fprintf('Done\n');
 188         end
 189         
 190         function delete(obj)
 191             fprintf('MEG Synch is closing...');
 192             if obj.isDAQ
 193                 obj.DAQ.release();
 194                 delete(obj.DAQ);
 195                 warning on daq:Session:onDemandOnlyChannelsAdded
 196             end
 197             if obj.isPTB, ListenChar(0); end
 198             fprintf('Done\n');
 199         end
 200         
 201         %% Utils
 202         function val = get.IsValid(obj)
 203             val = ~isempty(obj.DAQ) && obj.DAQ.isvalid;
 204         end
 205         
 206         function ResetClock(obj)
 207             obj.tID = tic;
 208             obj.TOA = zeros(1,obj.nChannels);
 209         end
 210         
 211         function val = get.Clock(obj)
 212             val = toc(obj.tID);
 213         end
 214         
 215         function set.Keys(obj,val)
 216             obj.Keys = val;
 217             if obj.isPTB, ListenChar(2); end % suppress passing keypresses to MATLAB
 218         end
 219         
 220         %% Trigger
 221         function SendTrigger(obj,val)
 222             if obj.isDAQ
 223                 outputSingleScan(obj.DAQ,dec2binvec(val,8));
 224             end
 225         end
 226         
 227         %% Buttons
 228         function SetButtonReadoutTime(obj,t)
 229             obj.ReadoutTime(:) = t;
 230             obj.BBoxReadout = false;
 231         end
 232         
 233         function SetButtonBoxReadoutTime(obj,t)
 234             obj.ReadoutTime(:) = t;
 235             obj.BBoxReadout = true;
 236         end
 237         
 238         function WaitForButtonPress(obj,timeout,ind)
 239             BBoxQuery = obj.Clock;
 240             
 241             % Reset indicator
 242             obj.ButtonPresses = [];
 243             obj.TimeOfButtonPresses = [];
 244             obj.LastButtonPress = [];
 245             
 246             % timeout
 247             if (nargin < 2 || isempty(timeout)), timeout = obj.BBoxTimeout; end
 248             wait = timeout < 0; % wait until timeout even in case of response
 249             timeout = abs(timeout);
 250             
 251             while (~obj.Buttons ||... % button pressed
 252                     wait || ...
 253                     (nargin >= 3 && ~isempty(ind) && ~any(obj.LastButtonPress == ind))) && ... % correct button pressed
 254                     (obj.Clock - BBoxQuery) < timeout % timeout
 255                 if ~isempty(obj.LastButtonPress)
 256                     if nargin >= 3 && ~isempty(ind) && ~any(obj.LastButtonPress == ind), continue; end % incorrect button
 257                     if ~isempty(obj.TimeOfButtonPresses) && (obj.TimeOfButtonPresses(end) == obj.TimeOfLastButtonPress), continue; end % same event
 258                     obj.ButtonPresses = horzcat(obj.ButtonPresses,obj.LastButtonPress); 
 259                     obj.TimeOfButtonPresses = horzcat(obj.TimeOfButtonPresses,ones(1,numel(obj.LastButtonPress))*obj.TimeOfLastButtonPress);
 260                 end
 261             end
 262         end
 263         
 264         function WaitForButtonRelease(obj,varargin)
 265             % backup settings
 266             rot = obj.ReadoutTime;
 267             bbro = obj.BBoxReadout;
 268             
 269             % config for release
 270             obj.BBoxWaitForRealease = true;
 271             obj.SetButtonBoxReadoutTime(0);
 272             
 273             WaitForButtonPress(obj,varargin{:});
 274             
 275             % restore settings
 276             obj.BBoxWaitForRealease = false;
 277             obj.ReadoutTime = rot;
 278             obj.BBoxReadout = bbro;
 279         end
 280         
 281         function val = get.TimeOfLastButtonPress(obj)
 282             val = max(obj.TOA) * ~isempty(obj.LastButtonPress);
 283         end
 284      
 285         %% Low level access
 286 		function val = get.Buttons(obj)
 287             val = 0;
 288             obj.Refresh;
 289             if obj.BBoxWaitForRealease
 290                 if any(obj.Datap) && all(~(obj.Data.*obj.Datap))
 291                     % obj.LastButtonPress = find(obj.Datap);
 292                     obj.LastButtonPress = obj.BTable(find(obj.Datap),:).Buttons;
 293                     obj.Datap(:) = 0;
 294                     val = 1;
 295                 end
 296             else
 297                 if any(obj.Data)
 298                     % obj.LastButtonPress = find(obj.Data);
 299                     obj.LastButtonPress = obj.BTable(find(obj.Data),:).Buttons;
 300                     obj.Data(:) = 0;
 301                     val = 1;
 302                 end
 303             end
 304         end
 305 	end
 306 	
 307 	methods (Access = private)	
 308         function Refresh(obj)
 309 			t = obj.Clock;
 310 			
 311             % get data
 312 			if obj.isDAQ
 313                 data = inputSingleScan(obj.DAQ);
 314             else
 315                 data = zeros(1,obj.nChannels);
 316             end
 317             
 318             
 319             % button press emulation (keyboard) via PTB
 320             nKeys = numel(obj.Keys);
 321             if obj.isPTB && nKeys
 322                 [ ~, ~, keyCode ] = KbCheck;
 323                 data(1:nKeys) = keyCode(KbName(obj.Keys));
 324             end
 325             
 326             if obj.BBoxReadout, obj.TOA = max(obj.TOA); end
 327             ind = obj.ReadoutTime < (t-obj.TOA);
 328             obj.Datap = obj.Data;
 329             obj.Data(ind) = data(ind);
 330             obj.TOA(logical(obj.Data)) = t;
 331         end
 332         
 333     end
 334     
 335 end

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2016-04-27 09:04:35, 12.4 KB) [[attachment:MEGSynchClass.m]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.