File: EyelinkGetQueuedData.c

package info (click to toggle)
psychtoolbox-3 3.0.19.14.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 86,796 kB
  • sloc: ansic: 176,245; cpp: 20,103; objc: 5,393; sh: 2,753; python: 1,397; php: 384; makefile: 193; java: 113
file content (451 lines) | stat: -rw-r--r-- 25,362 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/*
    Psychtoolbox-3/PsychSourceGL/Source/Common/Eyelink/EyelinkGetQueuedData.c

    PROJECTS: Eyelink

    AUTHORS:
        e_flister@yahoo.com         edf

    PLATFORMS: all

    HISTORY:

        21/03/2009  edf             created it

*/

#include "PsychEyelink.h"

static char useString[] = "[samples, events, drained] = Eyelink('GetQueuedData' [, eye])";

static char synopsisString[] =
"dequeues all samples and events from the link.\n"
"returns double matrices where columns are items and rows are fields from eyelink sample structs.\n"
"return flag 'drained' indicates whether queue was emptied or if this function needs to be called again.\n"
"if you include the eye argument (as LEFT_EYE or RIGHT_EYE as returned by EyelinkInitDefaults), samples will include raw fields for that eye.\n"
"if you don't remove items from the queue often enough, the oldest items will be replaced by a LOSTDATAEVENT, which will appear in the sample records at the location where items were dropped (fields other than type will be set to MISSING_DATA).\n\n"

"sample rows are as follows: \n"
"\t 1: time of sample (when camera imaged eye, in milliseconds since tracker was activated)\n"
"\t 2: type (always SAMPLE_TYPE or LOSTDATAEVENT as returned by EyelinkInitDefaults)\n"
"\t 3: flags (bits indicating what types of data are present, and for which eye(s) - see eye_data.h)\n"
"\t 4: left pupil center x (camera coordinates)\n"
"\t 5: right pupil center x (camera coordinates)\n"
"\t 6: left pupil center y (camera coordinates)\n"
"\t 7: right pupil center y (camera coordinates)\n"
"\t 8: left HEADREF x (angular gaze coordinates)\n"
"\t 9: right HEADREF x (angular gaze coordinates)\n"
"\t 10: left HEADREF y (angular gaze coordinates)\n"
"\t 11: right HEADREF y (angular gaze coordinates)\n"
"\t 12: left pupil size (arbitrary units, area or diameter as selected by pupil_size_diameter command)\n"
"\t 13: right pupil size (arbitrary units, area or diameter as selected by pupil_size_diameter command)\n"
"\t 14: left gaze position x (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 15: right gaze position x (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 16: left gaze position y (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 17: right gaze position y (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 18: angular resolution x (at gaze position in screen pixels per visual degree)\n"
"\t 19: angular resolution y (at gaze position in screen pixels per visual degree)\n"
"\t 20: status (error and status flags (only useful for EyeLink II and EyeLink1000, report CR status and tracking error). see eye_data.h.)\n"
"\t 21: input (data from input port(s))\n"
"\t 22: button input data: high 8 bits indicate changes from last sample, low 8 bits indicate current state of buttons 8 (MSB) to 1 (LSB)\n"
"\t 23: head-tracker data type (0=none)\n"
"\t 24: head-tracker data (not prescaled) 1\n"
"\t 25: head-tracker data (not prescaled) 2\n"
"\t 26: head-tracker data (not prescaled) 3\n"
"\t 27: head-tracker data (not prescaled) 4\n"
"\t 28: head-tracker data (not prescaled) 5\n"
"\t 29: head-tracker data (not prescaled) 6\n"
"\t 30: head-tracker data (not prescaled) 7\n"
"\t 31: head-tracker data (not prescaled) 8\n\n"

"if you request the raw sample fields, they will appear in the following additional rows:\n"
"\t 32: raw x sensor position of the pupil\n"
"\t 33: raw y sensor position of the pupil\n"
"\t 34: raw x sensor position of the corneal reflection\n"
"\t 35: raw y sensor position of the corneal reflection\n"
"\t 36: raw pupil area\n"
"\t 37: raw corneal reflection area\n"
"\t 38: raw width of pupil\n"
"\t 39: raw height of pupil\n"
"\t 40: raw width of corneal reflection\n"
"\t 41: raw height of corneal reflection\n"
"\t 42: raw x position of tracking window on sensor\n"
"\t 43: raw y position of tracking window on sensor\n"
"\t 44: (raw pupil x) - (raw corneal reflection x)\n"
"\t 45: (raw pupil y) - (raw corneal reflection y)\n"
"\t 46: raw area of 2nd corneal reflection candidate\n"
"\t 47: raw x sensor position of the 2nd corneal reflection candidate\n"
"\t 48: raw y sensor position of the 2nd corneal reflection candidate\n\n"

