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.You are not allowed to attach a file to this page.