1 
2 /*
3  * Argyll Color Correction System
4  *
5  * Gretag i1Display 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  * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
14  * see the License2.txt file for licencing details.
15  */
16 
17 /*
18    If you make use of the instrument driver code here, please note
19    that it is the author(s) of the code who take responsibility
20    for its operation. Any problems or queries regarding driving
21    instruments with the Argyll drivers, should be directed to
22    the Argyll's author(s), and not to any other party.
23 
24    If there is some instrument feature or function that you
25    would like supported here, it is recommended that you
26    contact Argyll's author(s) first, rather than attempt to
27    modify the software yourself, if you don't have firm knowledge
28    of the instrument communicate protocols. There is a chance
29    that an instrument could be damaged by an incautious command
30    sequence, and the instrument companies generally cannot and
31    will not support developers that they have not qualified
32    and agreed to support.
33  */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <time.h>
40 #include <stdarg.h>
41 #include <math.h>
42 #ifndef SALONEINSTLIB
43 #include "copyright.h"
44 #include "aconfig.h"
45 #include "numlib.h"
46 #else	/* !SALONEINSTLIB */
47 #include "sa_config.h"
48 #include "numsup.h"
49 #endif /* !SALONEINSTLIB */
50 #include "xspect.h"
51 #include "insttypes.h"
52 #include "conv.h"
53 #include "icoms.h"
54 #include "i1disp.h"
55 
56 static inst_code i1disp_interp_code(inst *pp, int ec);
57 static inst_code i1disp_do_fcal_setit(i1disp *p);
58 static inst_code i1disp_check_unlock(i1disp *p);
59 
60 #define MAX_MES_SIZE 500		/* Maximum normal message reply size */
61 #define MAX_RD_SIZE 5000		/* Maximum reading messagle reply size */
62 
63 #define CALFACTOR 3.428			/* Emissive magic calibration factor */
64 
65 /* ------------------------------------------------------------------------ */
66 /* Implementation */
67 
68 /* Interpret an icoms error into a I1DISP error */
icoms2i1disp_err(int se)69 static int icoms2i1disp_err(int se) {
70 	if (se != ICOM_OK)
71 		return I1DISP_COMS_FAIL;
72 	return I1DISP_OK;
73 }
74 
75 /* i1Display command codes - number:X is argument count:return type */
76 /* B = byte (8bit), S = short (16bit), W = word (32bit), */
77 /* A = string (5 bytes total max) , - = none */
78 typedef enum {
79     i1d_status       = 0x00,		/* -:A    Get status string */
80     i1d_rd_red       = 0x01,		/* -:W    Read the red channel clk count (and trig ?) */
81     i1d_rd_green     = 0x02,		/* -:W    Read the green channel clk count */
82     i1d_rd_blue      = 0x03,		/* -:W    Read the blue channel clk count */
83     i1d_getmeas_p    = 0x04,		/* -:W    Read the measure refresh period */
84     i1d_setintgt     = 0x05,		/* W:-    Set the integration time */
85     i1d_getintgt     = 0x06,		/* -:W    Get the integration time */
86     i1d_wrreg        = 0x07,		/* BB:-   Write a register value */
87     i1d_rdreg        = 0x08,		/* B:B    Read a register value */
88     i1d_getmeas_p2   = 0x09,		/* -:W    Read the measure refresh period (finer ?) */
89     i1d_m_red_p      = 0x0a,		/* B:W    Measure the red period for given edge count */
90     i1d_m_green_p    = 0x0b,		/* B:W    Measure the green period for given edge count */
91     i1d_m_blue_p     = 0x0c,		/* B:W    Measure the blue period for given edge count */
92     i1d_m_rgb_p      = 0x0d,		/* BBB:W  Measure the RGB period for given edge count */
93     i1d_unlock       = 0x0e,		/* BBBB:- Unlock the interface */
94 
95     i1d_m_red_p2     = 0x10,		/* S:W    Measure the red period (16 bit) */
96     i1d_m_green_p2   = 0x11,		/* S:W    Measure the green period (16 bit) */
97     i1d_m_blue_p2    = 0x12,		/* S:W    Measure the blue period (16 bit) */
98 									/* S = edge count */
99 
100     i1d_m_red_2      = 0x13,		/* B:W    Measure the red channel (16 bit) */
101 									/* B = sync mode, typically 1 */
102 
103     i1d_setmedges2   = 0x14,		/* SB:-   Set number of edges used for measurment 16 bit */
104 									/* B = channel */
105     i1d_getmedges2   = 0x15,		/* B:S    Get number of edges used for measurment 16 bit */
106 									/* B = channel */
107 
108     i1d_m_rgb_edge_2 = 0x16,		/* -:W    Measure RGB Edge (16 bit) */
109 
110 	/* Different meanings for different devices ? */
111     i1d_set_pll_p    = 0x11,		/* SS:-   Set PLL period */
112     i1d_get_pll_p    = 0x12,		/* -:W    Get PLL period */
113 
114     i1d_m_rgb_edge_3 = 0x10,		/* BBBB:W Measure RGB Edge & return red. */
115 									/* BBBB = edge counts ??? */
116     i1d_g_green_3    = 0x11,		/* -:W    Get green data */
117     i1d_g_blue_3     = 0x12,		/* -:W    Get blue data */
118 
119     i1d_wrxreg       = 0x13,		/* SB:-   Write an extra register value */
120     i1d_rdxreg       = 0x14, 		/* S:B    Read an extra register value */
121 									/*        The address range overlapps i1d_rdreg */
122 	/* Smile */
123 //  i1d_xxxxxxx      = 0x18, 		/* XXX:X  Unknown */
124     i1d_rdexreg      = 0x19 		/* BS:BBBB Read an extended register value */
125 
126 } i1DispCC;
127 
128 /* Do a command/response exchange with the i1disp. */
129 /* Return the error code */
130 /* The i1 display uses a rather convoluted means of communication (historical?). */
131 /* Messages from the host are conveyed one byte per USB control message, */
132 /* with the byte conveyed in bRequest, with each such control message reading */
133 /* 8 bytes of data from the device. The last 8 bytes read in an overall */
134 /* message contains up to 5 response bytes, the last always being nul (?). */
135 /* Muti-byte quantities are transmitted in big-endian order. */
136 /* (Instructions 1,2,3,8 & 9 seem to be retried up to 5 times. We're */
137 /* not doing this, as it may just be historical) */
138 static inst_code
i1disp_command_1(i1disp * p,i1DispCC cc,unsigned char * in,int insize,unsigned char * out,int bsize,int * rsize,double to)139 i1disp_command_1(
140 	i1disp *p,									/* i1display object */
141 	i1DispCC cc,								/* Command code */
142 	unsigned char *in, int insize,				/* Parameter to send */
143 	unsigned char *out, int bsize, int *rsize,	/* Parameter returned */
144 	double to									/* Timeout in seconds */
145 ) {
146 	int requesttype;		/* 8 bit request type (USB bmRequestType) */
147 	int request;			/* 8 bit request code (USB bRequest) */
148 	int value;				/* 16 bit value (USB wValue, sent little endian) */
149 	int index;				/* 16 bit index (USB wIndex, sent little endian) */
150 	int rwsize;				/* 16 bit data size (USB wLength, send little endian) */
151 	int rcc = 0;			/* Return cc code from instruction */
152 	int i, tsize;
153 	unsigned char buf[8];	/* 8 bytes to read */
154 	int se, ua = 0, rv = inst_ok;
155 
156 	tsize = insize + 2;
157 	*rsize = 0;
158 
159 	a1logd(p->log, 4, "i1disp: Sending cmd %02x args '%s'\n",cc, icoms_tohex(in, insize));
160 
161 	/* For each byte to be sent */
162 	for (i = 0; i < tsize; i++) {
163 		unsigned int smsec;
164 
165 		/* Control message to read 8 bytes */
166 		requesttype = IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_ENDPOINT;
167 		if (i == 0) 				/* Count */
168 			request = insize + 1;
169 		else if (i == 1)			/* Command */
170 			request = (int)cc;
171 		else						/* Data */
172 			request = (int)in[i-2];
173 		value = i;					/* Incrementing count */
174 		index = (tsize - i - 1);	/* Decrementing count */
175 		rwsize = 8;
176 
177 		smsec = msec_time();
178 		if ((se = p->icom->usb_control(p->icom, requesttype, request, value, index,
179 		                                                        buf, rwsize, to)) != 0) {
180 			a1logd(p->log, 1, "i1disp: Message send failed with ICOM err 0x%x\n",se);
181 			p->last_com_err = se;
182 			return i1disp_interp_code((inst *)p, I1DISP_COMS_FAIL);
183 		}
184 
185 		/* We could check the return data. This seems to be what we sent, */
186 		/* unless it's the last exchange in the message sequence. */
187 		/* I don't currently know how or if the device signals an error. */
188 
189 		/* If this is the last exchange, copy return value out */
190 		if (i == (tsize-1)) {
191 			*rsize = buf[1];
192 			if (*rsize > bsize)
193 				*rsize = bsize;
194 			if (*rsize > 5)
195 				*rsize = 5;
196 			memmove(out, buf + 3, *rsize);
197 
198 			/* buf[2] is usually the cc, except for i1d_unlock. */
199 			/* If it is not the cc, this may indicate that the command */
200 			/* should be retried up to a total of 5 times, before */
201 			/* assuming it has suceeded. */
202 			rcc = buf[2] & 0xff;
203 		}
204 	}
205 	rv = i1disp_interp_code((inst *)p, icoms2i1disp_err(ua));
206 
207 	if (rv == inst_ok && rcc != cc)
208 		rv = i1disp_interp_code((inst *)p, I1DISP_NOT_READY);
209 
210 	/* Instrument returns "LOCK" to any instruction if it is locked */
211 	if (rv == inst_ok && *rsize == 5 && strncmp((char *)out,"LOCK",4) == 0) {
212 		rv = i1disp_interp_code((inst *)p, I1DISP_LOCKED);
213 	}
214 
215 	a1logd(p->log, 4, "i1disp: response '%s' ICOM err 0x%x\n",icoms_tohex(out, *rsize),ua);
216 
217 	return rv;
218 }
219 
220 /* Do a command/response exchange with the i1disp, taking care of */
221 /* a LOCK error */
222 static inst_code
i1disp_command(i1disp * p,i1DispCC cc,unsigned char * in,int insize,unsigned char * out,int bsize,int * rsize,double to)223 i1disp_command(
224 	i1disp *p,									/* i1display object */
225 	i1DispCC cc,								/* Command code */
226 	unsigned char *in, int insize,				/* Parameter to send */
227 	unsigned char *out, int bsize, int *rsize,	/* Parameter returned */
228 	double to									/* Timeout in seconds */
229 ) {
230 	inst_code rv;
231 
232 	if ((rv = i1disp_command_1(p, cc, in, insize, out, bsize, rsize, to)) == inst_ok)
233 		return rv;
234 
235 	/* Unlock and try again */
236 	if ((rv & inst_imask) == I1DISP_LOCKED) {
237 		if ((rv = i1disp_check_unlock(p)) != inst_ok)
238 			return rv;
239 		rv = i1disp_command_1(p, cc, in, insize, out, bsize, rsize, to);
240 	}
241 	return rv;
242 }
243 
244 /* Take an int, and convert it into a byte buffer */
int2buf(unsigned char * buf,int inv)245 static void int2buf(unsigned char *buf, int inv) {
246 	buf[0] = (inv >> 24) & 0xff;
247 	buf[1] = (inv >> 16) & 0xff;
248 	buf[2] = (inv >> 8) & 0xff;
249 	buf[3] = (inv >> 0) & 0xff;
250 }
251 
252 /* Take a short, and convert it into a byte buffer */
short2buf(unsigned char * buf,int inv)253 static void short2buf(unsigned char *buf, int inv) {
254 	buf[0] = (inv >> 8) & 0xff;
255 	buf[1] = (inv >> 0) & 0xff;
256 }
257 
258 /* Take a word sized return buffer, and convert it to an int */
buf2int(unsigned char * buf)259 static int buf2int(unsigned char *buf) {
260 	int val;
261 	val = (signed char)buf[0];
262 	val = ((val << 8) + (0xff & buf[1]));
263 	val = ((val << 8) + (0xff & buf[2]));
264 	val = ((val << 8) + (0xff & buf[3]));
265 	return val;
266 }
267 
268 /* Read a byte from a register */
269 static inst_code
i1disp_rdreg_byte(i1disp * p,int * outp,int addr)270 i1disp_rdreg_byte(
271 	i1disp *p,				/* Object */
272 	int *outp,				/* Where to write value */
273 	int addr				/* Register Address, 0 - 127, 159 */
274 ) {
275 	unsigned char c, buf[16];
276 	int rsize;
277 	inst_code ev;
278 
279 	if (p->dtype == 0) {
280 		if (addr < 0 || addr > 127)
281 			return i1disp_interp_code((inst *)p, I1DISP_BAD_REG_ADDRESS);
282 	} else {
283 		if (addr < 0 || addr > 159)
284 			return i1disp_interp_code((inst *)p, I1DISP_BAD_REG_ADDRESS);
285 	}
286 	c = (unsigned char)addr;
287 
288 	/* Read a byte */
289 	if ((ev = i1disp_command(p, i1d_rdreg, &c, 1,
290 		         buf, 8, &rsize, 0.5)) != inst_ok)
291 		return ev;
292 
293 	if (rsize != 3)
294 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
295 
296 	if (buf[0] != c)
297 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_VAL);
298 
299 	*outp = (int)buf[1];
300 
301 	return inst_ok;
302 }
303 
304 /* Read a short from a register */
305 static inst_code
i1disp_rdreg_short(i1disp * p,int * outp,int addr)306 i1disp_rdreg_short(
307 	i1disp *p,				/* Object */
308 	int *outp,				/* Where to write value */
309 	int addr				/* Register Address, 0 - 126 */
310 ) {
311 	inst_code ev;
312 	int v, val;
313 
314 	if ((ev = i1disp_rdreg_byte(p, &v, addr)) != inst_ok)
315 		return ev;
316 	val = v;
317 
318 	if ((ev = i1disp_rdreg_byte(p, &v, addr+1)) != inst_ok)
319 		return ev;
320 	val = ((val << 8) + (0xff & v));
321 
322 	*outp = val;
323 
324 	return inst_ok;
325 }
326 
327 /* Read a word from a register */
328 static inst_code
i1disp_rdreg_word(i1disp * p,int * outp,int addr)329 i1disp_rdreg_word(
330 	i1disp *p,				/* Object */
331 	int *outp,				/* Where to write value */
332 	int addr				/* Register Address, 0 - 124 */
333 ) {
334 	inst_code ev;
335 	int v, val;
336 
337 	if ((ev = i1disp_rdreg_byte(p, &v, addr)) != inst_ok)
338 		return ev;
339 	val = v;
340 
341 	if ((ev = i1disp_rdreg_byte(p, &v, addr+1)) != inst_ok)
342 		return ev;
343 	val = ((val << 8) + (0xff & v));
344 
345 	if ((ev = i1disp_rdreg_byte(p, &v, addr+2)) != inst_ok)
346 		return ev;
347 	val = ((val << 8) + (0xff & v));
348 
349 	if ((ev = i1disp_rdreg_byte(p, &v, addr+3)) != inst_ok)
350 		return ev;
351 	val = ((val << 8) + (0xff & v));
352 
353 	*outp = val;
354 
355 	return inst_ok;
356 }
357 
358 
359 /* Read a float from a register */
360 /* Will return I1DISP_FLOAT_NOT_SET if the float value was 0xffffffff */
361 static inst_code
i1disp_rdreg_float(i1disp * p,double * outp,int addr)362 i1disp_rdreg_float(
363 	i1disp *p,				/* Object */
364 	double *outp,			/* Where to write value */
365 	int addr				/* Register Address, 0 - 124 */
366 ) {
367 	inst_code ev;
368 	int val;
369 
370 	if ((ev = i1disp_rdreg_word(p, &val, addr)) != inst_ok)
371 		return ev;
372 
373 	if (val == 0xffffffff) {
374 		return I1DISP_FLOAT_NOT_SET;
375 	}
376 
377 	*outp = IEEE754todouble((unsigned int)val);
378 	return inst_ok;
379 }
380 
381 
382 /* Write a byte to a register */
383 static inst_code
i1disp_wrreg_byte(i1disp * p,int inv,int addr)384 i1disp_wrreg_byte(
385 	i1disp *p,				/* Object */
386 	int inv,				/* Input value */
387 	int addr				/* Register Address, 0 - 127 */
388 ) {
389 	int cval;
390 	unsigned char ibuf[16], obuf[16];
391 	int rsize;
392 	inst_code ev;
393 
394 	inv &= 0xff;
395 
396 	/* Read it first, to see if it needs writing */
397 	if ((ev = i1disp_rdreg_byte(p, &cval, addr) ) != inst_ok)
398 		return ev;
399 
400 	if (cval == inv) 	/* No need to write */
401 		return inst_ok;
402 
403 	ibuf[0] = (unsigned char)addr;
404 	ibuf[1] = (unsigned char)inv;
405 
406 	/* Write a byte */
407 	if ((ev = i1disp_command(p, i1d_wrreg, ibuf, 2,
408 		         obuf, 8, &rsize, 0.5)) != inst_ok)
409 		return ev;
410 
411 	if (rsize != 2)
412 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
413 
414 	if (obuf[0] != addr)
415 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_VAL);
416 
417 	/* Check it got written properly */
418 	if ((ev = i1disp_rdreg_byte(p, &cval, addr) ) != inst_ok)
419 		return ev;
420 
421 	cval &= 0xff;
422 	if (cval != inv)	/* No need to write */
423 		return i1disp_interp_code((inst *)p, I1DISP_EEPROM_WRITE_FAIL);
424 
425 	return inst_ok;
426 }
427 
428 /* Write a word to a register */
429 static inst_code
i1disp_wrreg_word(i1disp * p,int inv,int addr)430 i1disp_wrreg_word(
431 	i1disp *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 = i1disp_wrreg_byte(p, v, addr) ) != inst_ok)
440 		return ev;
441 
442 	v = (inv >> 16) & 0xff;
443 	if ((ev = i1disp_wrreg_byte(p, v, addr+1) ) != inst_ok)
444 		return ev;
445 
446 	v = (inv >> 8) & 0xff;
447 	if ((ev = i1disp_wrreg_byte(p, v, addr+2) ) != inst_ok)
448 		return ev;
449 
450 	v = (inv >> 0) & 0xff;
451 	if ((ev = i1disp_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
i1disp_wrreg_float(i1disp * p,double inv,int addr)459 i1disp_wrreg_float(
460 	i1disp *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 = i1disp_wrreg_word(p, val, addr)) != inst_ok)
470 		return ev;
471 	return inst_ok;
472 }
473 
474 /* ColorMunki Smile: Read a byte from an extended register range */
475 static inst_code
i1disp_rdexreg_bytes(i1disp * p,unsigned char * outp,int addr,int len)476 i1disp_rdexreg_bytes(
477 	i1disp *p,				/* Object */
478 	unsigned char *outp,	/* Where to write values */
479 	int addr,				/* Register Address, 16 bit */
480 	int len					/* Number of bytes, 8 bits */
481 ) {
482 	unsigned char ibuf[16];
483 	unsigned char obuf[16];
484 	int ooff, rsize;
485 	inst_code ev;
486 
487 	if (p->dtype != 2)		/* Only ColorMunki Smile ? */
488 		return i1disp_interp_code((inst *)p, I1DISP_WRONG_DEVICE);
489 
490 	if (addr < 0 || addr > 0x0200)
491 		return i1disp_interp_code((inst *)p, I1DISP_BAD_REG_ADDRESS);
492 
493 	if (len < 0 || (addr + len) > 0x0200)
494 		return i1disp_interp_code((inst *)p, I1DISP_BAD_REG_ADDRESS);
495 
496 	for (ooff = 0; len > 0; ) {
497 		int rlen = len;
498 		if (rlen > 4)
499 			rlen = 4;
500 
501 		/* Read up to 4 bytes at a time */
502 		short2buf(ibuf+0, addr);	/* Address to read from */
503 		ibuf[2] = rlen;				/* Number of bytes to read */
504 		if ((ev = i1disp_command(p, i1d_rdexreg, ibuf, 3,
505 			                        obuf, 16, &rsize, 0.5)) != inst_ok)
506 			return ev;
507 
508 		if ((rsize < 4 && (rsize != (2 + rlen)))
509 		 || (rsize >= 4 && (rsize != (1 + rlen))))
510 			return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
511 
512 		if (obuf[0] != rlen)			/* Number of bytes returned */
513 			return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_VAL);
514 
515 		memcpy(outp + ooff, obuf + 1, rlen);
516 		ooff += rlen;
517 		addr += rlen;
518 		len -= rlen;
519 	}
520 	return inst_ok;
521 }
522 
523 /* Read the integration time */
524 static inst_code
i1disp_rd_int_time(i1disp * p,int * outp)525 i1disp_rd_int_time(
526 	i1disp *p,				/* Object */
527 	int *outp				/* Where to write value */
528 ) {
529 	unsigned char buf[16];
530 	int rsize;
531 	inst_code ev;
532 
533 	if ((ev = i1disp_command(p, i1d_getintgt, NULL, 0,
534 		         buf, 8, &rsize, 0.5)) != inst_ok)
535 		return ev;
536 
537 	if (rsize != 5)
538 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
539 
540 	*outp = buf2int(buf);
541 
542 	return inst_ok;
543 }
544 
545 /* Set the integration time */
546 static inst_code
i1disp_wr_int_time(i1disp * p,int inv)547 i1disp_wr_int_time(
548 	i1disp *p,				/* Object */
549 	int inv					/* Value to write */
550 ) {
551 	unsigned char buf[16];
552 	int rsize;
553 	inst_code ev;
554 
555 	int2buf(buf, inv);
556 	if ((ev = i1disp_command(p, i1d_setintgt, buf, 4,
557 		         buf, 8, &rsize, 0.5)) != inst_ok)
558 		return ev;
559 
560 	return inst_ok;
561 }
562 
563 /* Read the refresh period */
564 static inst_code
i1disp_rd_meas_ref_period(i1disp * p,int * outp)565 i1disp_rd_meas_ref_period(
566 	i1disp *p,				/* Object */
567 	int *outp				/* Where to write value */
568 ) {
569 	unsigned char buf[16];
570 	int rsize;
571 	inst_code ev;
572 
573 	if ((ev = i1disp_command(p, i1d_getmeas_p, NULL, 0,
574 		         buf, 8, &rsize, 1.5)) != inst_ok)
575 		return ev;
576 
577 	if (rsize != 5)
578 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
579 
580 	*outp = buf2int(buf);
581 
582 	return inst_ok;
583 }
584 
585 /* - - - - - - - - - - - - - - - - - - - - - - - - */
586 
587 /* Take a raw RGB period measurement from the device for an i1d1. */
588 /* The time taken to count the given number of L2F clock edges (+ve & -ve) */
589 /* is measured in clk clk_freq counts. */
590 static inst_code
i1d1_period_measure(i1disp * p,int edgec[3],double rgb[3])591 i1d1_period_measure(
592 	i1disp *p,				/* Object */
593 	int edgec[3],			/* Number of clock edges to count */
594 	double rgb[3]			/* Return the number of clk's */
595 ) {
596 	int i;
597 	unsigned char ibuf[16];
598 	unsigned char obuf[16];
599 	int rsize;
600 	inst_code ev;
601 
602 	/* Sanity check the number of edges */
603 	for (i = 0; i < 3; i++) {
604 		if (edgec[i] < 1 || edgec[i] > 255)
605 			return i1disp_interp_code((inst *)p, I1DISP_BAD_INT_THRESH);
606 		ibuf[i] = (char)edgec[i];
607 	}
608 
609 	/* Do the measurement, and return the Red value */
610 	if ((ev = i1disp_command(p, i1d_m_rgb_p, ibuf, 3,
611 		         obuf, 8, &rsize, 60.0)) != inst_ok)
612 		return ev;
613 	if (rsize != 5)
614 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
615 	rgb[0] = (double)buf2int(obuf);
616 
617 	/* Get the green value */
618 	if ((ev = i1disp_command(p, i1d_rd_green, NULL, 0,
619 		         obuf, 8, &rsize, 0.5)) != inst_ok)
620 		return ev;
621 	if (rsize != 5)
622 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
623 	rgb[1] = (double)buf2int(obuf);
624 
625 	/* Get the blue value */
626 	if ((ev = i1disp_command(p, i1d_rd_blue, NULL, 0,
627 		         obuf, 8, &rsize, 0.5)) != inst_ok)
628 		return ev;
629 	if (rsize != 5)
630 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
631 	rgb[2] = (double)buf2int(obuf);
632 
633 	return inst_ok;
634 }
635 
636 /* Take a cooked period measurement from the device for the i1d1 */
637 /* and return the frequency for each sensor. */
638 static inst_code
i1d1_take_measurement(i1disp * p,int cal,double rgb[3])639 i1d1_take_measurement(
640 	i1disp *p,				/* Object */
641 	int cal,				/* nz if black is not to be subtracted */
642 	double rgb[3]			/* Return the rgb frequency values */
643 ) {
644 	int i;
645 	int edgec[3];		/* Edge count 1..255 for each channel */
646 	inst_code ev;
647 	double edge_aim = p->clk_freq;
648 
649 	if (p->inited == 0)
650 		return i1disp_interp_code((inst *)p, I1DISP_NOT_INITED);
651 
652 	if (p->dtype != 0)
653 		return i1disp_interp_code((inst *)p, I1DISP_WRONG_DEVICE);
654 
655 	/* Do an initial measurement with minimum edge count of 1 */
656 	edgec[0] = edgec[1] = edgec[2] = 1;
657 
658 	if ((ev = i1d1_period_measure(p, edgec, rgb)) != inst_ok)
659 		return ev;
660 
661 	a1logd(p->log, 3, "Initial RGB = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
662 
663 	/* Compute adjusted edge count, aiming */
664 	/* for count values of clk_freq = 1 second (~1e6), */
665 	/* or 2 seconds if an older instrument */
666 	if (p->stype == i1d1_sencoreIV
667 	 || p->stype == i1d1_sencoreIII)
668 		edge_aim = 2.0 * p->clk_freq;;
669 
670 	for (i = 0; i < 3; i++) {
671 		double ns;
672 		if (edge_aim > ((255.0 - 0.5) * rgb[i]))
673 			ns = 255.0;
674 		else {
675 			ns = floor(edge_aim/rgb[i]) + 0.5;
676 			if (ns < 1.0)
677 				ns = 1.0;
678 		}
679 		edgec[i] = (int)ns;
680 	}
681 
682 	/* Only if we compute a different edge count, read again */
683 	if (edgec[0] > 1 || edgec[1] > 1 || edgec[2] > 1) {
684 		double rgb2[3];		/* 2nd RGB Readings */
685 
686 		if ((ev = i1d1_period_measure(p, edgec, rgb2)) != inst_ok)
687 			return ev;
688 
689 		/* Average readings if we repeated a measurement with the same edge count */
690 		/* (Minor advantage, but may as well use it) */
691 		for (i = 0; i < 3; i++) {
692 			if (edgec[i] == 1)
693 				rgb[i] = 0.5 * (rgb[i] + rgb2[i]);
694 			else
695 				rgb[i] = rgb2[i];
696 		}
697 	}
698 
699 	a1logd(p->log, 3, "scaled %d %d %d gives RGB = %f %f %f\n", edgec[0],edgec[1],edgec[2], rgb[0],rgb[1],rgb[2]);
700 
701 	/* Compute adjusted readings as a frequency. */
702 	/* We need to divide the number of edges/2 by the period in seconds */
703 	for (i = 0; i < 3; i++) {
704 		rgb[i] = (p->rgbadj[i] * 0.5 * (double)edgec[i] * p->clk_freq)/rgb[i];
705 		a1logd(p->log, 3, "%d sensor frequency = %f\n",i,rgb[i]);
706 
707 		/* If we're not calibrating the black */
708 		if (cal == 0) {
709 			rgb[i] -= p->reg103_F[i];		/* Subtract black level */
710 			a1logd(p->log, 3, "%d after sub black = %f\n",i,rgb[i]);
711 
712 			if (rgb[i] < 0.0001)
713 				rgb[i] = 0.0001;
714 			a1logd(p->log, 3, "%d after limit min = %f\n",i,rgb[i]);
715 		}
716 	}
717 	a1logd(p->log, 3, "Adjusted RGB = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
718 
719 	return inst_ok;
720 }
721 
722 /* . . . . . . . . . . . . . . . . . . . . . . . . */
723 
724 /* Take a fixed period frequency measurement from the device for an i1d2. */
725 /* This measures edge count over the set integration period. */
726 
727 /* Take a raw measurement using a given integration time. */
728 /* The measureent is the count of (both) edges from the L2V */
729 /* over the integration time */
730 static inst_code
i1d2_freq_measure(i1disp * p,double * inttime,double rgb[3])731 i1d2_freq_measure(
732 	i1disp *p,				/* Object */
733 	double *inttime,		/* Integration time in seconds. (Return clock rounded) */
734 	double rgb[3]			/* Return the RGB edge count values */
735 ) {
736 	unsigned char ibuf[16];
737 	unsigned char obuf[16];
738 	int intclks;
739 	int rsize;
740 	inst_code ev;
741 
742 	if (*inttime > 20.0)		/* Hmm */
743 		*inttime = 20.0;
744 
745 	intclks = (int)(*inttime * p->iclk_freq + 0.5);
746 	*inttime = (double)intclks / p->iclk_freq;
747 	if (intclks != p->int_clocks) {
748 		if ((ev = i1disp_wr_int_time(p, intclks)) != inst_ok)
749 			return ev;
750 		if ((ev = i1disp_rd_int_time(p, &intclks) ) != inst_ok)
751 			return ev;
752 		p->int_clocks = intclks;
753 		*inttime = (double)p->int_clocks/p->iclk_freq;
754 	}
755 
756 	/* Do the measurement, and return the Red value */
757 	ibuf[0] = 1;		/* Sync mode 1 */
758 	if ((ev = i1disp_command(p, i1d_m_red_2, ibuf, 1,
759 		         obuf, 8, &rsize, p->inttime + 1.0)) != inst_ok)
760 		return ev;
761 	if (rsize != 5)
762 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
763 	rgb[0] = (double)buf2int(obuf);
764 
765 	/* Get the green value */
766 	if ((ev = i1disp_command(p, i1d_rd_green, NULL, 0,
767 		         obuf, 8, &rsize, 0.5)) != inst_ok)
768 		return ev;
769 	if (rsize != 5)
770 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
771 	rgb[1] = (double)buf2int(obuf);
772 
773 	/* Get the blue value */
774 	if ((ev = i1disp_command(p, i1d_rd_blue, NULL, 0,
775 		         obuf, 8, &rsize, 0.5)) != inst_ok)
776 		return ev;
777 	if (rsize != 5)
778 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
779 	rgb[2] = (double)buf2int(obuf);
780 
781 	return inst_ok;
782 }
783 
784 /* Take a raw measurement that returns the number of clocks */
785 /* between and initial edge and edgec[] subsequent edges of the L2F. */
786 /* Both edges are counted. */
787 static inst_code
i1d2_period_measure(i1disp * p,int edgec[3],double rgb[3])788 i1d2_period_measure(
789 	i1disp *p,			/* Object */
790 	int edgec[3],		/* Measurement edge count for each channel */
791 	double rgb[3]		/* Return the RGB clock count values */
792 ) {
793 	int i;
794 	unsigned char ibuf[16];
795 	unsigned char obuf[16];
796 	int rsize;
797 	inst_code ev;
798 
799 	/* Set the edge count */
800 	for (i = 0; i < 3; i++) {
801 		short2buf(ibuf, edgec[i]);	/* Edge count */
802 		ibuf[2] = (unsigned char)i;	/* Channel number */
803 		if ((ev = i1disp_command(p, i1d_setmedges2, ibuf, 3,
804 			         obuf, 8, &rsize, 1.0)) != inst_ok)
805 			return ev;
806 	}
807 
808 	/* Do the measurement, and return the Red value */
809 	if ((ev = i1disp_command(p, i1d_m_rgb_edge_2, ibuf, 0,
810 		         obuf, 8, &rsize, 120.0)) != inst_ok) {
811 		return ev;
812 	}
813 	if (rsize != 5)
814 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
815 	rgb[0] = (double)buf2int(obuf);
816 
817 	/* Get the green value */
818 	if ((ev = i1disp_command(p, i1d_rd_green, NULL, 0,
819 		         obuf, 8, &rsize, 0.5)) != inst_ok)
820 		return ev;
821 	if (rsize != 5)
822 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
823 	rgb[1] = (double)buf2int(obuf);
824 
825 	/* Get the blue value */
826 	if ((ev = i1disp_command(p, i1d_rd_blue, NULL, 0,
827 		         obuf, 8, &rsize, 0.5)) != inst_ok)
828 		return ev;
829 	if (rsize != 5)
830 		return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
831 	rgb[2] = (double)buf2int(obuf);
832 
833 	return inst_ok;
834 }
835 
836 #define ME 1		/* One edge initially (should this be 2 ?) */
837 
838 #ifndef NEVER
839 
840 /* ### Quick and precise for low levels, but subject to long */
841 /* ### delays if the light level drops during measurement, and */
842 /* ### may be less accurate at low levels due to dark noise */
843 
844 /* Take a cooked measurement from the device for the i1d2 */
845 static inst_code
i1d2_take_measurement(i1disp * p,int refreshm,double rgb[3])846 i1d2_take_measurement(
847 	i1disp *p,				/* Object */
848 	int refreshm,			/* Measure in refresh mode flag */
849 	double rgb[3]			/* Return the rgb values */
850 ) {
851 	int i;
852 	double rmeas[3];			/* Raw measurement */
853 	int edgec[3] = {ME,ME,ME};	/* Measurement edge count for each channel */
854 	int cdgec[3] = {ME,ME,ME};	/* CRT computed edge count for re-measure */
855 	int mask = 0x0;				/* Period measure mask */
856 	inst_code ev;
857 
858 	if (p->inited == 0)
859 		return i1disp_interp_code((inst *)p, I1DISP_NOT_INITED);
860 
861 	if (p->dtype == 0)
862 		return i1disp_interp_code((inst *)p, I1DISP_WRONG_DEVICE);
863 
864 	a1logd(p->log, 3, "i1d2_take_measurement called with refreshm = %d\n",refreshm);
865 
866 	/* Do refresh period measurement */
867 	if (p->dtype == 1 && refreshm && p->rrset == 0) {
868 		if ((ev = i1disp_do_fcal_setit(p)) != inst_ok)
869 			return ev;
870 
871 		/* Quantize the sample time */
872 		if (p->refperiod > 0.0) {
873 			int n;
874 			n = (int)ceil(p->dinttime/p->refperiod);
875 			p->inttime = n * p->refperiod;
876 			a1logd(p->log, 3, "i1disp: integration time quantize to %f secs\n",p->inttime);
877 		} else {
878 			p->inttime = p->dinttime;
879 			a1logd(p->log, 3, "i1disp: integration time set to %f secs\n",p->inttime);
880 		}
881 	}
882 
883 // Do a frequency measurement first, to reduces chances of a light change causing delays.
884 // This makes LCD same as CRT as far as quantization errors in high level readings.
885 //	if (refreshm) {
886 		/* Do an initial fixed integration time frequency measurement. */
887 		a1logd(p->log, 3, "Doing fixed period frequency measurement over %f secs\n",p->inttime);
888 
889 		if ((ev = i1d2_freq_measure(p, &p->inttime, rmeas)) != inst_ok)
890 			return ev;
891 
892 		for (i = 0; i < 3; i++)
893 			rgb[i] = p->rgbadj[i] * 0.5 * rmeas[i]/p->inttime;
894 
895 		a1logd(p->log, 3, "Got %f %f %f raw, %f %f %f Hz\n",
896 		rmeas[0], rmeas[1], rmeas[2], rgb[0], rgb[1], rgb[2]);
897 
898 		/* Decide whether any channels need re-measuring, */
899 		/* and computed cooked values. Threshold is a count of 75 */
900 		for (i = 0; i < 3; i++) {
901 			if (rmeas[i] <= 75.0) {
902 				mask |= (1 << i);		/* Yes */
903 				if (rmeas[i] >= 10.0) {	/* Compute target edges */
904 					cdgec[i] = (int)(2.0 * rgb[i] * p->inttime + 0.5);
905 					if (cdgec[i] > 2000)
906 						cdgec[i] = 2000;
907 					else if (cdgec[i] < ME)
908 						cdgec[i] = ME;
909 				}
910 			}
911 		}
912 //	} else {
913 //		mask = 0x7;
914 //	}
915 	a1logd(p->log, 3, "Re-measure mask = 0x%x\n",mask);
916 	a1logd(p->log, 3, "cdgec = %d %d %d\n",cdgec[0],cdgec[1],cdgec[2]);
917 
918 	/* If any need re-measuring */
919 	if (mask != 0) {
920 
921 		/* See if we need to compute a target edge count */
922 		for (i = 0; i < 3; i++) {
923 			if ((mask & (1 << i)) && cdgec[i] == ME)
924 				break;
925 		}
926 
927 		/* Yes we do */
928 		if (i < 3) {
929 
930 			a1logd(p->log, 3, "Doing 1st period pre-measurement mask 0x%x, edgec %d %d %d\n",
931 			mask, edgec[0], edgec[1], edgec[2]);
932 
933 			/* Do an initial measurement of 1 edge to estimate the */
934 			/* number of edges needed for the whole integration time. */
935 			if ((ev = i1d2_period_measure(p, edgec, rmeas)) != inst_ok)
936 				return ev;
937 
938 			a1logd(p->log, 3, "Got %f %f %f raw %f %f %f Hz\n",
939 			rmeas[0], rmeas[1], rmeas[2],
940 			(p->rgbadj[0] * 0.5 * (double)edgec[0] * p->clk_freq)/rmeas[0],
941 			(p->rgbadj[1] * 0.5 * (double)edgec[1] * p->clk_freq)/rmeas[1],
942 			(p->rgbadj[2] * 0.5 * (double)edgec[2] * p->clk_freq)/rmeas[2]);
943 
944 			/* Compute adjusted edge count for channels we're remeasuring, */
945 			/* aiming for a values of int_clocks. */
946 			for (i = 0; i < 3; i++) {
947 				double ns;
948 				if ((mask & (1 << i)) == 0)
949 					continue;
950 				if (p->int_clocks > ((2000.0 - 0.5) * rmeas[i]))
951 					ns = 2000.0;			/* Maximum edge count */
952 				else {
953 					ns = floor(p->inttime * edgec[i] * p->clk_freq/rmeas[i] + 0.5);
954 					if (ns < ME)			/* Minimum edge count */
955 						ns = ME;
956 				}
957 				edgec[i] = (int)ns;
958 			}
959 		}
960 
961 		/* Use frequency computed edge count if available */
962 		for (i = 0; i < 3; i++) {
963 			if ((mask & (1 << i)) == 0)
964 				continue;
965 			if (cdgec[i] != ME)
966 				edgec[i] = cdgec[i];
967 		}
968 
969 		/* If we compute a different edge count, read again */
970 		if (edgec[0] > ME || edgec[1] > ME || edgec[2] > ME) {
971 			double rmeas2[3];		/* 2nd RGB Readings */
972 
973 			a1logd(p->log, 3, "Doing period re-measurement mask 0x%x, edgec %d %d %d\n",
974 			mask, edgec[0], edgec[1], edgec[2]);
975 
976 			if ((ev = i1d2_period_measure(p, edgec, rmeas2)) != inst_ok)
977 				return ev;
978 
979 			a1logd(p->log, 3, "Got %f %f %f raw %f %f %f Hz\n",
980 			rmeas[0], rmeas[1], rmeas[2],
981 			(p->rgbadj[0] * 0.5 * (double)edgec[0] * p->clk_freq)/rmeas2[0],
982 			(p->rgbadj[1] * 0.5 * (double)edgec[1] * p->clk_freq)/rmeas2[1],
983 			(p->rgbadj[2] * 0.5 * (double)edgec[2] * p->clk_freq)/rmeas2[2]);
984 
985 			a1logd(p->log, 3, "Int period %f %f %f secs\n",
986 			rmeas2[0]/p->clk_freq, rmeas2[1]/p->clk_freq, rmeas2[2]/p->clk_freq);
987 
988 			/* Average readings if we repeated a measurement with the same count */
989 			/* (Minor advantage, but may as well use it) */
990 			for (i = 0; i < 3; i++) {
991 				if (edgec[i] == ME)
992 					rmeas[i] = 0.5 * (rmeas[i] + rmeas2[i]);
993 				else
994 					rmeas[i] = rmeas2[i];
995 			}
996 		}
997 
998 		/* Compute adjusted readings, ovewritting initial cooked values */
999 		for (i = 0; i < 3; i++) {
1000 			if ((mask & (1 << i)) == 0)
1001 				continue;
1002 			rgb[i] = (p->rgbadj[i] * 0.5 * (double)edgec[i] * p->clk_freq)/rmeas[i];
1003 			a1logd(p->log, 3, "%d after scale = %f\n",i,rgb[i]);
1004 
1005 			rgb[i] -= p->reg103_F[i];		/* Subtract black level */
1006 			a1logd(p->log, 3, "%d after sub black = %f\n",i,rgb[i]);
1007 
1008 			if (rgb[i] < 0.0001)
1009 				rgb[i] = 0.0001;
1010 			a1logd(p->log, 3, "%d after limit min = %f\n",i,rgb[i]);
1011 		}
1012 	}
1013 
1014 	a1logd(p->log, 3, "Cooked RGB Hz = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
1015 
1016 	return inst_ok;
1017 }
1018 
1019 #else
1020 /* Less precise (worse quatization errors), but more robust */
1021 /* against excessive delays if the light level drops during measurement. */
1022 /* Limits period measurement to an edge count < 35, but that can */
1023 /* still take a long time in the dark. */
1024 
1025 /* Take a cooked measurement from the device for the i1d2 */
1026 static inst_code
i1d2_take_measurement(i1disp * p,int refreshm,double rgb[3])1027 i1d2_take_measurement(
1028 	i1disp *p,				/* Object */
1029 	int refreshm,				/* Measure in crt mode flag */
1030 	double rgb[3]			/* Return the rgb values */
1031 ) {
1032 	int i;
1033 	double rmeas[3];			/* Raw measurement */
1034 	int edgec[3] = {ME,ME,ME};	/* Measurement edge count for each channel */
1035 	int cdgec[3] = {ME,ME,ME};	/* CRT computed edge count for re-measure */
1036 	int fmask = 0x0;             /* Freq re-measure mask */
1037 	int mask = 0x0;				/* Period measure mask */
1038 	inst_code ev;
1039 
1040 	if (p->inited == 0)
1041 		return i1disp_interp_code((inst *)p, I1DISP_NOT_INITED);
1042 
1043 	if (p->dtype == 0)
1044 		return i1disp_interp_code((inst *)p, I1DISP_WRONG_DEVICE);
1045 
1046 	a1logd(p->log, 3, "i1d2_take_measurement called with refreshm = %d\n",refreshm);
1047 
1048 	/* Do refresh period measurement */
1049 	if (p->dtype == 1 && refreshm && p->rrset == 0) {
1050 		if ((ev = i1disp_do_fcal_setit(p)) != inst_ok)
1051 			return ev;
1052 
1053 		/* Quantize the sample time */
1054 		if (p->refperiod > 0.0) {
1055 			int n;
1056 			n = (int)ceil(p->dinttime/p->refperiod);
1057 			p->inttime = n * p->refperiod;
1058 			a1logd(p->log, 3, "i1disp: integration time quantize to %f secs\n",p->inttime);
1059 		} else {
1060 			p->inttime = p->dinttime;
1061 			a1logd(p->log, 3, "i1disp: ntegration time set to %f secs\n",p->inttime);
1062 		}
1063 	}
1064 
1065 	/* Do an initial fixed integration time frequency measurement. */
1066 	a1logd(p->log, 3, "Doing fixed period frequency measurement over %f secs\n",p->inttime)
1067 
1068 	if ((ev = i1d2_freq_measure(p, &p->inttime, rmeas)) != inst_ok)
1069 		return ev;
1070 
1071 	for (i = 0; i < 3; i++)
1072 		rgb[i] = p->rgbadj[i] * 0.5 * rmeas[i]/p->inttime;
1073 
1074 	a1logd(p->log, 3, "Got %f %f %f raw, %f %f %f Hz\n",
1075 	rmeas[0], rmeas[1], rmeas[2], rgb[0], rgb[1], rgb[2]);
1076 
1077 	/* Decide whether any channels need re-measuring. */
1078 	/* Threshold is a count of 75, and switch to period */
1079 	/* measurement mode on count less than 37. */
1080 	fmask = 0x0;
1081 	for (i = 0; i < 3; i++) {
1082 
1083 		if (rmeas[i] <= 75.0) {
1084 			fmask |= (1 << i);		/* Yes, do another freq re-measure */
1085 
1086 			if (rmeas[i] <= 37.5) {
1087 				mask |= (1 << i);		/* Do a period re-measure */
1088 				fmask = 0;				/* Don't bother with freq re-measure */
1089 			}
1090 			if (rmeas[i] >= 10.0) {	/* Compute target edges */
1091 				cdgec[i] = (int)(2.0 * rgb[i] * p->inttime + 0.5);
1092 				if (cdgec[i] > 2000)
1093 					cdgec[i] = 2000;
1094 				else if (cdgec[i] < ME)
1095 					cdgec[i] = ME;
1096 			}
1097 		}
1098 	}
1099 	a1logd(p->log, 3, "Freq mask = 0x%x, Period mask 0x%x\n",fmask, mask);
1100 	a1logd(p->log, 3, "cdgec = %d %d %d\n",cdgec[0],cdgec[1],cdgec[2]);
1101 
1102 	/* If there is a frequency re-measure */
1103 	/* ** This doesn't actually work. The quantization error */
1104 	/* for each read is 0.5, but averaging 2 reads it drops */
1105 	/* to 0.354, not the desired 0.25 that would have been */
1106 	/* acheived with double the integration time. ** */
1107 	if (fmask != 0) {
1108 		a1logd(p->log, 3, "Doing frequency re-measurement over %f secs\n",p->inttime);
1109 		if ((ev = i1d2_freq_measure(p, &p->inttime, rmeas)) != inst_ok)
1110 			return ev;
1111 
1112 		for (i = 0; i < 3; i++) {
1113 			rgb[i] += p->rgbadj[i] * 0.5 * rmeas[i]/p->inttime;
1114 			rgb[i] /= 2.0;
1115 		}
1116 
1117 		a1logd(p->log, 3, "Got %f %f %f raw, %f %f %f Avg. Hz\n",
1118 		rmeas[0], rmeas[1], rmeas[2], rgb[0], rgb[1], rgb[2]);
1119 
1120 	/* If there is a period re-measure */
1121 	} else if (mask != 0) {
1122 
1123 		/* See if we need to compute a target edge count */
1124 		for (i = 0; i < 3; i++) {
1125 			if ((mask & (1 << i)) && cdgec[i] == ME)
1126 				break;
1127 		}
1128 
1129 		/* Yes we do */
1130 		if (i < 3) {
1131 
1132 			a1logd(p->log, 3, "Doing 1st period pre-measurement mask 0x%x, edgec %d %d %d\n",
1133 			mask, edgec[0], edgec[1], edgec[2]);
1134 
1135 			/* Do an initial measurement of 1 edge to estimate the */
1136 			/* number of edges needed for the whole integration time. */
1137 			if ((ev = i1d2_period_measure(p, edgec, rmeas)) != inst_ok)
1138 				return ev;
1139 
1140 			a1logd(p->log, 3, "Got %f %f %f raw %f %f %f Hz\n",
1141 			rmeas[0], rmeas[1], rmeas[2],
1142 			(p->rgbadj[0] * 0.5 * (double)edgec[0] * p->clk_freq)/rmeas[0],
1143 			(p->rgbadj[1] * 0.5 * (double)edgec[1] * p->clk_freq)/rmeas[1],
1144 			(p->rgbadj[2] * 0.5 * (double)edgec[2] * p->clk_freq)/rmeas[2]);
1145 
1146 			/* Compute adjusted edge count for channels we're remeasuring, */
1147 			/* aiming for a values of int_clocks. */
1148 			for (i = 0; i < 3; i++) {
1149 				double ns;
1150 				if ((mask & (1 << i)) == 0)
1151 					continue;
1152 				if (p->int_clocks > ((2000.0 - 0.5) * rmeas[i]))
1153 					ns = 2000.0;			/* Maximum edge count */
1154 				else {
1155 					ns = floor(p->inttime * edgec[i] * p->clk_freq/rmeas[i] + 0.5);
1156 					if (ns < ME)			/* Minimum edge count */
1157 						ns = ME;
1158 				}
1159 				edgec[i] = (int)ns;
1160 
1161 				/* Sanity check cdgec value, in case light level has changed */
1162 				if ((edgec[i] * 3) < (cdgec[i] * 2)) {
1163 					cdgec[i] = edgec[i];
1164 				}
1165 			}
1166 		}
1167 
1168 		/* Use frequency computed edge count if available */
1169 		for (i = 0; i < 3; i++) {
1170 			if ((mask & (1 << i)) == 0)
1171 				continue;
1172 			if (cdgec[i] != ME)
1173 				edgec[i] = cdgec[i];
1174 		}
1175 
1176 		/* If we compute a different edge count, read again */
1177 		if (edgec[0] > ME || edgec[1] > ME || edgec[2] > ME) {
1178 			double rmeas2[3];		/* 2nd RGB Readings */
1179 
1180 			a1logd(p->log, 3, "Doing period re-measurement mask 0x%x, edgec %d %d %d\n",
1181 			mask, edgec[0], edgec[1], edgec[2]);
1182 
1183 			if ((ev = i1d2_period_measure(p, edgec, rmeas2)) != inst_ok)
1184 				return ev;
1185 
1186 			a1logd(p->log, 3, "Got %f %f %f raw %f %f %f Hz\n",
1187 			rmeas[0], rmeas[1], rmeas[2],
1188 			(p->rgbadj[0] * 0.5 * (double)edgec[0] * p->clk_freq)/rmeas2[0],
1189 			(p->rgbadj[1] * 0.5 * (double)edgec[1] * p->clk_freq)/rmeas2[1],
1190 			(p->rgbadj[2] * 0.5 * (double)edgec[2] * p->clk_freq)/rmeas2[2]);
1191 
1192 			a1logd(p->log, 3, "Int period %f %f %f secs\n",
1193 			rmeas2[0]/p->clk_freq, rmeas2[1]/p->clk_freq, rmeas2[2]/p->clk_freq);
1194 
1195 			/* Average readings if we repeated a measurement with the same count */
1196 			/* (Minor advantage, but may as well use it) */
1197 			for (i = 0; i < 3; i++) {
1198 				if (edgec[i] == ME)
1199 					rmeas[i] = 0.5 * (rmeas[i] + rmeas2[i]);
1200 				else
1201 					rmeas[i] = rmeas2[i];
1202 			}
1203 		}
1204 
1205 		/* Compute adjusted readings, ovewritting initial cooked values */
1206 		for (i = 0; i < 3; i++) {
1207 			if ((mask & (1 << i)) == 0)
1208 				continue;
1209 			rgb[i] = (p->rgbadj[i] * 0.5 * (double)edgec[i] * p->clk_freq)/rmeas[i];
1210 			a1logd(p->log, 3, "%d after scale = %f\n",i,rgb[i]);
1211 
1212 			rgb[i] -= p->reg103_F[i];		/* Subtract black level */
1213 			a1logd(p->log, 3, "%d after sub black = %f\n",i,rgb[i]);
1214 
1215 			if (rgb[i] < 0.0001)
1216 				rgb[i] = 0.0001;
1217 			a1logd(p->log, 3, "%d after limit min = %f\n",i,rgb[i]);
1218 		}
1219 	}
1220 
1221 	a1logd(p->log, 3, "Cooked RGB Hz = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
1222 
1223 	return inst_ok;
1224 }
1225 #endif
1226 #undef ME
1227 
1228 /* . . . . . . . . . . . . . . . . . . . . . . . . */
1229 
1230 /* Take a XYZ measurement from the device */
1231 static inst_code
i1disp_take_XYZ_measurement(i1disp * p,double XYZ[3])1232 i1disp_take_XYZ_measurement(
1233 	i1disp *p,				/* Object */
1234 	double XYZ[3]			/* Return the XYZ values */
1235 ) {
1236 	int i, j;
1237 	double rgb[3];		/* RGB Readings */
1238 	inst_code ev;
1239 	double *mat;		/* Pointer to matrix */
1240 
1241 	if (p->dtype == 0) {		/* i1 disp 1 */
1242 		if ((ev = i1d1_take_measurement(p, 0, rgb)) != inst_ok)
1243 			return ev;
1244 	} else {				/* i1 disp 2 or ColorMunki Smile */
1245 
1246 		if ((ev = i1d2_take_measurement(p, p->refrmode, rgb)) != inst_ok)
1247 			return ev;
1248 	}
1249 
1250 	/* Multiply by calibration matrix to arrive at XYZ */
1251 	if (IMODETST(p->mode, inst_mode_emis_ambient)) {
1252 		mat = p->amb;				/* Ambient matrix */
1253 	} else {
1254 		if (p->icx)
1255 			mat = p->reg54_F;		/* CRT/factory matrix/CCFL */
1256 		else
1257 			mat = p->reg4_F;		/* LCD/user matrix/LED */
1258 	}
1259 	for (i = 0; i < 3; i++) {
1260 		XYZ[i] = 0.0;
1261 		for (j = 0; j < 3; j++) {
1262 			XYZ[i] += mat[i * 3 + j] * rgb[j];
1263 		}
1264 
1265 
1266 		/* Magic factors for other devices ?? */
1267 		if (p->stype == i1d1_sencoreIV)
1268 			XYZ[i] *= CALFACTOR;			/* (Not sure about this factor!) */
1269 		else
1270 			XYZ[i] *= CALFACTOR;		/* Times magic scale factor */
1271 	}
1272 
1273 	if (!IMODETST(p->mode, inst_mode_emis_ambient)) {
1274 
1275 		/* Apply the colorimeter correction matrix */
1276 		icmMulBy3x3(XYZ, p->ccmat, XYZ);
1277 	}
1278 
1279 	a1logd(p->log, 3, "returning XYZ = %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]);
1280 	return inst_ok;
1281 }
1282 
1283 /* Do a black calibration (i1display 1) */
1284 static inst_code
i1disp_do_black_cal(i1disp * p)1285 i1disp_do_black_cal(
1286 	i1disp *p				/* Object */
1287 ) {
1288 	int i;
1289 	double rgb1[3], rgb2[3];	/* RGB Readings */
1290 	inst_code ev;
1291 
1292 	if (p->dtype != 0)
1293 		return i1disp_interp_code((inst *)p, I1DISP_CANT_BLACK_CALIB);
1294 
1295 	/* Do a couple of readings without subtracting the black */
1296 	if ((ev = i1d1_take_measurement(p, 1, rgb1)) != inst_ok)
1297 		return ev;
1298 	if ((ev = i1d1_take_measurement(p, 1, rgb2)) != inst_ok)
1299 		return ev;
1300 
1301 	/* Average the results */
1302 	for (i = 0; i < 3; i++) {
1303 		rgb1[i] = 0.5 * (rgb1[i] + rgb2[i]);
1304 
1305 		/* Since the readings are clamped to 0.0001, */
1306 		/* aim for a black level of 0.0001 */
1307 		rgb1[i] -= 0.0001;
1308 	}
1309 
1310 	a1logd(p->log, 3, "Black rgb = %f %f %f\n",rgb1[0],rgb1[1],rgb1[2]);
1311 
1312 	/* Save it to the EEPROM */
1313 	for (i = 0; i < 3; i++) {
1314 		if ((ev = i1disp_wrreg_float(p, rgb1[i], 103 + 4 * i)) != inst_ok)
1315 			return ev;
1316 		p->reg103_F[i] = rgb1[i];
1317 	}
1318 	return inst_ok;
1319 }
1320 
1321 /* Measure the refresh rate */
1322 static inst_code
i1disp_read_refrate(inst * pp,double * ref_rate)1323 i1disp_read_refrate(
1324 	inst *pp,
1325 	double *ref_rate		/* Return value, 0.0 if fails */
1326 ) {
1327 	i1disp *p = (i1disp *)pp;
1328 	int i;
1329 	inst_code ev;
1330 	double measp = 0.0;
1331 
1332 	a1logd(p->log, 3, "Frequency calibration called\n");
1333 
1334 	if (p->dtype != 1)
1335 		return inst_unsupported;
1336 
1337 	if (ref_rate != NULL)
1338 		*ref_rate = 0.0;
1339 
1340 	/* Average a few refresh period readings */
1341 	for (i = 0; i < p->nmeasprds; i++) {
1342 		int mp;
1343 
1344 		/* Measures period in clocks */
1345 		if ((ev = i1disp_rd_meas_ref_period(p, &mp)) != inst_ok)
1346 			return ev;
1347 		if (mp == 0) {
1348 			measp = 0.0;
1349 			break;			/* Too dark to measure */
1350 		}
1351 		measp += (double)mp;
1352 	}
1353 
1354 	/* Compute the measurement frequency */
1355 	if (measp != 0.0) {
1356 		double rrate = (p->clk_freq * (double)p->nmeasprds)/measp;
1357 		a1logd(p->log, 3, "Sample frequency measured = %f\n",rrate);
1358 		if (ref_rate != NULL)
1359 			*ref_rate = rrate;
1360 		return inst_ok;
1361 	} else {
1362 		a1logd(p->log, 3, "No discernable refresh frequency measured\n");
1363 		if (ref_rate != NULL)
1364 			*ref_rate = 0.0;
1365 		return inst_misread;
1366 	}
1367 }
1368 
1369 /* Do a refresh period cailbration  */
1370 static inst_code
i1disp_do_fcal_setit(i1disp * p)1371 i1disp_do_fcal_setit(
1372 	i1disp *p				/* Object */
1373 ) {
1374 	int i;
1375 	inst_code ev;
1376 
1377 	a1logd(p->log, 3, "Frequency calibration called\n");
1378 
1379 	if (p->dtype != 1)
1380 		return i1disp_interp_code((inst *)p, I1DISP_CANT_MEASP_CALIB);
1381 
1382 	if ((ev = i1disp_read_refrate((inst *)p, &p->refrate)) != inst_ok
1383 	 && ev != inst_misread)
1384 		return ev;
1385 
1386 	if (p->refrate != 0.0) {
1387 		p->refperiod = 1.0/p->refrate;
1388 		p->refrvalid = 1;
1389 	} else {
1390 		p->refrvalid = 0;
1391 	}
1392 	p->rrset = 1;
1393 
1394 	return inst_ok;
1395 }
1396 
1397 
1398 /* - - - - - - - - - - - - - - - - - - - - - - - - */
1399 
1400 /* Check the device is responding, and unlock if necessary */
1401 static inst_code
i1disp_check_unlock(i1disp * p)1402 i1disp_check_unlock(
1403 	i1disp *p				/* Object */
1404 ) {
1405 	unsigned char buf[16];
1406 	int rsize;
1407 	inst_code ev;
1408 	int i, vv;
1409 	double ver;
1410 
1411 	struct {
1412 		unsigned char code[4];
1413 		i1d2_dtype stype;
1414 	} codes[] = {
1415 		{ { 'G','r','M','b' },     i1d2_norm },		/* "GrMb" i1 Display */
1416 		{ { 'L','i','t','e' },     i1d2_lite },		/* "Lite" i1 Display LT */
1417 		{ { 'M','u','n','k' },     i1d2_munki },	/* "Munk" ColorMunki Create */
1418 		{ { 'O','b','i','W' },     i1d2_hpdream },	/* "ObiW" HP DreamColor */
1419 		{ { 'O','b','i','w' },     i1d2_hpdream },	/* "Obiw" HP DreamColor */
1420 		{ { 'C','M','X','2' },     i1d1_calmanx2 },	/* "CMX2" Calman X2 */
1421 		{ { 0x24,0xb6,0xb5,0x13 }, i1d2_norm },		/* ColorMunki Smile */
1422 		{ { 'S','p','C','3' },     i1d2_norm },		/* SpectraCal C3 (Based on Smile) */
1423 		{ { 'R','G','B','c' },     i1d2_norm },		/* */
1424 		{ { 'C','E','C','5' },     i1d2_norm },		/* */
1425 		{ { 'C','M','C','5' },     i1d2_norm },		/* */
1426 		{ { 'C','M','G','5' },     i1d2_norm },		/* */
1427 		{ { 0x00,0x00,0x01,0x00 }, i1d2_norm },		/* */
1428 		{ { 0x09,0x0b,0x0c,0x0d }, i1d2_norm },		/* */
1429 		{ { 0x0e,0x0e,0x0e,0x0e }, i1d2_norm },		/* */
1430 		{ { 0x11,0x02,0xde,0xf0 }, i1d2_norm },		/* Barco Chroma 5 ? */
1431 //		{ { 0xff,0xff,0xff,0xff }, i1d2_norm },		/* Chroma 5 isn't locked ? */
1432 		{ { ' ',' ',' ',' ' }, -1 }
1433 	};
1434 
1435 	a1logd(p->log, 3, "i1disp: about to check response and unlock instrument if needed\n");
1436 
1437 	/* Get status */
1438 	if ((ev = i1disp_command_1(p, i1d_status, NULL, 0,
1439 	                   buf, 8, &rsize, 0.5)) != inst_ok && (ev & inst_imask) != I1DISP_LOCKED)
1440 		return ev;		/* An error other than being locked */
1441 
1442 	/* Try and unlock it if it is locked */
1443 	if ((ev & inst_imask) == I1DISP_LOCKED) {
1444 
1445 		/* Try each code in turn */
1446 		for (i = 0; ;i++) {
1447 			if (codes[i].stype == -1) {
1448 				a1logd(p->log, 3, "Failed to find correct unlock code\n");
1449 				return i1disp_interp_code((inst *)p, I1DISP_UNKNOWN_MODEL);
1450 			}
1451 
1452 			/* Try unlock code. Ignore I1DISP_NOT_READY status. */
1453 			if (((ev = i1disp_command_1(p, i1d_unlock, codes[i].code, 4,
1454 			         buf, 8, &rsize, 0.5)) & inst_mask) != inst_ok
1455 			                 && (ev & inst_imask) != I1DISP_LOCKED)
1456 				return ev;		/* Some other sort of failure */
1457 
1458 			/* Get status */
1459 			if ((ev = i1disp_command_1(p, i1d_status, NULL, 0,
1460 			           buf, 8, &rsize, 0.5)) != inst_ok
1461 			                 && (ev & inst_imask) != I1DISP_LOCKED)
1462 				return ev;	/* An error other than being locked */
1463 
1464 			if (ev == inst_ok) {	/* Correct code */
1465 				p->stype = codes[i].stype;
1466 				a1logd(p->log, 3, "Unlocked with code '%c%c%c%c'\n",
1467 				codes[i].code[0], codes[i].code[1], codes[i].code[2], codes[i].code[3]);
1468 				break;
1469 			}
1470 		}
1471 	}
1472 
1473 	if (rsize != 5 || !isdigit(buf[0]) || buf[1] != '.'
1474 	               || !isdigit(buf[2]) || !isdigit(buf[3])) {
1475 		return i1disp_interp_code((inst *)p, I1DISP_BAD_STATUS);
1476 	}
1477 
1478 	buf[4] = '\000';
1479 	ver = atof((char *)buf);
1480 	a1logd(p->log, 3, "Version string = %5.3f\n",ver);
1481 
1482 	/* Read register 0x79 for the model identifier */
1483 	if ((ev = i1disp_rdreg_byte(p, &vv, 121) ) != inst_ok) {
1484 		return ev;
1485 	}
1486 	vv &= 0xff;
1487 
1488 	a1logd(p->log, 3, "Version character = 0x%02x = '%c'\n",vv,vv);
1489 
1490 	/* Barco Chroma 5 with ver = 5.01 vv = '5' */
1491 	if (ver >= 4.0 && ver < 5.1 && vv == '5') {
1492 		p->dtype = 0;			/* Sequel Chroma 4 ?? */
1493 		p->stype = i1d1_chroma4;	/* Treat like an Eye-One Display 1 */
1494 
1495 	/* Sequel Chroma 4 with vv == 0xff ???? */
1496 	/* Sencore ColorPro III with ver = 5.01 and vv = 0xff */
1497 	} else if (ver >= 4.0 && ver < 5.1 && vv == 0xff) {
1498 		p->dtype = 0;			/* Eye-One Display 1 */
1499 		p->stype = i1d1_sencoreIII;
1500 
1501 	/* Sencore ColorPro IV with ver = 5.01 and vv = 'L' */
1502 	} else if (ver >= 4.0 && ver < 5.1 && vv == 'L') {
1503 		p->dtype = 0;			/* Eye-One Display 1 */
1504 		p->stype = i1d1_sencoreIV;	/* Treat like an Eye-One Display 1 */
1505 
1506 	/* Sencore ColorPro V with ver = 5.01 and vv = 'B' */
1507 	} else if (ver >= 4.0 && ver < 5.1 && vv == 'B') {
1508 		p->dtype = 0;			/* Eye-One Display 1 */
1509 		p->stype = i1d1_sencoreV;	/* Treat like an Eye-One Display 1 */
1510 
1511 	} else if (ver >= 5.1 && ver <= 5.3 && vv == 'L') {
1512 		p->dtype = 0;			/* Eye-One Display 1 */
1513 
1514 	} else if (ver >= 6.0 && ver <= 6.29 && vv == 'L') {
1515 		p->dtype = 1;			/* Eye-One Display 2 */
1516 
1517 	} else if (ver >= 6.0 && ver <= 6.29
1518 		&& (vv = 0xff || vv == 'M')) {		// Faulty Smile's have vv = 0xff
1519 		/* ColorMunki Create ? */
1520 		/* ColorMunki Smile */
1521 		if (p->dtype == 0)		/* Not sure if this is set by Create */
1522 			p->dtype = 1;
1523 
1524 	} else {
1525 		/* Reject any version or model we don't know about */
1526 		a1logd(p->log, 1, "Version string = %5.3f\nID character = 0x%02x = '%c'\n",ver,vv,vv);
1527 		return i1disp_interp_code((inst *)p, I1DISP_UNKNOWN_VERS_ID);
1528 	}
1529 
1530 	a1logd(p->log, 2, "i1disp: instrument is responding, unlocked, and right type\n");
1531 
1532 	return inst_ok;
1533 }
1534 
1535 /* Read all the relevant register values */
1536 static inst_code
i1disp_read_all_regs(i1disp * p)1537 i1disp_read_all_regs(
1538 	i1disp *p				/* Object */
1539 ) {
1540 	inst_code ev;
1541 	int i;
1542 
1543 	a1logd(p->log, 3, "i1disp: about to read all the registers\n");
1544 
1545 	/* Serial number */
1546 	if ((ev = i1disp_rdreg_word(p, &p->reg0_W, 0) ) != inst_ok)
1547 		return ev;
1548 	a1logd(p->log, 3, "serial number = %d\n",p->reg0_W);
1549 
1550 	/* LCD/user calibration values */
1551 	for (i = 0; i < 9; i++) {
1552 		if ((ev = i1disp_rdreg_float(p, &p->reg4_F[i], 4 + 4 * i) ) != inst_ok)
1553 			return ev;
1554 		a1logd(p->log, 3, "LCD/user cal[%d] = %f\n",i,p->reg4_F[i]);
1555 	}
1556 	/* LCD/user calibration time */
1557 	if ((ev = i1disp_rdreg_word(p, &p->reg50_W, 50) ) != inst_ok)
1558 		return ev;
1559 	a1logd(p->log, 3, "LCD/user calibration time = 0x%x = %s\n",p->reg50_W, ctime_32(&p->reg50_W));
1560 
1561 	/* LCD/user calibration flag */
1562 	if ((ev = i1disp_rdreg_short(p, &p->reg126_S, 126) ) != inst_ok)
1563 		return ev;
1564 	a1logd(p->log, 3, "User cal flag = 0x%x\n",p->reg126_S);
1565 
1566 
1567 	/* CRT/factory calibration values */
1568 	for (i = 0; i < 9; i++) {
1569 		if ((ev = i1disp_rdreg_float(p, &p->reg54_F[i], 54 + 4 * i) ) != inst_ok)
1570 			return ev;
1571 		a1logd(p->log, 3, "CRT/factory cal[%d] = %f\n",i,p->reg54_F[i]);
1572 	}
1573 	/* CRT/factory calibration flag */
1574 	if ((ev = i1disp_rdreg_word(p, &p->reg90_W, 90) ) != inst_ok)
1575 		return ev;
1576 	a1logd(p->log, 3, "CRT/factory flag = 0x%x = %s\n",p->reg90_W, ctime_32(&p->reg90_W));
1577 
1578 
1579 	/* Integration clock period in nsec */
1580 	if ((ev = i1disp_rdreg_short(p, &p->reg40_S, 40) ) != inst_ok)
1581 		return ev;
1582 	a1logd(p->log, 3, "Reg40 = %d\n",p->reg40_S);
1583 
1584 	/* Calibration factor */
1585 	if ((ev = i1disp_rdreg_short(p, &p->reg42_S, 42) ) != inst_ok)
1586 		return ev;
1587 	a1logd(p->log, 3, "Reg42 = %d\n",p->reg42_S);
1588 
1589 	/* Calibration factors */
1590 	for (i = 0; i < 3; i++) {
1591 		if ((ev = i1disp_rdreg_short(p, &p->reg44_S[i], 44 + 2 * i) ) != inst_ok)
1592 			return ev;
1593 		a1logd(p->log, 3, "reg44[%d] = %d\n",i,p->reg44_S[i]);
1594 	}
1595 
1596 	/* Measurement/master clock period */
1597 	if ((ev = i1disp_rdreg_float(p, &p->clk_prd, 94) ) != inst_ok)
1598 		return ev;
1599 	a1logd(p->log, 3, "Master clock Frequency = %e\n",1/p->clk_prd);
1600 
1601 	/* unknown */
1602 	if ((ev = i1disp_rdreg_word(p, &p->reg98_W, 98) ) != inst_ok)
1603 		return ev;
1604 	a1logd(p->log, 3, "reg98 = 0x%x = %s\n",p->reg98_W,ctime_32(&p->reg98_W));
1605 
1606 	/* unknown */
1607 	if ((ev = i1disp_rdreg_byte(p, &p->reg102_B, 102) ) != inst_ok)
1608 		return ev;
1609 	a1logd(p->log, 3, "reg102 = 0x%x\n",p->reg102_B);
1610 
1611 	/* Dark current calibration values */
1612 	/* Should we set to a default 0.0 if reg126_S < 0xd ?? */
1613 	for (i = 0; i < 3; i++) {
1614 		if ((ev = i1disp_rdreg_float(p, &p->reg103_F[i], 103 + 4 * i)) != inst_ok) {
1615 			if ((ev & inst_imask) != I1DISP_FLOAT_NOT_SET)
1616 				return ev;
1617 			p->reg103_F[i] = 0.0;
1618 		}
1619 		a1logd(p->log, 3, "darkcal[%d] = %f\n",i,p->reg103_F[i]);
1620 	}
1621 
1622 	/* Unknown byte */
1623 	if ((ev = i1disp_rdreg_byte(p, &p->reg115_B, 115) ) != inst_ok)
1624 		return ev;
1625 	a1logd(p->log, 3, "Unknown 115 byte = 0x%x\n",p->reg115_B);
1626 
1627 	/* device ID byte */
1628 	if ((ev = i1disp_rdreg_byte(p, &p->reg121_B, 121) ) != inst_ok)
1629 		return ev;
1630 	a1logd(p->log, 3, "device type byte = 0x%x\n",p->reg121_B);
1631 
1632 	/* Unlock string */
1633 	for (i = 0; i < 4; i++) {
1634 		int vv;
1635 		if ((ev = i1disp_rdreg_byte(p, &vv, 122 + i) ) != inst_ok)
1636 			return ev;
1637 		p->reg122_B[i] = (char)vv;
1638 	}
1639 	p->reg122_B[i] = '\000';
1640 	a1logd(p->log, 3, "unlock string = '%s'\n",p->reg122_B);
1641 
1642 	p->serno[0] = '\000';
1643 
1644 	/* Read extra registers */
1645 	if (p->dtype == 1) {
1646 
1647 #ifdef NEVER	/* Not used, so don't bother */
1648 		for (i = 0; i < 3; i++) {
1649 			if ((ev = i1disp_rdreg_float(p, &p->reg128_F[i], 128 + 4 * i) ) != inst_ok)
1650 				return ev;
1651 			a1logd(p->log, 3, "reg128_F[%d] = %f\n",i,p->reg128_F[i]);
1652 		}
1653 #endif /* NEVER */
1654 
1655 		for (i = 0; i < 3; i++) {
1656 			if ((ev = i1disp_rdreg_float(p, &p->reg144_F[i], 144 + 4 * i) ) != inst_ok) {
1657 				if ((ev & inst_imask) != I1DISP_FLOAT_NOT_SET)
1658 					return ev;
1659 				p->reg144_F[i] = 1.0;
1660 			}
1661 			a1logd(p->log, 3, "Ambient scale factor [%d] = %f\n",i,p->reg144_F[i]);
1662 		}
1663 
1664 		/* Read the integration time */
1665 		if ((ev = i1disp_rd_int_time(p, &p->int_clocks) ) != inst_ok)
1666 			return ev;
1667 		a1logd(p->log, 3, "Integration time = %d\n",p->int_clocks);
1668 
1669 	/* ColorMunki Smile extra information */
1670 	/* (Colormunki Create too ????) */
1671 	} else  if (p->dtype == 2) {
1672 		int i, v;
1673 
1674 		/* Smile doesn't have ambient - reg144 seems to contain LCD cal type, */
1675 		/* ie. "CCFLWLED" */
1676 
1677 		/* Serial Number */
1678 		if ((ev = i1disp_rdexreg_bytes(p, (unsigned char *)p->serno, 0x104, 20)) != inst_ok)
1679 			return ev;
1680 		p->serno[19] = '\000';
1681 	}
1682 	a1logd(p->log, 2, "i1disp: all registers read OK\n");
1683 
1684 	return inst_ok;
1685 }
1686 
1687 /* Compute factors that depend on the register values */
1688 static inst_code
i1disp_compute_factors(i1disp * p)1689 i1disp_compute_factors(
1690 	i1disp *p				/* Object */
1691 ) {
1692 	int i;
1693 
1694 	/* Check that certain value are valid */
1695 	if (p->reg0_W == 0xffffffff)
1696 		return i1disp_interp_code((inst *)p, I1DISP_BAD_SERIAL_NUMBER);
1697 
1698 	/* LCD calibration date valid ? */
1699 	if (p->reg50_W == 0xffffffff)
1700 		return i1disp_interp_code((inst *)p, I1DISP_BAD_LCD_CALIBRATION);
1701 
1702 	/* The value stored in reg126_S ("user cal flag") seems hard to interpret. */
1703 	/* For the i1display 1&2, it has a value of 0x0d. */
1704 	/* Value 0x07 seems to be for a "user calibration" */
1705 	/* Values 3 & 6 seem to always "errors" as does a value */
1706 	/* < 7 in most circumstances. But the Heidelberg Viewmaker */
1707 	/* (from Sequel Imaging) and Lacie Blue Eye colorimeter seems to have a value of 2. */
1708 	/* The Barco sensor seems to have a value of 0x20 */
1709 	/* The ColorMunki Smile has a value of 0x21 */
1710 	if (p->reg126_S == 0xffffffff || (p->reg126_S < 7 && p->reg126_S != 2))
1711 		return i1disp_interp_code((inst *)p, I1DISP_BAD_LCD_CALIBRATION);
1712 
1713 	/* Not quite sure about this, but we're assuming this */
1714 	/* is set to 2 or 0xd if reg4-36 hold the LCD calibration, */
1715 	/* and some other number if they are not set, or set */
1716 	/* to a custom user calibration. */
1717 	if (p->reg126_S != 0x02
1718 	 && p->reg126_S != 0x0d
1719 	 && p->reg126_S != 0x20
1720 	 && p->reg126_S != 0x21)
1721 		return i1disp_interp_code((inst *)p, I1DISP_BAD_LCD_CALIBRATION);
1722 
1723 	if (p->reg90_W == 0xffffffff)
1724 		return i1disp_interp_code((inst *)p, I1DISP_BAD_CRT_CALIBRATION);
1725 
1726 	/* Compute ambient matrix for Lux */
1727 	for (i = 0; i < 9; i++)
1728 		p->amb[i] = 3.141592654 * p->reg144_F[i % 3] * 0.5 * (p->reg4_F[i] + p->reg54_F[i]);
1729 
1730 	/* Integration clock frequency */
1731 	p->iclk_freq = 1.0/(p->reg40_S * 1e-9);
1732 
1733 	/* Master/Measurement clock frequency */
1734 	p->clk_freq = 1.0/p->clk_prd;
1735 
1736 	/* RGB channel calibration factors */
1737 	for (i = 0; i < 3; i++) {
1738 
1739 		/* Individual channel calibration factors, typically 1.0 */
1740 		p->rgbadj[i] = (double)p->reg44_S[i] * 100.0/(double)p->reg42_S;
1741 		a1logd(p->log, 3, "reg44+%dcalc2 = %f\n",i,p->rgbadj[i]);
1742 	}
1743 
1744 	/* Set some defaults */
1745 	p->nmeasprds = 5;		/* Number of disp refresh period measurments to average */
1746 							/* in doing frequency calibration */
1747 	p->dinttime = 1.0;		/* 1.0 second integration time default */
1748 	p->inttime = p->dinttime;	/* Current integration time */
1749 
1750 	return inst_ok;
1751 }
1752 
1753 /* ------------------------------------------------------------------------ */
1754 
1755 /* Establish communications with a I1DISP */
1756 /* If it's a serial port, use the baud rate given, and timeout in to secs */
1757 /* Return I1DISP_COMS_FAIL on failure to establish communications */
1758 static inst_code
i1disp_init_coms(inst * pp,baud_rate br,flow_control fc,double tout)1759 i1disp_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
1760 	i1disp *p = (i1disp *) pp;
1761 	unsigned char buf[16];
1762 	int rsize;
1763 	int se;
1764 	inst_code ev = inst_ok;
1765 
1766 	a1logd(p->log, 2, "i1disp: About to init coms\n");
1767 
1768 	if (p->icom->port_type(p->icom) != icomt_usb) {
1769 		a1logd(p->log, 1, "i1disp_init_coms: wrong communications type for device!\n");
1770 		return inst_coms_fail;
1771 	}
1772 
1773 	/* Set config, interface, write end point, read end point */
1774 	/* ("serial" end points aren't used - the i1display uses USB control messages) */
1775 
1776 	/* Set config, interface, write end point, read end point, read quanta */
1777 	if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, icomuf_none, 0, NULL)) != ICOM_OK) {
1778 		a1logd(p->log, 1, "i1disp_init_coms: set_usbe_port failed ICOM err 0x%x\n",se);
1779 		return i1disp_interp_code((inst *)p, icoms2i1disp_err(se));
1780 	}
1781 
1782 	/* Check instrument is responding */
1783 	if ((ev = i1disp_command_1(p, i1d_status, NULL, 0, buf, 8, &rsize, 0.5)) != inst_ok
1784 	                                            && (ev & inst_imask) != I1DISP_LOCKED) {
1785 		a1logd(p->log, 1, "i1disp_init_coms: failed with rv = 0x%x\n",ev);
1786 		return ev;
1787 	}
1788 
1789 	a1logd(p->log, 2, "i1disp: init coms OK\n");
1790 
1791 	p->gotcoms = 1;
1792 	return inst_ok;
1793 }
1794 
1795 static inst_code set_default_disp_type(i1disp *p);
1796 
1797 /* Initialise the I1DISP */
1798 /* return non-zero on an error, with dtp error code */
1799 static inst_code
i1disp_init_inst(inst * pp)1800 i1disp_init_inst(inst *pp) {
1801 	i1disp *p = (i1disp *)pp;
1802 	inst_code ev = inst_ok;
1803 
1804 	a1logd(p->log, 2, "i1disp_init_inst: called\n");
1805 
1806 	if (p->gotcoms == 0)
1807 		return i1disp_interp_code((inst *)p, I1DISP_NO_COMS);	/* Must establish coms first */
1808 
1809 	/* Check instrument is responding, and right type */
1810 	if ((ev = i1disp_check_unlock(p)) != inst_ok)
1811 		return ev;
1812 
1813 	if (p->log->debug >= 3) {
1814 
1815 		/* Dump all the register space */
1816 		if (p->dtype < 2) {
1817 			unsigned char buf[0x200];
1818 			int i, len;
1819 
1820 			len = 128;
1821 			if (p->dtype != 0)
1822 				len = 160;
1823 
1824 			for (i = 0; i < len; i++) {
1825 				int v;
1826 				if ((ev = i1disp_rdreg_byte(p, &v, i)) != inst_ok) {
1827 					return ev;
1828 				}
1829 				buf[i] = v;
1830 			}
1831 			adump_bytes(p->log, "dump:", buf, 0, len);
1832 
1833 		/* Dump ColorMunki Smile extended range */
1834 		/* Main difference is Ascii serial number + other minor unknown */
1835 		} else {
1836 			unsigned char buf[0x200];
1837 
1838 
1839 			if ((ev = i1disp_rdexreg_bytes(p, buf, 0, 0x200)) != inst_ok) {
1840 				return ev;
1841 			}
1842 			adump_bytes(p->log, "dump:", buf, 0, 0x200);
1843 		}
1844 	}
1845 
1846 	/* Read all the registers and store their contents */
1847 	if ((ev = i1disp_read_all_regs(p)) != inst_ok)
1848 		return ev;
1849 
1850 	if ((ev = i1disp_compute_factors(p)) != inst_ok)
1851 		return ev;
1852 
1853 	p->trig = inst_opt_trig_user;
1854 
1855 	/* Set a default calibration */
1856 	if ((ev = set_default_disp_type(p)) != inst_ok) {
1857 		return ev;
1858 	}
1859 
1860 	p->inited = 1;
1861 	a1logd(p->log, 2, "i1disp_init_inst: inited OK\n");
1862 
1863 	return inst_ok;
1864 }
1865 
1866 /* Read a single sample */
1867 /* Return the dtp error code */
1868 static inst_code
i1disp_read_sample(inst * pp,char * name,ipatch * val,instClamping clamp)1869 i1disp_read_sample(
1870 inst *pp,
1871 char *name,			/* Strip name (7 chars) */
1872 ipatch *val,		/* Pointer to instrument patch value */
1873 instClamping clamp) {		/* NZ if clamp XYZ/Lab to be +ve */
1874 	i1disp *p = (i1disp *)pp;
1875 	int user_trig = 0;
1876 	int rv = inst_protocol_error;
1877 
1878 	if (!p->gotcoms)
1879 		return inst_no_coms;
1880 	if (!p->inited)
1881 		return inst_no_init;
1882 
1883 	if (p->trig == inst_opt_trig_user) {
1884 
1885 		if (p->uicallback == NULL) {
1886 			a1logd(p->log, 1, "i1disp: inst_opt_trig_user but no uicallback function set!\n");
1887 			return inst_unsupported;
1888 		}
1889 
1890 		for (;;) {
1891 			if ((rv = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
1892 				if (rv == inst_user_abort)
1893 					return rv;				/* Abort */
1894 				if (rv == inst_user_trig) {
1895 					user_trig = 1;
1896 					break;					/* Trigger */
1897 				}
1898 			}
1899 			msec_sleep(200);
1900 		}
1901 		/* Notify of trigger */
1902 		if (p->uicallback)
1903 			p->uicallback(p->uic_cntx, inst_triggered);
1904 
1905 	/* Progromatic Trigger */
1906 	} else {
1907 		/* Check for abort */
1908 		if (p->uicallback != NULL
1909 		 && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort)
1910 			return rv;				/* Abort */
1911 	}
1912 
1913 
1914 	/* Read the XYZ value */
1915 	rv = i1disp_take_XYZ_measurement(p, val->XYZ);
1916 
1917 	if (rv != inst_ok)
1918 		return rv;
1919 
1920 
1921 	/* This may not change anything since instrument may clamp */
1922 	if (clamp)
1923 		icmClamp3(val->XYZ, val->XYZ);
1924 	val->loc[0] = '\000';
1925 	if (IMODETST(p->mode, inst_mode_emis_ambient))
1926 		val->mtype = inst_mrt_ambient;
1927 	else
1928 		val->mtype = inst_mrt_emission;
1929 	val->XYZ_v = 1;		/* These are absolute XYZ readings ? */
1930 	val->sp.spec_n = 0;
1931 	val->duration = 0.0;
1932 
1933 
1934 	if (user_trig)
1935 		return inst_user_trig;
1936 	return rv;
1937 }
1938 
1939 static inst_code set_base_disp_type(i1disp *p, int cbid);
1940 
1941 /* Insert a colorimetric correction matrix in the instrument XYZ readings */
1942 /* This is only valid for colorimetric instruments. */
1943 /* To remove the matrix, pass NULL for the filter filename */
i1disp_col_cor_mat(inst * pp,disptech dtech,int cbid,double mtx[3][3])1944 inst_code i1disp_col_cor_mat(
1945 inst *pp,
1946 disptech dtech,		/* Use disptech_unknown if not known */				\
1947 int cbid,       	/* Calibration display type base ID, 1 if unknown */\
1948 double mtx[3][3]
1949 ) {
1950 	i1disp *p = (i1disp *)pp;
1951 	inst_code ev;
1952 
1953 	if (!p->gotcoms)
1954 		return inst_no_coms;
1955 	if (!p->inited)
1956 		return inst_no_init;
1957 
1958 	if ((ev = set_base_disp_type(p, cbid)) != inst_ok)
1959 		return ev;
1960 	if (mtx == NULL)
1961 		icmSetUnity3x3(p->ccmat);
1962 	else
1963 		icmCpy3x3(p->ccmat, mtx);
1964 	p->dtech = dtech;
1965 	p->refrmode = disptech_get_id(dtech)->refr;
1966 	p->cbid = 0;	/* Can't be base type now */
1967 
1968 	if (p->log->debug >= 4) {
1969 		a1logd(p->log,4,"ccmat           = %f %f %f\n",
1970 		                 p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
1971 		a1logd(p->log,4,"                  %f %f %f\n",
1972 		                 p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]);
1973 		a1logd(p->log,4,"                  %f %f %f\n\n",
1974 		                 p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
1975 		a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid);
1976 		a1logd(p->log,4,"\n");
1977 	}
1978 
1979 	return inst_ok;
1980 }
1981 
1982 /* Return needed and available inst_cal_type's */
i1disp_get_n_a_cals(inst * pp,inst_cal_type * pn_cals,inst_cal_type * pa_cals)1983 static inst_code i1disp_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
1984 	i1disp *p = (i1disp *)pp;
1985 	inst_cal_type n_cals = inst_calt_none;
1986 	inst_cal_type a_cals = inst_calt_none;
1987 
1988 	if (p->dtype == 0) {	/* Eye-One Display 1 */
1989 	   a_cals |= inst_calt_emis_offset;
1990 	}
1991 
1992 	if (p->dtype == 1 && p->refrmode != 0) {
1993 		if (p->rrset == 0)
1994 			n_cals |= inst_calt_ref_freq;
1995 		a_cals |= inst_calt_ref_freq;
1996 	}
1997 
1998 	if (pn_cals != NULL)
1999 		*pn_cals = n_cals;
2000 
2001 	if (pa_cals != NULL)
2002 		*pa_cals = a_cals;
2003 
2004 	return inst_ok;
2005 }
2006 
2007 /* Request an instrument calibration. */
2008 /* This is use if the user decides they want to do a calibration, */
2009 /* in anticipation of a calibration (needs_calibration()) to avoid */
2010 /* requiring one during measurement, or in response to measuring */
2011 /* returning inst_needs_cal. Initially us an inst_cal_cond of inst_calc_none, */
2012 /* and then be prepared to setup the right conditions, or ask the */
2013 /* user to do so, each time the error inst_cal_setup is returned. */
i1disp_calibrate(inst * pp,inst_cal_type * calt,inst_cal_cond * calc,inst_calc_id_type * idtype,char id[CALIDLEN])2014 inst_code i1disp_calibrate(
2015 inst *pp,
2016 inst_cal_type *calt,	/* Calibration type to do/remaining */
2017 inst_cal_cond *calc,	/* Current condition/desired condition */
2018 inst_calc_id_type *idtype,	/* Condition identifier type */
2019 char id[CALIDLEN]		/* Condition identifier (ie. white reference ID) */
2020 ) {
2021 	i1disp *p = (i1disp *)pp;
2022 	inst_code ev;
2023     inst_cal_type needed, available;
2024 
2025 	if (!p->gotcoms)
2026 		return inst_no_coms;
2027 	if (!p->inited)
2028 		return inst_no_init;
2029 
2030 	*idtype = inst_calc_id_none;
2031 	id[0] = '\000';
2032 
2033 	if ((ev = i1disp_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok)
2034 		return ev;
2035 
2036 	/* Translate inst_calt_all/needed into something specific */
2037 	if (*calt == inst_calt_all
2038 	 || *calt == inst_calt_needed
2039 	 || *calt == inst_calt_available) {
2040 		if (*calt == inst_calt_all)
2041 			*calt = (needed & inst_calt_n_dfrble_mask) | inst_calt_ap_flag;
2042 		else if (*calt == inst_calt_needed)
2043 			*calt = needed & inst_calt_n_dfrble_mask;
2044 		else if (*calt == inst_calt_available)
2045 			*calt = available & inst_calt_n_dfrble_mask;
2046 
2047 		a1logd(p->log,4,"i1disp_calibrate: doing calt 0x%x\n",calt);
2048 
2049 		if ((*calt & inst_calt_n_dfrble_mask) == 0)		/* Nothing todo */
2050 			return inst_ok;
2051 	}
2052 
2053 	/* See if it's a calibration we understand */
2054 	if (*calt & ~available & inst_calt_all_mask) {
2055 		return inst_unsupported;
2056 	}
2057 
2058 	/* Do the appropriate calibration */
2059 	if (p->dtype == 0) {		/* Eye-One Display 1 */
2060 		if (*calt & inst_calt_emis_offset) {
2061 
2062 			if ((*calc & inst_calc_cond_mask) != inst_calc_man_ref_dark) {
2063 				*calc = inst_calc_man_ref_dark;
2064 				return inst_cal_setup;
2065 			}
2066 
2067 			/* Do black offset calibration */
2068 			if ((ev = i1disp_do_black_cal(p)) != inst_ok)
2069 				return ev;
2070 
2071 			*calt &= ~inst_calt_emis_offset;
2072 		}
2073 
2074 	} else {				/* Eye-One Display 2 */
2075 		if ((*calt & inst_calt_ref_freq) && p->refrmode != 0) {
2076 
2077 			if ((*calc & inst_calc_cond_mask) != inst_calc_emis_80pc) {
2078 				*calc = inst_calc_emis_80pc;
2079 				return inst_cal_setup;
2080 			}
2081 
2082 			/* Do CRT frequency calibration and set integration time */
2083 			if ((ev = i1disp_do_fcal_setit(p)) != inst_ok)
2084 				return ev;
2085 
2086 			/* Quantize the sample time */
2087 			if (p->refperiod > 0.0) {
2088 				int n;
2089 				n = (int)ceil(p->dinttime/p->refperiod);
2090 				p->inttime = n * p->refperiod;
2091 				a1logd(p->log, 3, "i1disp: integration time quantize to %f secs\n",p->inttime);
2092 			} else {
2093 				p->inttime = p->dinttime;
2094 				a1logd(p->log, 3, "i1disp: integration time set to %f secs\n",p->inttime);
2095 			}
2096 
2097 			*calt &= ~inst_calt_ref_freq;
2098 		}
2099 	}
2100 
2101 	return inst_ok;
2102 }
2103 
2104 /* Return the last calibrated refresh rate in Hz. Returns: */
i1disp_get_refr_rate(inst * pp,double * ref_rate)2105 static inst_code i1disp_get_refr_rate(inst *pp,
2106 double *ref_rate
2107 ) {
2108 	i1disp *p = (i1disp *)pp;
2109 	if (p->refrvalid) {
2110 		*ref_rate = p->refrate;
2111 
2112 		return inst_ok;
2113 	} else if (p->rrset) {
2114 		*ref_rate = 0.0;
2115 		return inst_misread;
2116 	}
2117 	return inst_needs_cal;
2118 }
2119 
2120 /* Set the calibrated refresh rate in Hz. */
2121 /* Set refresh rate to 0.0 to mark it as invalid */
2122 /* Rates outside the range 5.0 to 150.0 Hz will return an error */
i1disp_set_refr_rate(inst * pp,double ref_rate)2123 static inst_code i1disp_set_refr_rate(inst *pp,
2124 double ref_rate
2125 ) {
2126 	i1disp *p = (i1disp *)pp;
2127 
2128 	if (ref_rate != 0.0 && (ref_rate < 5.0 || ref_rate > 150.0))
2129 		return inst_bad_parameter;
2130 
2131 	p->refrate = ref_rate;
2132 	if (ref_rate == 0.0)
2133 		p->refrvalid = 0;
2134 	else {
2135 		p->refperiod = 1.0/p->refrate;
2136 		p->refrvalid = 1;
2137 	}
2138 	p->rrset = 1;
2139 
2140 	return inst_ok;
2141 }
2142 
2143 /* Error codes interpretation */
2144 static char *
i1disp_interp_error(inst * pp,int ec)2145 i1disp_interp_error(inst *pp, int ec) {
2146 //	i1disp *p = (i1disp *)pp;
2147 	ec &= inst_imask;
2148 	switch (ec) {
2149 		case I1DISP_INTERNAL_ERROR:
2150 			return "Internal software error";
2151 		case I1DISP_COMS_FAIL:
2152 			return "Communications failure";
2153 		case I1DISP_UNKNOWN_MODEL:
2154 			return "Not a i1 Display";
2155 		case I1DISP_DATA_PARSE_ERROR:
2156 			return "Data from i1 Display didn't parse as expected";
2157 
2158 		case I1DISP_OK:
2159 			return "No device error";
2160 
2161 		case I1DISP_FLOAT_NOT_SET:
2162 			return "Float value is not set in EEPROM";
2163 		case I1DISP_NOT_READY:
2164 			return "Command didn't return command code - not ready ?";
2165 
2166 		case I1DISP_BAD_SERIAL_NUMBER:
2167 			return "Serial number isn't set";
2168 		case I1DISP_BAD_LCD_CALIBRATION:
2169 			return "LCD calibration values aren't set";
2170 		case I1DISP_BAD_CRT_CALIBRATION:
2171 			return "CRT calibration values aren't set";
2172 		case I1DISP_EEPROM_WRITE_FAIL:
2173 			return "Write to EEPROM failed to verify";
2174 
2175 		case I1DISP_UNEXPECTED_RET_SIZE:
2176 			return "Message from instrument has unexpected size";
2177 		case I1DISP_UNEXPECTED_RET_VAL:
2178 			return "Message from instrument has unexpected value";
2179 
2180 		case I1DISP_BAD_STATUS:
2181 			return "Instrument status is unrecognised format";
2182 		case I1DISP_UNKNOWN_VERS_ID:
2183 			return "Instrument version number or ID byte not recognised";
2184 
2185 		/* Internal errors */
2186 		case I1DISP_BAD_REG_ADDRESS:
2187 			return "Out of range register address";
2188 		case I1DISP_BAD_INT_THRESH:
2189 			return "Out of range integration threshold";
2190 		case I1DISP_NO_COMS:
2191 			return "Communications hasn't been established";
2192 		case I1DISP_NOT_INITED:
2193 			return "Insrument hasn't been initialised";
2194 		case I1DISP_CANT_BLACK_CALIB:
2195 			return "Device doesn't support black calibration";
2196 		case I1DISP_CANT_MEASP_CALIB:
2197 			return "Device doesn't support measurment period calibration";
2198 		case I1DISP_WRONG_DEVICE:
2199 			return "Wrong type of device for called function";
2200 		default:
2201 			return "Unknown error code";
2202 	}
2203 }
2204 
2205 
2206 /* Convert a machine specific error code into an abstract dtp code */
2207 static inst_code
i1disp_interp_code(inst * pp,int ec)2208 i1disp_interp_code(inst *pp, int ec) {
2209 //	i1disp *p = (i1disp *)pp;
2210 
2211 	ec &= inst_imask;
2212 	switch (ec) {
2213 
2214 		case I1DISP_OK:
2215 		case I1DISP_FLOAT_NOT_SET:		/* Internal indication */
2216 		case I1DISP_NOT_READY:			/* Internal indication */
2217 			return inst_ok;
2218 
2219 		case I1DISP_INTERNAL_ERROR:
2220 		case I1DISP_BAD_REG_ADDRESS:
2221 		case I1DISP_BAD_INT_THRESH:
2222 		case I1DISP_NO_COMS:
2223 		case I1DISP_NOT_INITED:
2224 		case I1DISP_CANT_BLACK_CALIB:
2225 		case I1DISP_CANT_MEASP_CALIB:
2226 		case I1DISP_WRONG_DEVICE:
2227 		case I1DISP_LOCKED:
2228 			return inst_internal_error | ec;
2229 
2230 		case I1DISP_COMS_FAIL:
2231 			return inst_coms_fail | ec;
2232 
2233 		case I1DISP_UNKNOWN_MODEL:
2234 		case I1DISP_BAD_STATUS:
2235 		case I1DISP_UNKNOWN_VERS_ID:
2236 			return inst_unknown_model | ec;
2237 
2238 		case I1DISP_DATA_PARSE_ERROR:
2239 		case I1DISP_UNEXPECTED_RET_SIZE:
2240 		case I1DISP_UNEXPECTED_RET_VAL:
2241 			return inst_protocol_error | ec;
2242 
2243 		case I1DISP_BAD_SERIAL_NUMBER:
2244 		case I1DISP_BAD_LCD_CALIBRATION:
2245 		case I1DISP_BAD_CRT_CALIBRATION:
2246 		case I1DISP_EEPROM_WRITE_FAIL:
2247 			return inst_hardware_fail | ec;
2248 
2249 		/* return inst_misread | ec; */
2250 		/* return inst_needs_cal_2 | ec; */
2251 	}
2252 	return inst_other_error | ec;
2253 }
2254 
2255 /* Destroy ourselves */
2256 static void
i1disp_del(inst * pp)2257 i1disp_del(inst *pp) {
2258 	i1disp *p = (i1disp *)pp;
2259 	if (p->icom != NULL)
2260 		p->icom->del(p->icom);
2261 	inst_del_disptype_list(p->dtlist, p->ndtlist);
2262 	p->vdel(pp);
2263 	free(p);
2264 }
2265 
2266 /* Return the instrument capabilities */
i1disp_capabilities(inst * pp,inst_mode * pcap1,inst2_capability * pcap2,inst3_capability * pcap3)2267 static void i1disp_capabilities(inst *pp,
2268 inst_mode *pcap1,
2269 inst2_capability *pcap2,
2270 inst3_capability *pcap3) {
2271 	i1disp *p = (i1disp *)pp;
2272 	inst_mode cap1 = 0;
2273 	inst2_capability cap2 = 0;
2274 
2275 	cap1 |= inst_mode_emis_spot
2276 	     |  inst_mode_colorimeter
2277 	        ;
2278 
2279 	cap2 |= inst2_prog_trig
2280 	     |  inst2_user_trig
2281 	     |  inst2_disptype
2282 	     |  inst2_ccmx
2283 	        ;
2284 
2285 	/* i1D2 has refresh display & ambient capability */
2286 	/* but i1D1 & ColorMunki Smile don't */
2287 	if (p->dtype == 1) {
2288 		cap1 |= inst_mode_emis_ambient
2289 	         |  inst_mode_emis_refresh_ovd
2290 	         |  inst_mode_emis_norefresh_ovd
2291 		        ;
2292 
2293 		cap2 |= inst2_get_refresh_rate
2294 		     |  inst2_set_refresh_rate
2295 		     |  inst2_emis_refr_meas
2296 		        ;
2297 	}
2298 
2299 	if (pcap1 != NULL)
2300 		*pcap1 = cap1;
2301 	if (pcap2 != NULL)
2302 		*pcap2 = cap2;
2303 	if (pcap3 != NULL)
2304 		*pcap3 = inst3_none;
2305 }
2306 
2307 /* Check device measurement mode */
i1disp_check_mode(inst * pp,inst_mode m)2308 static inst_code i1disp_check_mode(inst *pp, inst_mode m) {
2309 	i1disp *p = (i1disp *)pp;
2310 	inst_mode cap;
2311 
2312 	if (!p->gotcoms)
2313 		return inst_no_coms;
2314 	if (!p->inited)
2315 		return inst_no_init;
2316 
2317 	pp->capabilities(pp, &cap, NULL, NULL);
2318 
2319 	/* Simple filter for most modes */
2320 	if (m & ~cap)
2321 		return inst_unsupported;
2322 
2323 	/* Only display emission mode supported */
2324 	if (!IMODETST(m, inst_mode_emis_spot)
2325 	 && !(p->dtype == 1 && IMODETST(m, inst_mode_emis_ambient))) {
2326 		return inst_unsupported;
2327 	}
2328 
2329 	return inst_ok;
2330 }
2331 
2332 /* Set device measurement mode */
i1disp_set_mode(inst * pp,inst_mode m)2333 static inst_code i1disp_set_mode(inst *pp, inst_mode m) {
2334 	i1disp *p = (i1disp *)pp;
2335 	inst_code ev;
2336 
2337 	if ((ev = i1disp_check_mode(pp, m)) != inst_ok)
2338 		return ev;
2339 
2340 	p->mode = m;
2341 
2342 	if (     IMODETST(p->mode, inst_mode_emis_norefresh_ovd))	/* Must test this first! */
2343 		p->refrmode = 0;
2344 	else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd))
2345 		p->refrmode = 1;
2346 
2347 	return inst_ok;
2348 }
2349 
2350 static inst_disptypesel i1disp_disptypesel[3] = {
2351 	{
2352 		inst_dtflags_default,
2353 		1,
2354 		"l",
2355 		"LCD display",
2356 		0,
2357 		disptech_lcd,
2358 		0
2359 	},
2360 	{
2361 		inst_dtflags_none,		/* flags */
2362 		2,						/* cbid */
2363 		"c",					/* sel */
2364 		"CRT display",			/* desc */
2365 		1,						/* refr */
2366 		disptech_crt,			/* disptype */
2367 		1						/* ix */
2368 	},
2369 	{
2370 		inst_dtflags_end,
2371 		0,
2372 		"",
2373 		"",
2374 		0,
2375 		disptech_none,
2376 		0
2377 	}
2378 };
2379 
2380 
2381 static inst_disptypesel smile_disptypesel[3] = {
2382 	{
2383 		inst_dtflags_default,		/* flags */
2384 		1,							/* cbid */
2385 		"fl",						/* sel */
2386 		"LCD with CCFL backlight",	/* desc */
2387 		0,							/* refr */
2388 		disptech_lcd_ccfl,			/* disptype */
2389 		1							/* ix */
2390 	},
2391 	{
2392 		inst_dtflags_none,
2393 		0,
2394 		"e",
2395 		"LCD with White LED backlight",
2396 		0,
2397 		disptech_lcd_wled,
2398 		0
2399 	},
2400 	{
2401 		inst_dtflags_end,
2402 		0,
2403 		"",
2404 		"",
2405 		0,
2406 		disptech_none,
2407 		0
2408 	}
2409 };
2410 
set_base_disptype_list(i1disp * p)2411 static void set_base_disptype_list(i1disp *p) {
2412 	/* set the base display type list */
2413 	if (p->itype == instSmile) {
2414 		p->_dtlist = smile_disptypesel;
2415 	} else {
2416 		p->_dtlist = i1disp_disptypesel;
2417 	}
2418 }
2419 
2420 /* Get mode and option details */
i1disp_get_disptypesel(inst * pp,int * pnsels,inst_disptypesel ** psels,int allconfig,int recreate)2421 static inst_code i1disp_get_disptypesel(
2422 inst *pp,
2423 int *pnsels,				/* Return number of display types */
2424 inst_disptypesel **psels,	/* Return the array of display types */
2425 int allconfig,				/* nz to return list for all configs, not just current. */
2426 int recreate				/* nz to re-check for new ccmx & ccss files */
2427 ) {
2428 	i1disp *p = (i1disp *)pp;
2429 	inst_code rv = inst_ok;
2430 
2431 	/* Create/Re-create a current list of available display types */
2432 	if (p->dtlist == NULL || recreate) {
2433 		if ((rv = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist,
2434 		    p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
2435 			return rv;
2436 	}
2437 
2438 	if (pnsels != NULL)
2439 		*pnsels = p->ndtlist;
2440 
2441 	if (psels != NULL)
2442 		*psels = p->dtlist;
2443 
2444 	return inst_ok;
2445 }
2446 
2447 /* Given a display type entry, setup for that type */
set_disp_type(i1disp * p,inst_disptypesel * dentry)2448 static inst_code set_disp_type(i1disp *p, inst_disptypesel *dentry) {
2449 	int refrmode;
2450 
2451 	if (dentry->flags & inst_dtflags_ccmx) {
2452 		inst_code ev;
2453 		if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok)
2454 			return ev;
2455 		icmCpy3x3(p->ccmat, dentry->mat);
2456 		p->dtech = dentry->dtech;
2457 		p->cbid = 0; 				/* Can't be a base type now */
2458 
2459 	} else {	/* Native */
2460 
2461 		p->icx = dentry->ix;
2462 		p->dtech = dentry->dtech;
2463 		p->cbid = dentry->cbid;
2464 		p->ucbid = dentry->cbid;	/* This is underying base if dentry is base selection */
2465 		icmSetUnity3x3(p->ccmat);
2466 	}
2467 
2468 	/* Implement any refresh mode change */
2469 	refrmode = dentry->refr;
2470 
2471 	if (     IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) {	/* Must test this first! */
2472 		refrmode = 0;
2473 	} else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) {
2474 		refrmode = 1;
2475 	}
2476 
2477 	if (p->refrmode != refrmode) {
2478 		p->rrset = 0;					/* This is a hint we may have swapped displays */
2479 		p->refrvalid = 0;
2480 	}
2481 	p->refrmode = refrmode;
2482 
2483 	if (p->log->debug >= 4) {
2484 		a1logd(p->log,4,"ccmat           = %f %f %f\n",
2485 		                 p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
2486 		a1logd(p->log,4,"                  %f %f %f\n",
2487 		                 p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]);
2488 		a1logd(p->log,4,"                  %f %f %f\n\n",
2489 		                 p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
2490 		a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid);
2491 		a1logd(p->log,4,"\n");
2492 	}
2493 
2494 	return inst_ok;
2495 }
2496 
2497 /* Set the display type */
i1disp_set_disptype(inst * pp,int ix)2498 static inst_code i1disp_set_disptype(inst *pp, int ix) {
2499 	i1disp *p = (i1disp *)pp;
2500 	inst_code ev;
2501 	inst_disptypesel *dentry;
2502 
2503 	if (!p->gotcoms)
2504 		return inst_no_coms;
2505 	if (!p->inited)
2506 		return inst_no_init;
2507 
2508 	if (p->dtlist == NULL) {
2509 		if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist,
2510 		    p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
2511 			return ev;
2512 	}
2513 
2514 	if (ix < 0 || ix >= p->ndtlist)
2515 		return inst_unsupported;
2516 
2517 	dentry = &p->dtlist[ix];
2518 
2519 	if ((ev = set_disp_type(p, dentry)) != inst_ok) {
2520 		return ev;
2521 	}
2522 
2523 	return inst_ok;
2524 }
2525 
2526 /* Get the disptech corresponding to the current */
2527 /* Setup the default display type */
set_default_disp_type(i1disp * p)2528 static inst_code set_default_disp_type(i1disp *p) {
2529 	inst_code ev;
2530 	int i;
2531 
2532 	if (p->dtlist == NULL) {
2533 		if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
2534 		    p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
2535 			return ev;
2536 	}
2537 
2538 	for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) {
2539 		if (p->dtlist[i].flags & inst_dtflags_default)
2540 			break;
2541 	}
2542 	if (p->dtlist[i].flags & inst_dtflags_end) {
2543 		a1loge(p->log, 1, "set_default_disp_type: failed to find type!\n");
2544 		return inst_internal_error;
2545 	}
2546 	if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) {
2547 		return ev;
2548 	}
2549 
2550 	return inst_ok;
2551 }
2552 
2553 /* Setup the display type to the given base type */
set_base_disp_type(i1disp * p,int cbid)2554 static inst_code set_base_disp_type(i1disp *p, int cbid) {
2555 	inst_code ev;
2556 	int i;
2557 
2558 	if (cbid == 0) {
2559 		a1loge(p->log, 1, "i1disp set_base_disp_type: can't set base display type of 0\n");
2560 		return inst_wrong_setup;
2561 	}
2562 	if (p->dtlist == NULL) {
2563 		if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
2564 		    i1disp_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
2565 			return ev;
2566 	}
2567 
2568 	for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) {
2569 		if (!(p->dtlist[i].flags & inst_dtflags_ccmx)		/* Prevent infinite recursion */
2570 		 && p->dtlist[i].cbid == cbid)
2571 			break;
2572 	}
2573 	if (p->dtlist[i].flags & inst_dtflags_end) {
2574 		a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid);
2575 		return inst_wrong_setup;
2576 	}
2577 	if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) {
2578 		return ev;
2579 	}
2580 
2581 	return inst_ok;
2582 }
2583 
2584 /* Get the disptech and other corresponding info for the current */
2585 /* selected display type. Returns disptype_unknown by default. */
2586 /* Because refrmode can be overridden, it may not match the refrmode */
2587 /* of the dtech. (Pointers may be NULL if not needed) */
i1disp_get_disptechi(inst * pp,disptech * dtech,int * refrmode,int * cbid)2588 static inst_code i1disp_get_disptechi(
2589 inst *pp,
2590 disptech *dtech,
2591 int *refrmode,
2592 int *cbid) {
2593 	i1disp *p = (i1disp *)pp;
2594 	if (dtech != NULL)
2595 		*dtech = p->dtech;
2596 	if (refrmode != NULL)
2597 		*refrmode = p->refrmode;
2598 	if (cbid != NULL)
2599 		*cbid = p->cbid;
2600 	return inst_ok;
2601 }
2602 
2603 /*
2604  * set or reset an optional mode
2605  *
2606  * Since there is no interaction with the instrument,
2607  * was assume that all of these can be done before initialisation.
2608  */
2609 static inst_code
i1disp_get_set_opt(inst * pp,inst_opt_type m,...)2610 i1disp_get_set_opt(inst *pp, inst_opt_type m, ...) {
2611 	i1disp *p = (i1disp *)pp;
2612 	inst_code ev;
2613 
2614 	/* Record the trigger mode */
2615 	if (m == inst_opt_trig_prog
2616 	 || m == inst_opt_trig_user) {
2617 		p->trig = m;
2618 		return inst_ok;
2619 	}
2620 
2621 	/* Use default implementation of other inst_opt_type's */
2622 	{
2623 		inst_code rv;
2624 		va_list args;
2625 
2626 		va_start(args, m);
2627 		rv = inst_get_set_opt_def(pp, m, args);
2628 		va_end(args);
2629 
2630 		return rv;
2631 	}
2632 }
2633 
2634 /* Constructor */
new_i1disp(icoms * icom,instType itype)2635 extern i1disp *new_i1disp(icoms *icom, instType itype) {
2636 	i1disp *p;
2637 
2638 
2639 	if ((p = (i1disp *)calloc(sizeof(i1disp),1)) == NULL) {
2640 		a1loge(icom->log, 1, "new_i1disp: malloc failed!\n");
2641 		return NULL;
2642 	}
2643 
2644 	p->log = new_a1log_d(icom->log);
2645 
2646 	p->init_coms         = i1disp_init_coms;
2647 	p->init_inst         = i1disp_init_inst;
2648 	p->capabilities      = i1disp_capabilities;
2649 	p->check_mode        = i1disp_check_mode;
2650 	p->set_mode          = i1disp_set_mode;
2651 	p->get_disptechi     = i1disp_get_disptechi;
2652 	p->get_disptypesel   = i1disp_get_disptypesel;
2653 	p->set_disptype      = i1disp_set_disptype;
2654 	p->get_set_opt       = i1disp_get_set_opt;
2655 	p->read_sample       = i1disp_read_sample;
2656 	p->read_refrate      = i1disp_read_refrate;
2657 	p->get_n_a_cals      = i1disp_get_n_a_cals;
2658 	p->calibrate         = i1disp_calibrate;
2659 	p->col_cor_mat       = i1disp_col_cor_mat;
2660 	p->get_refr_rate     = i1disp_get_refr_rate;
2661 	p->set_refr_rate     = i1disp_set_refr_rate;
2662 	p->interp_error      = i1disp_interp_error;
2663 	p->del               = i1disp_del;
2664 
2665 	p->icom = icom;
2666 	p->itype = itype;
2667 
2668 	if (p->itype == instI1Disp2)
2669 		p->dtype = 1;			/* i1Display2 */
2670 
2671 	else if (p->itype == instSmile) {
2672 		p->dtype = 2;			/* Smile */
2673 	}
2674 
2675 	icmSetUnity3x3(p->ccmat);	/* Set the colorimeter correction matrix to do nothing */
2676 	set_base_disptype_list(p);
2677 	p->dtech = disptech_unknown;
2678 
2679 	return p;
2680 }
2681 
2682 /* ---------------------------------------------------------------- */
2683 
2684 
2685 
2686 
2687