"About raw fields:\n"
"Normal samples do not contain the corneal reflection data, but some (non-saccade-based) calibration methods require this information.\n"
"The Eyelink 1000 can be configured to send this information as part of 'raw' samples, and this is required before use of this function.\n"
"Sol Simpson at SR-Research emphasizes that this is not officially supported or guaranteed.\n"
"1. You need to install Eyelink.dll from the latest software developer kit at https://www.sr-support.com/forums/showthread.php?t=6 (windows) or https://www.sr-support.com/forums/showthread.php?t=15 (osx)\n"
"\t   and the latest tracker host software at https://www.sr-support.com/forums/showthread.php?t=179\n"
"2. Issue the commands:\n"
"\t   Eyelink('command','link_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,HREF,PUPIL,STATUS,INPUT,HMARKER');\n"
"\t   Eyelink('command','inputword_is_window = ON');\n"
"More info:\n"
"\t   HMARKER (originally for Eyelink2's infrared head tracking markers) and INPUT (originally for the TTL lines) are jury-rigged to hold the extra data.\n"
"\t   You can also set file_sample_data to collect raw samples in the .edf file.\n"
"CAUTION: It may or may not work on your setup with your tracker.\n\n"
" event rows are as follows: \n"
"\t 1: effective time of event\n"
"\t 2: event type\n"
"\t 3: read (bits indicating which data fields contain valid data - see eye_data.h.)\n"
"\t 4: eye\n"
"\t 5: start time\n"
"\t 6: end time\n"
"\t 7: HEADREF gaze position starting point x\n"
"\t 8: HEADREF gaze position starting point y\n"
"\t 9: display gaze position starting point x (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 10: display gaze position starting point y (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 11: starting pupil size (arbitrary units, area or diameter as selected by pupil_size_diameter command)\n"
"\t 12: HEADREF gaze position ending point x\n"
"\t 13: HEADREF gaze position ending point y\n"
"\t 14: display gaze position ending point x (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 15: display gaze position ending point y (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 16: ending pupil size (arbitrary units, area or diameter as selected by pupil_size_diameter command)\n"
"\t 17: HEADREF gaze position average x\n"
"\t 18: HEADREF gaze position average y\n"
"\t 19: display gaze position average x (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 20: display gaze position average y (in pixel coordinates set by screen_pixel_coords command)\n"
"\t 21: average pupil size (arbitrary units, area or diameter as selected by pupil_size_diameter command)\n"
"\t 22: average gaze velocity magnitude (absolute value) in visual degrees per second\n"
"\t 23: peak gaze velocity magnitude (absolute value) in visual degrees per second\n"
"\t 24: starting gaze velocity in visual degrees per second\n"
"\t 25: ending gaze velocity in visual degrees per second\n"
"\t 26: starting angular resolution x in screen pixels per visual degree\n"
"\t 27: ending angular resolution x in screen pixels per visual degree\n"
"\t 28: starting angular resolution y in screen pixels per visual degree\n"
"\t 29: ending angular resolution y in screen pixels per visual degree\n"
"\t 30: status (collected error and status flags from all samples in the event (only useful for EyeLink II and EyeLink1000, report CR status and tracking error). see eye_data.h.)\n\n";

static char seeAlsoString[] = "";

#define NUM_SAMPLE_FIELDS 31
#define NUM_RAW_SAMPLE_FIELDS 17
#define NUM_EVENT_FIELDS 30
#define FUDGE_FACTOR 2 // how much more space we allocate beyond the reported queue length to account for additional items arriving as we are dequeueing
#define ERR_BUFF_LEN 1000

/*
    ROUTINE: EyelinkGetQueuedData

    PURPOSE:

    Matlab is slow at dealing with structs and looping over eyelink_get_float_data to drain the queue, so we take care of this for the client.
    also eliminates usage error of supplying incorrect type from Eyelink('GetNextDataType') to Eyelink('GetFloatData').

    TODO:

    -enable BINOCULAR raws
    -request sr research to let us read inputword_is_window
    -option for output in struct format?  would allow deprecating EyelinkGetFloatData, EyelinkGetFloatDataRaw, and EyelinkGetNextDataType
    -would NaN be a better choice (PsychGetNanValue) than MISSING_DATA for LOST_DATA_EVENT?  right now, MISSING_DATA would not fit into the uint type of many of the fields, if we were to offer them in correctly-typed fields of a struct

*/

