1
2 /*
3 * Argyll Color Correction System
4 *
5 * Datacolor/ColorVision Spyder 2/3/4 related software.
6 *
7 * Author: Graeme W. Gill
8 * Date: 17/9/2007
9 *
10 * Copyright 2006 - 2014, Graeme W. Gill
11 * All rights reserved.
12 *
13 * (Based initially on i1disp.c)
14 *
15 * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
16 * see the License2.txt file for licencing details.
17 */
18
19 /*
20 IMPORTANT NOTES:
21
22 The Spyder 2 instrument cannot function without the driver software
23 having access to the vendor supplied PLD firmware pattern for it.
24 This firmware is not provided with Argyll, since it is not available
25 under a compatible license.
26
27 The purchaser of a Spyder 2 instrument should have received a copy
28 of this firmware along with their instrument, and should therefore be able to
29 enable the Argyll driver for this instrument by using the oeminst utility
30 to create a spyd2PLD.bin file.
31
32 [ The Spyder 3, 4 & 5 don't need a PLD firmware file. ]
33
34
35 The Spyder 4 & 5 instrument will not have the full range of manufacturer
36 calibration settings available without the vendor calibration data.
37 This calibration day is not provided with Argyll, since it is not
38 available under a compatible license.
39
40 The purchaser of a Spyder 4 or 5 instrument should have received a copy
41 of this calibration data along with their instrument, and should therefore
42 be able to enable the use of the full range of calibration settings
43 by using the spyd4en utility to create a spyd4cal.bin file.
44
45 Alternatively, you can use Argyll .ccss files to set a Spyder 4
46 calibration.
47
48 [ The Spyder 2 & 3 don't need a calibration data file. ]
49
50 The hwver == 5 code is not fully implemented.
51 It's not possible to get it going without an instrument to verify on.
52 (Perhaps it has only 4 sensors ?)
53
54 The frequency measurement is not very accurate, particularly for
55 the Spyder 3, 4 & 5, being too low by about 3.5%.
56
57 */
58
59 /*
60 If you make use of the instrument driver code here, please note
61 that it is the author(s) of the code who take responsibility
62 for its operation. Any problems or queries regarding driving
63 instruments with the Argyll drivers, should be directed to
64 the Argyll's author(s), and not to any other party.
65
66 If there is some instrument feature or function that you
67 would like supported here, it is recommended that you
68 contact Argyll's author(s) first, rather than attempt to
69 modify the software yourself, if you don't have firm knowledge
70 of the instrument communicate protocols. There is a chance
71 that an instrument could be damaged by an incautious command
72 sequence, and the instrument companies generally cannot and
73 will not support developers that they have not qualified
74 and agreed to support.
75 */
76
77 /* TTBD:
78
79 Would be good to add read/write data values if debug >= 3
80 */
81
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <ctype.h>
85 #include <string.h>
86 #include <time.h>
87 #include <stdarg.h>
88 #include <math.h>
89 #include <fcntl.h>
90 #ifndef SALONEINSTLIB
91 #include "copyright.h"
92 #include "aconfig.h"
93 #include "numlib.h"
94 #else /* SALONEINSTLIB */
95 #include "sa_config.h"
96 #include "numsup.h"
97 #endif /* SALONEINSTLIB */
98 #include "xspect.h"
99 #include "insttypes.h"
100 #include "conv.h"
101 #include "icoms.h"
102 #include "spyd2.h"
103
104 #undef PLOT_SPECTRA /* Plot the sensor senitivity spectra */
105 #undef PLOT_SPECTRA_EXTRA /* Plot the sensor senitivity spectra extra values */
106 #undef SAVE_SPECTRA /* Save the sensor senitivity spectra to "sensors.cmf" */
107 #undef SAVE_XYZSPECTRA /* Save the XYZ senitivity spectra to "sensorsxyz.cmf" (scale 1.4) */
108 #undef SAVE_STDXYZ /* save 1931 2 degree to stdobsxyz.cmf */
109
110
111 #define DO_RESETEP /* Do the miscelanous resetep()'s */
112 #define CLKRATE 1000000 /* Clockrate the Spyder 2 hardware runs at */
113 #define MSECDIV (CLKRATE/1000) /* Divider to turn clocks into msec */
114 #define DEFRRATE 50 /* Default display refresh rate */
115 #define DO_ADAPTIVE /* Adapt the integration time to the light level */
116 /* This helps repeatability at low levels A LOT */
117
118 #define LEVEL2 /* Second level (nonliniarity) calibration */
119 #define RETRIES 4 /* usb_reads are unreliable - bug in spyder H/W ?*/
120
121 #ifdef DO_ADAPTIVE
122 # define RINTTIME 2.0 /* Base time to integrate reading over - refresh display */
123 # define NINTTIME 2.0 /* Base time to integrate reading over - non-refresh display */
124 #else /* !DO_ADAPTIVE */
125 # define RINTTIME 5.0 /* Integrate over fixed longer time (manufacturers default) */
126 # define NINTTIME 5.0 /* Integrate over fixed longer time (manufacturers default) */
127 #endif /* !DO_ADAPTIVE */
128
129 static inst_code spyd2_interp_code(inst *pp, int ec);
130
131 /* ------------------------------------------------------------------------ */
132 /* Implementation */
133
134 /* Interpret an icoms error into a SPYD2 error */
icoms2spyd2_err(int se)135 static int icoms2spyd2_err(int se) {
136 if (se != ICOM_OK)
137 return SPYD2_COMS_FAIL;
138 return SPYD2_OK;
139 }
140
141 /* ----------------------------------------------------- */
142 /* Big endian wire format conversion routines */
143
144 /* Take an int, and convert it into a byte buffer big endian */
int2buf(unsigned char * buf,int inv)145 static void int2buf(unsigned char *buf, int inv) {
146 buf[0] = (unsigned char)(inv >> 24) & 0xff;
147 buf[1] = (unsigned char)(inv >> 16) & 0xff;
148 buf[2] = (unsigned char)(inv >> 8) & 0xff;
149 buf[3] = (unsigned char)(inv >> 0) & 0xff;
150 }
151
152 /* Take a short, and convert it into a byte buffer big endian */
short2buf(unsigned char * buf,int inv)153 static void short2buf(unsigned char *buf, int inv) {
154 buf[0] = (unsigned char)(inv >> 8) & 0xff;
155 buf[1] = (unsigned char)(inv >> 0) & 0xff;
156 }
157
158 /* Take a short sized buffer, and convert it to an int big endian */
buf2short(unsigned char * buf)159 static int buf2short(unsigned char *buf) {
160 int val;
161 val = buf[0];
162 val = ((val << 8) + (0xff & buf[1]));
163 return val;
164 }
165
166 /* Take a unsigned short sized buffer, and convert it to an int big endian */
buf2ushort(unsigned char * buf)167 static int buf2ushort(unsigned char *buf) {
168 int val;
169 val = (0xff & buf[0]);
170 val = ((val << 8) + (0xff & buf[1]));
171 return val;
172 }
173
174 /* Take a unsigned short sized buffer, and convert it to an int little endian */
buf2uleshort(unsigned char * buf)175 static int buf2uleshort(unsigned char *buf) {
176 int val;
177 val = (0xff & buf[1]);
178 val = ((val << 8) + (0xff & buf[0]));
179 return val;
180 }
181
182 /* Take a word sized buffer, and convert it to an int big endian. */
buf2int(unsigned char * buf)183 static int buf2int(unsigned char *buf) {
184 int val;
185 val = buf[0];
186 val = ((val << 8) + (0xff & buf[1]));
187 val = ((val << 8) + (0xff & buf[2]));
188 val = ((val << 8) + (0xff & buf[3]));
189 return val;
190 }
191
192 /* Take a word sized buffer, and convert it to an unsigned int big endian. */
buf2uint(unsigned char * buf)193 static unsigned int buf2uint(unsigned char *buf) {
194 unsigned int val;
195 val = buf[0];
196 val = ((val << 8) + (0xff & buf[1]));
197 val = ((val << 8) + (0xff & buf[2]));
198 val = ((val << 8) + (0xff & buf[3]));
199 return val;
200 }
201
202
203 /* Take a 3 byte buffer, and convert it to an unsigned int big endian. */
buf2uint24(unsigned char * buf)204 static unsigned int buf2uint24(unsigned char *buf) {
205 unsigned int val;
206 val = buf[0];
207 val = ((val << 8) + (0xff & buf[1]));
208 val = ((val << 8) + (0xff & buf[2]));
209 return val;
210 }
211
212 /* Take a 24 bit unsigned sized buffer in little endian */
213 /* format, and return an int */
buf2uint24le(unsigned char * buf)214 static unsigned int buf2uint24le(unsigned char *buf) {
215 unsigned int val;
216 val = (0xff & buf[2]);
217 val = ((val << 8) + (0xff & buf[1]));
218 val = ((val << 8) + (0xff & buf[0]));
219 return val;
220 }
221
222 /* Take a 24 bit unsigned sized buffer in little endian */
223 /* nibble swapped format, and return an int */
buf2uint24lens(unsigned char * buf)224 static unsigned int buf2uint24lens(unsigned char *buf) {
225 unsigned int val;
226 val = (0xf & buf[2]);
227 val = (val << 4) + (0xf & (buf[2] >> 4));
228 val = (val << 4) + (0xf & buf[1]);
229 val = (val << 4) + (0xf & (buf[1] >> 4));
230 val = (val << 4) + (0xf & buf[0]);
231 val = (val << 4) + (0xf & (buf[0] >> 4));
232 return val;
233 }
234
235 /* Take a 64 sized return buffer, and convert it to a ORD64 */
buf2ord64(unsigned char * buf)236 static ORD64 buf2ord64(unsigned char *buf) {
237 ORD64 val;
238 val = buf[7];
239 val = ((val << 8) + (0xff & buf[6]));
240 val = ((val << 8) + (0xff & buf[5]));
241 val = ((val << 8) + (0xff & buf[4]));
242 val = ((val << 8) + (0xff & buf[3]));
243 val = ((val << 8) + (0xff & buf[2]));
244 val = ((val << 8) + (0xff & buf[1]));
245 val = ((val << 8) + (0xff & buf[0]));
246 return val;
247 }
248
249 /* ============================================================ */
250 /* Low level commands */
251
252
253 /* USB Instrument commands */
254
255 /* Spyder 2: Reset the instrument */
256 static inst_code
spyd2_reset(spyd2 * p)257 spyd2_reset(
258 spyd2 *p
259 ) {
260 int se;
261 int retr;
262 inst_code rv = inst_ok;
263
264 a1logd(p->log, 3, "spyd2_reset: called\n");
265
266 for (retr = 0; ; retr++) {
267 se = p->icom->usb_control(p->icom,
268 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
269 0xC7, 0, 0, NULL, 0, 5.0);
270
271 if (se == ICOM_OK) {
272 a1logd(p->log, 6, "spyd2_reset: complete, ICOM code 0x%x\n",se);
273 break;
274 }
275 if (retr >= RETRIES ) {
276 a1logd(p->log, 1, "spyd2_reset: failed with ICOM err 0x%x\n",se);
277 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
278 break;
279 }
280 msec_sleep(500);
281 a1logd(p->log, 1, "spyd2_reset: reset retry with ICOM err 0x%x\n",se);
282 }
283
284 return rv;
285 }
286
287 /* Spyder 2: Get status */
288 /* return pointer may be NULL if not needed. */
289 static inst_code
spyd2_getstatus(spyd2 * p,int * stat)290 spyd2_getstatus(
291 spyd2 *p,
292 int *stat /* Return the 1 byte status code */
293 ) {
294 unsigned char pbuf[8]; /* status bytes read */
295 int _stat;
296 int se;
297 int retr;
298 inst_code rv = inst_ok;
299
300 a1logd(p->log, 3, "spyd2_getstatus: called\n");
301
302 for (retr = 0; ; retr++) {
303 se = p->icom->usb_control(p->icom,
304 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
305 0xC6, 0, 0, pbuf, 8, 5.0);
306
307 if (se == ICOM_OK)
308 break;
309 if (retr >= RETRIES ) {
310 a1logd(p->log, 1, "spyd2_getstatus: failed with ICOM err 0x%x\n",se);
311 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
312 }
313 msec_sleep(500);
314 a1logd(p->log, 1, "spyd2_getstatus: retry with ICOM err 0x%x\n",se);
315 }
316 msec_sleep(100); /* Limit rate status commands can be given */
317
318 _stat = pbuf[0]; /* Only the first byte is examined. */
319 /* Other bytes have information, but SW ignores them */
320
321 a1logd(p->log, 3, "spyd2_getstatus: returns %d ICOM err 0x%x\n", _stat, se);
322
323 if (stat != NULL) *stat = _stat;
324
325 return rv;
326 }
327
328 /* Read Serial EEProm bytes (implementation) */
329 /* Can't read more than 256 in one go */
330 static inst_code
spyd2_readEEProm_imp(spyd2 * p,unsigned char * buf,int addr,int size)331 spyd2_readEEProm_imp(
332 spyd2 *p,
333 unsigned char *buf, /* Buffer to return bytes in */
334 int addr, /* Serial EEprom address, 0 - 1023 */
335 int size /* Number of bytes to read, 0 - 128 (ie. max of bank) */
336 ) {
337 int se;
338 int retr;
339 inst_code rv = inst_ok;
340
341 a1logd(p->log, 3, "spyd2_readEEProm_imp: addr %d, bytes %d\n",addr,size);
342
343 if (addr < 0
344 || (p->hwver < 7 && (addr + size) > 512)
345 || (p->hwver >= 7 && (addr + size) > 1024))
346 return spyd2_interp_code((inst *)p, SPYD2_BAD_EE_ADDRESS);
347
348 if (size >= 256)
349 return spyd2_interp_code((inst *)p, SPYD2_BAD_EE_SIZE);
350
351 for (retr = 0; ; retr++) {
352 se = p->icom->usb_control(p->icom,
353 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
354 0xC4, addr, size, buf, size, 5.0);
355 if (se == ICOM_OK)
356 break;
357 if (retr >= RETRIES) {
358 a1logd(p->log, 1, "spyd2_readEEProm_imp: failed with ICOM err 0x%x\n",se);
359 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
360 }
361 msec_sleep(500);
362 a1logd(p->log, 1, "spyd2_readEEProm_imp: retry with ICOM err 0x%x\n",se);
363 }
364
365
366 a1logd(p->log, 3, "spyd2_readEEProm_imp: returning ICOM err 0x%x\n", se);
367
368 return rv;
369 }
370
371 /* Read Serial EEProm bytes */
372 /* (Handles reads > 256 bytes) */
373 static inst_code
spyd2_readEEProm(spyd2 * p,unsigned char * buf,int addr,int size)374 spyd2_readEEProm(
375 spyd2 *p,
376 unsigned char *buf, /* Buffer to return bytes in */
377 int addr, /* Serial EEprom address, 0 - 511 */
378 int size /* Number of bytes to read, 0 - 511 */
379 ) {
380
381 if (addr < 0
382 || (p->hwver < 7 && (addr + size) > 512)
383 || (p->hwver >= 7 && (addr + size) > 1024))
384 return spyd2_interp_code((inst *)p, SPYD2_BAD_EE_ADDRESS);
385
386 while (size > 255) { /* Single read is too big */
387 inst_code rv;
388 if ((rv = spyd2_readEEProm_imp(p, buf, addr, 255)) != inst_ok)
389 return rv;
390 size -= 255;
391 buf += 255;
392 addr += 255;
393 }
394 return spyd2_readEEProm_imp(p, buf, addr, size);
395 }
396
397 /* Spyder 2: Download PLD pattern */
398 static inst_code
spyd2_loadPLD(spyd2 * p,unsigned char * buf,int size)399 spyd2_loadPLD(
400 spyd2 *p,
401 unsigned char *buf, /* Bytes to download */
402 int size /* Number of bytes */
403 ) {
404 int se;
405 int retr;
406 inst_code rv = inst_ok;
407
408 a1logd(p->log, 6, "spyd2_loadPLD: Load PLD %d bytes\n",size);
409
410 for (retr = 0; ; retr++) {
411 se = p->icom->usb_control(p->icom,
412 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
413 0xC0, 0, 0, buf, size, 5.0);
414
415 if (se == ICOM_OK)
416 break;
417 if (retr >= RETRIES ) {
418 a1logd(p->log, 1, "spyd2_loadPLD: failed with ICOM err 0x%x\n",se);
419 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
420 }
421 msec_sleep(500);
422 a1logd(p->log, 1, "spyd2_loadPLD: retry with ICOM err 0x%x\n",se);
423 }
424
425 a1logd(p->log, 6, "spyd2_loadPLD: returns ICOM err 0x%x\n", se);
426
427 return rv;
428 }
429
430 /* Get minmax command. */
431 /* Figures out the current minimum and maximum frequency periods */
432 /* so as to be able to set a frame detect threshold. */
433 /* Note it returns 0,0 if there is not enough light. */
434 /* (The light to frequency output period size is inversly */
435 /* related to the lightness level) */
436 /* (This isn't used by the manufacturers Spyder3/4 driver, */
437 /* but the instrument seems to impliment it.) */
438 static inst_code
spyd2_GetMinMax(spyd2 * p,int * clocks,int * min,int * max)439 spyd2_GetMinMax(
440 spyd2 *p,
441 int *clocks, /* Number of clocks to use (may get limited) */
442 int *min, /* Return min and max light->frequency periods */
443 int *max
444 ) {
445 int rwbytes; /* Data bytes read or written */
446 int se;
447 inst_code rv = inst_ok;
448 int value;
449 int index;
450 int retr;
451 unsigned char buf[8]; /* return bytes read */
452
453 a1logd(p->log, 2, "spyd2_GetMinMax: %d clocks\n",*clocks);
454
455 /* Issue the triggering command */
456 if (*clocks > 0xffffff)
457 *clocks = 0xffffff; /* Maximum count hardware will take ? */
458 value = *clocks >> 8;
459 value = (value >> 8) | ((value << 8) & 0xff00); /* Convert to big endian */
460 index = (*clocks << 8) & 0xffff;
461 index = (index >> 8) | ((index << 8) & 0xff00); /* Convert to big endian */
462
463 for (retr = 0; ; retr++) {
464 /* Issue the trigger command */
465 se = p->icom->usb_control(p->icom,
466 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
467 0xC2, value, index, NULL, 0, 5.0);
468
469 if ((se != ICOM_OK && retr >= RETRIES)) {
470 /* Complete the operation so as not to leave the instrument in a hung state */
471 msec_sleep(*clocks/MSECDIV);
472 p->icom->usb_read(p->icom, NULL, 0x81, buf, 8, &rwbytes, 1.0);
473
474 a1logd(p->log, 1, "spyd2_GetMinMax: trig failed with ICOM err 0x%x\n",se);
475 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
476 }
477 if (se != ICOM_OK) {
478 /* Complete the operation so as not to leave the instrument in a hung state */
479 msec_sleep(*clocks/MSECDIV);
480 p->icom->usb_read(p->icom, NULL, 0x81, buf, 8, &rwbytes, 1.0);
481
482 msec_sleep(500);
483 a1logd(p->log, 1, "spyd2_GetMinMax: trig retry with ICOM err 0x%x\n",se);
484 continue;
485 }
486
487 a1logd(p->log, 3, "spyd2_GetMinMax: trig returns ICOM err 0x%x\n", se);
488
489 /* Allow some time for the instrument to respond */
490 msec_sleep(*clocks/MSECDIV);
491
492 /* Now read the bytes */
493 se = p->icom->usb_read(p->icom, NULL, 0x81, buf, 8, &rwbytes, 5.0);
494 if (se == ICOM_OK)
495 break;
496 if (retr >= RETRIES) {
497 a1logd(p->log, 1, "spyd2_GetMinMax: get failed with ICOM err 0x%x\n",se);
498 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
499 }
500 msec_sleep(500);
501 a1logd(p->log, 1, "spyd2_GetMinMax: get retry with ICOM err 0x%x\n",se);
502 }
503
504 if (rwbytes != 8) {
505 a1logd(p->log, 1, "spyd2_GetMinMax: got short data read %d",rwbytes);
506 return spyd2_interp_code((inst *)p, SPYD2_BADREADSIZE);
507 }
508
509 *min = buf2ushort(&buf[0]);
510 *max = buf2ushort(&buf[2]);
511
512 a1logd(p->log, 3, "spyd2_GetMinMax: got %d/%d returns ICOM err 0x%x\n", *min, *max, se);
513
514 return rv;
515 }
516
517 /* Get refresh rate (low level) command */
518 /* (This isn't used by the manufacturers Spyder3 driver, */
519 /* but the instrument seems to implement it.) */
520 static inst_code
spyd2_GetRefRate_ll(spyd2 * p,int * clocks,int nframes,int thresh,int * minfclks,int * maxfclks,int * clkcnt)521 spyd2_GetRefRate_ll(
522 spyd2 *p,
523 int *clocks, /* Maximum number of clocks to use */
524 int nframes, /* Number of frames to count */
525 int thresh, /* Frame detection threshold */
526 int *minfclks, /* Minimum number of clocks per frame */
527 int *maxfclks, /* Maximum number of clocks per frame */
528 int *clkcnt /* Return number of clocks for nframes frames */
529 ) {
530 int rwbytes; /* Data bytes read or written */
531 int se;
532 inst_code rv = inst_ok;
533 int value;
534 int index;
535 int flag;
536 int retr;
537 unsigned char buf1[8]; /* send bytes */
538 unsigned char buf2[8]; /* return bytes read */
539
540 a1logd(p->log, 3, "spyd2_GetRefRate_ll: %d clocks\n",*clocks);
541
542 /* Setup the triggering parameters */
543 if (*clocks > 0xffffff) /* Enforce hardware limits */
544 *clocks = 0xffffff;
545 if (*minfclks > 0x7fff)
546 *minfclks = 0x7fff;
547 if (*maxfclks > 0x7fff)
548 *maxfclks = 0x7fff;
549 value = *clocks >> 8;
550 value = (value >> 8) | ((value << 8) & 0xff00); /* Convert to big endian */
551 index = (*clocks << 8) & 0xffff;
552 index = (index >> 8) | ((index << 8) & 0xff00); /* Convert to big endian */
553
554 /* Setup parameters in send buffer */
555 short2buf(&buf1[0], thresh);
556 short2buf(&buf1[2], nframes);
557 short2buf(&buf1[4], *minfclks);
558 short2buf(&buf1[6], *maxfclks);
559
560 /* Issue the triggering command */
561 for (retr = 0; ; retr++) {
562 se = p->icom->usb_control(p->icom,
563 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
564 0xC3, value, index, buf1, 8, 5.0);
565
566 if (se != ICOM_OK && retr >= RETRIES) {
567 /* Complete the operation so as not to leave the instrument in a hung state */
568 msec_sleep(*clocks/MSECDIV);
569 p->icom->usb_read(p->icom, NULL, 0x81, buf2, 8, &rwbytes, 1.0);
570
571 a1logd(p->log, 1, "spyd2_GetRefRate_ll: trig failed with ICOM err 0x%x\n",se);
572 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
573 }
574 if (se != ICOM_OK) {
575 /* Complete the operation so as not to leave the instrument in a hung state */
576 msec_sleep(*clocks/MSECDIV);
577 p->icom->usb_read(p->icom, NULL, 0x81, buf2, 8, &rwbytes, 1.0);
578
579 msec_sleep(500);
580 a1logd(p->log, 1, "spyd2_GetRefRate_ll: trig retry with ICOM err 0x%x\n",se);
581 continue;
582 }
583
584 a1logd(p->log, 3, "spyd2_GetRefRate_ll: trig returns ICOM err 0x%x\n", se);
585
586 /* Allow some time for the instrument to respond */
587 msec_sleep(*clocks/MSECDIV);
588
589 /* Now read the bytes */
590 se = p->icom->usb_read(p->icom, NULL, 0x81, buf2, 8, &rwbytes, 5.0);
591 if (se == ICOM_OK)
592 break;
593 if (retr >= RETRIES) {
594 a1logd(p->log, 3, "spyd2_GetRefRate_ll: get failed with ICOM err 0x%x\n",se);
595 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
596 }
597 msec_sleep(500);
598 a1logd(p->log, 1, "spyd2_GetRefRate_ll: get retry with ICOM err 0x%x\n",se);
599 }
600
601 if (rwbytes != 8) {
602 a1logd(p->log, 1, "spyd2_GetRefRate_ll: got short data read %d",rwbytes);
603 return spyd2_interp_code((inst *)p, SPYD2_BADREADSIZE);
604 }
605
606 flag = buf2[0];
607 *clkcnt = buf2uint24(&buf2[1]);
608
609 /* Spyder2 */
610 if (p->hwver < 4 && flag == 1) {
611 a1logd(p->log, 1, "spyd2_GetRefRate_ll: got trigger timeout");
612 return spyd2_interp_code((inst *)p, SPYD2_TRIGTIMEOUT);
613 }
614
615 /* Spyder2 */
616 if (p->hwver < 4 && flag == 2) {
617 a1logd(p->log, 1, "spyd2_GetRefRate_ll: got overall timeout");
618 return spyd2_interp_code((inst *)p, SPYD2_OVERALLTIMEOUT);
619 }
620
621 a1logd(p->log, 3, "spyd2_GetRefRate_ll: result %d, returns ICOM err 0x%x\n", *clkcnt, se);
622
623 return rv;
624 }
625
626 /* Get a reading (low level) command */
627 static inst_code
spyd2_GetReading_ll(spyd2 * p,int * clocks,int nframes,int thresh,int * minfclks,int * maxfclks,double * sensv,int * maxtcnt,int * mintcnt)628 spyd2_GetReading_ll(
629 spyd2 *p,
630 int *clocks, /* Nominal/Maximum number of integration clocks to use */
631 int nframes, /* Number of refresh frames being measured (not used ?) */
632 int thresh, /* Frame detection threshold */
633 int *minfclks, /* Minimum number of clocks per frame */
634 int *maxfclks, /* Maximum number of clocks per frame */
635 double *sensv, /* Return the 8 sensor readings (may be NULL) */
636 int *maxtcnt, /* Return the maximum transition count (may be NULL) */
637 int *mintcnt /* Return the minimum transition count (may be NULL) */
638 ) {
639 int rwbytes; /* Data bytes read or written */
640 int se;
641 inst_code rv = inst_ok;
642 int value;
643 int index;
644 int flag;
645 int retr;
646 unsigned char buf1[8]; /* send bytes */
647 unsigned char buf2[9 * 8]; /* return bytes read */
648 int rvals[3][8]; /* Raw values */
649 int _maxtcnt = 0; /* Maximum transition count */
650 int _mintcnt = 0x7fffffff; /* Minumum transition count */
651 double maxfreq = 0.0; /* Maximum sensor frequency found */
652 int i, j, k;
653
654 a1logd(p->log, 3, "spyd2_GetReading_ll: clocks = %d, minfc = %d, maxfc = %d\n",*clocks,*minfclks,*maxfclks);
655
656 /* Setup the triggering parameters */
657 if (*clocks > 0xffffff)
658 *clocks = 0xffffff;
659 if (*minfclks > 0x7fff)
660 *minfclks = 0x7fff;
661 if (*maxfclks > 0x7fff)
662 *maxfclks = 0x7fff;
663 value = *clocks >> 8;
664 if (p->hwver == 5) { /* Hmm. not sure if this is right */
665 value /= 1000;
666 }
667 value = (value >> 8) | ((value << 8) & 0xff00); /* Convert to big endian */
668 index = (*clocks << 8) & 0xffff;
669 index = (index >> 8) | ((index << 8) & 0xff00); /* Convert to big endian */
670
671 /* Setup parameters in send buffer */
672 /* (Spyder3 doesn't seem to use these. Perhaps it does its */
673 /* own internal refresh detection and syncronization ?) */
674 thresh *= 256;
675 int2buf(&buf1[0], thresh);
676 short2buf(&buf1[4], *minfclks);
677 short2buf(&buf1[6], *maxfclks);
678
679 /* If we aborted a read, the prevraw values are now invalid. */
680 /* We fix it by doing a dumy reading. */
681 if (p->hwver < 4 && p->prevrawinv) {
682 int clocks = 500;
683 int minfclks = 0;
684 int maxfclks = 0;
685 p->prevrawinv = 0;
686 a1logd(p->log, 3, "spyd2_GetReading_ll: doing dummy read to get prevraw\n");
687 if ((rv = spyd2_GetReading_ll(p, &clocks, 10, 0, &minfclks, &maxfclks, NULL, NULL, NULL)) != inst_ok) {
688 a1logd(p->log, 1, "spyd2_GetReading_ll: dummy read failed\n");
689 p->prevrawinv = 1;
690 return rv;
691 }
692 }
693
694 /* The Spyder comms seems especially flakey... */
695 for (retr = 0; ; retr++) {
696
697 //int start = msec_time();
698
699 /* Issue the triggering command */
700 se = p->icom->usb_control(p->icom,
701 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
702 0xC1, value, index, buf1, 8, 5.0);
703
704 if (se != ICOM_OK && retr >= RETRIES) {
705 /* Complete the operation so as not to leave the instrument in a hung state */
706 msec_sleep(*clocks/MSECDIV);
707 for (i = 0; i < (1+9); i++)
708 p->icom->usb_read(p->icom, NULL, 0x81, buf2, 8, &rwbytes, 1.0);
709 p->prevrawinv = 1; /* prevraw are now invalid */
710
711 a1logd(p->log, 1, "spyd2_GetReading_ll: trig failed with ICOM err 0x%x\n",se);
712 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
713 }
714 if (se != ICOM_OK) {
715 /* Complete the operation so as not to leave the instrument in a hung state */
716 msec_sleep(*clocks/MSECDIV);
717 for (i = 0; i < (1+9); i++)
718 p->icom->usb_read(p->icom, NULL, 0x81, buf2, 8, &rwbytes, 1.0);
719 p->prevrawinv = 1; /* prevraw are now invalid */
720
721 msec_sleep(500);
722 a1logd(p->log, 1, "spyd2_GetReading_ll: trig retry with ICOM err 0x%x\n",se);
723 continue;
724 }
725
726 a1logd(p->log, 3, "spyd2_GetReading_ll: reading returns ICOM code 0x%x\n", se);
727
728 /* Allow some time for the instrument to respond */
729 msec_sleep(*clocks/MSECDIV);
730
731 /* Now read the first 8 bytes (status etc.) */
732 se = p->icom->usb_read(p->icom, NULL, 0x81, buf2, 8, &rwbytes, 5.0);
733 if (se != ICOM_OK && retr >= RETRIES) {
734 a1logd(p->log, 1, "spyd2_GetReading_ll: read stat failed with ICOM err 0x%x\n",se);
735 /* Complete the operation so as not to leave the instrument in a hung state */
736 for (i = 0; i < (1+9); i++)
737 p->icom->usb_read(p->icom, NULL, 0x81, buf2, 8, &rwbytes, 0.5);
738 p->prevrawinv = 1; /* prevraw are now invalid */
739 msec_sleep(500);
740
741 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
742 }
743 //printf("~1 trig -> read = %d msec\n",msec_time() - start);
744
745 if (se != ICOM_OK) {
746 a1logd(p->log, 1, "spyd2_GetReading_ll: read stat retry with ICOM err 0x%x\n", se);
747 /* Complete the operation so as not to leave the instrument in a hung state */
748 for (i = 0; i < (1+9); i++)
749 p->icom->usb_read(p->icom, NULL, 0x81, buf2, 8, &rwbytes, 0.5);
750 p->prevrawinv = 1; /* prevraw are now invalid */
751 msec_sleep(500);
752 continue; /* Retry the whole command */
753 }
754
755 if (rwbytes != 8) {
756 a1logd(p->log, 1, "spyd2_GetReading_ll: read stat got short data read %d",rwbytes);
757 /* Complete the operation so as not to leave the instrument in a hung state */
758 for (i = 0; i < (1+9); i++)
759 p->icom->usb_read(p->icom, NULL, 0x81, buf2, 8, &rwbytes, 0.5);
760 p->prevrawinv = 1; /* prevraw are now invalid */
761 msec_sleep(500);
762
763 return spyd2_interp_code((inst *)p, SPYD2_BADREADSIZE);
764 }
765
766 flag = buf2[0];
767
768 /* Spyder2 */
769 if (p->hwver < 4 && flag == 1) {
770 a1logd(p->log, 1, "spyd2_GetReading_ll: read stat is trigger timeout");
771 return spyd2_interp_code((inst *)p, SPYD2_TRIGTIMEOUT);
772 }
773
774 /* Spyder2 */
775 if (p->hwver < 4 && flag == 2) {
776 a1logd(p->log, 1, "spyd2_GetReading_ll: read stat is overall timeout");
777 return spyd2_interp_code((inst *)p, SPYD2_OVERALLTIMEOUT);
778 }
779
780 /* Now read the following 9 x 8 bytes of sensor data */
781 for (i = 0; i < 9; i++) {
782
783 se = p->icom->usb_read(p->icom, NULL, 0x81, buf2 + i * 8, 8, &rwbytes, 5.0);
784 if (se != ICOM_OK && retr >= RETRIES) {
785 int ii = i;
786 /* Complete the operation so as not to leave the instrument in a hung state */
787 for (; ii < (1+9); ii++)
788 p->icom->usb_read(p->icom, NULL, 0x81, buf2 + i * 8, 8, &rwbytes, 0.5);
789 p->prevrawinv = 1; /* prevraw are now invalid */
790
791 a1logd(p->log, 1, "spyd2_GetReading_ll: get reading failed with ICOM err 0x%x\n",se);
792 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
793 }
794 if (se != ICOM_OK) {
795 int ii = i;
796 /* Complete the operation so as not to leave the instrument in a hung state */
797 for (; ii < (1+9); ii++)
798 p->icom->usb_read(p->icom, NULL, 0x81, buf2 + i * 8, 8, &rwbytes, 0.5);
799 p->prevrawinv = 1; /* prevraw are now invalid */
800
801 break;
802 }
803 if (rwbytes != 8) {
804 int ii = i;
805 a1logd(p->log, 1, "spyd2_GetReading_ll: got short data read %d",rwbytes);
806
807 /* Complete the operation so as not to leave the instrument in a hung state */
808 for (; ii < (1+9); ii++)
809 p->icom->usb_read(p->icom, NULL, 0x81, buf2 + i * 8, 8, &rwbytes, 0.5);
810 p->prevrawinv = 1; /* prevraw are now invalid */
811
812 return spyd2_interp_code((inst *)p, SPYD2_BADREADSIZE);
813 }
814 }
815
816 if (i >= 9)
817 break; /* We're done */
818
819 msec_sleep(500);
820 a1logd(p->log, 1, "spyd2_GetReading_ll: reading retry with ICOM err 0x%x\n",se);
821
822 #ifdef DO_RESETEP /* Do the miscelanous resetep()'s every second time */
823 if ((retr & 1) == 0) {
824 a1logd(p->log, 1, "spyd2_GetReading_ll: resetting end point\n");
825 p->icom->usb_resetep(p->icom, 0x81);
826 msec_sleep(1); /* Let device recover ? */
827 }
828 #endif /* DO_RESETEP */
829 } /* End of whole command retries */
830
831 if (p->log->debug >= 5) {
832 a1logd(p->log, 5, "spyd2_GetReading_ll: got bytes:\n");
833 for (i = 0; i < 9; i++) {
834 a1logd(p->log, 5, " %d: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",i * 8,
835 buf2[i * 8 + 0], buf2[i * 8 + 1], buf2[i * 8 + 2], buf2[i * 8 + 3],
836 buf2[i * 8 + 4], buf2[i * 8 + 5], buf2[i * 8 + 6], buf2[i * 8 + 7]);
837 }
838 }
839
840 /* Spyder 2 decoding */
841 /* Spyder2 */
842 if (p->hwver < 4) {
843 /* Convert the raw buffer readings into 3 groups of 8 integers. */
844 /* At the start of each reading, the HW starts counting master */
845 /* (1MHz) clocks. When the first transition after the start of */
846 /* the reading is received from a light->frequency sensor (TAOS TSL237), */
847 /* the clock count is recorded, and returned as the second of the three */
848 /* numbers returned for each sensor. */
849 /* When the last transition before the end of the reading period is */
850 /* received, the clock count is recorded, and returned as the first */
851 /* of the three numbers. The integration period is therefore */
852 /* the first number minus the second. */
853 /* The third number is the number of transitions from the sensor */
854 /* counted during the integration period. Since this 24 bit counter is */
855 /* not reset between readings, the previous count is recorded (prevraw[]), */
856 /* and subtracted (modulo 24 bits) from the current value. */
857 /* The light level is directly proportional to the frequency, */
858 /* hence the transitions-1 counted. */
859 /* In the case of a CRT, the total number of clocks is assumed to be */
860 /* set to an integer number of refresh cycles, and the total transitions */
861 /* over that period are counted. */
862 for (i = j = 0; j < 3; j++) {
863 for (k = 0; k < 8; k++, i += 3) {
864 rvals[j][k] = buf2uint24lens(buf2 + i);
865 // a1logd(p->log, 1, "got rvals[%d][%d] = 0x%x\n",j,k,rvals[j][k]);
866 }
867 }
868
869 /* And convert 3 values per sensor into sensor values */
870 for (k = 0; k < 8; k++) {
871 int transcnt, intclks;
872
873 /* Compute difference of L2F count to previous value */
874 /* read, modulo 24 bits */
875 a1logd(p->log, 5, "%d: transcnt %d, previous %d\n", k,rvals[2][k],p->prevraw[k]);
876 if (p->prevraw[k] <= rvals[2][k]) {
877 transcnt = rvals[2][k] - p->prevraw[k];
878 } else {
879 transcnt = rvals[2][k] + 0x1000000 - p->prevraw[k];
880 }
881
882 p->prevraw[k] = rvals[2][k]; /* New previuos value */
883
884 /* Compute difference of 1MHz clock count of first to second value */
885 intclks = rvals[0][k] - rvals[1][k];
886
887 if (transcnt == 0 || intclks <= 0) { /* It's too dark ... */
888 if (sensv != NULL)
889 sensv[k] = 0.0;
890 _mintcnt = 0;
891 } else { /* We discard 0'th L2F transion to give transitions */
892 /* over the time period. */
893 if (sensv != NULL)
894 sensv[k] = ((double)transcnt - 1.0) * (double)CLKRATE / (double)intclks;
895 if (transcnt > _maxtcnt)
896 _maxtcnt = transcnt;
897 if (transcnt < _mintcnt)
898 _mintcnt = transcnt;
899 }
900 if (sensv != NULL && sensv[k] > maxfreq)
901 maxfreq = sensv[k];
902 if (p->log->debug >= 4 && sensv != NULL)
903 a1logd(p->log, 4, "%d: initial senv %f from transcnt %d and intclls %d\n",
904 k,sensv[k],transcnt,intclks);
905
906 #ifdef NEVER /* This seems to make repeatability worse ??? */
907 /* If CRT and bright enough */
908 if (sensv != NULL && sensv[k] > 1.5 && p->refrmode != 0) {
909 sensv[k] = ((double)transcnt) * (double)p->refrate/(double)nframes;
910 }
911 #endif
912 }
913
914 /* Spyder 3/4/5 decoding */
915 } else {
916 /* Convert the raw buffer readings into 3 groups of 8 integers. */
917 /* At the start of each reading, the HW starts counting master */
918 /* (1MHz) clocks. When the first transition after the start of */
919 /* the reading is received from a light->frequency sensor (TAOS TSL238T), */
920 /* the clock count is recorded, and returned as the second of the three */
921 /* numbers returned for each sensor. */
922 /* When the last transition before the end of the reading period is */
923 /* received, the clock count is recorded, and returned as the first */
924 /* of the three numbers. The integration period is therefore */
925 /* the first number minus the second. */
926 /* The third number is the number of transitions from the sensor */
927 /* counted during the integration period. */
928 /* The light level is directly proportional to the frequency, */
929 /* hence the transitions-1 counted. */
930
931 int *map;
932 // int nat[8] = { 0,1,2,3,4,5,6,7 }; /* Natural order */
933 int map3[8] = { 0,0,1,2,5,6,7,4 }; /* Map Sp3 sensors into Spyder 2 order */
934 int map4[8] = { 0,0,1,2,5,6,7,4 }; /* Map Sp4 sensors into Spyder 2 order */
935 int map5[8] = { 1,1,0,5,2,7,6,4 }; /* Map Sp5 sensors into Spyder 2 order */
936
937 map = map3;
938 if (p->hwver == 7)
939 map = map4;
940 else if (p->hwver == 10)
941 map = map5;
942
943 for (j = 0; j < 3; j++) {
944 for (k = 0; k < 8; k++) {
945 rvals[j][k] = buf2uint24le(buf2 + (j * 24 + map[k] * 3));
946 // a1logd(p->log, 1, "got rvals[%d][%d] = 0x%x\n",j,k,rvals[j][k]);
947 }
948 }
949
950 /* And convert 3 integers per sensor into sensor values */
951 for (k = 0; k < 8; k++) {
952 int transcnt, intclks;
953
954 /* Number of sensor transitions */
955 transcnt = rvals[2][k];
956
957 /* Compute difference of first integer to second */
958 intclks = rvals[0][k] - rvals[1][k];
959
960 if (transcnt == 0 || intclks <= 0) { /* It's too dark ... */
961 if (sensv != NULL)
962 sensv[k] = 0.0;
963 _mintcnt = 0;
964 } else { /* Transitions within integration period */
965 /* hence one is discarded ? */
966 if (sensv != NULL)
967 sensv[k] = ((double)transcnt - 1.0) * (double)CLKRATE
968 / ((double)intclks * 8.125);
969 if (transcnt > _maxtcnt)
970 _maxtcnt = transcnt;
971 if (transcnt < _mintcnt)
972 _mintcnt = transcnt;
973 }
974 if (sensv != NULL && (8.125 * sensv[k]) > maxfreq)
975 maxfreq = 8.125 * sensv[k];
976 if (p->log->debug >= 4 && sensv != NULL)
977 a1logd(p->log, 4, "%d: initial senv %f from transcnt %d and intclls %d\n",
978 k,sensv[k],transcnt,intclks);
979 }
980 }
981
982 if (maxtcnt != NULL)
983 *maxtcnt = _maxtcnt;
984 if (mintcnt != NULL)
985 *mintcnt = _mintcnt;
986
987 if (p->log->debug >= 4 && sensv != NULL)
988 a1logd(p->log, 4, "Maximum sensor frequency = %f\n",maxfreq);
989
990 /* Problem is that the HW starts loosing count above a certain */
991 /* frequency, so we depend on one less bright sensor acting as a canary, */
992 /* so we can't make the threshold too low. */
993 if (maxfreq > 500000.0) {
994 return spyd2_interp_code((inst *)p, SPYD2_TOOBRIGHT);
995 }
996
997 return rv;
998 }
999
1000 /* Spyder 3: Set the LED */
1001 static inst_code
spyd2_setLED(spyd2 * p,int mode,double period)1002 spyd2_setLED(
1003 spyd2 *p,
1004 int mode, /* LED mode: 0 = off, 1 = pulse, 2 = on */
1005 double period /* Pulse period in seconds */
1006 ) {
1007 int se;
1008 inst_code rv = inst_ok;
1009 int value;
1010 int index;
1011 int retr;
1012 int ptime; /* Pulse time, 1 - 255 x 20 msec */
1013
1014 if (mode < 0)
1015 mode = 0;
1016 else if (mode > 2)
1017 mode = 2;
1018 ptime = (int)(period/0.02 + 0.5);
1019 if (ptime < 0)
1020 ptime = 0;
1021 else if (ptime > 255)
1022 ptime = 255;
1023
1024 if (p->log->debug >= 2) {
1025 if (mode == 1)
1026 a1logd(p->log, 3, "spyd2_setLED: set to pulse, %f secs\n",ptime * 0.02);
1027 else
1028 a1logd(p->log, 3, "spyd2_setLED: set to %s\n",mode == 0 ? "off" : "on");
1029 }
1030
1031 value = mode; /* Leave little endian */
1032 index = ptime;
1033
1034 for (retr = 0; ; retr++) {
1035 /* Issue the trigger command */
1036 se = p->icom->usb_control(p->icom,
1037 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
1038 0xF6, value, index, NULL, 0, 5.0);
1039
1040 if (se == ICOM_OK) {
1041 a1logd(p->log, 5, "spyd2_setLED: OK, ICOM code 0x%x\n",se);
1042 break;
1043 }
1044 if (retr >= RETRIES) {
1045 a1logd(p->log, 1, "spyd2_setLED: failed with ICOM err 0x%x\n",se);
1046 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
1047 }
1048 msec_sleep(500);
1049 a1logd(p->log, 1, "spyd2_setLED: retry with ICOM err 0x%x\n",se);
1050 }
1051
1052 return rv;
1053 }
1054
1055
1056 /* Spyder 3: Set the ambient control register */
1057 static inst_code
spyd2_SetAmbReg(spyd2 * p,int val)1058 spyd2_SetAmbReg(
1059 spyd2 *p,
1060 int val /* 8 bit ambient config register value */
1061 ) {
1062 int se;
1063 inst_code rv = inst_ok;
1064 int value;
1065 int retr;
1066
1067 a1logd(p->log, 3, "spyd2_SetAmbReg: control register to %d\n",val);
1068
1069 if (val < 0)
1070 val = 0;
1071 else if (val > 255)
1072 val = 255;
1073 value = val; /* Leave little endian */
1074
1075 for (retr = 0; ; retr++) {
1076 /* Issue the trigger command */
1077 se = p->icom->usb_control(p->icom,
1078 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
1079 0xF3, value, 0, NULL, 0, 5.0);
1080
1081 if (se == ICOM_OK) {
1082 a1logd(p->log, 5, "spyd2_SetAmbReg: OK, ICOM code 0x%x\n",se);
1083 break;
1084 }
1085 if (retr >= RETRIES) {
1086 a1logd(p->log, 1, "spyd2_SetAmbReg: failed with ICOM err 0x%x\n",se);
1087 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
1088 }
1089 msec_sleep(500);
1090 a1logd(p->log, 1, "spyd2_SetAmbReg: retry with ICOM err 0x%x\n",se);
1091 }
1092
1093 return rv;
1094 }
1095
1096 /* Spyder3/4/5: Read ambient light timing */
1097 /* The byte value seems to be composed of:
1098 bits 0,1
1099 bits 4
1100 */
1101 static inst_code
spyd2_ReadAmbTiming(spyd2 * p,int * val)1102 spyd2_ReadAmbTiming(
1103 spyd2 *p,
1104 int *val /* Return the 8 bit timing value */
1105 ) {
1106 unsigned char pbuf[1]; /* Timing value read */
1107 int _val;
1108 int se;
1109 int retr;
1110 inst_code rv = inst_ok;
1111
1112 a1logd(p->log, 3, "spyd2_ReadAmbTiming: called\n");
1113
1114 for (retr = 0; ; retr++) {
1115 se = p->icom->usb_control(p->icom,
1116 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
1117 0xF4, 0, 0, pbuf, 1, 5.0);
1118
1119 if (se == ICOM_OK)
1120 break;
1121 if (retr >= RETRIES) {
1122 a1logd(p->log, 1, "spyd2_ReadAmbTiming: failed with ICOM err 0x%x\n",se);
1123 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
1124 }
1125 msec_sleep(500);
1126 a1logd(p->log, 1, "spyd2_ReadAmbTiming: retry with ICOM err 0x%x\n",se);
1127 }
1128
1129 _val = pbuf[0];
1130
1131 a1logd(p->log, 5, "spyd2_ReadAmbTiming: returning val %d ICOM err 0x%x\n",_val, se);
1132
1133 if (val != NULL) *val = _val;
1134
1135 return rv;
1136 }
1137
1138
1139 /* Spyder3/4/5: Read ambient light channel 0 or 1 */
1140 static inst_code
spyd2_ReadAmbChan(spyd2 * p,int chan,int * val)1141 spyd2_ReadAmbChan(
1142 spyd2 *p,
1143 int chan, /* Ambient channel, 0 or 1 */
1144 int *val /* Return the 16 bit channel value */
1145 ) {
1146 unsigned char pbuf[2]; /* Channel value read */
1147 int _val;
1148 int se;
1149 int retr;
1150 inst_code rv = inst_ok;
1151
1152 chan &= 1;
1153
1154 a1logd(p->log, 3, "spyd2_ReadAmbChan: channel %d\n",chan);
1155
1156 for (retr = 0; ; retr++) {
1157 se = p->icom->usb_control(p->icom,
1158 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
1159 0xF0 + chan, 0, 0, pbuf, 2, 5.0);
1160
1161 if (se == ICOM_OK)
1162 break;
1163 if (retr >= RETRIES) {
1164 a1logd(p->log, 2, "spyd2_ReadAmbChan: failed with ICOM err 0x%x\n",se);
1165 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
1166 }
1167 msec_sleep(500);
1168 a1logd(p->log, 2, "spyd2_ReadAmbChan: retry with ICOM err 0x%x\n",se);
1169 }
1170
1171 _val = buf2ushort(pbuf);
1172
1173 a1logd(p->log, 3, "spyd2_ReadAmbChan: chan %d returning %d ICOM err 0x%x\n", chan, _val, se);
1174
1175 if (val != NULL) *val = _val;
1176
1177 return rv;
1178 }
1179
1180 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1181
1182 /* Spyder3/4/5: Read temperature config */
1183 static inst_code
spyd2_ReadTempConfig(spyd2 * p,int * val)1184 spyd2_ReadTempConfig(
1185 spyd2 *p,
1186 int *val /* Return the 8 bit config value */
1187 ) {
1188 unsigned char pbuf[1]; /* Config value read */
1189 int _val;
1190 int se;
1191 int retr;
1192 inst_code rv = inst_ok;
1193
1194 a1logd(p->log, 3, "spyd2_ReadTempConfig: called\n");
1195
1196 for (retr = 0; ; retr++) {
1197 se = p->icom->usb_control(p->icom,
1198 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
1199 0xE1, 0, 0, pbuf, 1, 5.0);
1200
1201 if (se == ICOM_OK)
1202 break;
1203 if (retr >= RETRIES) {
1204 a1logd(p->log, 1, "spyd2_ReadTempConfig: failed with ICOM err 0x%x\n",se);
1205 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
1206 }
1207 msec_sleep(500);
1208 a1logd(p->log, 1, "spyd2_ReadTempConfig: retry with ICOM err 0x%x\n",se);
1209 }
1210
1211 _val = pbuf[0];
1212
1213 a1logd(p->log, 3, "spyd2_ReadTempConfig: returning %d ICOM err 0x%x\n", _val, se);
1214
1215 if (val != NULL) *val = _val;
1216
1217 return rv;
1218 }
1219
1220 /* Spyder 3/4/5: Write Register */
1221 static inst_code
spyd2_WriteReg(spyd2 * p,int reg,int val)1222 spyd2_WriteReg(
1223 spyd2 *p,
1224 int reg,
1225 int val /* 8 bit temp config register value */
1226 ) {
1227 int se;
1228 inst_code rv = inst_ok;
1229 int value;
1230 int retr;
1231
1232 a1logd(p->log, 3, "spyd2_WriteReg: val %d to register %d\n",reg, val);
1233
1234 if (val < 0)
1235 val = 0;
1236 else if (val > 255)
1237 val = 255;
1238 value = val; /* Leave little endian */
1239
1240 reg &= 0xff;
1241 value |= (reg << 8);
1242
1243 for (retr = 0; ; retr++) {
1244 /* Issue the trigger command */
1245 se = p->icom->usb_control(p->icom,
1246 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
1247 0xE2, value, 0, NULL, 0, 5.0);
1248
1249 if (se == ICOM_OK) {
1250 a1logd(p->log, 5, "spyd2_WriteReg: OK, ICOM code 0x%x\n",se);
1251 break;
1252 }
1253 if (retr >= RETRIES) {
1254 a1logd(p->log, 5, "spyd2_WriteReg: failed with ICOM err 0x%x\n",se);
1255 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
1256 }
1257 msec_sleep(500);
1258 a1logd(p->log, 5, "spyd2_WriteReg: retry with ICOM err 0x%x\n",se);
1259 }
1260
1261 return rv;
1262 }
1263
1264
1265 /* Spyder3/4/5: Read Register */
1266 static inst_code
spyd2_ReadRegister(spyd2 * p,int reg,int * pval)1267 spyd2_ReadRegister(
1268 spyd2 *p,
1269 int reg,
1270 int *pval /* Return the register value */
1271 ) {
1272 unsigned char pbuf[2]; /* Temp value read */
1273 int ival;
1274 // double _val;
1275 int se;
1276 int retr;
1277 inst_code rv = inst_ok;
1278
1279 reg &= 0xff;
1280
1281 a1logd(p->log, 3, "spyd2_ReadRegister: register %d\n",reg);
1282
1283 for (retr = 0; ; retr++) {
1284 se = p->icom->usb_control(p->icom,
1285 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
1286 0xE0, reg, 0, pbuf, 2, 5.0);
1287
1288 if (se == ICOM_OK)
1289 break;
1290 if (retr >= RETRIES) {
1291 a1logd(p->log, 1, "spyd2_ReadRegister: failed with ICOM err 0x%x\n",se);
1292 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
1293 }
1294 msec_sleep(500);
1295 a1logd(p->log, 1, "spyd2_ReadRegister: retry with ICOM err 0x%x\n",se);
1296 }
1297
1298 ival = buf2ushort(&pbuf[0]);
1299 // _val = (double)ival * 12.5; /* Read temperature */
1300
1301 a1logd(p->log, 1, "spyd2_ReadRegister: reg %d returning %d ICOM err 0x%x\n", ival, rv, se);
1302
1303 if (pval != NULL) *pval = ival;
1304
1305 return rv;
1306 }
1307
1308
1309 /* hwver == 5, set gain */
1310 /* Valid gain values 1, 4, 16, 64 */
1311 static inst_code
spyd2_SetGain(spyd2 * p,int gain)1312 spyd2_SetGain(
1313 spyd2 *p,
1314 int gain
1315 ) {
1316 int gv = 0;
1317
1318 p->gain = (double)gain;
1319
1320 switch (gain) {
1321 case 1:
1322 gv = 0;
1323 break;
1324 case 4:
1325 gv = 16;
1326 break;
1327 case 16:
1328 gv = 32;
1329 break;
1330 case 64:
1331 gv = 48;
1332 break;
1333 }
1334 return spyd2_WriteReg(p, 7, gv);
1335 }
1336
1337 /* ============================================================ */
1338 /* Medium level commands */
1339
1340 /* Read a 8 bits from the EEProm */
1341 static inst_code
spyd2_rd_ee_uchar(spyd2 * p,unsigned int * outp,int addr)1342 spyd2_rd_ee_uchar(
1343 spyd2 *p, /* Object */
1344 unsigned int *outp, /* Where to write value */
1345 int addr /* EEprom Address, 0 - 510 */
1346 ) {
1347 inst_code ev;
1348 unsigned char buf[1];
1349
1350 if ((ev = spyd2_readEEProm(p, buf, addr, 1)) != inst_ok)
1351 return ev;
1352
1353 *outp = buf[0];
1354
1355 return inst_ok;
1356 }
1357
1358 /* Read a 16 bit word from the EEProm */
1359 static inst_code
spyd2_rd_ee_ushort(spyd2 * p,unsigned int * outp,int addr)1360 spyd2_rd_ee_ushort(
1361 spyd2 *p, /* Object */
1362 unsigned int *outp, /* Where to write value */
1363 int addr /* EEprom Address, 0 - 510 */
1364 ) {
1365 inst_code ev;
1366 unsigned char buf[2];
1367
1368 if ((ev = spyd2_readEEProm(p, buf, addr, 2)) != inst_ok)
1369 return ev;
1370
1371 *outp = buf2ushort(buf);
1372
1373 return inst_ok;
1374 }
1375
1376 /* Read a 32 bit word from the EEProm */
1377 static inst_code
spyd2_rd_ee_int(spyd2 * p,int * outp,int addr)1378 spyd2_rd_ee_int(
1379 spyd2 *p, /* Object */
1380 int *outp, /* Where to write value */
1381 int addr /* EEprom Address, 0 - 508 */
1382 ) {
1383 inst_code ev;
1384 unsigned char buf[4];
1385
1386 if ((ev = spyd2_readEEProm(p, buf, addr, 4)) != inst_ok)
1387 return ev;
1388
1389 *outp = buf2int(buf);
1390
1391 return inst_ok;
1392 }
1393
1394 /* Read a float from the EEProm */
1395 static inst_code
spyd2_rdreg_float(spyd2 * p,double * outp,int addr)1396 spyd2_rdreg_float(
1397 spyd2 *p, /* Object */
1398 double *outp, /* Where to write value */
1399 int addr /* Register Address, 0 - 508 */
1400 ) {
1401 inst_code ev;
1402 int val;
1403
1404 if ((ev = spyd2_rd_ee_int(p, &val, addr)) != inst_ok)
1405 return ev;
1406
1407 *outp = IEEE754todouble((unsigned int)val);
1408 return inst_ok;
1409 }
1410
1411 unsigned int spyd4_crctab[256];
1412
spyd4_crc32_init(void)1413 static void spyd4_crc32_init(void) {
1414 int i, j;
1415
1416 unsigned int crc;
1417
1418 for (i = 0; i < 256; i++) {
1419 crc = i;
1420 for (j = 0; j < 8; j++) {
1421 if (crc & 1)
1422 crc = (crc >> 1) ^ 0xedb88320;
1423 else
1424 crc = crc >> 1;
1425 }
1426 spyd4_crctab[i] = crc;
1427 // a1logd(p->log, 1, "spyd4_crc32_init: crctab[%d] = 0x%08x\n",i,crctab[i]);
1428 }
1429 }
1430
spyd4_crc32(unsigned char * data,int len)1431 static unsigned int spyd4_crc32(unsigned char *data, int len) {
1432 unsigned int crc;
1433 int i;
1434
1435 crc = ~0;
1436 for (i = 0; i < len; i++)
1437 crc = spyd4_crctab[(crc ^ *data++) & 0xff] ^ (crc >> 8);
1438 return ~crc;
1439 }
1440
1441 /* For HWV 7, check the EEPRom CRC */
1442 static inst_code
spyd2_checkEECRC(spyd2 * p)1443 spyd2_checkEECRC(
1444 spyd2 *p /* Object */
1445 ) {
1446 inst_code ev;
1447 unsigned char buf[1024], *bp;
1448 unsigned int crct, crc; /* Target value, computed value */
1449 int i;
1450
1451 spyd4_crc32_init();
1452
1453 if ((ev = spyd2_readEEProm(p, buf, 0, 1024)) != inst_ok)
1454 return ev;
1455
1456 /* Target value */
1457 crct = buf2uint(buf + 1024 - 4);
1458
1459 bp = buf;
1460 crc = ~0;
1461 for (i = 0; i < (1024 - 4); i++, bp++)
1462 crc = spyd4_crctab[(crc ^ *bp) & 0xff] ^ (crc >> 8);
1463 crc = ~crc;
1464
1465 a1logd(p->log, 4, "spyd2_checkEECRC: EEProm CRC is 0x%x, should be 0x%x\n",crc,crct);
1466
1467 if (crc != crct)
1468 return spyd2_interp_code((inst *)p, SPYD2_BAD_EE_CRC);
1469
1470 return inst_ok;
1471 }
1472
1473 /* Special purpose float read, */
1474 /* Read three 9 vectors of floats from the EEprom */
1475 static inst_code
spyd2_rdreg_3x9xfloat(spyd2 * p,double * out0,double * out1,double * out2,int addr)1476 spyd2_rdreg_3x9xfloat(
1477 spyd2 *p, /* Object */
1478 double *out0, /* Where to write first 9 doubles */
1479 double *out1, /* Where to write second 9 doubles */
1480 double *out2, /* Where to write third 9 doubles */
1481 int addr /* Register Address, 0 - 1023 */
1482 ) {
1483 inst_code ev;
1484 unsigned char buf[3 * 9 * 4], *bp;
1485 int i;
1486
1487 if ((ev = spyd2_readEEProm(p, buf, addr, 3 * 9 * 4)) != inst_ok)
1488 return ev;
1489
1490 bp = buf;
1491 for (i = 0; i < 9; i++, bp +=4, out0++) {
1492 int val;
1493 val = buf2int(bp);
1494 *out0 = IEEE754todouble((unsigned int)val);
1495 }
1496
1497 for (i = 0; i < 9; i++, bp +=4, out1++) {
1498 int val;
1499 val = buf2int(bp);
1500 *out1 = IEEE754todouble((unsigned int)val);
1501 }
1502
1503 for (i = 0; i < 9; i++, bp +=4, out2++) {
1504 int val;
1505 val = buf2int(bp);
1506 *out2 = IEEE754todouble((unsigned int)val);
1507 }
1508
1509 return inst_ok;
1510 }
1511
1512 /* Special purpose short read, */
1513 /* Read 7 x 41 vectors of ints from the EEprom */
1514 static inst_code
spyd2_rdreg_7x41xshort(spyd2 * p,double sens[7][41],int addr)1515 spyd2_rdreg_7x41xshort(
1516 spyd2 *p, /* Object */
1517 double sens[7][41], /* Destination */
1518 int addr /* Register Address, 0 - 1023 */
1519 ) {
1520 inst_code ev;
1521 unsigned char buf[7 * 41 * 2], *bp;
1522 int i, j;
1523
1524 if ((ev = spyd2_readEEProm(p, buf, addr, 7 * 41 * 2)) != inst_ok)
1525 return ev;
1526
1527 bp = buf;
1528 for (i = 0; i < 7; i++) {
1529 for (j = 0; j < 41; j++, bp += 2) {
1530 int val;
1531 val = buf2ushort(bp);
1532 sens[i][j] = val / 100.0;
1533 }
1534 }
1535
1536 return inst_ok;
1537 }
1538
1539 /* Special purpose LE short read, */
1540 /* Read 7 x 41 vectors of Little Endian ints from the EEprom */
1541 static inst_code
spyd2_rdreg_7x41xleshort(spyd2 * p,double sens[7][41],int addr)1542 spyd2_rdreg_7x41xleshort(
1543 spyd2 *p, /* Object */
1544 double sens[7][41], /* Destination */
1545 int addr /* Register Address, 0 - 1023 */
1546 ) {
1547 inst_code ev;
1548 unsigned char buf[7 * 41 * 2], *bp;
1549 int i, j;
1550
1551 if ((ev = spyd2_readEEProm(p, buf, addr, 7 * 41 * 2)) != inst_ok)
1552 return ev;
1553
1554 bp = buf;
1555 for (i = 0; i < 7; i++) {
1556 for (j = 0; j < 41; j++, bp += 2) {
1557 int val;
1558 val = buf2uleshort(bp);
1559 sens[i][j] = val / 100.0;
1560 }
1561 }
1562
1563 return inst_ok;
1564 }
1565
1566 /* Get refresh rate command. Set it to 0.0 if not detectable */
1567 /* if no refresh rate can be established */
1568 /* (This isn't used by the manufacturers Spyder3/4 driver, */
1569 /* but the instrument seems to impliment it.) */
1570 static inst_code
spyd2_read_refrate(inst * pp,double * ref_rate)1571 spyd2_read_refrate(
1572 inst *pp,
1573 double *ref_rate
1574 ) {
1575 spyd2 *p = (spyd2 *)pp;
1576 inst_code ev;
1577 int clocks; /* Clocks to run commands */
1578 int min, max; /* min and max light intensity frequency periods */
1579
1580 a1logd(p->log, 3, "spyd2_read_refrate: called\n");
1581
1582 if (ref_rate != NULL)
1583 *ref_rate = 0.0;
1584
1585 /* Establish the frame rate detect threshold level */
1586 clocks = (10 * CLKRATE)/DEFRRATE;
1587
1588 if ((ev = spyd2_GetMinMax(p, &clocks, &min, &max)) != inst_ok)
1589 return ev;
1590
1591 if (min == 0 || max < (5 * min)) {
1592 a1logd(p->log, 3, "spyd2_read_refrate: no refresh rate detectable\n");
1593 if (ref_rate != NULL)
1594 *ref_rate = 0.0;
1595 return inst_misread | SPYD2_NO_REFRESH_DET;
1596 } else {
1597 int frclocks; /* notional clocks per frame */
1598 int nframes; /* Number of frames to count */
1599 int thresh; /* Frame detection threshold */
1600 int minfclks; /* Minimum number of clocks per frame */
1601 int maxfclks; /* Maximum number of clocks per frame */
1602 int clkcnt; /* Return number of clocks for nframes frames */
1603
1604 frclocks = CLKRATE/DEFRRATE;
1605 nframes = 50;
1606 thresh = (max - min)/5 + min; /* Threshold is at 80% of max brightness */
1607 minfclks = frclocks/3; /* Allow for 180 Hz */
1608 maxfclks = (frclocks * 5)/2; /* Allow for 24 Hz */
1609 clocks = nframes * frclocks * 2; /* Allow for 120 Hz */
1610
1611 if ((ev = spyd2_GetRefRate_ll(p, &clocks, nframes, thresh, &minfclks, &maxfclks,
1612 &clkcnt)) != inst_ok)
1613 return ev;
1614
1615 /* Compute the refresh rate */
1616 if (ref_rate != NULL)
1617 *ref_rate = ((double)nframes * (double)CLKRATE)/(double)clkcnt;
1618 return inst_ok;
1619 }
1620 }
1621
1622 /* Get refresh rate command. Set it to DEFRRATE if not detectable */
1623 /* if no refresh rate can be established */
1624 /* (This isn't used by the manufacturers Spyder3/4 driver, */
1625 /* but the instrument seems to impliment it.) */
1626 static inst_code
spyd2_GetRefRate(spyd2 * p)1627 spyd2_GetRefRate(
1628 spyd2 *p
1629 ) {
1630 int i;
1631 inst_code ev;
1632
1633 a1logd(p->log, 3, "Frequency calibration called\n");
1634
1635 if ((ev = spyd2_read_refrate((inst *)p, &p->refrate)) != inst_ok) {
1636 p->refrate = DEFRRATE;
1637 p->refrvalid = 0;
1638 return ev;
1639 }
1640 if (p->refrate != 0.0) {
1641 a1logd(p->log, 3, "spyd2_GetRefRate: refresh rate is %f Hz\n",p->refrate);
1642 p->refrvalid = 1;
1643 } else {
1644 a1logd(p->log, 3, "spyd2_GetRefRate: no refresh rate detectable\n");
1645 p->refrate = DEFRRATE;
1646 p->refrvalid = 0;
1647 }
1648 p->rrset = 1;
1649
1650 return inst_ok;
1651 }
1652
1653 /* Do a reading. */
1654 /* Note that the Spyder 3 seems to give USB errors on the data */
1655 /* read if the measurement time is too small (ie. 0.2 seconds) */
1656 /* when reading dark values. */
1657 static inst_code
spyd2_GetReading(spyd2 * p,double * XYZ)1658 spyd2_GetReading(
1659 spyd2 *p,
1660 double *XYZ /* return the XYZ values */
1661 ) {
1662 inst_code ev;
1663 int clocks1, clocks2; /* Clocks to run commands */
1664 int min, max; /* min and max light intensity frequency periods */
1665 int frclocks; /* notional clocks per frame */
1666 int nframes; /* Number of frames to measure over */
1667 int thresh; /* Frame detection threshold */
1668 int minfclks; /* Minimum number of clocks per frame */
1669 int maxfclks; /* Maximum number of clocks per frame */
1670 double sensv[8]; /* The 8 final sensor value readings */
1671 int maxtcnt; /* The maximum transition count measured */
1672 int mintcnt; /* The minumum transition count measured */
1673 double a_sensv[8]; /* Accumulated sensor value readings */
1674 double a_w[8]; /* Accumulated sensor value weight */
1675 double pows[9]; /* Power combinations of initial XYZ */
1676 int i, j, k;
1677 double inttime = 0.0;
1678
1679 a1logd(p->log, 3, "spyd2_GetReading: called\n");
1680
1681 if (p->refrmode != 0 && p->rrset != 0)
1682 inttime = RINTTIME; /* ie. 1 second */
1683 else
1684 inttime = NINTTIME; /* ie. 1 second */
1685
1686 /* Compute number of frames for desired base read time */
1687 nframes = (int)(inttime * p->refrate + 0.5);
1688
1689 /* Establish the frame rate detect threshold level */
1690 /* (The Spyder 3 doesn't use this ?) */
1691 clocks1 = (int)((nframes * CLKRATE)/(10 * p->refrate) + 0.5); /* Use 10% of measurement clocks */
1692
1693 if ((ev = spyd2_GetMinMax(p, &clocks1, &min, &max)) != inst_ok)
1694 return ev;
1695
1696 /* Setup for measurement */
1697 thresh = (max - min)/5 + min; /* Threshold is at 80% of max brightness */
1698 if (thresh == 0)
1699 thresh = 65535; /* Set to max, otherwise reading will be 0 */
1700 frclocks = (int)(CLKRATE/p->refrate + 0.5); /* Nominal clocks per frame */
1701 minfclks = frclocks/3; /* Allow for 180 Hz */
1702 maxfclks = (frclocks * 5)/2; /* Allow for 24 Hz */
1703
1704 if (p->hwver < 7) {
1705 /* Check calibration is valid */
1706 if ((p->icx & 1) == 0 && (p->fbits & 1) == 0) {
1707 // return spyd2_interp_code((inst *)p, SPYD2_NOCRTCAL);
1708 a1logd(p->log, 1, "spyd2_GetReading: instrument appears to have no CRT calibration "
1709 "table! Proceeding anyway..\n");
1710 }
1711
1712 if ((p->icx & 1) == 1 && (p->fbits & 2) == 0) {
1713 // return spyd2_interp_code((inst *)p, SPYD2_NOLCDCAL);
1714 a1logd(p->log, 1, "spyd2_GetReading: instrument appears to have no LCD calibration "
1715 "table! Proceeding anyway..\n");
1716 }
1717 }
1718
1719 a1logd(p->log, 3, "spyd2_GetReading: Using cal table %d\n",(p->icx & 1));
1720 if (p->hwver >= 7)
1721 a1logd(p->log, 3, "spyd2_GetReading: using spectral cal table %d\n",p->icx >> 1);
1722
1723 for (k = 0; k < 8; k++) /* Zero weighted average */
1724 a_sensv[k] = a_w[k] = 0.0;
1725
1726 /* For initial and possible adaptive readings */
1727 for (i = 0;; i++) {
1728 double itime; /* Integration time */
1729
1730 clocks2 = (int)((double)nframes/p->refrate * (double)CLKRATE + 0.5);
1731
1732 if ((ev = spyd2_GetReading_ll(p, &clocks2, nframes, thresh, &minfclks, &maxfclks,
1733 sensv, &maxtcnt, &mintcnt)) != inst_ok)
1734 return ev;
1735 // a1logd(p->log, 3, "spyd2_GetReading: returned number of clocks = %d\n",clocks2);
1736
1737 if (p->log->debug >= 3) {
1738 for (k = 0; k < 8; k++)
1739 a1logd(p->log, 3, "Sensor %d value = %f\n",k,sensv[k]);
1740 }
1741
1742 itime = (double)clocks2 / (double)CLKRATE;
1743 // a1logd(p->log, 3, "spyd2_GetReading: reading %d was %f secs\n",i,itime);
1744
1745 /* Accumulate it for weighted average */
1746 for (k = 0; k < 8; k++) {
1747 if (sensv[k] != 0.0) { /* Skip value where we didn't get any transitions */
1748 #ifndef NEVER
1749 /* Accumulate it for weighted average */
1750 a_sensv[k] += sensv[k] * itime;
1751 a_w[k] += itime;
1752 #else
1753 /* Just use the last measurement */
1754 a_sensv[k] = sensv[k] * itime;
1755 a_w[k] = itime;
1756 #endif
1757 }
1758 }
1759
1760 #ifdef DO_ADAPTIVE
1761 a1logd(p->log, 3, "spyd2_GetReading: Maxtcnt = %d, Mintcnt = %d\n",maxtcnt,mintcnt);
1762 if (i > 0)
1763 break; /* Done adaptive */
1764
1765 /* Decide whether to go around again */
1766
1767 if (maxtcnt <= (100/16)) {
1768 nframes *= 16; /* Typically 16 seconds */
1769 a1logd(p->log, 3, "spyd2_GetReading: using maximum integration time\n");
1770 } else if (maxtcnt < 100) {
1771 double mulf;
1772 mulf = 100.0/maxtcnt;
1773 mulf -= 0.8; /* Just want to accumulate up to target, not re-do it */
1774 nframes = (int)(nframes * mulf + 0.5);
1775 a1logd(p->log, 3, "spyd2_GetReading: increasing total integration time "
1776 "by %.1f times\n",1+mulf);
1777 } else {
1778 break; /* No need for another reading */
1779 }
1780 #else /* !DO_ADAPTIVE */
1781 a1logw(p->log, "!!!! Spyder 2 DO_ADAPTIVE is off !!!!\n");
1782 break;
1783 #endif /* !DO_ADAPTIVE */
1784 }
1785
1786 /* Compute weighted average and guard against silliness */
1787 for (k = 0; k < 8; k++) {
1788 if (a_w[k] > 0.0) {
1789 a_sensv[k] /= a_w[k];
1790 }
1791 }
1792
1793 /* hwver == 5 hasn't been tested... */
1794 if (p->hwver == 5) {
1795 double gainscale = 1.0;
1796 unsigned int v381;
1797
1798 if ((ev = spyd2_rd_ee_uchar(p, &v381, 381)) != inst_ok)
1799 return ev;
1800
1801 gainscale = (double)v381/p->gain;
1802 a1logd(p->log, 3, "spyd2_GetReading: hwver5 v381 = %d, gain = %f, gainscale = %f\n",
1803 v381,p->gain,gainscale);
1804 /* Convert sensor readings to XYZ value */
1805 for (j = 0; j < 3; j++) {
1806 XYZ[j] = p->cal_A[p->icx & 1][j][0]; /* First entry is a constant */
1807 for (k = 1; k < 8; k++)
1808 XYZ[j] += a_sensv[k] * p->cal_A[p->icx & 1][j][k+1] * gainscale;
1809 }
1810
1811 } else {
1812 /* Convert sensor readings to XYZ value */
1813 for (j = 0; j < 3; j++) {
1814 XYZ[j] = p->cal_A[p->icx & 1][j][0]; /* First entry is a constant */
1815 for (k = 1; k < 8; k++) {
1816 XYZ[j] += a_sensv[k] * p->cal_A[p->icx & 1][j][k+1];
1817 }
1818 }
1819 }
1820
1821 // a1logd(p->log, 3, "spyd2_GetReading: real Y = %f\n",XYZ[1]);
1822 a1logd(p->log, 3, "spyd2_GetReading: initial XYZ reading %f %f %f\n",XYZ[0], XYZ[1], XYZ[2]);
1823
1824 #ifdef LEVEL2
1825 /* Add "level 2" correction factors */
1826 pows[0] = XYZ[0];
1827 pows[1] = XYZ[1];
1828 pows[2] = XYZ[2];
1829 pows[3] = XYZ[0] * XYZ[1];
1830 pows[4] = XYZ[0] * XYZ[2];
1831 pows[5] = XYZ[1] * XYZ[2];
1832 pows[6] = XYZ[0] * XYZ[0];
1833 pows[7] = XYZ[1] * XYZ[1];
1834 pows[8] = XYZ[2] * XYZ[2];
1835
1836
1837 for (j = 0; j < 3; j++) {
1838 XYZ[j] = 0.0;
1839
1840 for (k = 0; k < 9; k++) {
1841 XYZ[j] += pows[k] * p->cal_B[p->icx & 1][j][k];
1842 }
1843 }
1844 a1logd(p->log, 3, "spyd2_GetReading: 2nd level XYZ reading %f %f %f\n",XYZ[0], XYZ[1], XYZ[2]);
1845 #endif
1846
1847 /* Protect against silliness (This may stuff up averages though!) */
1848 for (j = 0; j < 3; j++) {
1849 if (XYZ[j] < 0.0)
1850 XYZ[j] = 0.0;
1851 }
1852 a1logd(p->log, 3, "spyd2_GetReading: final XYZ reading %f %f %f\n",XYZ[0], XYZ[1], XYZ[2]);
1853
1854 return ev;
1855 }
1856
1857 /* Spyder3/4/5: Do an ambient reading */
1858
1859 /* NOTE :- the ambient sensor is something like a TAOS TLS 2562CS. */
1860 /* It has two sensors, one wide band and the other infra-red, */
1861 /* the idea being to subtract them to get a rough human response. */
1862 /* The reading is 16 bits, and the 8 bit conrol register */
1863 /* controls gain and integration time: */
1864
1865 /* Bits 0,1 inttime, 0 = scale 0.034, 1 = scale 0.252. 2 = scale 1, 3 = manual */
1866 /* Bit 2, manual, 0 = stop int, 1 = start int */
1867 /* Bit 4, gain, 0 = gain 1, 1 = gain 16 */
1868
1869
1870 static inst_code
spyd2_GetAmbientReading(spyd2 * p,double * XYZ)1871 spyd2_GetAmbientReading(
1872 spyd2 *p,
1873 double *XYZ /* return the ambient XYZ values */
1874 ) {
1875 inst_code ev = inst_ok;
1876 int tconf; /* Ambient timing config */
1877 int iamb0, iamb1;
1878 double amb0, amb1; /* The two ambient values */
1879 double trv; /* Trial value */
1880 double thr[8] = { 64/512.0, 128/512.0, 192/512.0, 256/512.0, /* Magic tables */
1881 312/512.0, 410/512.0, 666/512.0, 0 };
1882 double s0[8] = { 498/128.0, 532/128.0, 575/128.0, 624/128.0,
1883 367/128.0, 210/128.0, 24/128.0, 0 };
1884 double s1[8] = { 446/128.0, 721/128.0, 891/128.0, 1022/128.0,
1885 508/128.0, 251/128.0, 18/128.0, 0 };
1886 double amb; /* Combined ambient value */
1887 double sfact;
1888 int i;
1889
1890 a1logd(p->log, 3, "spyd2_GetAmbientReading: called\n");
1891
1892 /* Set the ambient control register to 3 */
1893 if ((ev = spyd2_SetAmbReg(p, 3)) != inst_ok)
1894 return ev;
1895
1896 /* Wait one second */
1897 msec_sleep(1000);
1898
1899 /* Read the ambient timing config value */
1900 if ((ev = spyd2_ReadAmbTiming(p, &tconf)) != inst_ok)
1901 return ev;
1902 // a1logd(p->log, 4, "spyd2_GetAmbientReading: timing = %d\n",tconf);
1903
1904 /* Read the ambient values */
1905 if ((ev = spyd2_ReadAmbChan(p, 0, &iamb0)) != inst_ok)
1906 return ev;
1907 if ((ev = spyd2_ReadAmbChan(p, 1, &iamb1)) != inst_ok)
1908 return ev;
1909
1910 // a1logd(p->log, 4, "spyd2_GetAmbientReading: values = %d, %d\n",iamb0,iamb1);
1911 amb0 = iamb0/128.0;
1912 amb1 = iamb1/128.0;
1913
1914 /* Set the ambient control register to 0 */
1915 if ((ev = spyd2_SetAmbReg(p, 0)) != inst_ok)
1916 return ev;
1917
1918 /* Compute the scale factor from the timing config value */
1919 if ((tconf & 3) == 0)
1920 sfact = 1.0/0.034;
1921 else if ((tconf & 3) == 1)
1922 sfact = 1.0/0.252;
1923 else
1924 sfact = 1.0;
1925
1926 if ((tconf & 0x10) == 0)
1927 sfact *= 16.0;
1928
1929 amb0 *= sfact;
1930 amb1 *= sfact;
1931
1932 if (amb0 > 0.0)
1933 trv = amb1/amb0;
1934 else
1935 trv = 0.0;
1936
1937 for (i = 0; i < 7; i++) {
1938 if (trv <= thr[i])
1939 break;
1940 }
1941 // a1logd(p->log, 4, "spyd2_GetAmbientReading: trv = %f, s0 = %f, s1 = %f\n",trv, s0[i],s1[i]);
1942 /* Compute ambient in Lux */
1943 amb = s0[i] * amb0 - s1[i] * amb1;
1944
1945 // a1logd(p->log, 4, "spyd2_GetAmbientReading: combined ambient = %f cd/^m\n",amb);
1946
1947 /* Compute the Y value */
1948 XYZ[1] = amb; /* cd/m^2 ??? - not very accurate, due to */
1949 /* spectral response and/or integration angle ? */
1950 XYZ[0] = icmD50.X * XYZ[1]; /* Convert to D50 neutral */
1951 XYZ[2] = icmD50.Z * XYZ[1];
1952
1953 a1logd(p->log, 3, "spyd2_GetAmbientReading: returning %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]);
1954
1955 return ev;
1956 }
1957
1958 /* ------------------------------------------------------------ */
1959 /* Spyder 4/5 manufacturer calibration data */
1960 int spyd4_nocals = 0; /* Number of calibrations */
1961 /* 6 for Spyder 4 */
1962 /* 7 for Spyder 5 */
1963 xspect *spyd4_cals = NULL; /* [nocals] Device spectrum */
1964
1965 /* ------------------------------------------------------------ */
1966 /* Spyder4: Create a calibration matrix using the manufacturers */
1967 /* calibration data. */
1968
1969 static inst_code
spyd4_set_cal_ix(spyd2 * p,int ix)1970 spyd4_set_cal_ix(
1971 spyd2 *p, /* Object */
1972 int ix /* Selection, 0 .. spyd4_nocals-1 */
1973 ) {
1974 int i, j, k;
1975 xspect *oc[3]; /* The XYZ observer curves */
1976
1977 if (ix < 0 || ix >= spyd4_nocals) {
1978 return spyd2_interp_code((inst *)p, SPYD2_DISP_SEL_RANGE) ;
1979 }
1980
1981 /* The Manufacturers calibration routine computes a least squares */
1982 /* spectral fit of the sensor spectral sensitivities to the */
1983 /* standard observer curves, weighted by the white illuminant */
1984 /* of the display technology. We use the same approach for the */
1985 /* default calibration selections, to be faithful to the Manufacturers */
1986 /* intentions. */
1987
1988 if (p->obType == icxOT_custom) {
1989 oc[0] = &p->custObserver[0];
1990 oc[1] = &p->custObserver[1];
1991 oc[2] = &p->custObserver[2];
1992 } else {
1993 if (standardObserver(oc, p->obType)) {
1994 return spyd2_interp_code((inst *)p, SPYD2_DISP_SEL_RANGE) ;
1995 }
1996 }
1997
1998 /* We compute X,Y & Z independently. */
1999 for (k = 0; k < 3; k++) {
2000 double target[81]; /* Spectral target @ 5nm spacing */
2001 double **wsens; /* Weighted sensor sensitivities */
2002 double **psisens; /* Pseudo inverse of sensitivies */
2003
2004 /* Load up the observer curve and weight it by the display spectrum */
2005 /* and mW Lumoinance efficiency factor. */
2006 for (i = 0; i < 81; i++) {
2007 double nm = 380.0 + i * 5.0;
2008 target[i] = value_xspect(&spyd4_cals[ix], nm) * value_xspect(oc[k], nm) * 0.683002;
2009 }
2010
2011 /* Load up the sensor curves and weight by the display spectrum */
2012 wsens = dmatrix(0, 6, 0, 80);
2013 psisens = dmatrix(0, 80, 0, 6);
2014 for (j = 0; j < 7; j++) {
2015 for (i = 0; i < 81; i++) {
2016 double nm = 380.0 + i * 5.0;
2017 wsens[j][i] = value_xspect(&spyd4_cals[ix], nm) * value_xspect(&p->sens[j], nm);
2018 }
2019 }
2020
2021 /* Compute the pseudo-inverse matrix */
2022 if (lu_psinvert(psisens, wsens, 7, 81) != 0) {
2023 free_dmatrix(wsens, 0, 6, 0, 80);
2024 free_dmatrix(psisens, 0, 80, 0, 6);
2025 return spyd2_interp_code((inst *)p, SPYD2_CAL_FAIL) ;
2026 }
2027
2028 {
2029 double *cc, *tt;
2030 p->cal_A[1][k][0] = 0.0; /* Offset is zero */
2031 p->cal_A[1][k][1] = 0.0; /* Unused is zero */
2032 cc = &p->cal_A[1][k][2]; /* 7 cal values go here */
2033 tt = target;
2034
2035 /* Multiply inverse by target to get calibration matrix */
2036 if (matrix_mult(&cc, 1, 7, &tt, 1, 81, psisens, 81, 7))
2037 return spyd2_interp_code((inst *)p, SPYD2_CAL_FAIL) ;
2038 }
2039 // a1logd(p->log, 3, "Cal %d = %f %f %f %f %f %f %f\n", k, p->cal_A[1][k][2], p->cal_A[1][k][3], p->cal_A[1][k][4], p->cal_A[1][k][5], p->cal_A[1][k][6], p->cal_A[1][k][7], p->cal_A[1][k][8]);
2040 }
2041
2042 #ifdef PLOT_SPECTRA
2043 /* Plot the calibrated sensor spectra */
2044 {
2045 int i, j, k;
2046 double xx[81];
2047 double yy[10][81], *yp[10];
2048
2049 for (i = 0; i < 81; i++)
2050 xx[i] = 380.0 + i * 5.0;
2051
2052 for (j = 0; j < 3; j++) {
2053 for (i = 0; i < 81; i++) {
2054 yy[j][i] = 0.0;
2055 for (k = 0; k < 7; k++) {
2056 yy[j][i] += p->cal_A[1][j][k+2] * value_xspect(&p->sens[k], xx[i]);
2057 }
2058 }
2059 yp[j] = yy[j];
2060 }
2061 for (; j < 10; j++)
2062 yp[j] = NULL;
2063
2064 printf("The calibrated sensor sensitivities\n");
2065 do_plot10(xx, yp[0], yp[1], yp[2], yp[3], yp[4], yp[5], yp[6], yp[7], yp[8], yp[9], 81, 0);
2066 }
2067 #endif /* PLOT_SPECTRA */
2068
2069 #ifdef SAVE_XYZSPECTRA /* Save the default XYZ senitivity spectra to "sensorsxyz.cmf" */
2070 {
2071 int i, j, k;
2072 xspect xyz[3];
2073 double wl;
2074
2075 for (j = 0; j < 3; j++) {
2076 xyz[j].spec_n = 81;
2077 xyz[j].spec_wl_short = 380;
2078 xyz[j].spec_wl_long = 780;
2079 xyz[j].norm = 1.0;
2080 for (i = 0; i < 81; i++) {
2081 wl = 380.0 + i * 5.0;
2082 xyz[j].spec[i] = 0.0;
2083 for (k = 0; k < 7; k++)
2084 xyz[j].spec[i] += p->cal_A[1][j][k+2] * value_xspect(&p->sens[k], wl);
2085 xyz[j].spec[i] *= 1.4; /* Align with std XYZ */
2086 }
2087 }
2088 write_nxspect("sensorsxyz.cmf", xyz, 3, 0);
2089 }
2090 #endif
2091 #ifdef SAVE_STDXYZ
2092 {
2093 xspect xyz[3];
2094 standardObserver(&xyz[0], &xyz[1], &xyz[2],icxOT_CIE_1931_2);
2095 write_nxspect("stdobsxyz.cmf", xyz, 3, 0);
2096 }
2097 #endif /* SAVE_STDXYZ */
2098
2099 return inst_ok;
2100 }
2101
2102
2103 /* The CCSS calibration uses a set of spectral samples, */
2104 /* and a least squares matrix is computed to map the sensor RGB */
2105 /* to the computed XYZ values. This allows better accuracy for */
2106 /* a typical display that has only 3 degrees of freedom, and */
2107 /* allows weigting towards a distribution of actual spectral samples. */
2108 /* Because the typical display has only three degrees of freedom, */
2109 /* while the instrument has 7 sensors, some extra dummy spectral */
2110 /* samples are added to the list to provide some slight extra goal. */
2111
2112 /* [ Given the poor curve shapes that can come out of this, it's not */
2113 /* clear that it wouldn't be better using the default flat-spetrum */
2114 /* calibration and computing a 3x3 calibration matrix over the top of it. ] */
2115 static inst_code
spyd4_comp_calmat(spyd2 * p,icxObserverType obType,xspect custObserver[3],xspect * samples,int nsamp)2116 spyd4_comp_calmat(
2117 spyd2 *p,
2118 icxObserverType obType, /* XYZ Observer type */
2119 xspect custObserver[3], /* Optional custom observer */ \
2120 xspect *samples, /* Array of nsamp spectral samples */
2121 int nsamp /* Number of real samples */
2122 ) {
2123 int i, j;
2124 int nasamp = nsamp + 81; /* Number of real + augmented samples */
2125 double exwt = 1.0; /* Extra spectral point weight */
2126 double **sampXYZ; /* Sample XYZ values */
2127 double **sampSENS; /* Sample Sensor values */
2128 double **isampSENS; /* Pseudo-inverse of sensor values */
2129 double **calm; /* Calibration matrix */
2130 xsp2cie *conv;
2131 double wl;
2132 xspect white;
2133
2134 if (nsamp < 3)
2135 return spyd2_interp_code((inst *)p, SPYD2_TOO_FEW_CALIBSAMP);
2136
2137 /* Create white spectrum samples */
2138 XSPECT_COPY_INFO(&white, &samples[0]);
2139 for (j = 0; j < white.spec_n; j++)
2140 white.spec[j] = 0.0;
2141 for (i = 0; i < nsamp; i++) {
2142 for (j = 0; j < white.spec_n; j++)
2143 if (samples[i].spec[j] > white.spec[j])
2144 white.spec[j] = samples[i].spec[j];
2145 }
2146
2147 /* Compute XYZ of the real sample array. */
2148 if ((conv = new_xsp2cie(icxIT_none, NULL, obType, custObserver, icSigXYZData, icxClamp)) == NULL)
2149 return spyd2_interp_code((inst *)p, SPYD2_INT_CIECONVFAIL);
2150 sampXYZ = dmatrix(0, nasamp-1, 0, 3-1);
2151 for (i = 0; i < nsamp; i++) {
2152 conv->convert(conv, sampXYZ[i], &samples[i]);
2153 // a1logd(p->log, 3, "asamp[%d] XYZ = %f %f %f\n", i,sampXYZ[nsamp+i][0],sampXYZ[nsamp+i][1], sampXYZ[nsamp+i][2]);
2154 }
2155
2156 /* Create extra spectral samples */
2157 for (i = 0; i < 81; i++) {
2158 for (j = 0; j < 3; j++) {
2159 wl = 380.0 + i * 5;
2160 sampXYZ[nsamp+i][j] = exwt * value_xspect(&white, wl)
2161 * value_xspect(&conv->observer[j], wl) * 0.683002;
2162 }
2163 // a1logd(p->log, 3, "asamp[%d] XYZ = %f %f %f\n", i,sampXYZ[nsamp+i][0],sampXYZ[nsamp+i][1], sampXYZ[nsamp+i][2]);
2164 }
2165 conv->del(conv);
2166
2167 sampSENS = dmatrix(0, nasamp-1, 0, 7-1);
2168
2169 /* Compute sensor values of the sample array */
2170 for (i = 0; i < nsamp; i++) {
2171 for (j = 0; j < 7; j++) {
2172 sampSENS[i][j] = 0.0;
2173 for (wl = p->sens[0].spec_wl_short; wl <= p->sens[0].spec_wl_long; wl += 1.0) {
2174 sampSENS[i][j] += value_xspect(&samples[i], wl) * value_xspect(&p->sens[j], wl);
2175 }
2176 }
2177 }
2178 /* Create sensor values of the extra sample array */
2179 for (i = 0; i < 81; i++) {
2180 for (j = 0; j < 7; j++) {
2181 wl = 380.0 + i * 5;
2182 sampSENS[nsamp+i][j] = exwt * value_xspect(&white, wl) * value_xspect(&p->sens[j], wl);
2183 }
2184 // a1logd(p->log, 3, "asamp[%d] Sens = %f %f %f %f %f %f %f\n", i,
2185 // sampSENS[nsamp+i][0],sampSENS[nsamp+i][1], sampSENS[nsamp+i][2],
2186 // sampSENS[nsamp+i][3],sampSENS[nsamp+i][4], sampSENS[nsamp+i][5],
2187 // sampSENS[nsamp+i][6]);
2188 }
2189 #if defined(PLOT_SPECTRA_EXTRA)
2190 /* Plot the target extra values */
2191 {
2192 int i, j, k;
2193 double xx[81];
2194 double yy[10][81], *yp[10];
2195
2196 for (i = 0; i < 81; i++)
2197 xx[i] = 380.0 + i * 5;
2198
2199 for (j = 0; j < 3; j++) {
2200 for (i = 0; i < 81; i++) {
2201 yy[j][i] = sampXYZ[nsamp+i][j];
2202 }
2203 yp[j] = yy[j];
2204 }
2205 for (; j < 10; j++)
2206 yp[j] = NULL;
2207
2208 printf("The target extra XYZ values\n");
2209 do_plot10(xx, yp[0], yp[1], yp[2], yp[3], yp[4], yp[5], yp[6], yp[7], yp[8], yp[9], 81, 0);
2210
2211 for (j = 0; j < 7; j++) {
2212 for (i = 0; i < 81; i++) {
2213 yy[j][i] = sampSENS[nsamp+i][j];
2214 }
2215 yp[j] = yy[j];
2216 }
2217 for (; j < 10; j++)
2218 yp[j] = NULL;
2219
2220 printf("The given extra sensor values\n");
2221 do_plot10(xx, yp[0], yp[1], yp[2], yp[3], yp[4], yp[5], yp[6], yp[7], yp[8], yp[9], 81, 0);
2222 }
2223 #endif /* PLOT_SPECTRA_EXTRA */
2224
2225
2226 isampSENS = dmatrix(0, 7-1, 0, nasamp-1);
2227
2228 /* Compute the pseudo inverse of sampSENS */
2229 if (lu_psinvert(isampSENS, sampSENS, nasamp, 7) != 0) {
2230 free_dmatrix(sampXYZ, 0, nasamp-1, 0, 3-1);
2231 free_dmatrix(sampSENS, 0, nasamp-1, 0, 7-1);
2232 free_dmatrix(isampSENS, 0, 7-1, 0, nasamp-1);
2233 return spyd2_interp_code((inst *)p, SPYD2_CAL_FAIL) ;
2234 }
2235
2236 calm = dmatrix(0, 7-1, 0, 3-1);
2237
2238 /* Multiply inverse by target to get calibration matrix */
2239 if (matrix_mult(calm, 7, 3, isampSENS, 7, nasamp, sampXYZ, nasamp, 3)) {
2240 free_dmatrix(sampXYZ, 0, nasamp-1, 0, 3-1);
2241 free_dmatrix(sampSENS, 0, nasamp-1, 0, 7-1);
2242 free_dmatrix(isampSENS, 0, 7-1, 0, nasamp-1);
2243 free_dmatrix(calm, 0, 7-1, 0, 3-1);
2244 return spyd2_interp_code((inst *)p, SPYD2_CAL_FAIL);
2245 }
2246
2247 /* Copy the matrix into place */
2248 for (i = 0; i < 7; i++) {
2249 for (j = 0; j < 3; j++) {
2250 p->cal_A[1][j][2+i] = calm[i][j];
2251 }
2252 }
2253
2254 free_dmatrix(calm, 0, 7-1, 0, 3-1);
2255
2256 #ifdef NEVER
2257
2258 /* Compute the residuals */
2259 {
2260 double **refXYZ;
2261 double t1, t2;
2262
2263 refXYZ = dmatrix(0, nasamp-1, 0, 3-1);
2264
2265 if (matrix_mult(refXYZ, nasamp, 3, sampSENS, nasamp, 7, calm, 7, 3)) {
2266 printf("Residual matrix mult failed\n");
2267 } else {
2268 t1 = 0.0;
2269 for (i = 0; i < nsamp; i++) {
2270 t1 += icmLabDE(refXYZ[i],sampXYZ[i]);
2271 }
2272 t1 /= nsamp;
2273 printf("Average error for sample points = %f\n",t1);
2274 t2 = 0.0;
2275 for (i = nsamp; i < (nsamp + 81); i++) {
2276 t2 += icmLabDE(refXYZ[i],sampXYZ[i]);
2277 // printf("Resid %d error = %f, %f %f %f, %f %f %f\n",
2278 // i, icmLabDE(refXYZ[i],sampXYZ[i]), sampXYZ[i][0], sampXYZ[i][1],
2279 // sampXYZ[i][2], refXYZ[i][0], refXYZ[i][1], refXYZ[i][2]);
2280 }
2281 t2 /= 81;
2282 printf("Average error for extra points = %f\n",t2);
2283 }
2284 }
2285 #endif
2286
2287 #ifdef PLOT_SPECTRA
2288 /* Plot the calibrated sensor spectra */
2289 {
2290 int i, j, k;
2291 double xx[81];
2292 double yy[10][81], *yp[10];
2293
2294 for (i = 0; i < 81; i++)
2295 xx[i] = 380.0 + i * 5.0;
2296
2297 for (j = 0; j < 3; j++) {
2298 for (i = 0; i < 81; i++) {
2299 yy[j][i] = 0.0;
2300 for (k = 0; k < 7; k++) {
2301 yy[j][i] += p->cal_A[1][j][k+2] * value_xspect(&p->sens[k], xx[i]);
2302 }
2303 }
2304 yp[j] = yy[j];
2305 }
2306 for (; j < 10; j++)
2307 yp[j] = NULL;
2308
2309 printf("The calibrated sensor sensitivities\n");
2310 do_plot10(xx, yp[0], yp[1], yp[2], yp[3], yp[4], yp[5], yp[6], yp[7], yp[8], yp[9], 81, 0);
2311 }
2312 #endif /* PLOT_SPECTRA */
2313
2314 free_dmatrix(sampXYZ, 0, nasamp-1, 0, 3-1);
2315 free_dmatrix(sampSENS, 0, nasamp-1, 0, 7-1);
2316 free_dmatrix(isampSENS, 0, 7-1, 0, nasamp-1);
2317
2318 return inst_ok;
2319 }
2320
2321
2322 /* Preset the calibration to a spectral sample type. */
2323 /* ccmat[][] is set to unity */
2324 static inst_code
spyd2_set_speccal(spyd2 * p,xspect * samples,int nsamp)2325 spyd2_set_speccal(
2326 spyd2 *p,
2327 xspect *samples, /* Array of nsamp spectral samples, or RGBcmfs for MIbLSr */
2328 int nsamp /* Number of samples */
2329 ) {
2330 int i;
2331
2332 /* Save a the spectral samples to the current state */
2333 if (p->samples != NULL)
2334 free(p->samples);
2335 p->nsamp = 0;
2336 if ((p->samples = (xspect *)calloc(sizeof(xspect), nsamp)) == NULL) {
2337 a1loge(p->log, inst_internal_error, "spyd2_set_speccal: malloc failed\n");
2338 return inst_internal_error;
2339 }
2340 for (i = 0; i < nsamp; i++ )
2341 p->samples[i] = samples[i]; /* Struct copy */
2342 p->nsamp = nsamp;
2343
2344 p->icx = (99 << 1) | 1; /* Out of range index */
2345
2346 icmSetUnity3x3(p->ccmat); /* No matrix */
2347
2348 return inst_ok;
2349 }
2350
2351
2352 /* Preset the calibration to a matrix. The spectral type is set to none */
2353 static inst_code
spyd2_set_matcal(spyd2 * p,double mtx[3][3])2354 spyd2_set_matcal(spyd2 *p, double mtx[3][3]) {
2355
2356 if (p->samples != NULL)
2357 free(p->samples);
2358 p->samples = NULL;
2359 p->nsamp = 0;
2360
2361 if (mtx == NULL)
2362 icmSetUnity3x3(p->ccmat);
2363 else
2364 icmCpy3x3(p->ccmat, mtx);
2365
2366 return inst_ok;
2367 }
2368
2369
2370 /* Set the calibration to the currently preset type */
2371 static inst_code
spyd2_set_cal(spyd2 * p)2372 spyd2_set_cal(spyd2 *p) {
2373 inst_code ev = inst_ok;
2374
2375 if (p->samples != NULL && p->nsamp > 0) {
2376
2377 /* Create matrix for specified samples */
2378 if ((ev = spyd4_comp_calmat(p, p->obType, p->custObserver, p->samples, p->nsamp))
2379 != inst_ok) {
2380 a1logd(p->log, 1, "spyd2_set_cal: comp_calmat ccss failed with rv = 0x%x\n",ev);
2381 return ev;
2382 }
2383
2384 p->icx = (99 << 1) | 1; /* Out of range index */
2385 icmSetUnity3x3(p->ccmat); /* to be sure to be sure... */
2386
2387 } else {
2388
2389 if (p->hwver >= 7) {
2390 if ((p->icx >> 1) > spyd4_nocals)
2391 return inst_unsupported;
2392
2393 /* Create the calibration matrix from internal spectral data */
2394 if ((ev = spyd4_set_cal_ix(p, p->icx >> 1)) != inst_ok)
2395 return ev;
2396 }
2397
2398 }
2399
2400 if (p->log->debug >= 4) {
2401 int i;
2402 if (p->hwver >= 7) {
2403 a1logd(p->log,4,"Spectral calibration matrix:\n");
2404 for (i = 0; i < 7; i++) {
2405 a1logd(p->log,4," %f %f %f\n",
2406 p->cal_A[1][0][2+i], p->cal_A[1][1][2+i], p->cal_A[1][2][2+i]);
2407 }
2408 }
2409 a1logd(p->log,4,"\n");
2410 a1logd(p->log,4,"ccmat = %f %f %f\n",
2411 p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
2412 a1logd(p->log,4," %f %f %f\n",
2413 p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]);
2414 a1logd(p->log,4," %f %f %f\n\n",
2415 p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
2416 a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid);
2417 a1logd(p->log,4,"\n");
2418 }
2419
2420 return inst_ok;
2421 }
2422
2423 /* ------------------------------------------------------------ */
2424
2425 /* Read all the relevant register values */
2426 static inst_code
spyd2_read_all_regs(spyd2 * p)2427 spyd2_read_all_regs(
2428 spyd2 *p /* Object */
2429 ) {
2430 inst_code ev;
2431
2432 a1logd(p->log, 3, "spyd2_read_all_regs: about to read all the EEProm values\n");
2433
2434 if (p->log->debug >= 8) {
2435 unsigned char buf[1024];
2436 int len = 512;
2437
2438 if (p->hwver == 7
2439 || p->hwver == 10)
2440 len = 1024;
2441
2442 if ((ev = spyd2_readEEProm(p, buf, 0, len)) != inst_ok)
2443 return ev;
2444 a1logd(p->log, 8, "EEPROM:\n");
2445 adump_bytes(p->log, " ", buf, 0, len);
2446 }
2447
2448 /* HW version */
2449 if ((ev = spyd2_rd_ee_uchar(p, &p->hwver, 5)) != inst_ok)
2450 return ev;
2451
2452 /* Feature bits */
2453 if ((ev = spyd2_rd_ee_uchar(p, &p->fbits, 6)) != inst_ok)
2454 return ev;
2455
2456 a1logd(p->log, 3, "spyd2_read_all_regs: hwver+fbits = 0x%02x%02x\n",p->hwver,p->fbits);
2457
2458 /* Check the EEProm checksum */
2459 if (p->hwver == 7
2460 || p->hwver == 10) {
2461 if ((ev = spyd2_checkEECRC(p)) != inst_ok) {
2462 a1logd(p->log, 3, "spyd2_read_all_regs: checksum failed\n");
2463 return ev;
2464 }
2465 a1logd(p->log, 6, "spyd2_read_all_regs: checksum OK\n");
2466 }
2467
2468 /* Serial number */
2469 if ((ev = spyd2_readEEProm(p, (unsigned char *)p->serno, 8, 8)) != inst_ok)
2470 return ev;
2471 p->serno[8] = '\000';
2472 a1logd(p->log, 3, "spyd2_read_all_regs: serno = '%s'\n",p->serno);
2473
2474 if (p->hwver < 7) {
2475
2476 /* Hmm. We deliberately ignore the fbits 0, 1 & 2 here, in case they are faulty */
2477 /* (Not sure if we should look at fbits 1 or not) */
2478
2479 /* Spyde2: CRT calibration values */
2480 /* Spyde3: Unknown calibration values */
2481 if ((ev = spyd2_rdreg_3x9xfloat(p, p->cal_A[0][0], p->cal_A[0][1], p->cal_A[0][2], 16))
2482 != inst_ok)
2483 return ev;
2484 if ((ev = spyd2_rdreg_3x9xfloat(p, p->cal_B[0][0], p->cal_B[0][1], p->cal_B[0][2], 128))
2485 != inst_ok)
2486 return ev;
2487
2488
2489 /* Hmm. The 0 table seems to sometimes be scaled. Is this a bug ? */
2490 /* (might be gain factor ?) */
2491 /* The spyder 3/4 doesn't use this anyway. */
2492 if (p->hwver >= 4) {
2493 int j, k, i;
2494 double avgmag = 0.0;
2495
2496 for (i = j = 0; j < 3; j++) {
2497 for (k = 0; k < 9; k++) {
2498 if (p->cal_A[0][j][k] != 0.0) {
2499 avgmag += fabs(p->cal_A[0][j][k]);
2500 i++;
2501 }
2502 }
2503 }
2504 avgmag /= (double)(i);
2505 a1logd(p->log, 4, "spyd2_read_all_regs: Cal_A avgmag = %f\n",avgmag);
2506
2507 if (avgmag < 0.05) {
2508 a1logd(p->log, 5, "spyd2_read_all_regs: Scaling Cal_A by 16\n");
2509 for (j = 0; j < 3; j++) {
2510 for (k = 0; k < 9; k++) {
2511 p->cal_A[0][j][k] *= 16.0;
2512 }
2513 }
2514 }
2515 }
2516
2517 /* Spyder2: LCD calibration values */
2518 /* Spyder3: Normal CRT/LCD calibration values */
2519 if ((ev = spyd2_rdreg_3x9xfloat(p, p->cal_A[1][0], p->cal_A[1][1], p->cal_A[1][2], 256))
2520 != inst_ok)
2521 return ev;
2522 if ((ev = spyd2_rdreg_3x9xfloat(p, p->cal_B[1][0], p->cal_B[1][1], p->cal_B[1][2], 384))
2523 != inst_ok)
2524 return ev;
2525
2526 /* The monochrome "TOKIOBLUE" calibration */
2527 /* (Not sure if this is fbits 2 and 4 or not) */
2528
2529 /* Luminence only calibration values ??? */
2530 if ((ev = spyd2_rdreg_float(p, &p->cal_F[0], 240)) != inst_ok)
2531 return ev;
2532 if ((ev = spyd2_rdreg_float(p, &p->cal_F[1], 244)) != inst_ok)
2533 return ev;
2534 if ((ev = spyd2_rdreg_float(p, &p->cal_F[2], 248)) != inst_ok)
2535 return ev;
2536 if ((ev = spyd2_rdreg_float(p, &p->cal_F[3], 252)) != inst_ok)
2537 return ev;
2538 if ((ev = spyd2_rdreg_float(p, &p->cal_F[4], 364)) != inst_ok)
2539 return ev;
2540 if ((ev = spyd2_rdreg_float(p, &p->cal_F[5], 368)) != inst_ok)
2541 return ev;
2542 if ((ev = spyd2_rdreg_float(p, &p->cal_F[6], 372)) != inst_ok)
2543 return ev;
2544
2545 if (p->log->debug >= 4) {
2546 int i, j, k;
2547
2548 a1logd(p->log, 4, "Cal_A:\n");
2549 for (i = 0; i < 2;i++) {
2550 for (j = 0; j < 3; j++) {
2551 for (k = 0; k < 9; k++) {
2552 a1logd(p->log, 4, "Cal_A [%d][%d][%d] = %f\n",i,j,k,p->cal_A[i][j][k]);
2553 }
2554 }
2555 }
2556 a1logd(p->log, 4, "\nCal_B:\n");
2557 for (i = 0; i < 2;i++) {
2558 for (j = 0; j < 3; j++) {
2559 for (k = 0; k < 9; k++) {
2560 a1logd(p->log, 4, "Cal_B [%d][%d][%d] = %f\n",i,j,k,p->cal_B[i][j][k]);
2561 }
2562 }
2563 }
2564 a1logd(p->log, 4, "\nCal_F:\n");
2565 for (i = 0; i < 7;i++) {
2566 a1logd(p->log, 4, "Cal_F [%d] = %f\n",i,p->cal_F[i]);
2567 }
2568 a1logd(p->log, 4, "\n");
2569 }
2570
2571 } else if (p->hwver == 7
2572 || p->hwver == 10) {
2573 int i, j;
2574 unsigned int sscal;
2575 double tsens[7][41];
2576
2577 /* Read sensor sensitivity spectral data */
2578 if (p->hwver == 7) { /* Spyder 4 */
2579 if ((ev = spyd2_rdreg_7x41xshort(p, tsens, 0xAA)) != inst_ok)
2580 return ev;
2581 } else { /* Spyder 5 */
2582 if ((ev = spyd2_rdreg_7x41xleshort(p, tsens, 0x12C)) != inst_ok)
2583 return ev;
2584 }
2585
2586 /* Sensor scale factor */
2587 if ((ev = spyd2_rd_ee_ushort(p, &sscal, 21)) != inst_ok)
2588 return ev;
2589
2590 /* And apply it to the sensor data */
2591 for (j = 0; j < 7; j++) {
2592 for (i = 0; i < 41; i++) {
2593 tsens[j][i] /= 1000; /* Convert to Hz per mW/nm/m^2 */
2594 tsens[j][i] /= sscal/1e5; /* Sensitivity scale value */
2595 }
2596 }
2597
2598 /* Convert sensor values to xspect's */
2599 for (i = 0; i < 7; i++) {
2600 p->sens[i].spec_n = 41;
2601 p->sens[i].spec_wl_short = 380;
2602 p->sens[i].spec_wl_long = 780;
2603 p->sens[i].norm = 1.0;
2604 for (j = 0; j < 41; j++) {
2605 p->sens[i].spec[j] = tsens[i][j];
2606 }
2607 }
2608 #ifdef SAVE_SPECTRA
2609 write_nxspect("sensors.cmf", p->sens, 7, 0);
2610 #endif
2611
2612 /* Linearization */
2613 if ((ev = spyd2_rdreg_3x9xfloat(p, p->cal_B[1][0], p->cal_B[1][1], p->cal_B[1][2], 60))
2614 != inst_ok)
2615 return ev;
2616
2617 #ifdef PLOT_SPECTRA
2618 /* Plot the sensor spectra */
2619 {
2620 int i, j;
2621 double xx[81];
2622 double yy[10][81], *yp[10];
2623
2624 for (i = 0; i < 81; i++)
2625 xx[i] = 380.0 + i * 5.0;
2626
2627 for (j = 0; j < 7; j++) {
2628 for (i = 0; i < 81; i++)
2629 yy[j][i] = value_xspect(&p->sens[j], xx[i]);
2630 yp[j] = yy[j];
2631 }
2632 for (; j < 10; j++)
2633 yp[j] = NULL;
2634
2635 printf("The sensor and ambient sensor sensitivy curves\n");
2636 do_plot10(xx, yp[0], yp[1], yp[2], yp[3], yp[4], yp[5], yp[6], yp[7], yp[8], yp[9], 81, 0);
2637
2638 for (j = 0; j < spyd4_nocals; j++) {
2639 double max = 0;
2640 for (i = 0; i < 81; i++) {
2641 if (yy[j][i] = value_xspect(&spyd4_cals[j], xx[i]) > max)
2642 max = value_xspect(&spyd4_cals[j], xx[i]);
2643 }
2644 for (i = 0; i < 81; i++)
2645 yy[j][i] = value_xspect(&spyd4_cals[j], xx[i])/max;
2646 yp[j] = yy[j];
2647 }
2648 for (; j < 10; j++)
2649 yp[j] = NULL;
2650
2651 printf("The %d display spectra\n",spyd4_nocals);
2652 do_plot10(xx, yp[0], yp[1], yp[2], yp[3], yp[4], yp[5], yp[6], yp[7], yp[8], yp[9], 81, 0);
2653 }
2654 #endif /* PLOT_SPECTRA */
2655
2656 }
2657
2658 a1logd(p->log, 3, "spyd2_read_all_regs: all EEProm read OK\n");
2659
2660 return inst_ok;
2661 }
2662
2663 /* ------------------------------------------------------------ */
2664
2665 /* Spyder 1/2 Colorimeter Xilinx XCS05XL firmware pattern */
2666 unsigned int spyder_pld_xsize[2] = { 6817, 6817 }; /* Expected size */
2667 unsigned int spyder_pld_size[2] = { 0, 0 }; /* Number of bytes to download */
2668 unsigned char *spyder_pld_bytes[2] = { NULL, NULL }; /* Bytes to download */
2669
2670 /* Spyder 2: Download the PLD if it is available, and check status */
2671 static inst_code
spyd2_download_pld(spyd2 * p)2672 spyd2_download_pld(
2673 spyd2 *p /* Object */
2674 ) {
2675 inst_code ev;
2676 int stat;
2677 int i;
2678 int id;
2679
2680 if (p->itype == instSpyder1)
2681 id = 0;
2682 else
2683 id = 1;
2684
2685 a1logd(p->log, 2, "spyd2_download_pld: called\n");
2686
2687 if (spyder_pld_size[id] == 0) /* Try and read PLD pattern */
2688 setup_spyd2(id);
2689
2690 if (spyder_pld_size[id] == 0) {
2691 a1logd(p->log, 1, "spyd2_download_pld: No PLD pattern available! (have you run oeminst ?)\n");
2692 return spyd2_interp_code((inst *)p, SPYD2_NO_PLD_PATTERN) ;
2693 }
2694
2695 for (i = 0; i < spyder_pld_size[id]; i += 8) {
2696 if ((ev = spyd2_loadPLD(p, spyder_pld_bytes[id] + i, 8)) != inst_ok)
2697 return ev;
2698 }
2699
2700 /* Let the PLD initialize */
2701 msec_sleep(500);
2702
2703 #ifdef DO_RESETEP /* Do the miscelanous resetep()'s */
2704 /* Reset the coms */
2705 p->icom->usb_resetep(p->icom, 0x81);
2706 msec_sleep(1); /* Let device recover ? */
2707 #endif /* DO_RESETEP */
2708
2709 /* Check the status */
2710 if ((ev = spyd2_getstatus(p, &stat)) != inst_ok)
2711 return ev;
2712
2713 if (stat != 0) {
2714 a1logd(p->log, 1, "spyd2_download_pld: PLD download failed!\n");
2715 return spyd2_interp_code((inst *)p, SPYD2_PLDLOAD_FAILED);
2716 }
2717
2718 a1logd(p->log, 2, "spyd2_download_pld: PLD download OK\n");
2719
2720 msec_sleep(500);
2721 #ifdef DO_RESETEP /* Do the miscelanous resetep()'s */
2722 p->icom->usb_resetep(p->icom, 0x81);
2723 msec_sleep(1); /* Let device recover ? */
2724 #endif /* DO_RESETEP */
2725
2726 return inst_ok;
2727 }
2728
2729
2730 /* ------------------------------------------------------------ */
2731 /* Setup Spyder4 native calibrations */
2732
2733 /* Load the manufacturers Spyder4 calibration data */
2734 /* Return a SPYD2_ error value */
2735 static int
spyd4_load_cal(spyd2 * p)2736 spyd4_load_cal(spyd2 *p) {
2737 char **bin_paths = NULL;
2738 int no_paths = 0;
2739 unsigned int size;
2740 unsigned char *buf = NULL;
2741 FILE *fp = NULL;
2742 int nocals = 0;
2743 int i, j;
2744
2745 /* If already loaded */
2746 if (spyd4_nocals != 0)
2747 return SPYD2_OK;
2748
2749
2750 for (;;) { /* So we can break */
2751 if ((no_paths = xdg_bds(NULL, &bin_paths, xdg_data, xdg_read, xdg_user,
2752 "ArgyllCMS/spyd4cal.bin" XDG_FUDGE "color/spyd4cal.bin"
2753 )) < 1)
2754 break;
2755
2756 /* open binary file */
2757 #if !defined(O_CREAT) && !defined(_O_CREAT)
2758 # error "Need to #include fcntl.h!"
2759 #endif
2760 #if defined(O_BINARY) || defined(_O_BINARY)
2761 if ((fp = fopen(bin_paths[0],"rb")) == NULL)
2762 #else
2763 if ((fp = fopen(bin_paths[0],"r")) == NULL)
2764 #endif
2765 break;
2766 xdg_free(bin_paths, no_paths);
2767
2768 /* Figure out how big file it is */
2769 if (fseek(fp, 0, SEEK_END)) {
2770 fclose(fp);
2771 break;
2772 }
2773 size = (unsigned long)ftell(fp);
2774
2775 if ((size % (41 * 8)) != 0) {
2776 fclose(fp);
2777 a1logd(p->log, 1, "spyd4_load_cal: calibration file '%s' is unexpected size\n",bin_paths[0]);
2778 break;
2779 }
2780
2781 nocals = size/(41 * 8);
2782 if (nocals != 6
2783 && nocals != 7) {
2784 fclose(fp);
2785 a1logd(p->log, 1, "spyd4_load_cal: calibration file '%s' is unexpected number of calibrations (%d)\n",bin_paths[0],nocals);
2786 break;
2787 }
2788
2789 if (fseek(fp, 0, SEEK_SET)) {
2790 fclose(fp);
2791 break;
2792 }
2793
2794 if ((buf = (unsigned char *)calloc(nocals * 41, 8)) == NULL) {
2795 fclose(fp);
2796 return SPYD2_MALLOC;
2797 }
2798
2799 if (fread(buf, 1, size, fp) != size) {
2800 free(buf);
2801 fclose(fp);
2802 break;
2803 }
2804 fclose(fp);
2805 break;
2806 }
2807
2808 if (buf == NULL)
2809 nocals = 1;
2810
2811 if ((spyd4_cals = (xspect *)calloc(nocals, sizeof(xspect))) == NULL) {
2812 if (buf != NULL)
2813 free(buf);
2814 return SPYD2_MALLOC;
2815 }
2816
2817 /* If we have calibrations */
2818 if (buf != NULL) {
2819 unsigned char *bp;
2820
2821 for (i = 0; i < nocals; i++) {
2822 bp = buf + 41 * 8 * i;
2823
2824 spyd4_cals[i].spec_n = 41;
2825 spyd4_cals[i].spec_wl_short = 380;
2826 spyd4_cals[i].spec_wl_long = 780;
2827 spyd4_cals[i].norm = 1.0;
2828
2829 for (j = 0; j < 41; j++, bp += 8) {
2830 ORD64 val;
2831
2832 val = buf2ord64(bp);
2833 spyd4_cals[i].spec[j] = IEEE754_64todouble(val);
2834 // a1logd(p->log, 3, "cal[%d][%d] = %f\n",i,j,spyd4_cals[i].spec[j]);
2835 }
2836 }
2837
2838 } else {
2839
2840 /* Create a default calibration */
2841 for (j = 0; j < 41; j++)
2842 spyd4_cals[0].spec_n = 41;
2843 spyd4_cals[0].spec_wl_short = 380;
2844 spyd4_cals[0].spec_wl_long = 780;
2845 spyd4_cals[0].norm = 1.0;
2846
2847 for (j = 0; j < 41; j++) {
2848 spyd4_cals[0].spec[j] = 1.0;
2849 }
2850 }
2851
2852 spyd4_nocals = nocals;
2853
2854 return SPYD2_OK;
2855 }
2856
2857 /* ============================================================ */
2858
2859
2860 /* Establish communications with a SPYD2 */
2861 /* If it's a serial port, use the baud rate given, and timeout in to secs */
2862 /* Return SPYD2_COMS_FAIL on failure to establish communications */
2863 static inst_code
spyd2_init_coms(inst * pp,baud_rate br,flow_control fc,double tout)2864 spyd2_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
2865 spyd2 *p = (spyd2 *) pp;
2866 int se;
2867 icomuflags usbflags = icomuf_none;
2868
2869 a1logd(p->log, 2, "spyd2_init_coms: about to init coms\n");
2870
2871 if (p->icom->port_type(p->icom) != icomt_usb) {
2872 a1logd(p->log, 1, "spyd2_init_coms: wrong communications type for device!\n");
2873 return inst_coms_fail;
2874 }
2875
2876 a1logd(p->log, 2, "spyd2_init_coms: about to init USB\n");
2877
2878 /* On MSWindows the Spyder 3 doesn't work reliably unless each */
2879 /* read is preceeded by a reset endpoint. */
2880 /* (!!! This needs checking to see if it's still true. */
2881 /* Should switch back to libusb0.sys and re-test.) */
2882 /* (and Spyder 2 hangs if a reset ep is done on MSWin.) */
2883 /* The spyder 2 doesn't work well with the winusb driver either, */
2884 /* it needs icomuf_resetep_before_read to work at all, and */
2885 /* gets retries anyway. So we use the libusb-win32 driver for it. */
2886 #if defined(NT)
2887 if (p->itype == instSpyder3) {
2888 usbflags |= icomuf_resetep_before_read; /* The spyder USB is buggy ? */
2889 }
2890 #endif
2891
2892 /* On OS X the Spyder 2 can't close properly */
2893 #if defined(UNIX_APPLE) /* OS X*/
2894 if (p->itype == instSpyder1
2895 || p->itype == instSpyder2) {
2896 usbflags |= icomuf_reset_before_close; /* The spyder 2 USB is buggy ? */
2897 }
2898 #endif
2899
2900 #ifdef NEVER /* Don't want this now that we avoid 2nd set_config on Linux */
2901 #if defined(UNIX_X11) /* Linux*/
2902 /* On Linux the Spyder 2 doesn't work reliably unless each */
2903 /* read is preceeded by a reset endpoint. */
2904 if (p->itype == instSpyder1
2905 || p->itype == instSpyder2) {
2906 usbflags |= icomuf_resetep_before_read; /* The spyder USB is buggy ? */
2907 }
2908 #endif
2909 #endif
2910
2911 /* Set config, interface, write end point, read end point */
2912 /* ("serial" end points aren't used - the spyd2lay uses USB control messages) */
2913 if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, usbflags, 0, NULL)) != ICOM_OK) {
2914 a1logd(p->log, 1, "spyd2_init_coms: failed ICOM err 0x%x\n",se);
2915 return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
2916 }
2917
2918 a1logd(p->log, 2, "spyd2_init_coms: suceeded\n");
2919
2920 p->gotcoms = 1;
2921 return inst_ok;
2922 }
2923
2924 static inst_code set_default_disp_type(spyd2 *p);
2925
2926 /* Initialise the SPYD2 */
2927 /* return non-zero on an error, with an inst_code */
2928 static inst_code
spyd2_init_inst(inst * pp)2929 spyd2_init_inst(inst *pp) {
2930 spyd2 *p = (spyd2 *)pp;
2931 inst_code ev = inst_ok;
2932 int stat;
2933 int i;
2934
2935 a1logd(p->log, 2, "spyd2_init_inst: called\n");
2936
2937 if (p->gotcoms == 0) /* Must establish coms before calling init */
2938 return spyd2_interp_code((inst *)p, SPYD2_NO_COMS);
2939
2940 if (p->itype != instSpyder1
2941 && p->itype != instSpyder2
2942 && p->itype != instSpyder3
2943 && p->itype != instSpyder4
2944 && p->itype != instSpyder5)
2945 return spyd2_interp_code((inst *)p, SPYD2_UNKNOWN_MODEL);
2946
2947 p->refrate = DEFRRATE;
2948 for (i = 0; i < 8; i++)
2949 p->prevraw[i] = 0; /* Internal counters will be reset */
2950 p->prevrawinv = 0; /* prevraw is valid */
2951
2952 /* For Spyder 1 & 2, reset the hardware and wait for it to become ready. */
2953 if (p->itype != instSpyder3
2954 && p->itype != instSpyder4
2955 && p->itype != instSpyder5) {
2956
2957 /* Reset the instrument */
2958 if ((ev = spyd2_reset(p)) != inst_ok)
2959 return ev;
2960
2961 /* Fetch status until we get a status = 1 */
2962 for (i = 0; i < 50; i++) {
2963 if ((ev = spyd2_getstatus(p, &stat)) != inst_ok)
2964 return ev;
2965
2966 if (stat == 1)
2967 break;
2968 }
2969 if (i >= 50)
2970 return spyd2_interp_code((inst *)p, SPYD2_BADSTATUS);
2971
2972 } else {
2973 /* Because the Spyder 3/4/5 doesn't have a reset command, */
2974 /* it may be left in a borked state if the driver is aborted. */
2975 /* Make sure there's no old read data hanging around. */
2976 /* Sometimes it takes a little while for the old data to */
2977 /* turn up, so try at least for 1 second. */
2978 /* This won't always work if the driver is re-started */
2979 /* quickly after aborting a long integration read. */
2980
2981 unsigned char buf[8]; /* return bytes read */
2982 int rwbytes; /* Data bytes read or written */
2983
2984
2985 for (i = 0; i < 10; i++) {
2986 if ((p->icom->usb_read(p->icom, NULL, 0x81, buf, 8, &rwbytes, 0.1) & ICOM_TO)
2987 && i > 9)
2988 break; /* Done when read times out */
2989 }
2990 }
2991
2992 /* Read the Serial EEProm contents */
2993 if ((ev = spyd2_read_all_regs(p)) != inst_ok)
2994 return ev;
2995
2996 /* Spyder 2 */
2997 if (p->hwver < 4) {
2998 /* Download the PLD pattern and check the status */
2999 if ((ev = spyd2_download_pld(p)) != inst_ok)
3000 return ev;
3001 }
3002
3003 p->gain = 1.0;
3004 if (p->hwver == 5) {
3005 if ((ev = spyd2_SetGain(p, 4)) != inst_ok)
3006 return ev;
3007 }
3008
3009 /* Set a default calibration */
3010 if ((ev = set_default_disp_type(p)) != inst_ok) {
3011 return ev;
3012 }
3013
3014 /* Do a dumy sensor read. This will set prevraw[] values. */
3015 {
3016 int clocks = 500;
3017 int minfclks = 0;
3018 int maxfclks = 0;
3019 msec_sleep(100);
3020 if ((ev = spyd2_GetReading_ll(p, &clocks, 10, 0, &minfclks, &maxfclks, NULL, NULL, NULL)) != inst_ok)
3021 return ev;
3022 }
3023
3024 p->trig = inst_opt_trig_user; /* default trigger mode */
3025
3026 p->inited = 1;
3027 a1logd(p->log, 2, "spyd2_init_inst: inited OK\n");
3028
3029 if (p->hwver >= 4) {
3030 /* Flash the LED, just cos we can! */
3031 if ((ev = spyd2_setLED(p, 2, 0.0)) != inst_ok)
3032 return ev;
3033 msec_sleep(200);
3034 if ((ev = spyd2_setLED(p, 0, 0.0)) != inst_ok)
3035 return ev;
3036 }
3037
3038 a1logv(p->log, 1, "Instrument Type: %s\n"
3039 "Serial Number: %s\n"
3040 "Hardware version: 0x%02x%02x\n"
3041 ,inst_name(p->itype) ,p->serno ,p->hwver,p->fbits);
3042
3043 return inst_ok;
3044 }
3045
3046 /* Read a single sample */
3047 /* Return the dtp error code */
3048 static inst_code
spyd2_read_sample(inst * pp,char * name,ipatch * val,instClamping clamp)3049 spyd2_read_sample(
3050 inst *pp,
3051 char *name, /* Strip name (7 chars) */
3052 ipatch *val, /* Pointer to instrument patch value */
3053 instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */
3054 spyd2 *p = (spyd2 *)pp;
3055 int user_trig = 0;
3056 inst_code ev = inst_protocol_error;
3057
3058 if (!p->gotcoms)
3059 return inst_no_coms;
3060 if (!p->inited)
3061 return inst_no_init;
3062
3063 if (p->trig == inst_opt_trig_user) {
3064
3065 if (p->uicallback == NULL) {
3066 a1logd(p->log, 1, "sptyd2: inst_opt_trig_user but no uicallback function set!\n");
3067 return inst_unsupported;
3068 }
3069
3070 for (;;) {
3071 if ((ev = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
3072 if (ev == inst_user_abort)
3073 return ev; /* Abort */
3074 if (ev == inst_user_trig) {
3075 user_trig = 1;
3076 break; /* Trigger */
3077 }
3078 }
3079 msec_sleep(200);
3080 }
3081 /* Notify of trigger */
3082 if (p->uicallback)
3083 p->uicallback(p->uic_cntx, inst_triggered);
3084
3085 /* Progromatic Trigger */
3086 } else {
3087 /* Check for abort */
3088 if (p->uicallback != NULL
3089 && (ev = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) {
3090 return ev; /* Abort */
3091 }
3092 }
3093
3094 if (IMODETST(p->mode, inst_mode_emis_ambient)) {
3095 ev = spyd2_GetAmbientReading(p, val->XYZ);
3096
3097 } else {
3098 ev = inst_ok;
3099
3100 /* Attempt a CRT frame rate calibration if needed */
3101 if (p->refrmode != 0 && p->rrset == 0)
3102 ev = spyd2_GetRefRate(p);
3103
3104 if (ev != inst_ok) {
3105 warning("Spyder: measuring refresh rate failed");
3106 ev = inst_ok;
3107 }
3108
3109 if (ev == inst_ok) {
3110 /* Read the XYZ value */
3111 if ((ev = spyd2_GetReading(p, val->XYZ)) == inst_ok) {
3112
3113 /* Apply the colorimeter correction matrix */
3114 icmMulBy3x3(val->XYZ, p->ccmat, val->XYZ);
3115 }
3116 }
3117 }
3118
3119 if (ev != inst_ok)
3120 return ev;
3121
3122
3123 /* This may not change anything since instrument may clamp */
3124 if (clamp)
3125 icmClamp3(val->XYZ, val->XYZ);
3126
3127 val->loc[0] = '\000';
3128 if (IMODETST(p->mode, inst_mode_emis_ambient))
3129 val->mtype = inst_mrt_ambient;
3130 else
3131 val->mtype = inst_mrt_emission;
3132 val->XYZ_v = 1; /* These are absolute XYZ readings ? */
3133 val->sp.spec_n = 0;
3134 val->duration = 0.0;
3135
3136
3137 if (user_trig)
3138 return inst_user_trig;
3139 return ev;
3140 }
3141
3142 /* Make a possible change of the refresh mode */
update_refmode(spyd2 * p,int refrmode)3143 static void update_refmode(spyd2 *p, int refrmode) {
3144
3145 if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */
3146 refrmode = 0;
3147 } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) {
3148 refrmode = 1;
3149 }
3150
3151 if (p->refrmode != refrmode) {
3152 p->rrset = 0; /* This is a hint we may have swapped displays */
3153 p->refrvalid = 0;
3154 }
3155 p->refrmode = refrmode;
3156 }
3157
3158 static inst_code set_base_disp_type(spyd2 *p, int cbid);
3159
3160 /* Insert a colorimetric correction matrix in the instrument XYZ readings */
3161 /* This is only valid for colorimetric instruments. */
3162 /* To remove the matrix, pass NULL for the filter filename */
spyd2_col_cor_mat(inst * pp,disptech dtech,int cbid,double mtx[3][3])3163 inst_code spyd2_col_cor_mat(
3164 inst *pp,
3165 disptech dtech, /* Use disptech_unknown if not known */ \
3166 int cbid, /* Calibration display type base ID, 1 if unknown */\
3167 double mtx[3][3]
3168 ) {
3169 spyd2 *p = (spyd2 *)pp;
3170 inst_code ev = inst_ok;
3171
3172 if (!p->gotcoms)
3173 return inst_no_coms;
3174 if (!p->inited)
3175 return inst_no_init;
3176
3177 if ((ev = set_base_disp_type(p, cbid)) != inst_ok)
3178 return ev;
3179
3180 p->dtech = dtech;
3181 update_refmode(p, disptech_get_id(dtech)->refr);
3182 p->cbid = 0;
3183
3184 if ((ev = spyd2_set_matcal(p, mtx)) != inst_ok)
3185 return ev;
3186
3187 return spyd2_set_cal(p);
3188 }
3189
3190 /* Use a Colorimeter Calibration Spectral Set to set the */
3191 /* instrumen calibration. */
3192 /* This is only valid for colorimetric instruments. */
3193 /* To set calibration back to default, pass NULL for sets. */
spyd2_col_cal_spec_set(inst * pp,disptech dtech,xspect * sets,int no_sets)3194 inst_code spyd2_col_cal_spec_set(
3195 inst *pp,
3196 disptech dtech,
3197 xspect *sets,
3198 int no_sets
3199 ) {
3200 spyd2 *p = (spyd2 *)pp;
3201 inst_code ev = inst_ok;
3202
3203 if (!p->gotcoms)
3204 return inst_no_coms;
3205 if (!p->inited)
3206 return inst_no_init;
3207 if (p->hwver < 7)
3208 return inst_unsupported;
3209
3210 p->dtech = dtech;
3211
3212 if (sets == NULL || no_sets <= 0) {
3213 if ((ev = set_default_disp_type(p)) != inst_ok)
3214 return ev;
3215 } else {
3216 if ((ev = spyd2_set_speccal(p, sets, no_sets)) != inst_ok)
3217 return ev;
3218
3219 p->ucbid = 0; /* We're using external samples */
3220 ev = spyd2_set_cal(p);
3221 }
3222 update_refmode(p, disptech_get_id(dtech)->refr);
3223
3224 return ev;
3225 }
3226
3227 /* Return needed and available inst_cal_type's */
spyd2_get_n_a_cals(inst * pp,inst_cal_type * pn_cals,inst_cal_type * pa_cals)3228 static inst_code spyd2_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
3229 spyd2 *p = (spyd2 *)pp;
3230
3231 inst_cal_type n_cals = inst_calt_none;
3232 inst_cal_type a_cals = inst_calt_none;
3233
3234 if (p->refrmode != 0) {
3235 if (p->rrset == 0)
3236 n_cals |= inst_calt_ref_freq;
3237 a_cals |= inst_calt_ref_freq;
3238 }
3239
3240 if (pn_cals != NULL)
3241 *pn_cals = n_cals;
3242
3243 if (pa_cals != NULL)
3244 *pa_cals = a_cals;
3245
3246 return inst_ok;
3247 }
3248
3249 /* Request an instrument calibration. */
3250 /* This is use if the user decides they want to do a calibration, */
3251 /* in anticipation of a calibration (needs_calibration()) to avoid */
3252 /* requiring one during measurement, or in response to measuring */
3253 /* returning inst_needs_cal. Initially us an inst_cal_cond of inst_calc_none, */
3254 /* and then be prepared to setup the right conditions, or ask the */
3255 /* user to do so, each time the error inst_cal_setup is returned. */
spyd2_calibrate(inst * pp,inst_cal_type * calt,inst_cal_cond * calc,inst_calc_id_type * idtype,char id[CALIDLEN])3256 static inst_code spyd2_calibrate(
3257 inst *pp,
3258 inst_cal_type *calt, /* Calibration type to do/remaining */
3259 inst_cal_cond *calc, /* Current condition/desired condition */
3260 inst_calc_id_type *idtype, /* Condition identifier type */
3261 char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */
3262 ) {
3263 spyd2 *p = (spyd2 *)pp;
3264 inst_code ev = inst_ok;
3265 inst_cal_type needed, available;
3266
3267 if (!p->gotcoms)
3268 return inst_no_coms;
3269 if (!p->inited)
3270 return inst_no_init;
3271
3272 *idtype = inst_calc_id_none;
3273 id[0] = '\000';
3274
3275 if ((ev = spyd2_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok)
3276 return ev;
3277
3278 /* Translate inst_calt_all/needed into something specific */
3279 if (*calt == inst_calt_all
3280 || *calt == inst_calt_needed
3281 || *calt == inst_calt_available) {
3282 if (*calt == inst_calt_all)
3283 *calt = (needed & inst_calt_n_dfrble_mask) | inst_calt_ap_flag;
3284 else if (*calt == inst_calt_needed)
3285 *calt = needed & inst_calt_n_dfrble_mask;
3286 else if (*calt == inst_calt_available)
3287 *calt = available & inst_calt_n_dfrble_mask;
3288
3289 a1logd(p->log,4,"spyd2_calibrate: doing calt 0x%x\n",calt);
3290
3291 if ((*calt & inst_calt_n_dfrble_mask) == 0) /* Nothing todo */
3292 return inst_ok;
3293 }
3294
3295 if ((*calt & inst_calt_ref_freq) && p->refrmode != 0) {
3296
3297 if ((*calc & inst_calc_cond_mask) != inst_calc_emis_80pc) {
3298 *calc = inst_calc_emis_80pc;
3299 return inst_cal_setup;
3300 }
3301
3302 /* Do CRT frame rate calibration */
3303 if ((ev = spyd2_GetRefRate(p)) != inst_ok)
3304 return ev;
3305
3306 *calt &= ~inst_calt_ref_freq;
3307 }
3308
3309 return inst_ok;
3310 }
3311
3312 /* Return the last calibrated refresh rate in Hz. Returns: */
spyd2_get_refr_rate(inst * pp,double * ref_rate)3313 static inst_code spyd2_get_refr_rate(inst *pp,
3314 double *ref_rate
3315 ) {
3316 spyd2 *p = (spyd2 *)pp;
3317 if (p->refrvalid) {
3318
3319 *ref_rate = p->refrate;
3320 return inst_ok;
3321 } else if (p->rrset) {
3322 *ref_rate = 0.0;
3323 return inst_misread | SPYD2_NO_REFRESH_DET;
3324 }
3325 return inst_needs_cal;
3326 }
3327
3328 /* Set the calibrated refresh rate in Hz. */
3329 /* Set refresh rate to 0.0 to mark it as invalid */
3330 /* Rates outside the range 5.0 to 150.0 Hz will return an error */
spyd2_set_refr_rate(inst * pp,double ref_rate)3331 static inst_code spyd2_set_refr_rate(inst *pp,
3332 double ref_rate
3333 ) {
3334 spyd2 *p = (spyd2 *)pp;
3335
3336 if (ref_rate != 0.0 && (ref_rate < 5.0 || ref_rate > 150.0))
3337 return inst_bad_parameter;
3338
3339 p->refrate = ref_rate;
3340 if (ref_rate == 0.0)
3341 p->refrate = DEFRRATE;
3342 else
3343 p->refrvalid = 1;
3344 p->rrset = 1;
3345
3346 return inst_ok;
3347 }
3348
3349 /* Error codes interpretation */
3350 static char *
spyd2_interp_error(inst * pp,int ec)3351 spyd2_interp_error(inst *pp, int ec) {
3352 // spyd2 *p = (spyd2 *)pp;
3353 ec &= inst_imask;
3354 switch (ec) {
3355 case SPYD2_INTERNAL_ERROR:
3356 return "Non-specific software internal software error";
3357 case SPYD2_COMS_FAIL:
3358 return "Communications failure";
3359 case SPYD2_UNKNOWN_MODEL:
3360 return "Not a Spyder 2, 3, 4 or 5";
3361 case SPYD2_DATA_PARSE_ERROR:
3362 return "Data from i1 Display didn't parse as expected";
3363
3364 case SPYD2_OK:
3365 return "No device error";
3366
3367 /* device specific errors */
3368 case SPYD2_BADSTATUS:
3369 return "Too many retries waiting for status to come good";
3370 case SPYD2_PLDLOAD_FAILED:
3371 return "Wrong status after download of PLD";
3372 case SPYD2_BADREADSIZE:
3373 return "Didn't read expected amount of data";
3374 case SPYD2_TRIGTIMEOUT:
3375 return "Trigger timout";
3376 case SPYD2_OVERALLTIMEOUT:
3377 return "Overall timout";
3378 case SPYD2_BAD_EE_CRC:
3379 return "Serial EEProm CRC failed";
3380
3381 /* Internal errors */
3382 case SPYD2_BAD_EE_ADDRESS:
3383 return "Serial EEProm read is out of range";
3384 case SPYD2_BAD_EE_SIZE:
3385 return "Serial EEProm read size > 256";
3386 case SPYD2_NO_PLD_PATTERN:
3387 return "No PLD firmware pattern is available (have you run oeminst ?)";
3388 case SPYD2_NO_COMS:
3389 return "Communications hasn't been established";
3390 case SPYD2_NOT_INITED:
3391 return "Insrument hasn't been initialised";
3392 case SPYD2_NOCRTCAL:
3393 return "Insrument is missing the CRT calibration table";
3394 case SPYD2_NOLCDCAL:
3395 return "Insrument is missing the Normal or LCD calibration table";
3396 case SPYD2_MALLOC:
3397 return "Memory allocation failure";
3398 case SPYD2_OBS_SELECT:
3399 return "Failed to set observer type";
3400 case SPYD2_CAL_FAIL:
3401 return "Calibration calculation failed";
3402 case SPYD2_INT_CIECONVFAIL:
3403 return "Creating spectral to CIE converted failed";
3404 case SPYD2_TOO_FEW_CALIBSAMP:
3405 return "There are too few spectral calibration samples - need at least 3";
3406
3407 /* Configuration */
3408 case SPYD2_DISP_SEL_RANGE:
3409 return "Display device selection out of range";
3410
3411 /* User error */
3412 case SPYD2_TOOBRIGHT:
3413 return "Too bright to read accuractly";
3414 case SPYD2_NO_REFRESH_DET:
3415 return "Unable to detect & measure refresh rate";
3416
3417 default:
3418 return "Unknown error code";
3419 }
3420 }
3421
3422
3423 /* Convert a machine specific error code into an abstract dtp code */
3424 static inst_code
spyd2_interp_code(inst * pp,int ec)3425 spyd2_interp_code(inst *pp, int ec) {
3426 // spyd2 *p = (spyd2 *)pp;
3427
3428 ec &= inst_imask;
3429 switch (ec) {
3430
3431 case SPYD2_OK:
3432 return inst_ok;
3433
3434 case SPYD2_INTERNAL_ERROR:
3435 case SPYD2_NO_COMS:
3436 case SPYD2_NOT_INITED:
3437 case SPYD2_BAD_EE_ADDRESS:
3438 case SPYD2_BAD_EE_SIZE:
3439 case SPYD2_NO_PLD_PATTERN:
3440 case SPYD2_MALLOC:
3441 case SPYD2_OBS_SELECT:
3442 case SPYD2_CAL_FAIL:
3443 case SPYD2_INT_CIECONVFAIL:
3444 case SPYD2_TOO_FEW_CALIBSAMP:
3445 return inst_internal_error | ec;
3446
3447 case SPYD2_COMS_FAIL:
3448 case SPYD2_BADREADSIZE:
3449 case SPYD2_TRIGTIMEOUT:
3450 case SPYD2_BADSTATUS:
3451 case SPYD2_OVERALLTIMEOUT:
3452 return inst_coms_fail | ec;
3453
3454 case SPYD2_UNKNOWN_MODEL:
3455 return inst_unknown_model | ec;
3456
3457 // return inst_protocol_error | ec;
3458
3459 case SPYD2_NOCRTCAL:
3460 case SPYD2_NOLCDCAL:
3461 case SPYD2_PLDLOAD_FAILED:
3462 case SPYD2_BAD_EE_CRC:
3463 return inst_hardware_fail | ec;
3464
3465 case SPYD2_DISP_SEL_RANGE:
3466 return inst_wrong_setup | ec;
3467
3468 case SPYD2_TOOBRIGHT:
3469 case SPYD2_NO_REFRESH_DET:
3470 return inst_misread | ec;
3471
3472 }
3473 return inst_other_error | ec;
3474 }
3475
3476 /* Destroy ourselves */
3477 static void
spyd2_del(inst * pp)3478 spyd2_del(inst *pp) {
3479 spyd2 *p = (spyd2 *)pp;
3480 if (p->icom != NULL)
3481 p->icom->del(p->icom);
3482 inst_del_disptype_list(p->dtlist, p->ndtlist);
3483 if (p->samples != NULL)
3484 free(p->samples);
3485 p->vdel(pp);
3486 free(p);
3487 }
3488
3489 /* Return the instrument mode capabilities */
spyd2_capabilities(inst * pp,inst_mode * pcap1,inst2_capability * pcap2,inst3_capability * pcap3)3490 static void spyd2_capabilities(inst *pp,
3491 inst_mode *pcap1,
3492 inst2_capability *pcap2,
3493 inst3_capability *pcap3) {
3494 spyd2 *p = (spyd2 *)pp;
3495 inst_mode cap1= 0;
3496 inst2_capability cap2 = 0;
3497
3498 cap1 |= inst_mode_emis_spot
3499 | inst_mode_emis_refresh_ovd
3500 | inst_mode_emis_norefresh_ovd
3501 | inst_mode_colorimeter
3502 ;
3503
3504 /* We don't seem to have a way of detecting the lack */
3505 /* of ambinent capability, short of doing a read */
3506 /* and noticing the result is zero. */
3507 if (p->itype == instSpyder3
3508 || p->itype == instSpyder4
3509 || p->itype == instSpyder5) {
3510 cap1 |= inst_mode_emis_ambient;
3511 }
3512
3513 cap2 |= inst2_prog_trig
3514 | inst2_user_trig
3515 | inst2_ccmx
3516 | inst2_get_refresh_rate
3517 | inst2_set_refresh_rate
3518 | inst2_emis_refr_meas
3519 ;
3520
3521 if (p->itype == instSpyder3
3522 || p->itype == instSpyder4
3523 || p->itype == instSpyder5) {
3524 cap2 |= inst2_disptype;
3525 cap2 |= inst2_has_leds;
3526 cap2 |= inst2_ambient_mono;
3527 } else {
3528 cap2 |= inst2_disptype;
3529 }
3530
3531 if (p->itype == instSpyder4
3532 || p->itype == instSpyder5)
3533 cap2 |= inst2_ccss; /* Spyder4 & 5 has spectral sensivities */
3534
3535 if (pcap1 != NULL)
3536 *pcap1 = cap1;
3537 if (pcap2 != NULL)
3538 *pcap2 = cap2;
3539 if (pcap3 != NULL)
3540 *pcap3 = inst3_none;
3541 }
3542
3543 /* Check device measurement mode */
spyd2_check_mode(inst * pp,inst_mode m)3544 static inst_code spyd2_check_mode(inst *pp, inst_mode m) {
3545 spyd2 *p = (spyd2 *)pp;
3546 inst_mode cap;
3547
3548 if (!p->gotcoms)
3549 return inst_no_coms;
3550 if (!p->inited)
3551 return inst_no_init;
3552
3553 pp->capabilities(pp, &cap, NULL, NULL);
3554
3555 /* Simple test */
3556 if (m & ~cap)
3557 return inst_unsupported;
3558
3559 if (!IMODETST(m, inst_mode_emis_spot)
3560 && !IMODETST(m, inst_mode_emis_ambient)) {
3561 return inst_unsupported;
3562 }
3563
3564 return inst_ok;
3565 }
3566
3567 /* Set device measurement mode */
spyd2_set_mode(inst * pp,inst_mode m)3568 static inst_code spyd2_set_mode(inst *pp, inst_mode m) {
3569 spyd2 *p = (spyd2 *)pp;
3570 inst_code ev;
3571
3572 if ((ev = spyd2_check_mode(pp, m)) != inst_ok)
3573 return ev;
3574
3575 p->mode = m;
3576
3577 /* Effective refresh mode may change */
3578 update_refmode(p, p->refrmode);
3579
3580 return inst_ok;
3581 }
3582
3583 static inst_disptypesel spyd2_disptypesel[3] = {
3584 {
3585 inst_dtflags_default,
3586 1,
3587 "l",
3588 "LCD display",
3589 0,
3590 disptech_lcd,
3591 1
3592 },
3593 {
3594 inst_dtflags_none, /* flags */
3595 2, /* cbid */
3596 "c", /* sel */
3597 "CRT display", /* desc */
3598 1, /* refr */
3599 disptech_crt, /* disptype */
3600 0 /* ix */
3601 },
3602 {
3603 inst_dtflags_end,
3604 0,
3605 "",
3606 "",
3607 0,
3608 disptech_none,
3609 0
3610 }
3611 };
3612
3613 static inst_disptypesel spyd3_disptypesel[3] = {
3614 {
3615 inst_dtflags_default,
3616 1,
3617 "nl",
3618 "Non-Refresh display",
3619 0,
3620 disptech_lcd,
3621 1
3622 },
3623 {
3624 inst_dtflags_none, /* flags */
3625 2, /* cbid */
3626 "rc", /* sel */
3627 "Refresh display", /* desc */
3628 1, /* refr */
3629 disptech_crt, /* disptype */
3630 1 /* ix */
3631 },
3632 {
3633 inst_dtflags_end,
3634 0,
3635 "",
3636 "",
3637 0,
3638 disptech_unknown,
3639 0
3640 }
3641 };
3642
3643 static inst_disptypesel spyd4_disptypesel_1[8] = {
3644 {
3645 inst_dtflags_default,
3646 1,
3647 "nl",
3648 "Generic Non-Refresh Display",
3649 0,
3650 disptech_lcd,
3651 1
3652 },
3653 {
3654 inst_dtflags_none, /* flags */
3655 2, /* cbid */
3656 "rc", /* sel */
3657 "Generic Refresh Display", /* desc */
3658 1, /* refr */
3659 disptech_crt, /* disptype */
3660 1 /* ix */
3661 },
3662 {
3663 inst_dtflags_end,
3664 0,
3665 "",
3666 "",
3667 0,
3668 disptech_none,
3669 0
3670 }
3671 };
3672
3673 static inst_disptypesel spyd4_disptypesel[8] = {
3674 {
3675 inst_dtflags_default,
3676 1,
3677 "nl",
3678 "Generic Non-Refresh Display",
3679 0,
3680 disptech_lcd,
3681 1
3682 },
3683 {
3684 inst_dtflags_none, /* flags */
3685 2, /* cbid */
3686 "rc", /* sel */
3687 "Generic Refresh Display", /* desc */
3688 1, /* refr */
3689 disptech_crt, /* disptype */
3690 1 /* ix = hw bit + spec table << 1 */
3691 },
3692 {
3693 inst_dtflags_none, /* flags */
3694 0,
3695 "f",
3696 "LCD, CCFL Backlight",
3697 0,
3698 disptech_lcd_ccfl,
3699 (1 << 1) | 1
3700 },
3701 {
3702 inst_dtflags_none, /* flags */
3703 0,
3704 "L",
3705 "Wide Gamut LCD, CCFL Backlight",
3706 0,
3707 disptech_lcd_ccfl_wg,
3708 (2 << 1) | 1
3709 },
3710 {
3711 inst_dtflags_none, /* flags */
3712 0,
3713 "e",
3714 "LCD, White LED Backlight",
3715 0,
3716 disptech_lcd_wled,
3717 (3 << 1) | 1
3718 },
3719 {
3720 inst_dtflags_none, /* flags */
3721 0,
3722 "B",
3723 "Wide Gamut LCD, RGB LED Backlight",
3724 0,
3725 disptech_lcd_rgbled,
3726 (4 << 1) | 1
3727 },
3728 {
3729 inst_dtflags_none, /* flags */
3730 0,
3731 "x",
3732 "LCD, CCFL Backlight (Laptop ?)",
3733 0,
3734 disptech_lcd_ccfl,
3735 (5 << 1) | 1
3736 },
3737 {
3738 inst_dtflags_end,
3739 0,
3740 "",
3741 "",
3742 0,
3743 disptech_none,
3744 0
3745 }
3746 };
3747
set_base_disptype_list(spyd2 * p)3748 static void set_base_disptype_list(spyd2 *p) {
3749 /* set the base display type list */
3750 if (p->itype == instSpyder4
3751 || p->itype == instSpyder5) {
3752 if (spyd4_nocals <= 1) {
3753 p->_dtlist = spyd4_disptypesel_1;
3754 } else { /* spyd4_nocals == 6 or 7, Spyder 4 or 5. */
3755 /* Spyder 5 has exactly the same list as the Spyder 4, with an extra */
3756 /* entry at the end that is the same as the first (flat spectrum). */
3757 /* So use the spyder 4 list */
3758 p->_dtlist = spyd4_disptypesel;
3759 }
3760 } else if (p->itype == instSpyder3) {
3761 p->_dtlist = spyd3_disptypesel;
3762 } else {
3763 p->_dtlist = spyd2_disptypesel;
3764 }
3765 }
3766
3767 /* Get mode and option details */
spyd2_get_disptypesel(inst * pp,int * pnsels,inst_disptypesel ** psels,int allconfig,int recreate)3768 static inst_code spyd2_get_disptypesel(
3769 inst *pp,
3770 int *pnsels, /* Return number of display types */
3771 inst_disptypesel **psels, /* Return the array of display types */
3772 int allconfig, /* nz to return list for all configs, not just current. */
3773 int recreate /* nz to re-check for new ccmx & ccss files */
3774 ) {
3775 spyd2 *p = (spyd2 *)pp;
3776 inst_code rv = inst_ok;
3777
3778 /* Create/Re-create a current list of available display types */
3779 if (p->dtlist == NULL || recreate) {
3780 if ((rv = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist,
3781 p->_dtlist, p->hwver >= 7 ? 1 : 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
3782 return rv;
3783 }
3784
3785 if (pnsels != NULL)
3786 *pnsels = p->ndtlist;
3787
3788 if (psels != NULL)
3789 *psels = p->dtlist;
3790
3791 return inst_ok;
3792 }
3793
3794 /* Given a display type entry, setup for that type */
set_disp_type(spyd2 * p,inst_disptypesel * dentry)3795 static inst_code set_disp_type(spyd2 *p, inst_disptypesel *dentry) {
3796 inst_code ev;
3797
3798 p->icx = dentry->ix;
3799 p->dtech = dentry->dtech;
3800 p->cbid = dentry->cbid;
3801
3802 update_refmode(p, dentry->refr);
3803
3804 if (dentry->flags & inst_dtflags_ccss) { /* Spectral sample */
3805
3806 if ((ev = spyd2_set_speccal(p, dentry->sets, dentry->no_sets)) != inst_ok)
3807 return ev;
3808 p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */
3809
3810 } else { /* Matrix */
3811
3812 if (dentry->flags & inst_dtflags_ccmx) {
3813 if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok)
3814 return ev;
3815 if ((ev = spyd2_set_matcal(p, dentry->mat)) != inst_ok)
3816 return ev;
3817 p->cbid = 0; /* Matrix is an override of cbid */
3818
3819 } else {
3820 if ((ev = spyd2_set_matcal(p, NULL)) != inst_ok) /* Noop */
3821 return ev;
3822 p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */
3823 }
3824 }
3825 return spyd2_set_cal(p);
3826 }
3827
3828
3829 /* Set the display type */
spyd2_set_disptype(inst * pp,int ix)3830 static inst_code spyd2_set_disptype(inst *pp, int ix) {
3831 spyd2 *p = (spyd2 *)pp;
3832 inst_code ev;
3833 inst_disptypesel *dentry;
3834
3835 if (!p->gotcoms)
3836 return inst_no_coms;
3837 if (!p->inited)
3838 return inst_no_init;
3839
3840 if (p->dtlist == NULL) {
3841 if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist,
3842 p->_dtlist, p->hwver >= 7 ? 1 : 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
3843 return ev;
3844 }
3845
3846 if (ix < 0 || ix >= p->ndtlist)
3847 return inst_unsupported;
3848
3849 dentry = &p->dtlist[ix];
3850
3851 if ((ev = set_disp_type(p, dentry)) != inst_ok) {
3852 return ev;
3853 }
3854
3855 return inst_ok;
3856 }
3857
3858 /* Setup the default display type */
set_default_disp_type(spyd2 * p)3859 static inst_code set_default_disp_type(spyd2 *p) {
3860 inst_code ev;
3861 int i;
3862
3863 if (p->dtlist == NULL) {
3864 if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
3865 p->_dtlist, p->hwver >= 7 ? 1 : 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
3866 return ev;
3867 }
3868
3869 for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) {
3870 if (p->dtlist[i].flags & inst_dtflags_default)
3871 break;
3872 }
3873 if (p->dtlist[i].flags & inst_dtflags_end) {
3874 a1loge(p->log, 1, "set_default_disp_type: failed to find type!\n");
3875 return inst_internal_error;
3876 }
3877 if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) {
3878 return ev;
3879 }
3880
3881 return inst_ok;
3882 }
3883
3884 /* Setup the display type to the given base type */
set_base_disp_type(spyd2 * p,int cbid)3885 static inst_code set_base_disp_type(spyd2 *p, int cbid) {
3886 inst_code ev;
3887 int i;
3888
3889 if (cbid == 0) {
3890 a1loge(p->log, 1, "spyd2 set_base_disp_type: can't set base display type of 0\n");
3891 return inst_wrong_setup;
3892 }
3893 if (p->dtlist == NULL) {
3894 if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
3895 p->_dtlist, p->hwver >= 7 ? 1 : 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
3896 return ev;
3897 }
3898
3899 for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) {
3900 if (!(p->dtlist[i].flags & inst_dtflags_ccmx) /* Prevent infinite recursion */
3901 && p->dtlist[i].cbid == cbid)
3902 break;
3903 }
3904 if (p->dtlist[i].flags & inst_dtflags_end) {
3905 a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid);
3906 return inst_wrong_setup;
3907 }
3908 if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) {
3909 return ev;
3910 }
3911
3912 return inst_ok;
3913 }
3914
3915 /* Get the disptech and other corresponding info for the current */
3916 /* selected display type. Returns disptype_unknown by default. */
3917 /* Because refrmode can be overridden, it may not match the refrmode */
3918 /* of the dtech. (Pointers may be NULL if not needed) */
spyd2_get_disptechi(inst * pp,disptech * dtech,int * refrmode,int * cbid)3919 static inst_code spyd2_get_disptechi(
3920 inst *pp,
3921 disptech *dtech,
3922 int *refrmode,
3923 int *cbid) {
3924 spyd2 *p = (spyd2 *)pp;
3925 if (dtech != NULL)
3926 *dtech = p->dtech;
3927 if (refrmode != NULL) {
3928 *refrmode = p->refrmode;
3929 }
3930 if (cbid != NULL)
3931 *cbid = p->cbid;
3932 return inst_ok;
3933 }
3934
3935 /*
3936 * set or reset an optional mode
3937 *
3938 * Some options talk to the instrument, and these will
3939 * error if it hasn't been initialised.
3940 * [We could fix this by setting a flag and adding
3941 * some extra logic in init()]
3942 */
3943 static inst_code
spyd2_get_set_opt(inst * pp,inst_opt_type m,...)3944 spyd2_get_set_opt(inst *pp, inst_opt_type m, ...) {
3945 spyd2 *p = (spyd2 *)pp;
3946 inst_code ev = inst_ok;
3947
3948 /* Record the trigger mode */
3949 if (m == inst_opt_trig_prog
3950 || m == inst_opt_trig_user) {
3951 p->trig = m;
3952 return inst_ok;
3953 }
3954
3955 if (!p->gotcoms)
3956 return inst_no_coms;
3957 if (!p->inited)
3958 return inst_no_init;
3959
3960 /* Set the ccss observer type */
3961 if (m == inst_opt_set_ccss_obs) {
3962 va_list args;
3963 icxObserverType obType;
3964 xspect *custObserver;
3965
3966 va_start(args, m);
3967 obType = va_arg(args, icxObserverType);
3968 custObserver = va_arg(args, xspect *);
3969 va_end(args);
3970
3971 if (obType == icxOT_default)
3972 obType = icxOT_CIE_1931_2;
3973 p->obType = obType;
3974 if (obType == icxOT_custom) {
3975 p->custObserver[0] = custObserver[0];
3976 p->custObserver[1] = custObserver[1];
3977 p->custObserver[2] = custObserver[2];
3978 }
3979
3980 return spyd2_set_cal(p); /* Recompute calibration */
3981 }
3982
3983 /* Operate the LED */
3984 if (p->hwver >= 4) {
3985 if (m == inst_opt_get_gen_ledmask) {
3986 va_list args;
3987 int *mask = NULL;
3988
3989 va_start(args, m);
3990 mask = va_arg(args, int *);
3991 va_end(args);
3992 *mask = 0x1; /* One general LED */
3993 return inst_ok;
3994 } else if (m == inst_opt_get_led_state) {
3995 va_list args;
3996 int *mask = NULL;
3997
3998 va_start(args, m);
3999 mask = va_arg(args, int *);
4000 va_end(args);
4001 *mask = p->led_state;
4002 return inst_ok;
4003 } else if (m == inst_opt_set_led_state) {
4004 va_list args;
4005 int mask = 0;
4006
4007 va_start(args, m);
4008 mask = 1 & va_arg(args, int);
4009 va_end(args);
4010 if ((ev = spyd2_setLED(p, mask & 1 ? 2 : 0, 0.0)) == inst_ok) {
4011 p->led_state = mask;
4012 }
4013 return ev;
4014 }
4015 }
4016
4017 if (m == inst_opt_get_pulse_ledmask) {
4018 va_list args;
4019 int *mask = NULL;
4020
4021 va_start(args, m);
4022 mask = va_arg(args, int *);
4023 va_end(args);
4024 *mask = 0x1; /* General LED is pulsable */
4025 return inst_ok;
4026 } else if (m == inst_opt_set_led_pulse_state) {
4027 va_list args;
4028 double period, on_time_prop, trans_time_prop;
4029 int mode;
4030
4031 va_start(args, m);
4032 period = va_arg(args, double);
4033 on_time_prop = va_arg(args, double);
4034 trans_time_prop = va_arg(args, double);
4035 va_end(args);
4036 if (period < 0.0
4037 || on_time_prop < 0.0 || on_time_prop > 1.0
4038 || trans_time_prop < 0.0 || trans_time_prop > 1.0
4039 || trans_time_prop > on_time_prop || trans_time_prop > (1.0 - on_time_prop))
4040 return inst_bad_parameter;
4041 if (period == 0.0 || on_time_prop == 0.0) {
4042 period = 0.0;
4043 mode = 0;
4044 p->led_state = 0;
4045 } else {
4046 mode = 1;
4047 p->led_state = 1;
4048 }
4049 p->led_period = period;
4050 p->led_on_time_prop = on_time_prop;
4051 p->led_trans_time_prop = trans_time_prop;
4052 return spyd2_setLED(p, mode, period);
4053 } else if (m == inst_opt_get_led_state) {
4054 va_list args;
4055 double *period, *on_time_prop, *trans_time_prop;
4056
4057 va_start(args, m);
4058 period = va_arg(args, double *);
4059 on_time_prop = va_arg(args, double *);
4060 trans_time_prop = va_arg(args, double *);
4061 va_end(args);
4062 if (period != NULL) *period = p->led_period;
4063 if (on_time_prop != NULL) *on_time_prop = p->led_on_time_prop;
4064 if (trans_time_prop != NULL) *trans_time_prop = p->led_trans_time_prop;
4065 return inst_ok;
4066 }
4067
4068 /* Use default implementation of other inst_opt_type's */
4069 {
4070 inst_code rv;
4071 va_list args;
4072
4073 va_start(args, m);
4074 rv = inst_get_set_opt_def(pp, m, args);
4075 va_end(args);
4076
4077 return rv;
4078 }
4079 }
4080
4081 /* Constructor */
new_spyd2(icoms * icom,instType itype)4082 extern spyd2 *new_spyd2(icoms *icom, instType itype) {
4083 spyd2 *p;
4084 if ((p = (spyd2 *)calloc(sizeof(spyd2),1)) == NULL) {
4085 a1loge(icom->log, 1, "new_spyd2: malloc failed!\n");
4086 return NULL;
4087 }
4088
4089 p->log = new_a1log_d(icom->log);
4090
4091 p->init_coms = spyd2_init_coms;
4092 p->init_inst = spyd2_init_inst;
4093 p->capabilities = spyd2_capabilities;
4094 p->check_mode = spyd2_check_mode;
4095 p->set_mode = spyd2_set_mode;
4096 p->get_disptypesel = spyd2_get_disptypesel;
4097 p->set_disptype = spyd2_set_disptype;
4098 p->get_disptechi = spyd2_get_disptechi;
4099 p->get_set_opt = spyd2_get_set_opt;
4100 p->read_sample = spyd2_read_sample;
4101 p->read_refrate = spyd2_read_refrate;
4102 p->get_n_a_cals = spyd2_get_n_a_cals;
4103 p->calibrate = spyd2_calibrate;
4104 p->col_cor_mat = spyd2_col_cor_mat;
4105 p->col_cal_spec_set = spyd2_col_cal_spec_set;
4106 p->get_refr_rate = spyd2_get_refr_rate;
4107 p->set_refr_rate = spyd2_set_refr_rate;
4108 p->interp_error = spyd2_interp_error;
4109 p->del = spyd2_del;
4110
4111 p->icom = icom;
4112 p->itype = itype;
4113
4114 /* Load manufacturers Spyder4 calibrations */
4115 if (itype == instSpyder4
4116 || itype == instSpyder5) {
4117 int rv;
4118 p->hwver = 7; /* Set preliminary version */
4119 if ((rv = spyd4_load_cal(p)) != SPYD2_OK)
4120 a1logd(p->log, 1, "Loading Spyder4 calibrations failed with '%s'\n",p->interp_error((inst *)p, rv));
4121 if (spyd4_nocals < 1)
4122 a1logd(p->log, 1, "Spyder4 choice of calibrations not available\n");
4123 }
4124 if (itype == instSpyder3) {
4125 p->hwver = 4; /* Set preliminary version */
4126 }
4127 if (itype == instSpyder1 // ????
4128 || itype == instSpyder2) {
4129 p->hwver = 3; /* Set preliminary version */
4130 }
4131
4132 icmSetUnity3x3(p->ccmat); /* Set the colorimeter correction matrix to do nothing */
4133 set_base_disptype_list(p);
4134 p->dtech = disptech_unknown;
4135
4136 return p;
4137 }
4138
4139
4140 /* This is called by utilities that need to be able to access the Spyder 2 colorimeter. */
4141 /* and be able to check if the firmware is available. */
4142
4143 /* id = 0 for Spyder 1, 1 for Spyder 2 */
4144 /* Return 0 if Spyder firmware is not available */
4145 /* Return 1 if Spyder firmware is available */
setup_spyd2(int id)4146 int setup_spyd2(int id) {
4147 #ifdef ENABLE_USB
4148 char **bin_paths = NULL;
4149 int no_paths = 0;
4150 unsigned int size, rsize;
4151 char *p1;
4152 FILE *fp;
4153 int i;
4154
4155 id &= 1;
4156
4157 /* If not loaded, try and load it */
4158 if (spyder_pld_size[id] == 0) {
4159
4160
4161 for (;;) { /* So we can break out */
4162 if (id == 0)
4163 p1 = "ArgyllCMS/spyd1PLD.bin" XDG_FUDGE "color/spyd1PLD.bin";
4164 else
4165 p1 = "ArgyllCMS/spyd2PLD.bin" XDG_FUDGE "color/spyd2PLD.bin";
4166
4167 if ((no_paths = xdg_bds(NULL, &bin_paths, xdg_data, xdg_read, xdg_user, p1)) < 1) {
4168 a1logd(g_log, 1, "setup_spyd2: failed to find PLD file on path '%s'\n",p1);
4169 break;
4170 }
4171
4172 /* open binary file */
4173 #if !defined(O_CREAT) && !defined(_O_CREAT)
4174 # error "Need to #include fcntl.h!"
4175 #endif
4176 #if defined(O_BINARY) || defined(_O_BINARY)
4177 if ((fp = fopen(bin_paths[0],"rb")) == NULL)
4178 #else
4179 if ((fp = fopen(bin_paths[0],"r")) == NULL)
4180 #endif
4181 {
4182 a1logd(g_log, 1, "setup_spyd2: couldn't find '%s'\n",bin_paths[0]);
4183 break;
4184 }
4185
4186 /* Figure out how file it is */
4187 if (fseek(fp, 0, SEEK_END)) {
4188 fclose(fp);
4189 break;
4190 }
4191 size = (unsigned long)ftell(fp);
4192 rsize = (size + 7) & ~7; /* Rounded up size */
4193
4194 if ((spyder_pld_bytes[id] = malloc(rsize)) == NULL) {
4195 a1logd(g_log,1,"Spyder pld load malloc failed\n");
4196 fclose(fp);
4197 break;
4198 }
4199
4200 if (fseek(fp, 0, SEEK_SET)) {
4201 fclose(fp);
4202 break;
4203 }
4204
4205 if (fread(spyder_pld_bytes[id], 1, size, fp) != size) {
4206 fclose(fp);
4207 break;
4208 }
4209
4210 /* Pad out to even 8 bytes */
4211 for (i = size; i < rsize; i++)
4212 spyder_pld_bytes[id][i] = 0xff;
4213
4214 spyder_pld_size[id] = rsize;
4215
4216 a1logd(g_log, 1, "setup_spyd2: loaded '%s' OK\n",bin_paths[0]);
4217
4218 fclose(fp);
4219 break;
4220 }
4221 xdg_free(bin_paths, no_paths);
4222 }
4223
4224 if (spyder_pld_size[id] != 0)
4225 return 1; /* Available */
4226 #endif /* ENABLE_USB */
4227 return 0; /* Not available */
4228 }
4229