1 
2 /*
3  * Argyll Color Correction System
4  *
5  * GretagMacbeth Huey related functions
6  *
7  * Author: Graeme W. Gill
8  * Date:   18/10/2006
9  *
10  * Copyright 2006 - 2014, Graeme W. Gill
11  * All rights reserved.
12  *
13  * (Based 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    If you make use of the instrument driver code here, please note
21    that it is the author(s) of the code who take responsibility
22    for its operation. Any problems or queries regarding driving
23    instruments with the Argyll drivers, should be directed to
24    the Argyll's author(s), and not to any other party.
25 
26    If there is some instrument feature or function that you
27    would like supported here, it is recommended that you
28    contact Argyll's author(s) first, rather than attempt to
29    modify the software yourself, if you don't have firm knowledge
30    of the instrument communicate protocols. There is a chance
31    that an instrument could be damaged by an incautious command
32    sequence, and the instrument companies generally cannot and
33    will not support developers that they have not qualified
34    and agreed to support.
35  */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <ctype.h>
40 #include <string.h>
41 #include <time.h>
42 #include <stdarg.h>
43 #include <math.h>
44 #ifndef SALONEINSTLIB
45 #include "copyright.h"
46 #include "aconfig.h"
47 #include "numlib.h"
48 #else /* SALONEINSTLIB */
49 #include "sa_config.h"
50 #include "numsup.h"
51 #endif /* SALONEINSTLIB */
52 #include "xspect.h"
53 #include "insttypes.h"
54 #include "conv.h"
55 #include "icoms.h"
56 #include "huey.h"
57 
58 #define dbgo stderr
59 
60 static inst_code huey_interp_code(inst *pp, int ec);
61 static inst_code huey_check_unlock(huey *p);
62 
63 #define CALFACTOR 3.428         	/* Emissive magic calibration factor */
64 //#define AMB_SCALE_FACTOR 5.772e-3	/* Ambient mode scale factor Lux/pi*/
65 #define AMB_SCALE_FACTOR 18.1333e-3	/* Ambient mode scale factor (Lux) */
66 									/* This is only approximate, and were derived */
67 									/* by matching readings from the i1pro. */
68 
69 /* ------------------------------------------------------------------------ */
70 /* Implementation */
71 
72 /* Interpret an icoms error into a HUEY error */
73 /* If torc is nz, then a trigger or command is OK, */
74 /* othewise  they are treated as an abort. */
icoms2huey_err(int se,int torc)75 static int icoms2huey_err(int se, int torc) {
76 	if (se != ICOM_OK)
77 		return HUEY_COMS_FAIL;
78 	return HUEY_OK;
79 }
80 
81 /* i1Display command codes */
82 /* B = byte (8bit), S = short (16bit), W = word (32bit), A = string */
83 /* U = unused byte, - = no arguments/results */
84 /* The is a 7 byte command buffer and 6 response recieve buffer. */
85 /* :2 means the read is from a second 8 byte ep x81 read. */
86 /* cbuf[-] is command byte */
87 /* rbuf[-2] is continuation byte */
88 /* rbuf[-1] is echo of command byte */
89 /* rbuf2[-2] is an error byte if nz */
90 typedef enum {
91     i1d_status       = 0x00,		/* -:A         Get status string starting at 0, 1sec */
92     i1d_rd_green     = 0x02,		/* -:W         Read the green channel, 1sec */
93     i1d_rd_blue      = 0x03,		/* -:W         Read the blue channel, 1sec */
94     i1d_setintgt     = 0x05,		/* W:-         Set the integration time, 1sec */
95     i1d_getintgt     = 0x06,		/* -:W         Get the integration time, 1sec */
96     i1d_wrreg        = 0x07,		/* BB:?        Write a register value, 1sec */
97     i1d_rdreg        = 0x08,		/* B:B         Read a register value, 1sec */
98     i1d_unlock       = 0x0e,		/* BBBB:-      Unlock the interface */
99     i1d_m_red_2      = 0x13,		/* B:2:W       Measure the red channel in freq mode, 1,10sec */
100 									/* B = sync mode, typically 2 */
101     i1d_m_rgb_edge_2 = 0x16,		/* SSS:2:WB    Measure RGB edge/period mode, 1.70sec, ret red */
102 									/* 2nd return value is not used ? */
103     i1d_rdambient    = 0x17,		/* BB:2:BWB    Read Ambient, 1,10sec */
104 									/* Returns first B param as first response */
105 									/* Returns W as value read */
106 									/* Returns aditional byte at end */
107 
108     i1d_set_leds     = 0x18, 		/* BB:B        Set 4 LEDs state, 1sec */
109 									/* 1st B is always 0 in practice */
110 									/* 2nd B bits 0-4, 0 == on */
111 									/* Echo's led state with returned B */
112     i1d_rgb_edge_3   = 0x19 		/* SB:2:WB  Unknown measurement command 1,10sec */
113 									/* S is number of edges ?? */
114 									/* B is the channel */
115 									/* W is the reading */
116 									/* B is not used ? */
117 
118 } i1DispCC;
119 
120 /* Diagnostic - return a description given the instruction code */
inst_desc(int cc)121 static char *inst_desc(int cc) {
122 	static char buf[40];			/* Fallback string */
123 	switch(cc) {
124 		case 0x00:
125 			return "GetStatus";
126 		case 0x02:
127 			return "RdGreen";
128 		case 0x03:
129 			return "RdBlue";
130 		case 0x05:
131 			return "SetIntTime";
132 		case 0x06:
133 			return "GetIntTime";
134 		case 0x07:
135 			return "WrReg";
136 		case 0x08:
137 			return "RdReg";
138 		case 0x09:
139 			return "GetMeasPeriod";
140 		case 0x0e:
141 			return "Unlock";
142 		case 0x13:
143 			return "RdRedFreqMode";
144 		case 0x16:
145 			return "MeasRGBPeriMode";
146 		case 0x17:
147 			return "RdAmbient";
148 		case 0x18:
149 			return "SetLEDs";
150 		case 0x19:
151 			return "MeasRGBPeriMode2";
152 	}
153 	sprintf(buf,"Unknown %02x",cc);
154 	return buf;
155 }
156 
157 /* Do a command/response exchange with the huey. */
158 /* Return the error code */
159 /* The Huey is set up as an HID device, which can ease the need */
160 /* for providing a kernel driver on MSWindows systems, */
161 /* but it doesn't seem to actually be used as an HID device. */
162 /* We allow for communicating via usbio, or an HID driver. */
163 static inst_code
huey_command(huey * p,i1DispCC cc,unsigned char * in,unsigned char * out,double to,double to2)164 huey_command(
165 	huey *p,					/* huey object */
166 	i1DispCC cc,				/* Command code */
167 	unsigned char *in,			/* 7 Command bytes to send */
168 	unsigned char *out,			/* 6 Response bytes returned */
169 	double to,					/* Timeout in seconds */
170 	double to2					/* Timeout in seconds for 2nd read */
171 ) {
172 	int i;
173 	unsigned char buf[8];	/* 8 bytes to write/read */
174 	int wbytes;				/* bytes written */
175 	int rbytes;				/* bytes read from ep */
176 	int se, ua = 0, rv = inst_ok;
177 	int ishid = p->icom->port_type(p->icom) == icomt_hid;
178 
179 	a1logd(p->log,5,"huey_command: Sending '%s' args '%s'\n",inst_desc(cc), icoms_tohex(in, 7));
180 
181 	/* Send the command using the control interface */
182 	buf[0] = cc;				/* Construct the command == HID report number */
183 	memmove(buf + 1, in, 7);
184 
185 	if (ishid) {
186 		se = p->icom->hid_write(p->icom, buf, 8, &wbytes, to);
187 	} else {
188 		se = p->icom->usb_control(p->icom,
189 		      IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_CLASS | IUSB_REQ_RECIP_INTERFACE, 0x9, 0x200, 0, buf, 8, to);
190 		wbytes = 8;
191 	}
192 	if (se != 0) {
193 		a1logd(p->log,1,"huey_command: command send failed with ICOM err 0x%x\n",se);
194 		return huey_interp_code((inst *)p, HUEY_COMS_FAIL);
195 	}
196 	rv = huey_interp_code((inst *)p, icoms2huey_err(ua, 0));
197 
198 	if (rv == inst_ok && wbytes != 8)
199 		rv = huey_interp_code((inst *)p, HUEY_BAD_WR_LENGTH);
200 
201 	a1logd(p->log,6,"huey_command: get inst code\n",rv);
202 
203 	if (rv != inst_ok) {
204 		/* Flush any response if write failed */
205 		if (ishid)
206 			p->icom->hid_read(p->icom, buf, 8, &rbytes, to);
207 		else
208 			p->icom->usb_read(p->icom, NULL, 0x81, buf, 8, &rbytes, to);
209 		return rv;
210 	}
211 
212 	/* Now fetch the response */
213 	a1logd(p->log,6,"huey_command: Reading response\n");
214 
215 	if (ishid) {
216 		se = p->icom->hid_read(p->icom, buf, 8, &rbytes, to);
217 	} else {
218 		se = p->icom->usb_read(p->icom, NULL, 0x81, buf, 8, &rbytes, to);
219 	}
220 	if (se != 0) {
221 		a1logd(p->log,1,"huey_command: read failed with ICOM err 0x%x\n",se);
222 		return huey_interp_code((inst *)p, HUEY_COMS_FAIL);
223 	}
224 	rv = huey_interp_code((inst *)p, icoms2huey_err(ua, 0));
225 	if (rv == inst_ok && rbytes != 8)
226 		rv = huey_interp_code((inst *)p, HUEY_BAD_RD_LENGTH);
227 	if (rv == inst_ok && buf[1] != cc)
228 		rv = huey_interp_code((inst *)p, HUEY_BAD_RET_CMD);
229 
230 	/* Some commands don't use the first response, but need to */
231 	/* fetch a second response, with a longer timeout. */
232 	/* This seems to be all of the measurement trigger commands */
233 	if (rv == inst_ok && buf[0] == 0x90) {	/* there is more */
234 		a1logd(p->log,6,"huey_command: Reading extended response\n");
235 
236 		if (ishid) {
237 			se = p->icom->hid_read(p->icom, buf, 8, &rbytes, to2);
238 		} else {
239 			se = p->icom->usb_read(p->icom, NULL, 0x81, buf, 8, &rbytes, to2);
240 		}
241 		if (se != 0) {
242 			a1logd(p->log,1,"huey_command: read failed with ICOM err 0x%x\n",se);
243 			return huey_interp_code((inst *)p, HUEY_COMS_FAIL);
244 		}
245 		rv = huey_interp_code((inst *)p, icoms2huey_err(ua, 0));
246 		if (rv == inst_ok && rbytes != 8)
247 			rv = huey_interp_code((inst *)p, HUEY_BAD_RD_LENGTH);
248 		if (rv == inst_ok && buf[1] != cc) {
249 			rv = huey_interp_code((inst *)p, HUEY_BAD_RET_CMD);
250 		}
251 	}
252 
253 	/* The first byte returned seems to be a  command result error code. */
254 	/* Not all codes are known, but it seems that the 6 byte payload */
255 	/* is an error message, for instance 0x80 "NoCmd". */
256 	/* The second byte is always the command code being echo'd back. */
257 	if (rv == inst_ok && cc != 0x00 && buf[0] != 0x00) {
258 		ua = HUEY_BAD_RET_STAT;
259 		if (buf[0] == 0x80)
260 			ua = HUEY_BAD_COMMAND;
261 		rv = huey_interp_code((inst *)p, ua);
262 	}
263 
264 	if (rv == inst_ok) {
265 		memmove(out, buf + 2, 6);
266 	} else {
267 		memset(out, 0, 6);
268 	}
269 	a1logd(p->log,5,"huey_command: returning '%s' ICOM err 0x%x\n",icoms_tohex(out, 6),ua);
270 
271 	return rv;
272 }
273 
274 /* Take an int, and convert it into a byte buffer */
int2buf(unsigned char * buf,int inv)275 static void int2buf(unsigned char *buf, int inv) {
276 	buf[0] = (inv >> 24) & 0xff;
277 	buf[1] = (inv >> 16) & 0xff;
278 	buf[2] = (inv >> 8) & 0xff;
279 	buf[3] = (inv >> 0) & 0xff;
280 }
281 
282 /* Take a short, and convert it into a byte buffer */
short2buf(unsigned char * buf,int inv)283 static void short2buf(unsigned char *buf, int inv) {
284 	buf[0] = (inv >> 8) & 0xff;
285 	buf[1] = (inv >> 0) & 0xff;
286 }
287 
288 /* Take a word sized return buffer, and convert it to an int */
buf2int(unsigned char * buf)289 static int buf2int(unsigned char *buf) {
290 	int val;
291 	val = buf[0];
292 	val = ((val << 8) + (0xff & buf[1]));
293 	val = ((val << 8) + (0xff & buf[2]));
294 	val = ((val << 8) + (0xff & buf[3]));
295 	return val;
296 }
297 
298 /* Read a byte from a register */
299 static inst_code
huey_rdreg_byte(huey * p,int * outp,int addr)300 huey_rdreg_byte(
301 	huey *p,				/* Object */
302 	int *outp,				/* Where to write value */
303 	int addr				/* Register Address, 0 - 255 */
304 ) {
305 	unsigned char buf[8];
306 	int rsize;
307 	inst_code ev;
308 
309 	if (addr < 0 || addr > 255)
310 		return huey_interp_code((inst *)p, HUEY_BAD_REG_ADDRESS);
311 
312 	/* Read a byte */
313 	memset(buf, 0, 7);
314 	buf[0] = addr;
315 	if ((ev = huey_command(p, i1d_rdreg, buf, buf, 1.0, 1.0)) != inst_ok)
316 		return ev;
317 
318 	/* We expect the address to be returned */
319 	if ((buf[0] & 0xff) != addr)
320 		return huey_interp_code((inst *)p, HUEY_UNEXPECTED_RET_VAL);
321 
322 	*outp = (int)(buf[1] & 0xff);
323 
324 	return inst_ok;
325 }
326 
327 /* Read a short from a register */
328 static inst_code
huey_rdreg_short(huey * p,int * outp,int addr)329 huey_rdreg_short(
330 	huey *p,				/* Object */
331 	int *outp,				/* Where to write value */
332 	int addr				/* Register Address, 0 - 126 */
333 ) {
334 	inst_code ev;
335 	int v, val;
336 
337 	if ((ev = huey_rdreg_byte(p, &v, addr)) != inst_ok)
338 		return ev;
339 	val = v;
340 
341 	if ((ev = huey_rdreg_byte(p, &v, addr+1)) != inst_ok)
342 		return ev;
343 	val = ((val << 8) + (0xff & v));
344 
345 	*outp = val;
346 
347 	return inst_ok;
348 }
349 
350 /* Read a word from a register */
351 static inst_code
huey_rdreg_word(huey * p,int * outp,int addr)352 huey_rdreg_word(
353 	huey *p,				/* Object */
354 	int *outp,				/* Where to write value */
355 	int addr				/* Register Address, 0 - 124 */
356 ) {
357 	inst_code ev;
358 	int v, val;
359 
360 	if ((ev = huey_rdreg_byte(p, &v, addr)) != inst_ok)
361 		return ev;
362 	val = v;
363 
364 	if ((ev = huey_rdreg_byte(p, &v, addr+1)) != inst_ok)
365 		return ev;
366 	val = ((val << 8) + (0xff & v));
367 
368 	if ((ev = huey_rdreg_byte(p, &v, addr+2)) != inst_ok)
369 		return ev;
370 	val = ((val << 8) + (0xff & v));
371 
372 	if ((ev = huey_rdreg_byte(p, &v, addr+3)) != inst_ok)
373 		return ev;
374 	val = ((val << 8) + (0xff & v));
375 
376 	*outp = val;
377 
378 	return inst_ok;
379 }
380 
381 
382 /* Read a float from a register */
383 /* Will return HUEY_FLOAT_NOT_SET if the float value was 0xffffffff */
384 static inst_code
huey_rdreg_float(huey * p,double * outp,int addr)385 huey_rdreg_float(
386 	huey *p,				/* Object */
387 	double *outp,			/* Where to write value */
388 	int addr				/* Register Address, 0 - 124 */
389 ) {
390 	inst_code ev;
391 	int val;
392 
393 	if ((ev = huey_rdreg_word(p, &val, addr)) != inst_ok)
394 		return ev;
395 
396 	if (val == 0xffffffff) {
397 		return inst_ok;
398 	}
399 
400 	*outp = IEEE754todouble((unsigned int)val);
401 	return inst_ok;
402 }
403 
404 
405 /* Write a byte to a register */
406 static inst_code
huey_wrreg_byte(huey * p,int inv,int addr)407 huey_wrreg_byte(
408 	huey *p,				/* Object */
409 	int inv,				/* Input value */
410 	int addr				/* Register Address, 0 - 127 */
411 ) {
412 	int cval;
413 	unsigned char ibuf[8], obuf[8];
414 	int rsize;
415 	inst_code ev;
416 
417 	memset(ibuf, 0, 7);
418 	ibuf[0] = addr;
419 	ibuf[1] = inv;
420 
421 	/* Write a byte */
422 	if ((ev = huey_command(p, i1d_wrreg, ibuf, obuf, 1.0, 1.0)) != inst_ok)
423 		return ev;
424 
425 	return inst_ok;
426 }
427 
428 /* Write a word to a register */
429 static inst_code
huey_wrreg_word(huey * p,int inv,int addr)430 huey_wrreg_word(
431 	huey *p,				/* Object */
432 	int inv,				/* Where to write value */
433 	int addr				/* Register Address, 0 - 124 */
434 ) {
435 	inst_code ev;
436 	int v;
437 
438 	v = (inv >> 24) & 0xff;
439 	if ((ev = huey_wrreg_byte(p, v, addr) ) != inst_ok)
440 		return ev;
441 
442 	v = (inv >> 16) & 0xff;
443 	if ((ev = huey_wrreg_byte(p, v, addr+1) ) != inst_ok)
444 		return ev;
445 
446 	v = (inv >> 8) & 0xff;
447 	if ((ev = huey_wrreg_byte(p, v, addr+2) ) != inst_ok)
448 		return ev;
449 
450 	v = (inv >> 0) & 0xff;
451 	if ((ev = huey_wrreg_byte(p, v, addr+3) ) != inst_ok)
452 		return ev;
453 
454 	return inst_ok;
455 }
456 
457 /* Write a float to a register */
458 static inst_code
huey_wrreg_float(huey * p,double inv,int addr)459 huey_wrreg_float(
460 	huey *p,				/* Object */
461 	double inv,				/* Value to write */
462 	int addr				/* Register Address, 0 - 124 */
463 ) {
464 	inst_code ev;
465 	int val;
466 
467 	val = (int)doubletoIEEE754(inv);
468 
469 	if ((ev = huey_wrreg_word(p, val, addr)) != inst_ok)
470 		return ev;
471 	return inst_ok;
472 }
473 
474 /* Read the integration time */
475 static inst_code
huey_rd_int_time(huey * p,int * outp)476 huey_rd_int_time(
477 	huey *p,				/* Object */
478 	int *outp				/* Where to write value */
479 ) {
480 	unsigned char buf[8];
481 	int rsize;
482 	inst_code ev;
483 
484 	memset(buf, 0, 7);
485 	if ((ev = huey_command(p, i1d_getintgt, buf, buf, 1.0, 1.0)) != inst_ok)
486 		return ev;
487 
488 	*outp = buf2int(buf);
489 
490 	return inst_ok;
491 }
492 
493 /* Set the integration time */
494 /* (Not used for Huey ?) */
495 static inst_code
huey_wr_int_time(huey * p,int inv)496 huey_wr_int_time(
497 	huey *p,				/* Object */
498 	int inv					/* Value to write */
499 ) {
500 	unsigned char buf[16];
501 	int rsize;
502 	inst_code ev;
503 
504 	memset(buf, 0, 7);
505 	int2buf(buf, inv);
506 	if ((ev = huey_command(p, i1d_setintgt, buf, buf, 1.0, 1.0)) != inst_ok)
507 		return ev;
508 
509 	return inst_ok;
510 }
511 
512 /* - - - - - - - - - - - - - - - - - - - - - - - - */
513 
514 /* Take a raw measurement using a given integration time. */
515 /* The measureent is the count of (both) edges from the L2V */
516 /* over the integration time */
517 static inst_code
huey_freq_measure(huey * p,double rgb[3])518 huey_freq_measure(
519 	huey *p,				/* Object */
520 	double rgb[3]			/* Return the RGB edge count values */
521 ) {
522 	int i;
523 	unsigned char ibuf[8];
524 	unsigned char obuf[8];
525 	inst_code ev;
526 
527 	/* Do the measurement, and return the Red value */
528 	memset(ibuf, 0, 7);
529 	ibuf[0] = 2;		/* Sync mode 2 for CRT */
530 	if ((ev = huey_command(p, i1d_m_red_2, ibuf, obuf, 1.0, 10.0)) != inst_ok)
531 		return ev;
532 	rgb[0] = (double)buf2int(obuf);
533 
534 	/* Get the green value */
535 	memset(ibuf, 0, 7);
536 	if ((ev = huey_command(p, i1d_rd_green, ibuf, obuf, 1.0, 1.0)) != inst_ok)
537 		return ev;
538 	rgb[1] = (double)buf2int(obuf);
539 
540 	/* Get the blue value */
541 	memset(ibuf, 0, 7);
542 	if ((ev = huey_command(p, i1d_rd_blue, ibuf, obuf, 1.0, 1.0)) != inst_ok)
543 		return ev;
544 	rgb[2] = (double)buf2int(obuf);
545 
546 	return inst_ok;
547 }
548 
549 /* Take a raw measurement that returns the number of clocks */
550 /* between and initial edge and edgec[] subsequent edges of the L2F. */
551 /* Both edges are counted. */
552 static inst_code
huey_period_measure(huey * p,int edgec[3],double rgb[3])553 huey_period_measure(
554 	huey *p,			/* Object */
555 	int edgec[3],		/* Measurement edge count for each channel */
556 	double rgb[3]		/* Return the RGB values */
557 ) {
558 	int i;
559 	unsigned char ibuf[16];
560 	unsigned char obuf[16];
561 	int rsize;
562 	inst_code ev;
563 
564 	/* Set the edge count */
565 	memset(ibuf, 0, 7);
566 	short2buf(ibuf + 0, edgec[0]);
567 	short2buf(ibuf + 2, edgec[1]);
568 	short2buf(ibuf + 4, edgec[2]);
569 
570 	/* Do the measurement, and return the Red value */
571 	if ((ev = huey_command(p, i1d_m_rgb_edge_2, ibuf, obuf, 1.0, 70.0)) != inst_ok)
572 		return ev;
573 	rgb[0] = (double)buf2int(obuf);
574 
575 	/* Get the green value */
576 	memset(ibuf, 0, 7);
577 	if ((ev = huey_command(p, i1d_rd_green, ibuf, obuf, 1.0, 1.0)) != inst_ok)
578 		return ev;
579 	rgb[1] = (double)buf2int(obuf);
580 
581 	/* Get the blue value */
582 	memset(ibuf, 0, 7);
583 	if ((ev = huey_command(p, i1d_rd_blue, ibuf, obuf, 1.0, 1.0)) != inst_ok)
584 		return ev;
585 	rgb[2] = (double)buf2int(obuf);
586 
587 	return inst_ok;
588 }
589 
590 /* Take a another mode raw mesurement from the device for */
591 /* (not currently used ?) */
592 static inst_code
huey_take_raw_measurement_3(huey * p,int edgec[3],double rgb[3])593 huey_take_raw_measurement_3(
594 	huey *p,			/* Object */
595 	int edgec[3],		/* Measurement edge count for each channel */
596 	double rgb[3]		/* Return the RGB values */
597 ) {
598 	int i;
599 	unsigned char ibuf[16];
600 	unsigned char obuf[16];
601 	int rsize;
602 	inst_code ev;
603 
604 	/* Do the measurement, and return the Red value */
605 	memset(ibuf, 0, 7);
606 	short2buf(ibuf + 0, edgec[0]);
607 	ibuf[2] = 0;	/* Channel */
608 	if ((ev = huey_command(p, i1d_m_rgb_edge_2, ibuf, obuf, 1.0, 10.0)) != inst_ok)
609 		return ev;
610 	rgb[0] = (double)buf2int(obuf);
611 
612 	/* Do the measurement, and return the Green value */
613 	memset(ibuf, 0, 7);
614 	short2buf(ibuf + 0, edgec[1]);
615 	ibuf[2] = 1;	/* Channel */
616 	if ((ev = huey_command(p, i1d_m_rgb_edge_2, ibuf, obuf, 1.0, 10.0)) != inst_ok)
617 		return ev;
618 	rgb[1] = (double)buf2int(obuf);
619 
620 	/* Do the measurement, and return the Blue value */
621 	memset(ibuf, 0, 7);
622 	short2buf(ibuf + 0, edgec[2]);
623 	ibuf[2] = 2;	/* Channel */
624 	if ((ev = huey_command(p, i1d_m_rgb_edge_2, ibuf, obuf, 1.0, 10.0)) != inst_ok)
625 		return ev;
626 	rgb[2] = (double)buf2int(obuf);
627 
628 	return inst_ok;
629 }
630 
631 /* Take a cooked measurement from the device for Huey. */
632 /* The sensors are likely to be light to frequency converters */
633 /* such as the TAOS TSL237, and there are only so many ways */
634 /* the output can be measured. If the light level is high enough, */
635 /* you could simply count the number of output transitions (edges) */
636 /* in a fixed period of time. If the frequency is low though, */
637 /* this limits precision due to quantization of the count. */
638 /* Another way is to time how long it takes to count a certain */
639 /* number of edges. This has higher precision at low frequencies, */
640 /* but has the problem of having an unknown measurement duration, */
641 /* and seems to be the main method chosen for the Huey. */
642 static inst_code
huey_take_measurement_2(huey * p,int refr,double rgb[3])643 huey_take_measurement_2(
644 	huey *p,				/* Object */
645 	int refr,				/* nz if crt mode */
646 	double rgb[3]			/* Return the rgb values */
647 ) {
648 	int i, j;
649 	int edgec[3] = {1,1,1};	/* Measurement edge count for each channel */
650 	int rem[3] = {1,1,1};	/* remeasure flags */
651 	inst_code ev;
652 
653 	if (p->inited == 0)
654 		return huey_interp_code((inst *)p, HUEY_NOT_INITED);
655 
656 	a1logd(p->log,4,"take_measurement_2 called with refr = %d\n",refr);
657 
658 	/* For Refresh mode, do an initial set of measurements over a fixed period */
659 	if (refr) {
660 
661 		if ((ev = huey_freq_measure(p, rgb)) != inst_ok)
662 			return ev;
663 
664 		a1logd(p->log,4,"Raw initial CRT RGB = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
665 
666 		/* Decide whether any channels need re-measuring, */
667 		/* and computed cooked values. Threshold is typically 75 */
668 		for (i = 0; i < 3; i++) {
669 			rem[i] = (rgb[i] <= (0.75 * (double)p->sampno)) ? 1 : 0;
670 			rgb[i] = 0.5 * rgb[i] * 1e6/(double)p->int_clocks;
671 		}
672 		a1logd(p->log,4,"Re-measure flags = %d %d %d\n",rem[0],rem[1],rem[2]);
673 	}
674 
675 	/* If any need re-measuring */
676 	if (rem[0] || rem[1] || rem[2]) {
677 		double rgb2[3];
678 
679 		/* Do a first or second set of measurements */
680 		if ((ev = huey_period_measure(p, edgec, rgb2)) != inst_ok)
681 			return ev;
682 		a1logd(p->log,4,"Raw initial/subsequent ecount %d %d %d RGB = %f %f %f\n",
683 		     edgec[0], edgec[1], edgec[2], rgb2[0], rgb2[1], rgb2[2]);
684 
685 		/* Compute adjusted edge count for channels we're remeasuring, */
686 		/* aiming for count values of clk_freq (~1e6). */
687 		for (i = 0; i < 3; i++) {
688 			double ns;
689 			if (rem[i]) {
690 				if (p->clk_freq > ((2000.0 - 0.5) * rgb2[i]))
691 					ns = 2000.0;
692 				else {
693 					ns = floor(p->clk_freq/rgb2[i]) + 0.5;
694 					if (ns < 1.0)
695 						ns = 1.0;
696 				}
697 				edgec[i] = (int)ns;
698 			}
699 		}
700 
701 		/* If we compute a different edge count, read again */
702 		if (edgec[0] > 1 || edgec[1] > 1 || edgec[2] > 1) {
703 			double rgb3[3];		/* 2nd RGB Readings */
704 
705 			if ((ev = huey_period_measure(p, edgec, rgb3)) != inst_ok)
706 				return ev;
707 
708 			a1logd(p->log,4,"Raw subsequent2 ecount %d %d %d RGB = %f %f %f\n",
709 			     edgec[0], edgec[1], edgec[2], rgb3[0], rgb3[1], rgb3[2]);
710 
711 			/* Average readings if we repeated a measurement with the same threshold */
712 			/* (Minor advantage, but may as well use it) */
713 			for (i = 0; i < 3; i++) {
714 				if (edgec[i] == 1)
715 					rgb2[i] = 0.5 * (rgb2[i] + rgb3[i]);
716 				else
717 					rgb2[i] = rgb3[i];
718 			}
719 		}
720 
721 		/* Compute adjusted readings, ovewritting initial cooked values */
722 		for (i = 0; i < 3; i++) {
723 			if (rem[i]) {
724 				rgb[i] = ((double)edgec[i])/(rgb2[i] * 2.0 * p->clk_prd);
725 				a1logd(p->log,4,"%d after scale = %f\n",i,rgb[i]);
726 
727 				rgb[i] -= p->dark_cal[i];		/* Subtract black level */
728 				a1logd(p->log,4,"%d after sub black = %f\n",i,rgb[i]);
729 
730 				if (rgb[i] < 0.0001)
731 					rgb[i] = 0.0001;
732 				a1logd(p->log,4,"%d after limit min = %f\n",i,rgb[i]);
733 			}
734 		}
735 	}
736 
737 	a1logd(p->log,4,"Cooked RGB = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
738 
739 	return inst_ok;
740 }
741 
742 /* Take a raw ambient measurement */
743 static inst_code
huey_take_amb_measurement_1(huey * p,int a1,int syncmode,double * amb,int * rb)744 huey_take_amb_measurement_1(
745 	huey *p,			/* Object */
746 	int a1,				/* 8 bit argument - unknown */
747 	int syncmode,		/* 8 bit argument - sync mode */
748 	double *amb,		/* Return the raw ambient value */
749 	int *rb				/* Returned byte */
750 ) {
751 	int i;
752 	unsigned char ibuf[16];
753 	unsigned char obuf[16];
754 	int rsize;
755 	inst_code ev;
756 
757 	memset(ibuf, 0, 7);
758 	a1 &= 0xff;
759 	ibuf[0] = a1;
760 	ibuf[1] = syncmode;
761 
762 	/* Do the measurement */
763 	if ((ev = huey_command(p, i1d_rdambient, ibuf, obuf, 1.0, 10.0)) != inst_ok)
764 		return ev;
765 
766 	/* Expect first argument to be returned */
767 	if (obuf[0] != a1)
768 		return huey_interp_code((inst *)p, HUEY_UNEXPECTED_RET_VAL);
769 
770 	*amb = (double)buf2int(obuf+1);
771 	*rb = 0xff & obuf[5];
772 
773 	return inst_ok;
774 }
775 
776 /* Take a cooked ambient measurement */
777 static inst_code
huey_take_amb_measurement(huey * p,int refr,double * amb)778 huey_take_amb_measurement(
779 	huey *p,			/* Object */
780 	int refr,			/* nz if refresh mode */
781 	double *amb			/* Return the ambient value */
782 ) {
783 	int rb;				/* Returned byte - not used */
784 	inst_code ev;
785 
786 	if (p->inited == 0)
787 		return huey_interp_code((inst *)p, HUEY_NOT_INITED);
788 
789 	a1logd(p->log,4,"take_amb_measurement_2 called with refr = %d\n",refr);
790 
791 	/* First param is always 3, second is sync mode */
792 	if ((ev = huey_take_amb_measurement_1(p, 3, refr ? 2 : 0, amb, &rb)) != inst_ok)
793  		return ev;
794 	a1logd(p->log,4,"Raw ambient = %f\n",*amb);
795 	return inst_ok;
796 }
797 
798 /* Set the indicator LED's state. */
799 /* The bottom 4 bits set the LED state from bottom (0) */
800 /* to top (3), 0 = off, 1 = on */
801 static inst_code
huey_set_LEDs(huey * p,int mask)802 huey_set_LEDs(
803 	huey *p,			/* Object */
804 	int mask			/* 8 bit LED mask */
805 ) {
806 	int i;
807 	unsigned char ibuf[8];
808 	unsigned char obuf[8];
809 	inst_code ev;
810 
811 	memset(ibuf, 0, 7);
812 	mask &= 0xf;
813 	p->led_state = mask;
814 
815 	ibuf[0] = 0;
816 	ibuf[1] = 0xf & (~mask);
817 
818 	/* Do command */
819 	if ((ev = huey_command(p, i1d_set_leds, ibuf, obuf, 1.0, 1.0)) != inst_ok)
820 		return ev;
821 
822 	return inst_ok;
823 }
824 
825 /* . . . . . . . . . . . . . . . . . . . . . . . . */
826 
827 /* Take a XYZ measurement from the device */
828 static inst_code
huey_take_XYZ_measurement(huey * p,double XYZ[3])829 huey_take_XYZ_measurement(
830 	huey *p,				/* Object */
831 	double XYZ[3]			/* Return the XYZ values */
832 ) {
833 	int i, j;
834 	double rgb[3];		/* RGB Readings */
835 	inst_code ev;
836 	double *mat;		/* Pointer to matrix */
837 
838 	if (IMODETST(p->mode, inst_mode_emis_ambient)) {
839 		if ((ev = huey_take_amb_measurement(p, 0, &XYZ[1])) != inst_ok)
840 			return ev;
841 		XYZ[1] *= AMB_SCALE_FACTOR;		/* Times aproximate fudge factor to Lux */
842 		XYZ[0] = icmD50.X * XYZ[1];		/* Convert to D50 neutral */
843 		XYZ[2] = icmD50.Z * XYZ[1];
844 	} else {
845 		if ((ev = huey_take_measurement_2(p, p->refrmode, rgb)) != inst_ok)
846 			return ev;
847 
848 		if (p->icx)
849 			mat = p->CRT_cal;		/* CRT/factory matrix */
850 		else
851 			mat = p->LCD_cal;		/* LCD/user matrix */
852 
853 		for (i = 0; i < 3; i++) {
854 			XYZ[i] = 0.0;
855 			for (j = 0; j < 3; j++) {
856 				XYZ[i] += mat[i * 3 + j] * rgb[j];
857 			}
858 			XYZ[i] *= CALFACTOR;	/* Times magic scale factor */
859 		}
860 
861 		/* Apply the colorimeter correction matrix */
862 		icmMulBy3x3(XYZ, p->ccmat, XYZ);
863 	}
864 	a1logd(p->log,3,"huey_take_XYZ_measurement: XYZ = %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]);
865 	return inst_ok;
866 }
867 
868 /* - - - - - - - - - - - - - - - - - - - - - - - - */
869 
870 /* Check the device is responding, and unlock if necessary */
871 static inst_code
huey_check_unlock(huey * p)872 huey_check_unlock(
873 	huey *p				/* Object */
874 ) {
875 	unsigned char buf[8];
876 	int rsize;
877 	inst_code ev;
878 	int vv;
879 	double ver;
880 
881 	a1logd(p->log,2,"huey_check_unlock: called\n");
882 
883 	/* Check the instrument status */
884 	memset(buf, 0, 7);
885 	if ((ev = huey_command(p, i1d_status, buf, buf, 1.0,1.0)) != inst_ok)
886 		return ev;
887 
888 	/* Hmm. Some Lenovo Huey's say they are unlocked, even when they are not. */
889 	if (p->lenovo || strncmp((char *)buf, "Locked", 6) == 0) {
890 		memset(buf, 0, 7);
891 		if (p->lenovo)
892 			strcpy((char *)buf,"huyL");
893 		else
894 			strcpy((char *)buf,"GrMb");
895 
896 		if ((ev = huey_command(p, i1d_unlock, buf, buf, 1.0,1.0)) != inst_ok)
897 			return ev;
898 
899 		memset(buf, 0, 7);
900 		if ((ev = huey_command(p, i1d_status, buf, buf, 1.0,1.0)) != inst_ok)
901 			return ev;
902 	}
903 
904 	if (strncmp((char *)buf, "huL002", 6) != 0		/* Lenovo Huey ? */
905 	 && strncmp((char *)buf, "ECCM2 ", 6) != 0		/* Lenovo Thinkpad W530 Huey ? */
906 	 && strncmp((char *)buf, "ECCM3 ", 6) != 0		/* Lenovo Thinkpad W530 Huey ? */
907 	 && strncmp((char *)buf, "Cir001", 6) != 0) {	/* Huey */
908 		a1logd(p->log,1,"huey_check_unlock: unknown model '%s'\n",buf);
909 		return huey_interp_code((inst *)p, HUEY_UNKNOWN_MODEL);
910 	}
911 
912 	a1logd(p->log,2,"huey_check_unlock: instrument is responding, unlocked, and right type\n");
913 
914 	return inst_ok;
915 }
916 
917 /* Read all the relevant register values */
918 static inst_code
huey_read_all_regs(huey * p)919 huey_read_all_regs(
920 	huey *p				/* Object */
921 ) {
922 	inst_code ev;
923 	int i;
924 
925 	a1logd(p->log,2,"huey_read_all_regs: about to read all the registers\n");
926 
927 	/* Serial number */
928 	if ((ev = huey_rdreg_word(p, &p->ser_no, 0) ) != inst_ok)
929 		return ev;
930 	a1logd(p->log,4,"serial number = %d\n",p->ser_no);
931 
932 
933 	/* LCD/user calibration values */
934 	for (i = 0; i < 9; i++) {
935 		if ((ev = huey_rdreg_float(p, &p->LCD_cal[i], 4 + 4 * i) ) != inst_ok)
936 			return ev;
937 		a1logd(p->log,4,"LCD/user cal[%d] = %f\n",i,p->LCD_cal[i]);
938 	}
939 	/* LCD/user calibration time */
940 	if ((ev = huey_rdreg_word(p, &p->LCD_caltime, 50) ) != inst_ok)
941 		return ev;
942 	a1logd(p->log,2,"LCD/user calibration time = 0x%x = %s\n",p->LCD_caltime, ctime_32(&p->LCD_caltime));
943 
944 	/* CRT/factory calibration values */
945 	for (i = 0; i < 9; i++) {
946 		if ((ev = huey_rdreg_float(p, &p->CRT_cal[i], 54 + 4 * i) ) != inst_ok)
947 			return ev;
948 		a1logd(p->log,3,"CRT/factory cal[%d] = %f\n",i,p->CRT_cal[i]);
949 	}
950 	/* CRT/factory calibration flag */
951 	if ((ev = huey_rdreg_word(p, &p->CRT_caltime, 90) ) != inst_ok)
952 		return ev;
953 	a1logd(p->log,3,"CRT/factory flag = 0x%x = %s\n",p->CRT_caltime, ctime_32(&p->CRT_caltime));
954 
955 
956 	/* Hard coded in Huey */
957 	p->clk_prd = 1e-6;
958 	a1logd(p->log,3,"Clock period = %f\n",p->clk_prd);
959 
960 	/* Dark current calibration values */
961 	for (i = 0; i < 3; i++) {
962 		if ((ev = huey_rdreg_float(p, &p->dark_cal[i], 103 + 4 * i)) != inst_ok) {
963 			if ((ev & inst_imask) != HUEY_FLOAT_NOT_SET)
964 				return ev;
965 			p->dark_cal[i] = 0.0;
966 		}
967 		a1logd(p->log,3,"darkcal[%d] = %f\n",i,p->dark_cal[i]);
968 	}
969 
970 	/* Ambient darkcurrent calibration value ? */
971 	if ((ev = huey_rdreg_float(p, &p->amb_cal, 148)) != inst_ok) {
972 		if ((ev & inst_imask) != HUEY_FLOAT_NOT_SET)
973 			return ev;
974 		p->amb_cal = 0.0;
975 	}
976 	a1logd(p->log,3,"Ambient cal = %f\n",p->amb_cal);
977 
978 	/* Unlock string */
979 	for (i = 0; i < 4; i++) {
980 		int vv;
981 		if ((ev = huey_rdreg_byte(p, &vv, 122 + i) ) != inst_ok)
982 			return ev;
983 		p->unlk_string[i] = (char)vv;
984 	}
985 	p->unlk_string[i] = '\000';
986 	a1logd(p->log,3,"unlock string = '%s'\n",p->unlk_string);
987 
988 	/* Read the integration time */
989 	if ((ev = huey_rd_int_time(p, &p->int_clocks) ) != inst_ok)
990 		return ev;
991 	a1logd(p->log,3,"Integration time = %d\n",p->int_clocks);
992 
993 	a1logd(p->log,2,"huey_read_all_regs: all registers read OK\n");
994 
995 	return inst_ok;
996 }
997 
998 /* Compute factors that depend on the register values */
999 static inst_code
huey_compute_factors(huey * p)1000 huey_compute_factors(
1001 	huey *p				/* Object */
1002 ) {
1003 	int i;
1004 
1005 	/* Check that certain value are valid */
1006 	if (p->ser_no == 0xffffffff) /* (It appears that some instruments have no serial number!) */
1007 		a1logw(p->log,"huey: bad instrument serial number\n");
1008 
1009 	if (p->LCD_caltime == 0xffffffff)
1010 		return huey_interp_code((inst *)p, HUEY_BAD_LCD_CALIBRATION);
1011 
1012 	if (p->CRT_caltime == 0xffffffff)
1013 		return huey_interp_code((inst *)p, HUEY_BAD_CRT_CALIBRATION);
1014 
1015 	/* clk_prd inversion */
1016 	p->clk_freq = 1.0/p->clk_prd;
1017 	a1logd(p->log,3,"clk_freq = %f\n",p->clk_freq);
1018 
1019 	/* Set some defaults */
1020 	p->sampno = 100;		/* Minimum sampling count */
1021 
1022 	return inst_ok;
1023 }
1024 
1025 /* ------------------------------------------------------------------------ */
1026 
1027 static inst_code set_default_disp_type(huey *p);
1028 
1029 /* Establish communications with a HUEY */
1030 /* If it's a serial port, use the baud rate given, and timeout in to secs */
1031 /* Return HUEY_COMS_FAIL on failure to establish communications */
1032 static inst_code
huey_init_coms(inst * pp,baud_rate br,flow_control fc,double tout)1033 huey_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
1034 	huey *p = (huey *) pp;
1035 	unsigned char buf[8];
1036 	int rsize;
1037 	long etime;
1038 	int bi, i, se, rv;
1039 	inst_code ev = inst_ok;
1040 	char **pnames = NULL;
1041 	int retries = 0;
1042 
1043 	a1logd(p->log, 2, "huey_init_coms: About to init coms\n");
1044 
1045 	/* Open as an HID if available */
1046 	if (p->icom->port_type(p->icom) == icomt_hid) {
1047 
1048 		a1logd(p->log, 3, "huey_init_coms: About to init HID\n");
1049 
1050 		/* Set config, interface */
1051 		if ((se = p->icom->set_hid_port(p->icom, icomuf_none, retries, pnames)) != ICOM_OK) {
1052 			a1logd(p->log, 1, "huey_init_coms: set_hid_port failed ICOM err 0x%x\n",se);
1053 			return huey_interp_code((inst *)p, icoms2huey_err(se, 0));
1054 		}
1055 
1056 	} else if (p->icom->port_type(p->icom) == icomt_usb) {
1057 
1058 		a1logd(p->log, 3, "huey_init_coms: About to init USB\n");
1059 
1060 		/* Set config, interface, write end point, read end point */
1061 		/* ("serial" end points aren't used - the huey uses USB control messages) */
1062 		/* We need to detatch the HID driver on Linux */
1063 
1064 		if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, icomuf_detach, 0, NULL))
1065 		                                                                     != ICOM_OK) {
1066 			a1logd(p->log, 1, "huey_init_coms: set_usb_port failed ICOM err 0x%x\n",se);
1067 			return huey_interp_code((inst *)p, icoms2huey_err(se, 0));
1068 		}
1069 
1070 	} else {
1071 		a1logd(p->log, 1, "huey_init_coms: wrong communications type for device!\n");
1072 		return inst_coms_fail;
1073 	}
1074 
1075 	if ((p->icom->vid == 0x0765 && p->icom->pid == 0x5001)
1076 	||  (p->icom->vid == 0x0765 && p->icom->pid == 0x5010)) {
1077 		a1logd(p->log, 2, "huey_init_coms: Lenovo version\n");
1078 		p->lenovo = 1;
1079 	}
1080 
1081 	/* Check instrument is responding */
1082 	memset(buf, 0, 7);
1083 	if ((ev = huey_command(p, i1d_status, buf, buf, 1.0, 1.0)) != inst_ok) {
1084 		a1logd(p->log, 1, "huey_init_coms: instrument didn't respond 0x%x\n",ev);
1085 		return ev;
1086 	}
1087 
1088 	/* Setup the default display type */
1089 	if ((ev = set_default_disp_type(p)) != inst_ok) {
1090 		return ev;
1091 	}
1092 
1093 	a1logd(p->log, 2, "huey_init_coms: inited coms OK\n");
1094 
1095 	p->gotcoms = 1;
1096 	return inst_ok;
1097 }
1098 
1099 /* Initialise the HUEY */
1100 /* return non-zero on an error, with dtp error code */
1101 static inst_code
huey_init_inst(inst * pp)1102 huey_init_inst(inst *pp) {
1103 	huey *p = (huey *)pp;
1104 	inst_code ev = inst_ok;
1105 
1106 	a1logd(p->log, 2, "huey_init_inst: called\n");
1107 
1108 	if (p->gotcoms == 0)
1109 		return huey_interp_code((inst *)p, HUEY_NO_COMS);	/* Must establish coms first */
1110 
1111 	/* Check instrument is responding, and right type */
1112 	if ((ev = huey_check_unlock(p)) != inst_ok)
1113 		return ev;
1114 
1115 	/* Turn the LEDs off */
1116 	if ((ev = huey_set_LEDs(p, 0x0)) != inst_ok)
1117 		return ev;
1118 
1119 	/* Read all the registers and store their contents */
1120 	if ((ev = huey_read_all_regs(p)) != inst_ok)
1121 		return ev;
1122 
1123 	if ((ev = huey_compute_factors(p)) != inst_ok)
1124 		return ev;
1125 
1126 	p->trig = inst_opt_trig_user;
1127 
1128 	p->inited = 1;
1129 	a1logd(p->log, 2, "huey_init_inst: inited OK\n");
1130 
1131 	/* Flash the LEDs, just cos we can! */
1132 	if ((ev = huey_set_LEDs(p, 0x1)) != inst_ok)
1133 		return ev;
1134 	msec_sleep(50);
1135 	if ((ev = huey_set_LEDs(p, 0x2)) != inst_ok)
1136 		return ev;
1137 	msec_sleep(50);
1138 	if ((ev = huey_set_LEDs(p, 0x4)) != inst_ok)
1139 		return ev;
1140 	msec_sleep(50);
1141 	if ((ev = huey_set_LEDs(p, 0x8)) != inst_ok)
1142 		return ev;
1143 	msec_sleep(50);
1144 	if ((ev = huey_set_LEDs(p, 0x4)) != inst_ok)
1145 		return ev;
1146 	msec_sleep(50);
1147 	if ((ev = huey_set_LEDs(p, 0x2)) != inst_ok)
1148 		return ev;
1149 	msec_sleep(50);
1150 	if ((ev = huey_set_LEDs(p, 0x1)) != inst_ok)
1151 		return ev;
1152 	msec_sleep(50);
1153 	if ((ev = huey_set_LEDs(p, 0x0)) != inst_ok)
1154 		return ev;
1155 
1156 	return inst_ok;
1157 }
1158 
1159 /* Read a single sample */
1160 /* Return the dtp error code */
1161 static inst_code
huey_read_sample(inst * pp,char * name,ipatch * val,instClamping clamp)1162 huey_read_sample(
1163 inst *pp,
1164 char *name,			/* Strip name (7 chars) */
1165 ipatch *val,		/* Pointer to instrument patch value */
1166 instClamping clamp) {		/* NZ if clamp XYZ/Lab to be +ve */
1167 	huey *p = (huey *)pp;
1168 	int user_trig = 0;
1169 	int rv = inst_protocol_error;
1170 
1171 	a1logd(p->log, 1, "huey: huey_read_sample called\n");
1172 
1173 	if (!p->gotcoms)
1174 		return inst_no_coms;
1175 	if (!p->inited)
1176 		return inst_no_init;
1177 
1178 	if (p->trig == inst_opt_trig_user) {
1179 
1180 		if (p->uicallback == NULL) {
1181 			a1logd(p->log, 1, "huey: inst_opt_trig_user but no uicallback function set!\n");
1182 			return inst_unsupported;
1183 		}
1184 
1185 		for (;;) {
1186 			if ((rv = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
1187 				if (rv == inst_user_abort)
1188 					return rv;				/* Abort */
1189 				if (rv == inst_user_trig) {
1190 					user_trig = 1;
1191 					break;					/* Trigger */
1192 				}
1193 			}
1194 			msec_sleep(200);
1195 		}
1196 		/* Notify of trigger */
1197 		if (p->uicallback)
1198 			p->uicallback(p->uic_cntx, inst_triggered);
1199 
1200 	/* Progromatic Trigger */
1201 	} else {
1202 		/* Check for abort */
1203 		if (p->uicallback != NULL
1204 		 && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort)
1205 			return rv;				/* Abort */
1206 	}
1207 
1208 	/* Read the XYZ value */
1209 	if ((rv = huey_take_XYZ_measurement(p, val->XYZ)) != inst_ok) {
1210 		return rv;
1211 	}
1212 
1213 	/* This may not change anything since instrument may clamp */
1214 	if (clamp)
1215 		icmClamp3(val->XYZ, val->XYZ);
1216 
1217 	val->loc[0] = '\000';
1218 	if (IMODETST(p->mode, inst_mode_emis_ambient))
1219 		val->mtype = inst_mrt_ambient;
1220 	else
1221 		val->mtype = inst_mrt_none;
1222 	val->XYZ_v = 1;		/* These are absolute XYZ readings ? */
1223 	val->sp.spec_n = 0;
1224 	val->duration = 0.0;
1225 
1226 	if (user_trig)
1227 		return inst_user_trig;
1228 	return rv;
1229 }
1230 
1231 static inst_code set_base_disp_type(huey *p, int cbid);
1232 
1233 /* Insert a colorimetric correction matrix in the instrument XYZ readings */
1234 /* This is only valid for colorimetric instruments. */
1235 /* To remove the matrix, pass NULL for the filter filename */
huey_col_cor_mat(inst * pp,disptech dtech,int cbid,double mtx[3][3])1236 inst_code huey_col_cor_mat(
1237 inst *pp,
1238 disptech dtech,		/* Use disptech_unknown if not known */				\
1239 int cbid,       	/* Calibration display type base ID, 1 if unknown */\
1240 double mtx[3][3]
1241 ) {
1242 	huey *p = (huey *)pp;
1243 	inst_code ev;
1244 
1245 	if (!p->gotcoms)
1246 		return inst_no_coms;
1247 	if (!p->inited)
1248 		return inst_no_init;
1249 
1250 	if ((ev = set_base_disp_type(p, cbid)) != inst_ok)
1251 		return ev;
1252 	if (mtx == NULL)
1253 		icmSetUnity3x3(p->ccmat);
1254 	else
1255 		icmCpy3x3(p->ccmat, mtx);
1256 
1257 	p->dtech = dtech;
1258 	p->refrmode = disptech_get_id(dtech)->refr;
1259 	p->cbid = 0;	/* Can't be base type now */
1260 
1261 	if (p->log->debug >= 4) {
1262 		a1logd(p->log,4,"ccmat           = %f %f %f\n",
1263 		                 p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
1264 		a1logd(p->log,4,"                  %f %f %f\n",
1265 		                 p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]);
1266 		a1logd(p->log,4,"                  %f %f %f\n\n",
1267 		                 p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
1268 		a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid);
1269 		a1logd(p->log,4,"\n");
1270 	}
1271 
1272 	return inst_ok;
1273 }
1274 
1275 /* Error codes interpretation */
1276 static char *
huey_interp_error(inst * pp,int ec)1277 huey_interp_error(inst *pp, int ec) {
1278 //	huey *p = (huey *)pp;
1279 	ec &= inst_imask;
1280 	switch (ec) {
1281 		case HUEY_INTERNAL_ERROR:
1282 			return "Internal software error";
1283 		case HUEY_COMS_FAIL:
1284 			return "Communications failure";
1285 		case HUEY_UNKNOWN_MODEL:
1286 			return "Not a known Huey Model";
1287 		case HUEY_DATA_PARSE_ERROR:
1288 			return "Data from i1 Display didn't parse as expected";
1289 
1290 		case HUEY_OK:
1291 			return "No device error";
1292 
1293 		case HUEY_FLOAT_NOT_SET:
1294 			return "Float value is not set in EEPROM";
1295 		case HUEY_NOT_READY:
1296 			return "Command didn't return command code - not ready ?";
1297 
1298 		case HUEY_BAD_SERIAL_NUMBER:
1299 			return "Serial number isn't set";
1300 		case HUEY_BAD_LCD_CALIBRATION:
1301 			return "LCD calibration values aren't set";
1302 		case HUEY_BAD_CRT_CALIBRATION:
1303 			return "CRT calibration values aren't set";
1304 		case HUEY_EEPROM_WRITE_FAIL:
1305 			return "Write to EEPROM failed to verify";
1306 
1307 		case HUEY_BAD_WR_LENGTH:
1308 			return "Unable to write full message to instrument";
1309 		case HUEY_BAD_RD_LENGTH:
1310 			return "Unable to read full message to instrument";
1311 		case HUEY_BAD_RET_CMD:
1312 			return "Message from instrument didn't echo command code";
1313 		case HUEY_BAD_RET_STAT:
1314 			return "Message from instrument had bad status code";
1315 		case HUEY_UNEXPECTED_RET_VAL:
1316 			return "Message from instrument has unexpected value";
1317 
1318 		case HUEY_BAD_STATUS:
1319 			return "Instrument status is unrecognised format";
1320 		case HUEY_UNKNOWN_VERS_ID:
1321 			return "Instrument version number or ID byte not recognised";
1322 		case HUEY_BAD_COMMAND:
1323 			return "Instrument didn't recognise the command";
1324 
1325 		/* Internal errors */
1326 		case HUEY_BAD_REG_ADDRESS:
1327 			return "Out of range register address";
1328 		case HUEY_BAD_INT_THRESH:
1329 			return "Out of range integration threshold";
1330 		case HUEY_NO_COMS:
1331 			return "Communications hasn't been established";
1332 		case HUEY_NOT_INITED:
1333 			return "Insrument hasn't been initialised";
1334 		case HUEY_CANT_BLACK_CALIB:
1335 			return "Device doesn't support black calibration";
1336 		case HUEY_CANT_MEASP_CALIB:
1337 			return "Device doesn't support measurment period calibration";
1338 		case HUEY_WRONG_DEVICE:
1339 			return "Wrong type of device for called function";
1340 		default:
1341 			return "Unknown error code";
1342 	}
1343 }
1344 
1345 
1346 /* Convert a machine specific error code into an abstract dtp code */
1347 static inst_code
huey_interp_code(inst * pp,int ec)1348 huey_interp_code(inst *pp, int ec) {
1349 //	huey *p = (huey *)pp;
1350 
1351 	ec &= inst_imask;
1352 	switch (ec) {
1353 
1354 		case HUEY_OK:
1355 		case HUEY_FLOAT_NOT_SET:		/* Internal indication */
1356 		case HUEY_NOT_READY:			/* Internal indication */
1357 			return inst_ok;
1358 
1359 		case HUEY_INTERNAL_ERROR:
1360 		case HUEY_BAD_REG_ADDRESS:
1361 		case HUEY_BAD_INT_THRESH:
1362 		case HUEY_NO_COMS:
1363 		case HUEY_NOT_INITED:
1364 		case HUEY_CANT_BLACK_CALIB:
1365 		case HUEY_CANT_MEASP_CALIB:
1366 		case HUEY_WRONG_DEVICE:
1367 			return inst_internal_error | ec;
1368 
1369 		case HUEY_COMS_FAIL:
1370 			return inst_coms_fail | ec;
1371 
1372 		case HUEY_UNKNOWN_MODEL:
1373 		case HUEY_BAD_STATUS:
1374 		case HUEY_UNKNOWN_VERS_ID:
1375 			return inst_unknown_model | ec;
1376 
1377 		case HUEY_DATA_PARSE_ERROR:
1378 		case HUEY_BAD_WR_LENGTH:
1379 		case HUEY_BAD_RD_LENGTH:
1380 		case HUEY_BAD_RET_CMD:
1381 		case HUEY_BAD_RET_STAT:
1382 		case HUEY_UNEXPECTED_RET_VAL:
1383 		case HUEY_BAD_COMMAND:
1384 			return inst_protocol_error | ec;
1385 
1386 		case HUEY_BAD_SERIAL_NUMBER:
1387 		case HUEY_BAD_LCD_CALIBRATION:
1388 		case HUEY_BAD_CRT_CALIBRATION:
1389 		case HUEY_EEPROM_WRITE_FAIL:
1390 			return inst_hardware_fail | ec;
1391 
1392 		/* return inst_misread | ec; */
1393 		/* return inst_needs_cal_2 | ec; */
1394 	}
1395 	return inst_other_error | ec;
1396 }
1397 
1398 /* Destroy ourselves */
1399 static void
huey_del(inst * pp)1400 huey_del(inst *pp) {
1401 	huey *p = (huey *)pp;
1402 	if (p->icom != NULL)
1403 		p->icom->del(p->icom);
1404 	inst_del_disptype_list(p->dtlist, p->ndtlist);
1405 	p->vdel(pp);
1406 	free(p);
1407 }
1408 
1409 /* Return the instrument mode capabilities */
huey_capabilities(inst * pp,inst_mode * pcap1,inst2_capability * pcap2,inst3_capability * pcap3)1410 static void huey_capabilities(inst *pp,
1411 inst_mode *pcap1,
1412 inst2_capability *pcap2,
1413 inst3_capability *pcap3) {
1414 	huey *p = (huey *)pp;
1415 	inst_mode cap1 = 0;
1416 	inst2_capability cap2 = 0;
1417 
1418 	cap1 |= inst_mode_emis_spot
1419 	     |  inst_mode_emis_ambient
1420 	     |  inst_mode_colorimeter
1421 	        ;
1422 
1423 	cap2 |= inst2_prog_trig
1424 	     |  inst2_user_trig
1425 	     |  inst2_has_leds
1426 	     |  inst2_disptype
1427 	     |  inst2_ambient_mono
1428 	     |  inst2_ccmx
1429 		    ;
1430 
1431 	if (pcap1 != NULL)
1432 		*pcap1 = cap1;
1433 	if (pcap2 != NULL)
1434 		*pcap2 = cap2;
1435 	if (pcap3 != NULL)
1436 		*pcap3 = inst3_none;
1437 }
1438 
1439 /* Check device measurement mode */
huey_check_mode(inst * pp,inst_mode m)1440 static inst_code huey_check_mode(inst *pp, inst_mode m) {
1441 	huey *p = (huey *)pp;
1442 	inst_mode cap;
1443 
1444 	if (!p->gotcoms)
1445 		return inst_no_coms;
1446 	if (!p->inited)
1447 		return inst_no_init;
1448 
1449 	pp->capabilities(pp, &cap, NULL, NULL);
1450 
1451 	/* Simple test */
1452 	if (m & ~cap)
1453 		return inst_unsupported;
1454 
1455 	/* only display emission mode and ambient supported */
1456 	if (!IMODETST(m, inst_mode_emis_spot)
1457 	 && !IMODETST(m, inst_mode_emis_ambient)) {
1458 		return inst_unsupported;
1459 	}
1460 
1461 	return inst_ok;
1462 }
1463 
1464 /* Set device measurement mode */
huey_set_mode(inst * pp,inst_mode m)1465 static inst_code huey_set_mode(inst *pp, inst_mode m) {
1466 	huey *p = (huey *)pp;
1467 	inst_code ev;
1468 
1469 	if ((ev = huey_check_mode(pp, m)) != inst_ok)
1470 		return ev;
1471 
1472 	p->mode = m;
1473 
1474 	return inst_ok;
1475 }
1476 
1477 static inst_disptypesel huey_disptypesel[3] = {
1478 	{
1479 		inst_dtflags_default,
1480 		1,
1481 		"l",
1482 		"LCD display",
1483 		0,
1484 		disptech_lcd,
1485 		0
1486 	},
1487 	{
1488 		inst_dtflags_none,		/* flags */
1489 		2,						/* cbid */
1490 		"c",					/* sel */
1491 		"CRT display",			/* desc */
1492 		1,						/* refr */
1493 		disptech_crt,			/* disptype */
1494 		1						/* ix */
1495 	},
1496 	{
1497 		inst_dtflags_end,
1498 		0,
1499 		"",
1500 		"",
1501 		0,
1502 		disptech_none,
1503 		0
1504 	}
1505 };
1506 
1507 /* Get mode and option details */
huey_get_disptypesel(inst * pp,int * pnsels,inst_disptypesel ** psels,int allconfig,int recreate)1508 static inst_code huey_get_disptypesel(
1509 inst *pp,
1510 int *pnsels,				/* Return number of display types */
1511 inst_disptypesel **psels,	/* Return the array of display types */
1512 int allconfig,				/* nz to return list for all configs, not just current. */
1513 int recreate				/* nz to re-check for new ccmx & ccss files */
1514 ) {
1515 	huey *p = (huey *)pp;
1516 	inst_code rv = inst_ok;
1517 
1518 	/* Create/Re-create a current list of abailable display types */
1519 	if (p->dtlist == NULL || recreate) {
1520 		if ((rv = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist,
1521 		    huey_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
1522 			return rv;
1523 	}
1524 
1525 	if (pnsels != NULL)
1526 		*pnsels = p->ndtlist;
1527 
1528 	if (psels != NULL)
1529 		*psels = p->dtlist;
1530 
1531 	return inst_ok;
1532 }
1533 
1534 /* Given a display type entry, setup for that type */
set_disp_type(huey * p,inst_disptypesel * dentry)1535 static inst_code set_disp_type(huey *p, inst_disptypesel *dentry) {
1536 
1537 	if (dentry->flags & inst_dtflags_ccmx) {
1538 		inst_code ev;
1539 		if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok)
1540 			return ev;
1541 		icmCpy3x3(p->ccmat, dentry->mat);
1542 		p->dtech = dentry->dtech;
1543 		p->cbid = 0; 	/* Can't be a base type */
1544 
1545 	} else {
1546 		p->icx = dentry->ix;
1547 		p->dtech = dentry->dtech;
1548 		p->cbid = dentry->cbid;
1549 		p->ucbid = dentry->cbid;	/* This is underying base if dentry is base selection */
1550 		icmSetUnity3x3(p->ccmat);
1551 	}
1552 
1553 	p->refrmode = dentry->refr;
1554 
1555 	if (p->log->debug >= 4) {
1556 		a1logd(p->log,4,"ccmat           = %f %f %f\n",
1557 		                 p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
1558 		a1logd(p->log,4,"                  %f %f %f\n",
1559 		                 p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]);
1560 		a1logd(p->log,4,"                  %f %f %f\n\n",
1561 		                 p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
1562 		a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid);
1563 		a1logd(p->log,4,"\n");
1564 	}
1565 
1566 	return inst_ok;
1567 }
1568 
1569 /* Set the display type */
huey_set_disptype(inst * pp,int ix)1570 static inst_code huey_set_disptype(inst *pp, int ix) {
1571 	huey *p = (huey *)pp;
1572 	inst_code ev;
1573 	inst_disptypesel *dentry;
1574 
1575 	if (p->dtlist == NULL) {
1576 		if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist,
1577 		    huey_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
1578 			return ev;
1579 	}
1580 
1581 	if (ix < 0 || ix >= p->ndtlist)
1582 		return inst_unsupported;
1583 
1584 	dentry = &p->dtlist[ix];
1585 
1586 	if ((ev = set_disp_type(p, dentry)) != inst_ok) {
1587 		return ev;
1588 	}
1589 
1590 	return inst_ok;
1591 }
1592 
1593 /* Setup the default display type */
set_default_disp_type(huey * p)1594 static inst_code set_default_disp_type(huey *p) {
1595 	inst_code ev;
1596 	int i;
1597 
1598 	if (p->dtlist == NULL) {
1599 		if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
1600 		    huey_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
1601 			return ev;
1602 	}
1603 
1604 	for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) {
1605 		if (p->dtlist[i].flags & inst_dtflags_default)
1606 			break;
1607 	}
1608 	if (p->dtlist[i].flags & inst_dtflags_end) {
1609 		a1loge(p->log, 1, "set_default_disp_type: failed to find type!\n");
1610 		return inst_internal_error;
1611 	}
1612 	if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) {
1613 		return ev;
1614 	}
1615 
1616 	return inst_ok;
1617 }
1618 
1619 /* Setup the display type to the given base type */
set_base_disp_type(huey * p,int cbid)1620 static inst_code set_base_disp_type(huey *p, int cbid) {
1621 	inst_code ev;
1622 	int i;
1623 
1624 	if (cbid == 0) {
1625 		a1loge(p->log, 1, "huey set_base_disp_type: can't set base display type of 0\n");
1626 		return inst_wrong_setup;
1627 	}
1628 	if (p->dtlist == NULL) {
1629 		if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
1630 		    huey_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
1631 			return ev;
1632 	}
1633 
1634 	for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) {
1635 		if (!(p->dtlist[i].flags & inst_dtflags_ccmx)		/* Prevent infinite recursion */
1636 		 && p->dtlist[i].cbid == cbid)
1637 			break;
1638 	}
1639 	if (p->dtlist[i].flags & inst_dtflags_end) {
1640 		a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid);
1641 		return inst_wrong_setup;
1642 	}
1643 	if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) {
1644 		return ev;
1645 	}
1646 
1647 	return inst_ok;
1648 }
1649 
1650 /* Get the disptech and other corresponding info for the current */
1651 /* selected display type. Returns disptype_unknown by default. */
1652 /* Because refrmode can be overridden, it may not match the refrmode */
1653 /* of the dtech. (Pointers may be NULL if not needed) */
huey_get_disptechi(inst * pp,disptech * dtech,int * refrmode,int * cbid)1654 static inst_code huey_get_disptechi(
1655 inst *pp,
1656 disptech *dtech,
1657 int *refrmode,
1658 int *cbid) {
1659 	huey *p = (huey *)pp;
1660 	if (dtech != NULL)
1661 		*dtech = p->dtech;
1662 	if (refrmode != NULL)
1663 		*refrmode = p->refrmode;
1664 	if (cbid != NULL)
1665 		*cbid = p->cbid;
1666 	return inst_ok;
1667 }
1668 
1669 /*
1670  * set or reset an optional mode
1671  *
1672  * Some options talk to the instrument, and these will
1673  * error if it hasn't been initialised.
1674  */
1675 static inst_code
huey_get_set_opt(inst * pp,inst_opt_type m,...)1676 huey_get_set_opt(inst *pp, inst_opt_type m, ...) {
1677 	huey *p = (huey *)pp;
1678 	inst_code ev = inst_ok;
1679 
1680 	/* Record the trigger mode */
1681 	if (m == inst_opt_trig_prog
1682 	 || m == inst_opt_trig_user) {
1683 		p->trig = m;
1684 		return inst_ok;
1685 	}
1686 
1687 	if (!p->gotcoms)
1688 		return inst_no_coms;
1689 	if (!p->inited)
1690 		return inst_no_init;
1691 
1692 	/* Operate the LEDS */
1693 	if (m == inst_opt_get_gen_ledmask) {
1694 		va_list args;
1695 		int *mask = NULL;
1696 
1697 		va_start(args, m);
1698 		mask = va_arg(args, int *);
1699 		va_end(args);
1700 		*mask = 0xf;			/* Four general LEDs */
1701 		return inst_ok;
1702 	} else if (m == inst_opt_get_led_state) {
1703 		va_list args;
1704 		int *mask = NULL;
1705 
1706 		va_start(args, m);
1707 		mask = va_arg(args, int *);
1708 		va_end(args);
1709 		*mask = p->led_state;
1710 		return inst_ok;
1711 	} else if (m == inst_opt_set_led_state) {
1712 		va_list args;
1713 		int mask = 0;
1714 
1715 		va_start(args, m);
1716 		mask = va_arg(args, int);
1717 		va_end(args);
1718 		return huey_set_LEDs(p, mask);
1719 	}
1720 
1721 	/* Use default implementation of other inst_opt_type's */
1722 	{
1723 		inst_code rv;
1724 		va_list args;
1725 
1726 		va_start(args, m);
1727 		rv = inst_get_set_opt_def(pp, m, args);
1728 		va_end(args);
1729 
1730 		return rv;
1731 	}
1732 }
1733 
1734 /* Constructor */
new_huey(icoms * icom,instType itype)1735 extern huey *new_huey(icoms *icom, instType itype) {
1736 	huey *p;
1737 	if ((p = (huey *)calloc(sizeof(huey),1)) == NULL) {
1738 		a1loge(icom->log, 1, "new_huey: malloc failed!\n");
1739 		return NULL;
1740 	}
1741 
1742 	p->log = new_a1log_d(icom->log);
1743 
1744 	p->init_coms         = huey_init_coms;
1745 	p->init_inst         = huey_init_inst;
1746 	p->capabilities      = huey_capabilities;
1747 	p->check_mode        = huey_check_mode;
1748 	p->set_mode          = huey_set_mode;
1749 	p->get_disptypesel   = huey_get_disptypesel;
1750 	p->set_disptype      = huey_set_disptype;
1751 	p->get_disptechi     = huey_get_disptechi;
1752 	p->get_set_opt       = huey_get_set_opt;
1753 	p->read_sample       = huey_read_sample;
1754 	p->col_cor_mat       = huey_col_cor_mat;
1755 	p->interp_error      = huey_interp_error;
1756 	p->del               = huey_del;
1757 
1758 	p->icom = icom;
1759 	p->itype = itype;
1760 
1761 	icmSetUnity3x3(p->ccmat);	/* Set the colorimeter correction matrix to do nothing */
1762 	p->dtech = disptech_unknown;
1763 
1764 	return p;
1765 }
1766 
1767