PsychError EyelinkGetQueuedData(void)
{
    FSAMPLE      fs;
    FSAMPLE_RAW  fr;
    FEVENT       fe;
    int numSamples = 0, numEvents = 0, maxSamples, maxEvents, type, eye, fieldNum, index, numSampleFields, err;
    double *samples, *events;
    psych_bool useEye=FALSE;
    PsychNativeBooleanType drained=(PsychNativeBooleanType)FALSE;
    char errmsg[ERR_BUFF_LEN]="";

    //all sub functions should have these two lines
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

    //check to see if the user supplied superfluous arguments
    PsychErrorExit(PsychCapNumInputArgs(1));
    PsychErrorExit(PsychRequireNumInputArgs(0));
    PsychErrorExit(PsychCapNumOutputArgs(3));

    // Verify eyelink is up and running
    EyelinkSystemIsConnected();
    EyelinkSystemIsInitialized();

    if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: boilerplate done\n");

    if (PsychCopyInIntegerArg(1, kPsychArgOptional, &eye)) {
        if (eye!=LEFT_EYE && eye!=RIGHT_EYE) {
            PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "EyeLink: GetQueuedData:  eye argument must be LEFT_EYE or RIGHT_EYE as returned by EyelinkInitDefaults\n");
        }

        TrackerOKForRawValues();

        useEye=TRUE;
        numSampleFields=NUM_SAMPLE_FIELDS+NUM_RAW_SAMPLE_FIELDS;
    } else {
        numSampleFields=NUM_SAMPLE_FIELDS;
        useEye=FALSE;
    }

    if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: eye/raw chosen\n");

    maxSamples = FUDGE_FACTOR*eyelink_data_count(1,0) + 1; //need to allocate at least 1 in case we get a eyelink_get_next_data not predicted by eyelink_data_count
    maxEvents = FUDGE_FACTOR*eyelink_data_count(0,1) + 1;

    samples = (double *)PsychMallocTemp(maxSamples*numSampleFields*sizeof(double)); // according to mario if OOM, ultimately calls to mxCreateNumericArray/mxMalloc will error inside matlab rather than return NULL
    events = (double *)PsychMallocTemp(maxEvents*NUM_EVENT_FIELDS*sizeof(double));

    if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: memory allocated for %d samples and %d events\n",maxSamples,maxEvents);

    while(!drained && numSamples<maxSamples && numEvents<maxEvents) {
        type = eyelink_get_next_data(NULL);
        if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: doing item of type %d\n",type);
        switch(type) {
            case SAMPLE_TYPE:
                if (eyelink_get_float_data((ALLF_DATA*) &fs) != type) {
                    PsychErrorExitMsg(PsychError_internal, "Eyelink: GetQueuedData: eyelink_get_float_data did not return same sample type as eyelink_get_next_data.");
                }
                if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: get_float called on sample\n");
                if (useEye) {
                    if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: calling get_raw\n");
                    memset(&fr, 0, sizeof(fr));
                    if((err = eyelink_get_extra_raw_values_v2(&fs, eye, &fr))){
                        if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: raw value error\n");
                        snprintf(errmsg, ERR_BUFF_LEN, "Eyelink: GetQueuedData: eyelink_get_extra_raw_values_v2 returned error code %d: %s",
                                 err, eyelink_get_error(err, "eyelink_get_extra_raw_values_v2"));
                        PsychErrorExitMsg(PsychError_internal, errmsg);
                    }
                }

                index=PsychIndexElementFrom2DArray(numSampleFields, maxSamples, 0, numSamples++);
                samples[index++]=(double)(FLOAT_TIME(&fs)); // 1
                samples[index++]=(double)(fs.type); // 2
                samples[index++]=(double)(fs.flags); // 3
                samples[index++]=(double)(fs.px[0]); // 4
                samples[index++]=(double)(fs.px[1]); // 5
                samples[index++]=(double)(fs.py[0]); // 6
                samples[index++]=(double)(fs.py[1]); // 7
                samples[index++]=(double)(fs.hx[0]); // 8
                samples[index++]=(double)(fs.hx[1]); // 9
                samples[index++]=(double)(fs.hy[0]); // 10
                samples[index++]=(double)(fs.hy[1]); // 11
                samples[index++]=(double)(fs.pa[0]); // 12
                samples[index++]=(double)(fs.pa[1]); // 13
                samples[index++]=(double)(fs.gx[0]); // 14
                samples[index++]=(double)(fs.gx[1]); // 15
                samples[index++]=(double)(fs.gy[0]); // 16
                samples[index++]=(double)(fs.gy[1]); // 17
                samples[index++]=(double)(fs.rx); // 18
                samples[index++]=(double)(fs.ry); // 19
                samples[index++]=(double)(fs.status); // 20
                samples[index++]=(double)(fs.input); // 21
                samples[index++]=(double)(fs.buttons); // 22
                samples[index++]=(double)(fs.htype); // 23
                samples[index++]=(double)(fs.hdata[0]); // 24
                samples[index++]=(double)(fs.hdata[1]); // 25
                samples[index++]=(double)(fs.hdata[2]); // 26
                samples[index++]=(double)(fs.hdata[3]); // 27
                samples[index++]=(double)(fs.hdata[4]); // 28
                samples[index++]=(double)(fs.hdata[5]); // 29
                samples[index++]=(double)(fs.hdata[6]); // 30
                samples[index++]=(double)(fs.hdata[7]); // 31

                if (useEye) {
                    samples[index++]=(double)(fr.raw_pupil[0]); // 32
                    samples[index++]=(double)(fr.raw_pupil[1]); // 33
                    samples[index++]=(double)(fr.raw_cr[0]); // 34
                    samples[index++]=(double)(fr.raw_cr[1]); // 35
                    samples[index++]=(double)(fr.pupil_area); // 36
                    samples[index++]=(double)(fr.cr_area); // 37
                    samples[index++]=(double)(fr.pupil_dimension[0]); // 38
                    samples[index++]=(double)(fr.pupil_dimension[1]); // 39
                    samples[index++]=(double)(fr.cr_dimension[0]); // 40
                    samples[index++]=(double)(fr.cr_dimension[1]); // 41
                    samples[index++]=(double)(fr.window_position[0]); // 42
                    samples[index++]=(double)(fr.window_position[1]); // 43
                    samples[index++]=(double)(fr.pupil_cr[0]); // 44
                    samples[index++]=(double)(fr.pupil_cr[1]); // 45
                    samples[index++]=(double)(fr.cr_area2); // 46
                    samples[index++]=(double)(fr.raw_cr2[0]); // 47
                    samples[index++]=(double)(fr.raw_cr2[1]); // 48
                }

                if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: sample copied\n");
                break;

            case LOST_DATA_EVENT: // queue overflowed, we are not supposed to call eyelink_get_float_data on this
                index=PsychIndexElementFrom2DArray(numSampleFields, maxSamples, 0, numSamples++);
                for(fieldNum=0; fieldNum<numSampleFields; fieldNum++){
                    samples[index++]= (double)((fieldNum==1) ? LOST_DATA_EVENT : MISSING_DATA);
                }
                if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: did lost_data\n");
                break;

            case 0: // queue empty
                drained=(PsychNativeBooleanType)TRUE;
                if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: found end of queue\n");
                break;

            default: // it is an event
                if (eyelink_get_float_data((ALLF_DATA*) &fe) != type) {
                    PsychErrorExitMsg(PsychError_internal, "Eyelink: GetQueuedData: eyelink_get_float_data did not return same event type as eyelink_get_next_data.");
                }
                if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: get_float called on event\n");
                index=PsychIndexElementFrom2DArray(NUM_EVENT_FIELDS, maxEvents, 0, numEvents++);
                events[index++]=(double)(fe.time); // 1 %FLOAT_TIME currently a noop on events
                events[index++]=(double)(fe.type); // 2
                events[index++]=(double)(fe.read); // 3
                events[index++]=(double)(fe.eye); // 4
                events[index++]=(double)(fe.sttime); // 5
                events[index++]=(double)(fe.entime); // 6
                events[index++]=(double)(fe.hstx); // 7
                events[index++]=(double)(fe.hsty); // 8
                events[index++]=(double)(fe.gstx); // 9
                events[index++]=(double)(fe.gsty); // 10
                events[index++]=(double)(fe.sta); // 11
                events[index++]=(double)(fe.henx); // 12
                events[index++]=(double)(fe.heny); // 13
                events[index++]=(double)(fe.genx); // 14
                events[index++]=(double)(fe.geny); // 15
                events[index++]=(double)(fe.ena); // 16
                events[index++]=(double)(fe.havx); // 17
                events[index++]=(double)(fe.havy); // 18
                events[index++]=(double)(fe.gavx); // 19
                events[index++]=(double)(fe.gavy); // 20
                events[index++]=(double)(fe.ava); // 21
                events[index++]=(double)(fe.avel); // 22
                events[index++]=(double)(fe.pvel); // 23
                events[index++]=(double)(fe.svel); // 24
                events[index++]=(double)(fe.evel); // 25
                events[index++]=(double)(fe.supd_x); // 26
                events[index++]=(double)(fe.eupd_x); // 27
                events[index++]=(double)(fe.supd_y); // 28
                events[index++]=(double)(fe.eupd_y); // 29
                events[index++]=(double)(fe.status); // 30
        }
    }

    if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: done after dequeueing %d samples (of %d) and %d events (of %d) (drained is %d) (clauses: %d %d %d)\n",numSamples,maxSamples,numEvents,maxEvents,drained,!drained, numSamples<maxSamples, numEvents<maxEvents);

    //bug: who frees memory allocated by PsychAllocOutDoubleMatArg if there is no output arg?  according to matlab doc one should not rely on matlab to do it:
    //   "It is more efficient to perform this cleanup in the source MEX-file than to rely on the automatic mechanism."
    //mario reasons that mathworks would have to have done something stupid for this to be true

    //these only work for cutting off the unused parts of the buffers because matlab storage is columnwise
    //this is a little fragile - would be nice if PsychCopy* took the dims of the source and handled noncontiguous cases
    //mario says this is purposesly discouraged and inconvenient to encourage columnwise storage and prevent inefficient fragged memcpys
    PsychCopyOutDoubleMatArg(1, kPsychArgOptional, numSampleFields, numSamples, 1, samples);
    PsychCopyOutDoubleMatArg(2, kPsychArgOptional, NUM_EVENT_FIELDS, numEvents, 1, events);

    PsychCopyOutBooleanArg(3, kPsychArgOptional, drained);

    if (Verbosity() > 6) mexPrintf("Eyelink: GetQueuedData: done with outputs\n");

    // PsychFreeTemp(samples); apparently not paradigmatic to do this oneself
    // PsychFreeTemp(events);

    return(PsychError_none);
}


