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