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