psych_bool TrackerOKForRawValues(void) {
    static psych_bool OK=FALSE;

    int err,ver[4];
    char errmsg[2 * ERR_BUFF_LEN]="",buf[ERR_BUFF_LEN]="";
    psych_bool tryAgain=TRUE;

    if (OK) { //check static value so we don't do the expensive stuff below more than once
        return OK;
    }

    while(tryAgain) {
        if ((err=eyelink_read_request("link_sample_data"))){
            snprintf(errmsg, 2 * ERR_BUFF_LEN, "Eyelink: eyelink_read_request returned error code '%d': '%s'", err, eyelink_get_error(err,"eyelink_read_request"));
            PsychErrorExitMsg(PsychError_internal, errmsg);
        }

        err=NO_REPLY;
        while(err==NO_REPLY){
            err=eyelink_read_reply(buf);
        }

        if(err == OK_RESULT){
            if(strlen(buf)>0){
                if (Verbosity() > 6) mexPrintf("Eyelink: TrackerOKForRawValues: link_sample_data is: '%s'\n",buf);
                // currently getting "LEFT, RIGHT, , RAW, HREF, GAZE, GAZERES, AREA, STATUS, INPUT, HMARKER,"
                // what happened to PUPIL?

                if(!strstr(buf,"INPUT") || !strstr(buf,"HMARKER")){
                    snprintf(errmsg, 2 * ERR_BUFF_LEN, "Eyelink: your current link_sample_data is '%s', but you must include INPUT and HMARKER to get raw values.  You must also set inputword_is_window = ON.  Use Eyelink('command','link_sample_data = INPUT,HMARKER') and Eyelink('command','inputword_is_window = ON') to do this.",buf);
                    PsychErrorExitMsg(PsychError_user, errmsg);
                }
                tryAgain=FALSE;
            } else {
                // sent mail to suganthan: eyelink_read_reply(buf) is frequently returning OK_RESULT without setting buf
                // he says send him an eye.log from the tracker host when this happens.
                mexPrintf("Eyelink: TrackerOKForRawValues: eyelink_read_request(\"link_sample_data\") needs to try again\n");
            }

        } else {
            snprintf(errmsg, 2 * ERR_BUFF_LEN, "Eyelink: eyelink_read_reply returned error code '%d': '%s'", err, eyelink_get_error(err,"eyelink_read_reply"));
            PsychErrorExitMsg(PsychError_internal, errmsg);
        }
    }

    /* disabled this check cuz reading inputword_is_window isn't supported
    if (err=eyelink_read_request("inputword_is_window")){
        snprintf(errmsg, 2 * ERR_BUFF_LEN, "Eyelink: eyelink_read_request returned error code %d: '%s'", err, eyelink_get_error(err,"eyelink_read_request"));
        PsychErrorExitMsg(PsychError_internal, errmsg);
    }

    err=NO_REPLY;
    while(err==NO_REPLY){
        err=eyelink_read_reply(buf);
    }

    if(err == OK_RESULT){
        if (Verbosity() > 6) mexPrintf("Eyelink: TrackerOKForRawValues: inputword_is_window is: '%s'\n",buf); //must be Eyelink('command','inputword_is_window = ON')
        // currently getting "Variable read not supported"

         if(!strstr(buf,"ON")){
            snprintf(errmsg, 2 * ERR_BUFF_LEN, "Eyelink: your current inputword_is_window is '%s', but must be ON to get raw values.  Use Eyelink('command','inputword_is_window = ON') to do this.",buf);
            PsychErrorExitMsg(PsychError_user, errmsg);
        }

    } else {
        snprintf(errmsg, 2 * ERR_BUFF_LEN, "Eyelink: eyelink_read_reply returned error code '%d': '%s'", err, eyelink_get_error(err,"eyelink_read_reply"));
        PsychErrorExitMsg(PsychError_internal, errmsg);
    }
     */

    eyelink_dll_version(buf);
    if (Verbosity() > 6) mexPrintf("Eyelink: TrackerOKForRawValues: eyelink dll version: '%s'\n",buf); //currently "1,8,1,0"
    if(sscanf(buf,"%d,%d,%d,%d",&(ver[0]),&(ver[1]),&(ver[2]),&(ver[3]))==4){
        if(ver[0]<1 || (ver[0]==1 && ver[1]<8) || (ver[0]==1 && ver[1]==8 && ver[2]<1)){
            snprintf(errmsg, 2 * ERR_BUFF_LEN, "EyeLink: eyelink_dll_version reports '%s' - collecting raw values requires that you install 1.8.1.0 or higher (https://www.sr-support.com/forums/showthread.php?t=6 (windows) or https://www.sr-support.com/forums/showthread.php?t=15 (osx)).",buf);
            PsychErrorExitMsg(PsychError_unimplemented, errmsg);
        }
    } else {
        snprintf(errmsg, 2 * ERR_BUFF_LEN, "Eyelink: couldn't read output of eyelink_dll_version: '%s'",buf);
        PsychErrorExitMsg(PsychError_internal, errmsg);
    }

    if(eyelink_get_tracker_version(buf)<3){
        PsychErrorExitMsg(PsychError_unimplemented, "Eyelink: can't get raw values without eyelink 1000 or better");
    } else {
        if (Verbosity() > 6) mexPrintf("Eyelink: TrackerOKForRawValues: tracker version: '%s'\n",buf); //currently "EYELINK CL 4.31"
    }
    if(sscanf(buf,"EYELINK CL %d.%d",&(ver[0]),&(ver[1]))==2){
        if(ver[0]<4 || (ver[0]==4 && ver[1]<31)){
            snprintf(errmsg, 2 * ERR_BUFF_LEN, "EyeLink: eyelink_get_tracker_version reports '%s' - collecting raw values requires that you install host software 4.31 or higher (https://www.sr-support.com/forums/showthread.php?t=179).",buf);
            PsychErrorExitMsg(PsychError_unimplemented,errmsg);
        }
    } else {
        snprintf(errmsg, 2 * ERR_BUFF_LEN, "Eyelink: couldn't read output of eyelink_get_tracker_version: '%s'",buf);
        PsychErrorExitMsg(PsychError_internal, errmsg);
    }

    OK=TRUE;
    return OK;
}