attachment:MEGSynchClass.m of MEGSynch - CBU MRI facility Wiki
location: attachment:MEGSynchClass.m of MEGSynch

Attachment 'MEGSynchClass.m'

Download

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