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