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