1
2 /*
3 * Argyll Color Correction System
4 *
5 * JETI kleink10 1211/1201 related functions
6 *
7 * Author: Graeme W. Gill
8 * Date: 29/4/2014
9 *
10 * Copyright 1996 - 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 * Based on DTP92.c & specbos.c
17 */
18
19 /*
20 If you make use of the instrument driver code here, please note
21 that it is the author(s) of the code who take responsibility
22 for its operation. Any problems or queries regarding driving
23 instruments with the Argyll drivers, should be directed to
24 the Argyll's author(s), and not to any other party.
25
26 If there is some instrument feature or function that you
27 would like supported here, it is recommended that you
28 contact Argyll's author(s) first, rather than attempt to
29 modify the software yourself, if you don't have firm knowledge
30 of the instrument communicate protocols. There is a chance
31 that an instrument could be damaged by an incautious command
32 sequence, and the instrument companies generally cannot and
33 will not support developers that they have not qualified
34 and agreed to support.
35 */
36
37 /*
38
39 TTBD:
40
41 */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <ctype.h>
46 #include <string.h>
47 #include <time.h>
48 #include <stdarg.h>
49 #ifndef SALONEINSTLIB
50 #include "copyright.h"
51 #include "aconfig.h"
52 #include "numlib.h"
53 #else /* !SALONEINSTLIB */
54 #include "sa_config.h"
55 #include "numsup.h"
56 #endif /* !SALONEINSTLIB */
57 #include "xspect.h"
58 #include "insttypes.h"
59 #include "conv.h"
60 #include "icoms.h"
61 #include "kleink10.h"
62
63 #undef HIGH_SPEED /* [und] Use high speed flicker measure for refresh rate etc. */
64 #define AUTO_AVERAGE /* [def] Automatically average more readings for low light */
65 #define RETRY_RANGE_ERROR 4 /* [4] Retry range error readings 4 times */
66
67 #undef PLOT_REFRESH /* [und] Plot refresh rate measurement info */
68 #undef PLOT_UPDELAY /* [und] Plot update delay measurement info */
69
70 #undef TEST_BAUD_CHANGE /* Torture test baud rate change on non high speed K10 */
71
72 static inst_disptypesel k10_disptypesel[98];
73 static inst_code k10_interp_code(kleink10 *p, int ec);
74 static inst_code k10_read_cal_list(kleink10 *p);
75 static inst_code set_default_disp_type(kleink10 *p);
76 static inst_code k10_read_flicker_samples(kleink10 *p, double duration, double *srate,
77 double **pvals, int *pnsamp, int usefast);
78
79 #define MAX_MES_SIZE 500 /* Maximum normal message reply size */
80 #define MAX_RD_SIZE 8000 /* Maximum reading message reply size */
81
82 /* Decode a K10 error letter */
decodeK10err(char c)83 static int decodeK10err(char c) {
84 //printf("~1 decoding error code 0x%x\n",c);
85 if (c == '0') {
86 return K10_OK;
87 } else if (c == 'B') {
88 return K10_FIRMWARE;
89 } else if (c == 'X') {
90 return K10_FIRMWARE;
91 } else if (c == 'b') {
92 return K10_BLACK_EXCESS;
93 } else if (c == 's') {
94 return K10_BLACK_OVERDRIVE;
95 } else if (c == 't') {
96 return K10_BLACK_ZERO;
97 } else if (c == 'w') {
98 return K10_OVER_HIGH_RANGE;
99 } else if (c == 'v') {
100 return K10_TOP_OVER_RANGE;
101 } else if (c == 'u') {
102 return K10_BOT_UNDER_RANGE;
103 } else if (c == 'L') {
104 return K10_AIMING_LIGHTS;
105 } else {
106 return K10_UNKNOWN;
107 }
108 }
109
110 /* Extract an error code from a reply string */
111 /* Remove the error code from the string and return the */
112 /* new length in *nlength */
113 /* Return K10_BAD_RETVAL if no error code can be found */
114 static int
extract_ec(char * s,int * nlength,int bread)115 extract_ec(char *s, int *nlength, int bread) {
116 #define MAXECHARS 1
117 char *f, *p;
118 char tt[MAXECHARS+1];
119 int rv;
120 p = s + bread;
121
122 //printf("Got '%s' bread %d\n",s,bread);
123
124 /* Find the trailing '>' */
125 for (p--; p >= s; p--) {
126 if (*p == '>')
127 break;
128 }
129 if (p < s) {
130 //printf("p %d < s %d ? %d\n", p, s, p < s);
131 return K10_BAD_RETVAL;
132 }
133 //printf("trailing is at %d '%s'\n",p - s, p);
134
135 /* Find the leading '<' */
136 for (f = p-1; f >= (p-MAXECHARS-1) && f >= s; f--) {
137 if (*f == '<')
138 break;
139 /* Turns out the error code may be non-text */
140 #ifdef NEVER
141 if ((*f < '0' || *f > '9')
142 && (*f < 'a' || *f > 'z')
143 && (*f < 'A' || *f > 'Z'))
144 return K10_BAD_RETVAL;
145 #endif /* NEVER */
146 }
147 if (f < s || f < (p-MAXECHARS-1) || (p-f) <= 1) {
148 //printf("f < s ? %d, f < (p-MAXECHARS-1) ? %d, (p-f) <= 1 ? %d\n", f < s, f < (p-10), (p-f) <= 1);
149 return K10_BAD_RETVAL;
150 }
151 //printf("leading is at %d '%s'\n",f - s, f);
152
153 if (p-f-1 <= 0) {
154 //printf("p-f-1 %d <= 0 ? %d\n", p-f-1, p-f-1 <= 0);
155 return K10_BAD_RETVAL;
156 }
157
158 strncpy(tt, f+1, p-f-1);
159 tt[p-f-1] = '\000';
160 //printf("error code is '%s'\n",tt);
161
162 /* Interpret the error character(s) */
163 /* It's not clear if more than one error can be returned. */
164 /* We are only looking at the first character - we should */
165 /* really prioritize them if more than one can occur. */
166 for (p = tt; *p != '\000'; p++) {
167 rv = decodeK10err(*p);
168 break;
169 }
170
171 /* Remove the error code from the reply */
172 if (nlength != NULL)
173 *nlength = f - s;
174 *f = '\000';
175 return rv;
176 }
177
178 /* Interpret an icoms error into a KLEINK10 error */
icoms2k10_err(int se)179 static int icoms2k10_err(int se) {
180 if (se != ICOM_OK) {
181 if (se & ICOM_TO)
182 return K10_TIMEOUT;
183 return K10_COMS_FAIL;
184 }
185 return K10_OK;
186 }
187
188 typedef enum {
189 ec_n = 0, /* No error code or command echo */
190 ec_e = 1, /* Error code */
191 ec_c = 2, /* Command echo */
192 ec_ec = 3 /* Both error code and command echo */
193 } ichecks;
194
195 /* Do a full command/response echange with the kleink10 */
196 /* (This level is not multi-thread safe) */
197 /* Return the kleink10 error code. */
198 static int
k10_fcommand(struct _kleink10 * p,char * in,char * out,int bsize,int * pbread,int nchar,double to,ichecks xec,int nd)199 k10_fcommand(
200 struct _kleink10 *p,
201 char *in, /* In string */
202 char *out, /* Out string buffer */
203 int bsize, /* Out buffer size */
204 int *pbread, /* Bytes read (including '\000') */
205 int nchar, /* Number of characters to expect */
206 double to, /* Timeout in seconds */
207 ichecks xec, /* Error check */
208 int nd /* nz to disable debug messages */
209 ) {
210 int se, rv = K10_OK;
211 int bwrite, bread = 0;
212 char cmd[10];
213
214 bwrite = strlen((char *)in);
215 strncpy((char *)cmd, (char *)in, 2);
216 cmd[2] = '\000';
217
218 if ((se = p->icom->write_read(p->icom, in, 0, out, bsize, &bread, NULL, nchar, to))
219 != ICOM_OK) {
220 rv = icoms2k10_err(se);
221
222 } else {
223
224 if (!nd && p->log->debug >= 6) {
225 a1logd(p->log, 6, "k10_fcommand: command sent\n");
226 adump_bytes(p->log, " ", (unsigned char *)in, 0, bwrite);
227 a1logd(p->log, 6, " returned %d bytes:\n",bread);
228 adump_bytes(p->log, " ", (unsigned char *)out, 0, bread);
229 }
230
231 if (xec & ec_e) {
232 rv = extract_ec(out, &bread, bread);
233 }
234
235 if ((xec & ec_c) && rv == K10_OK && strncmp(cmd, out, 2) != 0) {
236 rv = K10_CMD_VERIFY;
237 }
238 }
239 if (!nd) a1logd(p->log, 6, " error code 0x%x\n",rv);
240
241 if (pbread != NULL)
242 *pbread = bread;
243
244 return rv;
245 }
246
247 /* Do a normal command/response echange with the kleink10. */
248 /* (This level is not multi-thread safe) */
249 /* Return the inst code */
250 static inst_code
k10_command(kleink10 * p,char * in,char * out,int bsize,int * bread,int nchar,ichecks xec,double to)251 k10_command(
252 kleink10 *p,
253 char *in, /* In string */
254 char *out, /* Out string buffer */
255 int bsize, /* Out buffer size */
256 int *bread, /* Bytes read */
257 int nchar, /* Number of characters to expect */
258 ichecks xec, /* Error check */
259 double to) { /* Timout in seconds */
260 int rv = k10_fcommand(p, in, out, bsize, bread, nchar, to, xec, 0);
261 return k10_interp_code(p, rv);
262 }
263
264 /* Do a write to the kleink10 */
265 /* (This level is not multi-thread safe) */
266 /* Return the kleink10 error code. */
267 static int
k10_write(struct _kleink10 * p,char * in,double to)268 k10_write(
269 struct _kleink10 *p,
270 char *in, /* In string */
271 double to /* Timeout in seconds */
272 ) {
273 int rv = K10_OK;
274 int se;
275
276 if ((se = p->icom->write(p->icom, in, 0, to)) != ICOM_OK) {
277 rv = icoms2k10_err(se);
278
279 } else {
280
281 if (p->log->debug >= 6) {
282 a1logd(p->log, 6, "k10_write: command sent\n");
283 adump_bytes(p->log, " ", (unsigned char *)in, 0, strlen((char *)in));
284 }
285 }
286 a1logd(p->log, 6, " error code 0x%x\n",rv);
287
288 return rv;
289 }
290
291 /* Do a read from the kleink10 */
292 /* (This level is not multi-thread safe) */
293 /* Return the kleink10 error code. */
294 static int
k10_read(struct _kleink10 * p,char * out,int bsize,int * pbread,char * tc,int nchar,double to)295 k10_read(
296 struct _kleink10 *p,
297 char *out, /* Out string buffer */
298 int bsize, /* Out buffer size */
299 int *pbread, /* Bytes read (including '\000') */
300 char *tc, /* Terminating characters, NULL for none or char count mode */
301 int nchar, /* Number of terminating characters needed, or char count needed */
302 double to /* Timeout in seconds */
303 ) {
304 int se, rv = K10_OK;
305 int bread = 0;
306
307 if ((se = p->icom->read(p->icom, out, bsize, &bread, tc, nchar, to)) != ICOM_OK) {
308 rv = icoms2k10_err(se);
309 } else {
310
311 if (p->log->debug >= 6) {
312 a1logd(p->log, 6, "k10_read: read %d bytes\n",bread);
313 adump_bytes(p->log, " ", (unsigned char *)out, 0, bread);
314 }
315 }
316 a1logd(p->log, 6, " error code 0x%x\n",rv);
317
318 if (pbread != NULL)
319 *pbread = bread;
320
321 return rv;
322 }
323
324 /* Change baud rates */
325 /* (This level is not multi-thread safe) */
326 /* Return the kleink10 error code. */
327 static int
k10_set_baud(struct _kleink10 * p,baud_rate br)328 k10_set_baud(
329 struct _kleink10 *p,
330 baud_rate br
331 ) {
332 int se, rv = K10_OK;
333 if ((se = p->icom->set_ser_port(p->icom, fc_HardwareDTR, br, parity_none,
334 stop_1, length_8)) != ICOM_OK) {
335 rv = icoms2k10_err(se);
336 } else {
337 if (p->log->debug >= 6) {
338 a1logd(p->log, 6, "k10_set_baud: %d\n",br);
339 }
340 }
341 a1logd(p->log, 6, " error code 0x%x\n",rv);
342
343 return rv;
344 }
345
346 /* ------------------------------------------------------------ */
347
348 /* Establish communications with a kleink10 */
349 /* Return K10_COMS_FAIL on failure to establish communications */
350 static inst_code
k10_init_coms(inst * pp,baud_rate br,flow_control fc,double tout)351 k10_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
352 kleink10 *p = (kleink10 *) pp;
353 char buf[MAX_MES_SIZE];
354 baud_rate brt[] = { baud_9600, baud_nc };
355 unsigned int etime;
356 unsigned int i;
357 instType itype = pp->itype;
358 int se;
359 char *cp;
360
361 inst_code ev = inst_ok;
362
363 a1logd(p->log, 2, "k10_init_coms: About to init Serial I/O\n");
364
365 if (p->gotcoms) {
366 a1logd(p->log, 2, "k10_init_coms: already inited\n");
367 return inst_ok;
368 }
369
370 amutex_lock(p->lock);
371
372 if (!(p->icom->port_type(p->icom) & icomt_serial)) {
373 amutex_unlock(p->lock);
374 a1logd(p->log, 1, "k10_init_coms: wrong communications type for device!\n");
375 return inst_coms_fail;
376 }
377
378 /* The tick to give up on */
379 etime = msec_time() + (long)(500.0 + 0.5);
380
381 a1logd(p->log, 1, "k10_init_coms: Trying different baud rates (%u msec to go)\n",etime - msec_time());
382
383 /* Until we time out, find the correct baud rate */
384 for (i = 0; msec_time() < etime; i++) {
385 if (brt[i] == baud_nc) {
386 i = 0;
387 }
388 a1logd(p->log, 5, "k10_init_coms: Trying %s baud, %d msec to go\n",
389 baud_rate_to_str(brt[i]), etime- msec_time());
390 if ((se = p->icom->set_ser_port(p->icom, fc_HardwareDTR, brt[i], parity_none,
391 stop_1, length_8)) != ICOM_OK) {
392 amutex_unlock(p->lock);
393 a1logd(p->log, 5, "k10_init_coms: set_ser_port failed with 0x%x\n",se);
394 return k10_interp_code(p, icoms2k10_err(se));; /* Give up */
395 }
396
397 /* Check instrument is responding */
398 if (((ev = k10_command(p, "P0\r", buf, MAX_MES_SIZE, NULL, 21, ec_ec, 0.5)) & inst_mask)
399 != inst_coms_fail) {
400 goto got_coms; /* We've got coms or user abort */
401 }
402
403 /* Check for user abort */
404 if (p->uicallback != NULL) {
405 inst_code ev;
406 if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) {
407 amutex_unlock(p->lock);
408 a1logd(p->log, 1, "k10_init_coms: user aborted\n");
409 return inst_user_abort;
410 }
411 }
412 }
413
414 /* We haven't established comms */
415 amutex_unlock(p->lock);
416 a1logd(p->log, 2, "k10_init_coms: failed to establish coms\n");
417 return inst_coms_fail;
418
419 got_coms:;
420
421 /* Check the response */
422 if (ev != inst_ok) {
423 amutex_unlock(p->lock);
424 a1logd(p->log, 2, "k10_init_coms: status command failed\n");
425 return ev;
426 }
427
428 if (strncmp (buf+2, "K-10 ", 7) == 0)
429 p->model = k10_k10;
430 else if (strncmp (buf+2, "K-10-A ", 7) == 0)
431 p->model = k10_k10a;
432 else if (strncmp (buf+2, "KV-10-A", 7) == 0)
433 p->model = k10_kv10a;
434 else {
435 amutex_unlock(p->lock);
436 a1logd(p->log, 2, "k10_init_coms: unrecognised model '%s'\n",buf);
437 return inst_unknown_model;
438 }
439
440 /* Extract the serial number */
441 strncpy(p->serial_no, buf+9, 9);
442 p->serial_no[20] = '\000';
443
444 a1logd(p->log, 2, "k10_init_coms: coms established\n");
445
446 p->gotcoms = 1;
447
448 amutex_unlock(p->lock);
449
450 /* Get the list of calibrations */
451 if ((ev = k10_read_cal_list(p)) != inst_ok) {
452 return ev;
453 }
454
455 a1logd(p->log, 2, "k10_init_coms: init coms is returning\n");
456 return inst_ok;
457 }
458
459 /* Initialise the KLEINK10 */
460 /* return non-zero on an error, with dtp error code */
461 static inst_code
k10_init_inst(inst * pp)462 k10_init_inst(inst *pp) {
463 kleink10 *p = (kleink10 *)pp;
464 char mes[100];
465 char buf[MAX_MES_SIZE];
466 unsigned int stime;
467 int se;
468 inst_code ev = inst_ok;
469
470 a1logd(p->log, 2, "k10_init_inst: called\n");
471
472 if (p->gotcoms == 0)
473 return inst_internal_error; /* Must establish coms before calling init */
474
475 amutex_lock(p->lock);
476
477 /* Make sure the target lights are off */
478 if ((ev = k10_command(p, "L0\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok
479 /* Strangely the L0/1 command mat return irrelevant error codes... */
480 && (ev & inst_imask) != K10_UNKNOWN
481 && (ev & inst_imask) != K10_BLACK_EXCESS
482 && (ev & inst_imask) != K10_BLACK_OVERDRIVE
483 && (ev & inst_imask) != K10_BLACK_ZERO
484 && (ev & inst_imask) != K10_OVER_HIGH_RANGE
485 && (ev & inst_imask) != K10_TOP_OVER_RANGE
486 && (ev & inst_imask) != K10_BOT_UNDER_RANGE) {
487 amutex_unlock(p->lock);
488 return ev;
489 }
490 p->lights = 0;
491
492 /* Make sure we are auto ranging by default */
493 if ((ev = k10_command(p, "J8\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) {
494 amutex_unlock(p->lock);
495 return ev;
496 }
497 p->autor = 1;
498
499 /* Grab the firware version */
500 stime = msec_time();
501 if ((ev = k10_command(p, "P2\r", buf, MAX_MES_SIZE, NULL, 2+8+3, ec_ec, 2.0)) != inst_ok) {
502 amutex_unlock(p->lock);
503 return ev;
504 }
505 p->comdel = (msec_time() - stime)/2; /* Or is this the FD232 update latency ? */
506 strncpy(p->firm_ver, buf+2, 8);
507 p->firm_ver[8] = '\000';
508
509 amutex_unlock(p->lock);
510
511 /* Set a default calibration */
512 if ((ev = set_default_disp_type(p)) != inst_ok) {
513 return ev;
514 }
515
516 p->inited = 1;
517
518 /* Do a flicker read to work around glitch at the 0.4 second mark of the */
519 /* first one after power up. We ignore any error. */
520 if ((ev = k10_read_flicker_samples(p, 0.5, NULL, NULL, NULL, 0)) != inst_ok) {
521 a1logd(p->log, 1, "k10_init_inst: warning - startup k10_read_flicker_samples failed with 0x%x - ignored\n",ev);
522 }
523
524 a1logd(p->log, 2, "k10_init_inst: instrument inited OK\n");
525
526 if (p->log->verb) {
527 char *model = "Unknown";
528 switch (p->model) {
529 case k10_k1:
530 model = "K-1";
531 break;
532 case k10_k8:
533 model = "K-8";
534 break;
535 case k10_k10:
536 model = "K-10";
537 break;
538 case k10_k10a:
539 model = "K-10A";
540 break;
541 case k10_kv10a:
542 model = "KV-10A";
543 break;
544 }
545 a1logv(p->log, 1, " Model: '%s'\n",model);
546 a1logv(p->log, 1, " Serial number: '%s'\n",p->serial_no);
547 a1logv(p->log, 1, " Firmware version: '%s'\n",p->firm_ver);
548 }
549
550 return inst_ok;
551 }
552
553
554 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
555
556 /* Convert a Klein Measurement encoded 24 bit value to a double */
KleinMeas2double(char * ibuf)557 double KleinMeas2double(char *ibuf) {
558 unsigned char *buf = (unsigned char *)ibuf;
559 unsigned int ip;
560 double op;
561 int sn = 0, ma;
562 int ep;
563
564 ip = (buf[0] << 8) + buf[1];
565 sn = (ip >> 15) & 0x1;
566 ma = ip & ((1 << 15)-1);
567 ep = buf[2];
568 if (ep >= 128)
569 ep -= 256;
570
571 op = (double)ma;
572 op *= pow(2.0, (double)ep-16);
573 if (sn)
574 op = -op;
575 return op;
576 }
577
578 /* Decode measurement RGB range into 3 x 1..6 */
decodeRange(int * out,char iin)579 static void decodeRange(int *out, char iin) {
580 unsigned char in = (unsigned char)iin;
581 int t, r0, r1, r2, r3;
582 int tt;
583
584 out[0] = (in >> 7) & 1;
585 out[1] = (in >> 6) & 1;
586 out[2] = (in >> 5) & 1;
587
588 in &= 0x1F;
589
590 out[0] += 1 + 2 * ((in / 9) % 3);
591 out[1] += 1 + 2 * ((in / 3) % 3);
592 out[2] += 1 + 2 * (in % 3);
593 }
594
595
596 /* Convert a Klein Calibration encoded 24 bit value to a double */
KleinCal2double(char * ibuf)597 double KleinCal2double(char *ibuf) {
598 unsigned char *buf = (unsigned char *)ibuf;
599 ORD32 ip;
600 double op;
601 ORD32 sn = 0, ma;
602 int ep;
603
604 ip = (buf[0] << 8) + buf[1];
605 sn = (ip >> 15) & 0x1;
606 ma = ip & ((1 << 15)-1);
607 ep = buf[2];
608 if (ep >= 128)
609 ep -= 256;
610
611 op = (double)ma;
612 op *= pow(2.0, (double)ep-15);
613 if (sn)
614 op = -op;
615 return op;
616 }
617
618 /* Convert a native double to an Klein Calibration encoded 24 bit value, */
double2KleinCal(char * ibuf,double d)619 void double2KleinCal(char *ibuf, double d) {
620 unsigned char *buf = (unsigned char *)ibuf;
621 ORD32 sn = 0, ma;
622 int ep;
623 double n;
624
625 if (d < 0.0) {
626 sn = 1;
627 d = -d;
628 }
629 if (d != 0.0) {
630 ep = (int)floor(log(d)/log(2.0)) + 1;
631
632 n = pow(0.5, (double)(ep - 15)); /* Normalisation factor */
633
634 /* If rounding would cause an overflow, adjust exponent */
635 if (floor(d * n + 0.5) >= (double)(1 << 15)) {
636 n *= 0.5;
637 ep++;
638 }
639
640 if (ep < -128) { /* Alow denormalised */
641 ep = -128;
642 n = pow(0.5, (double)(ep - 15)); /* Normalisation factor */
643 }
644
645 if (ep > 127) { /* Saturate maximum */
646 ep = 127;
647 d = (double)(1 << 15)-1;
648 } else {
649 d *= n;
650 if (d < 0.5)
651 ep = 0;
652 }
653 } else {
654 ep = 0; /* Zero */
655 }
656 ma = (((ORD32)floor(d + 0.5)) & ((1 << 16)-1)) | (sn << 15);
657 buf[0] = ((ma >> 8) & 0xff);
658 buf[1] = (ma & 0xff);
659
660 buf[2] = ep;
661 }
662
CalMan2double(char * ibuf)663 double CalMan2double(char *ibuf) {
664 unsigned char *buf = (unsigned char *)ibuf;
665 ORD64 val;
666
667 /* Load LE into 64 bit */
668 val = buf[7];
669 val = ((val << 8) + (0xff & buf[6]));
670 val = ((val << 8) + (0xff & buf[5]));
671 val = ((val << 8) + (0xff & buf[4]));
672 val = ((val << 8) + (0xff & buf[3]));
673 val = ((val << 8) + (0xff & buf[2]));
674 val = ((val << 8) + (0xff & buf[1]));
675 val = ((val << 8) + (0xff & buf[0]));
676
677 return IEEE754_64todouble(val);
678 }
679
680 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
681
682 /* Decode an N5 measre command response */
decodeN5(kleink10 * p,double * XYZ,int * range,char * buf,int blen)683 static inst_code decodeN5(kleink10 *p, double *XYZ, int *range, char *buf, int blen) {
684
685 if (blen < (2 + 3 * 3 + 1)) {
686 a1logd(p->log, 1, "decodeN5: failed to parse '%s'\n",icoms_fix(buf));
687 return inst_protocol_error;
688 }
689
690 if (XYZ != NULL) {
691 XYZ[0] = KleinMeas2double(buf+2);
692 XYZ[1] = KleinMeas2double(buf+5);
693 XYZ[2] = KleinMeas2double(buf+8);
694 }
695
696 if (range != NULL)
697 decodeRange(range, buf[11]);
698
699 return inst_ok;
700 }
701
702
703 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
704
705 /* Read a given calibration matrix */
706 static inst_code
k10_read_cal_matrix(kleink10 * p,inst_disptypesel * m,int ix)707 k10_read_cal_matrix(
708 kleink10 *p,
709 inst_disptypesel *m, /* Matrix calibration to write */
710 int ix /* Klein calibration index 1 - 96 */
711 ) {
712 inst_code ev = inst_protocol_error;
713 int se;
714 char cmd[3];
715 char buf[MAX_MES_SIZE];
716 int bread, i, j, k;
717
718 if (!p->gotcoms)
719 return inst_no_coms;
720
721 amutex_lock(p->lock);
722
723 /* Trigger cal matrix read */
724 if ((ev = k10_command(p, "D1\r", buf, MAX_MES_SIZE, &bread, 2, ec_c, 2.0)) != inst_ok) {
725 amutex_unlock(p->lock);
726 return ev;
727 }
728
729 if (buf[0] != 'D' || buf[1] != '1') {
730 amutex_unlock(p->lock);
731 a1logd(p->log, 1, "k10_read_cal_matrix: didn't get echo'd commad D1\n");
732 return inst_protocol_error;
733 }
734
735 /* Send the cal index and read matrix */
736 cmd[0] = ix;
737 cmd[1] = '\r';
738 cmd[2] = '\000';
739
740 if ((ev = k10_command(p, cmd, buf, MAX_MES_SIZE, &bread, 128+3, ec_e, 2.0)) != inst_ok) {
741 amutex_unlock(p->lock);
742 return ev;
743 }
744
745 if (bread < 128) {
746 amutex_unlock(p->lock);
747 a1logd(p->log, 1, "k10_read_cal_matrix: not enough bytes returned (%d)\n",bread);
748 return inst_protocol_error;
749 }
750
751 a1logd(p->log, 6, "Cal '%s':\n",m->desc);
752
753 /* CalMan format matrix */
754 if (buf[21] == 'C') {
755 for (k = 24, i = 0; i < 3; i++) {
756 for (j = 0; j < 3; j++) {
757 if ((bread-k) < 8) {
758 amutex_unlock(p->lock);
759 return inst_protocol_error;
760 }
761 m->mat[i][j] = CalMan2double(buf + k);
762 k += 8;
763 a1logd(p->log, 6, " Mat[%d][%d] = %f\n",i,j,m->mat[i][j]);
764 }
765 }
766
767 /* Klein format matrix */
768 } else {
769 for (k = 101, i = 0; i < 3; i++) {
770 for (j = 0; j < 3; j++) {
771 if ((bread-k) < 3) {
772 amutex_unlock(p->lock);
773 return inst_protocol_error;
774 }
775 m->mat[i][j] = KleinCal2double(buf + k);
776 k += 3;
777 a1logd(p->log, 6, " Mat[%d][%d] = %f\n",i,j,m->mat[i][j]);
778 }
779 }
780 }
781 m->flags |= inst_dtflags_ld; /* It's now loaded */
782 amutex_unlock(p->lock);
783
784 return inst_ok;
785 }
786
787 /* Guess appropriate disptype and selector letters for standard calibrations */
guess_disptype(inst_disptypesel * s,char * desc)788 static void guess_disptype(inst_disptypesel *s, char *desc) {
789 disptech dtype;
790 disptech_info *i;
791 char *sel = NULL;
792
793 if (strcmp(desc, "Default CRT File") == 0) {
794 dtype = disptech_crt;
795 } else if (strcmp(desc, "Klein DLP Lux") == 0) {
796 dtype = disptech_dlp;
797 sel = "P";
798 } else if (strcmp(desc, "Klein SMPTE C") == 0) {
799 dtype = disptech_crt;
800 sel = "E";
801 } else if (strcmp(desc, "TVL XVM245") == 0) { /* RGB LED LCD Video display */
802 dtype = disptech_lcd_rgbled;
803 } else if (strcmp(desc, "Klein LED Bk LCD") == 0) {
804 dtype = disptech_lcd_rgbled;
805 sel = "d";
806 } else if (strcmp(desc, "Klein Plasma") == 0) {
807 dtype = disptech_plasma;
808 } else if (strcmp(desc, "DLP Screen") == 0) {
809 dtype = disptech_dlp;
810 } else if (strcmp(desc, "TVL LEM150") == 0) { /* OLED */
811 dtype = disptech_oled;
812 } else if (strcmp(desc, "Sony EL OLED") == 0) { /* OLED */
813 dtype = disptech_oled;
814 sel = "O";
815 } else if (strcmp(desc, "Eizo CG LCD") == 0) { /* Wide gamut IPS LCD RGB ? (or RG+P ?)*/
816 dtype = disptech_lcd_rgbled_ips;
817 sel = "z";
818 } else if (strcmp(desc, "FSI 2461W") == 0) { /* Wide gamut IPS ? LCD CCFL */
819 dtype = disptech_lcd_ccfl_wg;
820 } else if (strcmp(desc, "HP DreamColor 2") == 0) { /* Wide gamut IPS ? LCD RG+P */
821 dtype = disptech_lcd_rgledp;
822 } else {
823 dtype = disptech_unknown;
824 }
825
826 i = disptech_get_id(dtype);
827 s->dtech = dtype;
828 if (sel != NULL)
829 strcpy(s->sel, sel);
830 else
831 strcpy(s->sel, i->sel);
832 }
833
834 /* Read the list of calibrations available */
835 static inst_code
k10_read_cal_list(kleink10 * p)836 k10_read_cal_list(
837 kleink10 *p) {
838 inst_code ev = inst_protocol_error;
839 char buf[MAX_RD_SIZE];
840 int bread, i, j, ix, n;
841 char name[21];
842
843 if (!p->gotcoms)
844 return inst_no_coms;
845
846 /* Make sure factory matrix values is in the first entry */
847 for (i = 0; i < 3; i++) {
848 for (j = 0; j < 3; j++) {
849 if (i == j)
850 k10_disptypesel[0].mat[i][j] = 1.0;
851 else
852 k10_disptypesel[0].mat[i][j] = 0.0;
853 }
854 }
855 k10_disptypesel[0].flags |= inst_dtflags_ld;
856
857 amutex_lock(p->lock);
858
859 /* Grab the raw info */
860 if ((ev = k10_command(p, "D7\r", buf, MAX_RD_SIZE, &bread, 1925, ec_ec, 6.0)) != inst_ok) {
861 amutex_unlock(p->lock);
862 a1logd(p->log, 1, "k10_read_cal_list D7 returning error 0x%x\n",ev);
863 return ev;
864 }
865
866 /* Parse it. There should be 96 calibrations */
867 name[20] = '\000';
868 for (i = 2, ix = 1, n = 1; ix <= 96 && (bread-i) >= 20; i += 20, ix++) {
869
870 for (j = 0; j < 20; j++)
871 name[j] = buf[i + j];
872
873 if (((unsigned char *)name)[0] == 0xff) {
874 continue;
875 }
876 for (j = 19; j >= 0; j--) {
877 if (name[j] != ' ') {
878 name[j+1] = '\000';
879 break;
880 }
881 }
882
883 // printf("Adding Cal %d is '%s'\n",ix,name);
884
885 /* Add it to the list */
886 memset((void *)&k10_disptypesel[n], 0, sizeof(inst_disptypesel));
887 k10_disptypesel[n].flags = inst_dtflags_mtx | inst_dtflags_wr; /* Not loaded yet */
888 k10_disptypesel[n].cbid = 0;
889 strcpy(k10_disptypesel[n].desc, name);
890 k10_disptypesel[n].refr = 0;
891 k10_disptypesel[n].ix = ix;
892 guess_disptype(&k10_disptypesel[n], name);
893 n++;
894 }
895
896 /* Put marker at end */
897 k10_disptypesel[n].flags = inst_dtflags_end;
898 k10_disptypesel[n].cbid = 0;
899 k10_disptypesel[n].sel[0] = '\000';
900 k10_disptypesel[n].desc[0] = '\000';
901 k10_disptypesel[n].refr = 0;
902 k10_disptypesel[n].ix = 0;
903
904 amutex_unlock(p->lock);
905
906 return inst_ok;
907 }
908
909 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
910
abort_flicker(kleink10 * p,int isnew,double * retbuf)911 static void abort_flicker(kleink10 *p, int isnew, double *retbuf) {
912 char buf[MAX_MES_SIZE];
913 int bread;
914
915 /* Abort flicker transfer */
916 k10_write(p, "N5\r", 2.0);
917
918 /* Flush the buffer of any remaining characters. */
919 k10_read(p, buf, MAX_MES_SIZE, &bread, "<0>", 3, 1.0);
920
921 /* Return the baud rate to normal */
922 if (isnew)
923 k10_set_baud(p, baud_9600);
924
925 #ifdef TEST_BAUD_CHANGE
926 else {
927 k10_set_baud(p, baud_19200);
928 k10_set_baud(p, baud_9600);
929 }
930 #endif
931
932 /* Clean up everything else */
933 amutex_unlock(p->lock);
934
935 if (retbuf != NULL)
936 free(retbuf);
937 }
938
939 /* Read flicker samples */
940 /* Free *pvals after use */
941 static inst_code
k10_read_flicker_samples(kleink10 * p,double duration,double * srate,double ** pvals,int * pnsamp,int usefast)942 k10_read_flicker_samples(
943 kleink10 *p,
944 double duration, /* duration to take samples */
945 double *srate, /* Return the sampel rate */
946 double **pvals, /* Return the sample values */
947 int *pnsamp, /* Return the number of samples */
948 int usefast /* If nz use fast rate is possible */
949 ) {
950 int se = K10_OK;
951 inst_code ev = inst_ok;
952 int isnew = 0;
953 double rate = 256;
954 double *retbuf;
955 int tsamp, nsamp;
956 char mes[4] = "JX\r";
957 char buf[MAX_MES_SIZE];
958 int boff, bread;
959 int range[3];
960 unsigned int stime;
961 int derr = 0, rerr = 0;
962 int i;
963
964 stime = msec_time();
965
966 if (!p->gotcoms)
967 return inst_no_coms;
968 if (!p->inited)
969 return inst_no_init;
970
971 amutex_lock(p->lock);
972
973 #ifdef HIGH_SPEED
974 /* This isn't reliable, because there is no way to ensure that */
975 /* the T1 command has been sent before we change the baud rate, */
976 /* anf if we wait too long will will loose the measurements. */
977 if (usefast && strcmp(p->firm_ver, "v01.09fh") > 0) {
978 isnew = 1; /* We can use faster T1 command */
979 rate = 384;
980 a1logd(p->log, 1, "k10_read_flicker: using faster T1\n");
981 }
982 #endif /* HIGH_SPEED */
983
984 /* Target number of samples */
985 tsamp = (int)(duration * (double)rate + 0.5);
986
987 if (tsamp < 1)
988 tsamp = 1;
989
990 a1logd(p->log, 1, "k10_read_flicker: taking %d samples\n",tsamp);
991
992 if ((retbuf = (double *)malloc(sizeof(double) * tsamp)) == NULL) {
993 amutex_unlock(p->lock);
994 a1logd(p->log, 1, "k10_read_flicker: malloc of %d bytes failed\n",sizeof(double) * tsamp);
995 return k10_interp_code(p, K10_INT_MALLOC);
996 }
997
998 /* Make sure the target lights are off */
999 if (p->lights) {
1000 int se;
1001 if ((ev = k10_command(p, "L0\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 0.5)) != inst_ok
1002 /* Strangely the L0/1 command mat return irrelevant error codes... */
1003 && (ev & inst_imask) != K10_UNKNOWN
1004 && (ev & inst_imask) != K10_BLACK_EXCESS
1005 && (ev & inst_imask) != K10_BLACK_OVERDRIVE
1006 && (ev & inst_imask) != K10_BLACK_ZERO
1007 && (ev & inst_imask) != K10_OVER_HIGH_RANGE
1008 && (ev & inst_imask) != K10_TOP_OVER_RANGE
1009 && (ev & inst_imask) != K10_BOT_UNDER_RANGE) {
1010 amutex_unlock(p->lock);
1011 free(retbuf);
1012 a1logd(p->log, 1, "k10_read_flicker: L0 failed\n");
1013 return ev;
1014 }
1015 p->lights = 0;
1016 }
1017
1018 /* Make sure we are auto ranging */
1019 if (!p->autor) {
1020 if ((ev = k10_command(p, "J8\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) {
1021 amutex_unlock(p->lock);
1022 a1logd(p->log, 1, "k10_read_flicker: J8 failed with 0x%x\n",ev);
1023 return ev;
1024 }
1025 p->autor = 1;
1026 }
1027
1028 /* Take a measurement to get ranges ? */
1029 if ((ev = k10_command(p, "N5\r", buf, MAX_MES_SIZE, &bread, 15, ec_ec, 2.0)) != inst_ok) {
1030 amutex_unlock(p->lock);
1031 free(retbuf);
1032 a1logd(p->log, 1, "k10_read_flicker: N5 failed with 0x%x\n",ev);
1033 return ev;
1034 }
1035
1036 if ((ev = decodeN5(p, NULL, range, buf, bread)) != inst_ok) {
1037 a1logd(p->log, 1, "k10_read_flicker: decodeN5 failed with 0x%x\n",ev);
1038 amutex_unlock(p->lock);
1039 return ev;
1040 }
1041
1042 /* Set a fixed range to avoid a range change error */
1043 p->autor = 0;
1044 if ((ev = k10_command(p, "J7\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) {
1045 amutex_unlock(p->lock);
1046 a1logd(p->log, 1, "k10_read_flicker: J7 failed with 0x%x\n",ev);
1047 return ev;
1048 }
1049 mes[1] = '0' + range[1]; /* Green range */
1050 if ((ev = k10_command(p, mes, buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) {
1051 amutex_unlock(p->lock);
1052 a1logd(p->log, 1, "k10_read_flicker: %s failed with 0x%x\n",buf,ev);
1053 return ev;
1054 }
1055
1056 /* Issue an T2 for normal speed flicker measure, or T1 for fast */
1057 a1logd(p->log, 6, "k10_read_flicker: issuing T1/T2 command\n");
1058 if ((se = k10_write(p, isnew ? "T1\r" : "T2\r", 2.0)) != K10_OK) {
1059 amutex_unlock(p->lock);
1060 free(retbuf);
1061 a1logd(p->log, 1, "k10_read_flicker: T1/T2 failed with 0x%x\n",icoms2k10_err(se));
1062 return k10_interp_code(p, se);
1063 }
1064
1065 stime = msec_time() - stime;
1066 stime -= p->comdel;
1067
1068 /* Switch to 19200 baud if using fast */
1069 if (isnew) {
1070 /* Allow the T1/T2 to flow out before changing the baud rate */
1071 msec_sleep(2);
1072
1073 if ((se = k10_set_baud(p, baud_19200)) != K10_OK) {
1074 abort_flicker(p, isnew, retbuf);
1075 a1logd(p->log, 1, "k10_read_flicker: T1 19200 baud failed with 0x%x\n",
1076 icoms2k10_err(se));
1077 return k10_interp_code(p, se);
1078 }
1079 }
1080
1081 #ifdef TEST_BAUD_CHANGE
1082 else {
1083 msec_sleep(2);
1084 k10_set_baud(p, baud_19200);
1085 k10_set_baud(p, baud_9600);
1086 }
1087 #endif
1088
1089 /* Capture flicker packets until we've got enough samples */
1090 for (boff = nsamp = 0; nsamp < tsamp; ) {
1091 if ((se = k10_read(p, buf + boff, MAX_MES_SIZE - boff, &bread,
1092 NULL, 96, 2.0)) != K10_OK) {
1093 abort_flicker(p, isnew, retbuf);
1094 a1logd(p->log, 1, "k10_read_flicker: reading packet failed with 0x%x\n",icoms2k10_err(se));
1095 return k10_interp_code(p, se);
1096 }
1097
1098 boff += bread;
1099
1100 /* Extract the values we want */
1101 /* (We could get XYZ, range & error value too) */
1102 if (boff >= 96) {
1103 int trange[3];
1104 unsigned char *ubuf = (unsigned char *)buf;
1105
1106 for (i = 0; i < 32 && nsamp < tsamp; i++, nsamp++)
1107 retbuf[nsamp] = ubuf[i * 3 + 1] * 256.0 + ubuf[i * 3 + 2];
1108
1109 /* Check the error and range */
1110 if ((se = decodeK10err(buf[3 * 13])) != K10_OK) {
1111 a1logd(p->log, 1, "k10_read_flicker: decode error 0x%x\n",se);
1112 derr = se;
1113
1114 } else {
1115
1116 decodeRange(trange, buf[3 * 11]);
1117
1118 if (trange[0] != range[0]
1119 || trange[1] != range[1]
1120 || trange[2] != range[2]) {
1121 a1logd(p->log, 1, "k10_read_flicker: range changed\n");
1122 rerr = 1;
1123 }
1124 }
1125
1126 /* Shuffle any remaining bytes down */
1127 if (boff > 96)
1128 memmove(buf, buf + 96, boff - 96);
1129 boff -= 96;
1130
1131 #ifdef NEVER
1132 { /* Dump */
1133 char xtra[32];
1134 double XYZ[3];
1135 int range[3];
1136 char err;
1137
1138 for (i = 0; i < 32; i++)
1139 xtra[i] = buf[i * 3 + 0];
1140
1141 adump_bytes(p->log, " ", (unsigned char *)buf, 0, 96);
1142 printf("Extra bytes:\n");
1143 adump_bytes(p->log, " ", (unsigned char *)xtra, 0, 32);
1144
1145 XYZ[0] = KleinMeas2double(xtra+2);
1146 XYZ[1] = KleinMeas2double(xtra+5);
1147 XYZ[2] = KleinMeas2double(xtra+8);
1148
1149 decodeRange(range, xtra[11]);
1150 err = xtra[13];
1151 printf("XYZ %f %f %f range %d %d %d err '%c'\n\n",
1152 XYZ[0], XYZ[1], XYZ[2], range[0], range[1], range[2],err);
1153 }
1154 #endif
1155
1156 }
1157 }
1158
1159 a1logd(p->log, 6, "k10_read_flicker: read %d samples\n",nsamp);
1160
1161 /* Then issue an N5 to cancel, and clean up */
1162 abort_flicker(p, isnew, NULL);
1163
1164 if (derr != 0) {
1165 free(retbuf);
1166 a1logd(p->log, 1, "k10_read_flicker: got error 0x%x during readings\n",derr);
1167 return icoms2k10_err(derr);
1168 }
1169
1170 if (rerr != 0) {
1171 free(retbuf);
1172 a1logd(p->log, 1, "k10_read_flicker: range changed during readings\n");
1173 return icoms2k10_err(K10_RANGE_CHANGE);
1174 }
1175
1176 #ifdef NEVER
1177 { /* Plot */
1178 double *xx;
1179
1180 xx = (double *)malloc(sizeof(double) * tsamp);
1181 for (i = 0; i < tsamp; i++)
1182 xx[i] = (double)i/(double)rate;
1183
1184 do_plot(xx, retbuf, NULL, NULL, tsamp);
1185 free(xx);
1186 }
1187 #endif
1188
1189 if (pvals != NULL)
1190 *pvals = retbuf;
1191 else
1192 free(retbuf);
1193 if (pnsamp != NULL)
1194 *pnsamp = nsamp;
1195 if (srate != NULL)
1196 *srate = (double)rate;
1197
1198 return inst_ok;
1199 }
1200
1201 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1202
1203 /* Read a single sample */
1204 static inst_code
k10_read_sample(inst * pp,char * name,ipatch * val,instClamping clamp)1205 k10_read_sample(
1206 inst *pp,
1207 char *name, /* Strip name (7 chars) */
1208 ipatch *val, /* Pointer to instrument patch value */
1209 instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */
1210 kleink10 *p = (kleink10 *)pp;
1211 char buf[MAX_RD_SIZE];
1212 int user_trig = 0;
1213 int bsize;
1214 inst_code rv = inst_protocol_error;
1215 int range[3]; /* Range for RGB sensor values */
1216 int i, tries, ntav = 1; /* Number of readings to average */
1217 double v, vv;
1218
1219 if (!p->gotcoms)
1220 return inst_no_coms;
1221 if (!p->inited)
1222 return inst_no_init;
1223
1224 amutex_lock(p->lock);
1225
1226 if (p->trig == inst_opt_trig_user) {
1227 amutex_unlock(p->lock);
1228
1229 if (p->uicallback == NULL) {
1230 a1logd(p->log, 1, "kleink10: inst_opt_trig_user but no uicallback function set!\n");
1231 return inst_unsupported;
1232 }
1233
1234 for (;;) {
1235 if ((rv = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
1236 if (rv == inst_user_abort) {
1237 return rv; /* Abort */
1238 }
1239 if (rv == inst_user_trig) {
1240 user_trig = 1;
1241 break; /* Trigger */
1242 }
1243 }
1244 msec_sleep(200);
1245 }
1246 /* Notify of trigger */
1247 if (p->uicallback)
1248 p->uicallback(p->uic_cntx, inst_triggered);
1249 amutex_lock(p->lock);
1250
1251 /* Progromatic Trigger */
1252 } else {
1253 /* Check for abort */
1254 if (p->uicallback != NULL
1255 && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) {
1256 amutex_unlock(p->lock);
1257 return rv; /* Abort */
1258 }
1259 }
1260
1261 /* Make sure the target lights are off */
1262 if (p->lights) {
1263 if ((rv = k10_command(p, "L0\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok
1264 /* Strangely the L0/1 command mat return irrelevant error codes... */
1265 && (rv & inst_imask) != K10_UNKNOWN
1266 && (rv & inst_imask) != K10_BLACK_EXCESS
1267 && (rv & inst_imask) != K10_BLACK_OVERDRIVE
1268 && (rv & inst_imask) != K10_BLACK_ZERO
1269 && (rv & inst_imask) != K10_OVER_HIGH_RANGE
1270 && (rv & inst_imask) != K10_TOP_OVER_RANGE
1271 && (rv & inst_imask) != K10_BOT_UNDER_RANGE) {
1272 amutex_unlock(p->lock);
1273 a1logd(p->log, 1, "k10_read_sample: L0 failed\n");
1274 return rv;
1275 }
1276 p->lights = 0;
1277 }
1278
1279 /* Make sure we are auto ranging */
1280 if (!p->autor) {
1281 if ((rv = k10_command(p, "J8\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) {
1282 amutex_unlock(p->lock);
1283 return rv;
1284 }
1285 p->autor = 1;
1286 }
1287
1288
1289 for (tries = 0; tries < RETRY_RANGE_ERROR; tries++) {
1290
1291 /* Take a measurement */
1292 rv = k10_command(p, "N5\r", buf, MAX_MES_SIZE, &bsize, 15, ec_ec, 2.0);
1293
1294 if (rv == inst_ok
1295 || ( (rv & inst_imask) != K10_TOP_OVER_RANGE
1296 && (rv & inst_imask) != K10_BOT_UNDER_RANGE))
1297 break;
1298 }
1299
1300 if (rv == inst_ok)
1301 rv = decodeN5(p, val->XYZ, range, buf, bsize);
1302
1303 if (rv == inst_ok) {
1304 double thr[4] = { 0.2, 2.0, 20.0, 50.0 }; /* Threshold */
1305 double nav[4] = { 20, 10, 4, 2 }; /* Count */
1306
1307 /* Make v the largest */
1308 v = val->XYZ[1];
1309 if (val->XYZ[0] > v)
1310 v = val->XYZ[0];
1311 if (val->XYZ[2] > v)
1312 v = val->XYZ[2];
1313
1314 #ifdef AUTO_AVERAGE
1315 if (!IMODETST(p->mode, inst_mode_emis_nonadaptive)) {
1316 /* Decide how many extra readings to average into result. */
1317 /* Interpolate between the thresholds */
1318 if (v < 0.2) {
1319 ntav = nav[0];
1320 } else if (v < thr[1]) {
1321 vv = 1.0 - (v - thr[0]) / (thr[1] - thr[0]);
1322 vv = vv * vv * vv;
1323 ntav = (int)(vv * (nav[0] - 10) + 10.0 + 0.5);
1324 } else if (v < thr[2]) {
1325 vv = 1.0 - (v - thr[1]) / (thr[2] - thr[1]);
1326 vv = vv * vv * vv;
1327 ntav = (int)(vv * (nav[1] - nav[2]) + nav[2] + 0.5);
1328 } else if (v < thr[3]) {
1329 vv = 1.0 - (v - thr[2]) / (thr[3] - thr[2]);
1330 vv = vv * vv * vv;
1331 ntav = (int)(vv * (nav[2] - nav[3]) + nav[3] + 0.5);
1332 } /* else default 1 */
1333 }
1334 #endif
1335
1336 /* Measure extras up to ntav */
1337 for (i = 1; i < ntav; i++) {
1338 double XYZ[3];
1339
1340 for (tries = 0; tries < RETRY_RANGE_ERROR; tries++) {
1341 rv = k10_command(p, "N5\r", buf, MAX_MES_SIZE, &bsize, 15, ec_ec, 2.0);
1342 if (rv == inst_ok
1343 || ( (rv & inst_imask) != K10_TOP_OVER_RANGE
1344 && (rv & inst_imask) != K10_BOT_UNDER_RANGE))
1345 break;
1346 }
1347
1348 if (rv != inst_ok) { // An error, or retry failed
1349 break;
1350 }
1351
1352 if ((rv = decodeN5(p, XYZ, range, buf, bsize)) != inst_ok)
1353 break;
1354
1355 val->XYZ[0] += XYZ[0];
1356 val->XYZ[1] += XYZ[1];
1357 val->XYZ[2] += XYZ[2];
1358 }
1359 }
1360
1361
1362 if (rv != inst_ok) {
1363 amutex_unlock(p->lock);
1364 return rv;
1365 }
1366
1367 val->XYZ[0] /= (double)ntav;
1368 val->XYZ[1] /= (double)ntav;
1369 val->XYZ[2] /= (double)ntav;
1370
1371
1372 amutex_unlock(p->lock);
1373
1374 /* Apply the calibration correction matrix */
1375 icmMulBy3x3(val->XYZ, p->ccmat, val->XYZ);
1376
1377 //printf("matrix = %f %f %f\n", p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
1378 //printf(" %f %f %f\n", p->ccmat[1][0], p->ccmat[1][1], p->ccmat[2][2]);
1379 //printf(" %f %f %f\n", p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
1380 //printf("XYZ = %f %f %f\n", val->XYZ[0], val->XYZ[1], val->XYZ[2]);
1381 //printf("range = %d %d %d\n", range[0], range[1], range[2]);
1382
1383 /* This may not change anything since instrument may clamp */
1384 if (clamp)
1385 icmClamp3(val->XYZ, val->XYZ);
1386
1387 val->loc[0] = '\000';
1388
1389 /* Check if the matrix seems to be an Ambient matrix */
1390 if ((p->ccmat[0][0] + p->ccmat[1][1] + p->ccmat[2][2])/3.0 > 5.0)
1391 val->mtype = inst_mrt_ambient;
1392 else
1393 val->mtype = inst_mrt_emission;
1394 val->XYZ_v = 1; /* These are absolute XYZ readings */
1395 val->sp.spec_n = 0;
1396 val->duration = 0.0;
1397 rv = inst_ok;
1398
1399
1400 if (user_trig)
1401 return inst_user_trig;
1402 return rv;
1403 }
1404
1405 /* - - - - - - - - - - - - - - - - */
1406 /*
1407
1408 Determining the refresh rate for a refresh type display.
1409
1410 This is easy because the sample rate of the Kleoin
1411 is well above the refresh rates we ant to measure.
1412
1413 If there is no aparent refresh, or the refresh rate is not determinable,
1414 return a period of 0.0 and inst_ok;
1415 */
1416
1417 #undef FREQ_SLOW_PRECISE /* [und] Interpolate then autocorrelate, else autc & filter */
1418
1419 #define NFSAMPS 450 /* Maximum number of samples to read (= 1.0sec) */
1420 #define NFMXTIME 0.5 /* Time to take measurements over */
1421 #define PBPMS 20 /* bins per msec */
1422 #define PERMIN ((1000 * PBPMS)/40) /* 40 Hz */
1423 #define PERMAX ((1000 * PBPMS)/4) /* 4 Hz*/
1424 #define NPER (PERMAX - PERMIN + 1)
1425 #define PWIDTH (8 * PBPMS) /* 8 msec bin spread to look for peak in */
1426 #define MAXPKS 20 /* Number of peaks to find */
1427
k10_imp_measure_refresh(kleink10 * p,double * ref_rate)1428 static inst_code k10_imp_measure_refresh(
1429 kleink10 *p,
1430 double *ref_rate
1431 ) {
1432 inst_code ev;
1433 int i, j, k, mm;
1434
1435 int nfsamps; /* Actual samples read */
1436 double *samp; /* Samples */
1437 double srate; /* Sampling rate used to measure frequency */
1438 double rsamp; /* Sampling time */
1439
1440 double minv; /* Minimum reading */
1441 double maxv; /* Maximum reading */
1442 double maxt; /* Time range */
1443
1444 #ifdef FREQ_SLOW_PRECISE
1445 int nbins;
1446 double *bins; /* PBPMS sample bins */
1447 #else
1448 double tcorr[NPER]; /* Temp for initial autocorrelation */
1449 int ntcorr[NPER]; /* Number accumulated */
1450 #endif
1451 double corr[NPER]; /* Filtered correlation for each period value */
1452 double mincv, maxcv; /* Max and min correlation values */
1453 double crange; /* Correlation range */
1454 double peaks[MAXPKS]; /* Peak wavelength */
1455 double peakh[MAXPKS]; /* Peak heighheight */
1456 int npeaks; /* Number of peaks */
1457 double pval; /* Period value */
1458 double rfreq; /* Computed refresh frequency for each try */
1459 int tix = 0; /* try index */
1460
1461 a1logd(p->log,2,"k10_imp_meas_refrate called\n");
1462
1463 if (ref_rate != NULL)
1464 *ref_rate = 0.0; /* Define refresh rate on error */
1465
1466 rfreq = 0.0;
1467 npeaks = 0; /* Number of peaks */
1468
1469 if ((ev = k10_read_flicker_samples(p, NFMXTIME, &srate, &samp, &nfsamps, 1)) != inst_ok) {
1470 return ev;
1471 }
1472
1473 rsamp = 1.0/srate;
1474
1475 #ifdef PLOT_REFRESH
1476 /* Plot the raw sensor values */
1477 {
1478 double xx[NFSAMPS];
1479
1480 for (i = 0; i < nfsamps; i++)
1481 xx[i] = i * rsamp;
1482 printf("Fast scan sensor values and time (sec)\n");
1483 do_plot(xx, samp, NULL, NULL, nfsamps);
1484 }
1485 #endif /* PLOT_REFRESH */
1486
1487 /* Locate the smallest values and maximum time */
1488 maxt = -1e6;
1489 minv = minv = minv = 1e20;
1490 maxv = maxv = maxv = -11e20;
1491 for (i = nfsamps-1; i >= 0; i--) {
1492 if (samp[i] < minv)
1493 minv = samp[i];
1494 if (samp[i] > maxv)
1495 maxv = samp[i];
1496 }
1497 maxt = (nfsamps-1) * rsamp;
1498
1499 /* Zero offset the readings */
1500 for (i = nfsamps-1; i >= 0; i--)
1501 samp[i] -= minv;
1502
1503 #ifdef FREQ_SLOW_PRECISE /* Interp then autocorrelate */
1504
1505 /* Create PBPMS bins and interpolate readings into them */
1506 nbins = 1 + (int)(maxt * 1000.0 * PBPMS + 0.5);
1507 if ((bins = (double *)calloc(sizeof(double), nbins)) == NULL) {
1508 a1loge(p->log, inst_internal_error, "k10_imp_measure_refresh: malloc nbins %d failed\n",nbins);
1509 free(samp);
1510 return k10_interp_code(p, K10_INT_MALLOC);
1511 }
1512
1513 /* Do the interpolation */
1514 for (k = 0; k < (nfsamps-1); k++) {
1515 int sbin, ebin;
1516 double ksec = k * rsamp;
1517 double ksecp1 = (k+1) * rsamp;
1518 sbin = (int)(ksec * 1000.0 * PBPMS + 0.5);
1519 ebin = (int)(ksecp1 * 1000.0 * PBPMS + 0.5);
1520 for (i = sbin; i <= ebin; i++) {
1521 double bl;
1522 #if defined(__APPLE__) && defined(__POWERPC__)
1523 gcc_bug_fix(i);
1524 #endif
1525 bl = (i - sbin)/(double)(ebin - sbin); /* 0.0 to 1.0 */
1526 bins[i] = (1.0 - bl) * samp[k] + bl * samp[k+1];
1527 }
1528 }
1529
1530 #ifdef NEVER
1531
1532 /* Plot interpolated values */
1533 {
1534 double *xx = malloc(sizeof(double) * nbins);
1535
1536 if (xx == NULL) {
1537 a1loge(p->log, inst_internal_error, "k10_imp_measure_refresh: malloc plot nbins %d failed\n",nbins);
1538 free(samp);
1539 return k10_interp_code(p, K10_INT_MALLOC);
1540 }
1541 for (i = 0; i < nbins; i++)
1542 xx[i] = i / (double)PBPMS; /* msec */
1543 printf("Interpolated fast scan sensor values and time (msec)\n");
1544 do_plot(xx, bins, NULL, NULL, nbins);
1545 free(xx);
1546 }
1547 #endif /* NEVER */
1548
1549 /* Compute auto-correlation at 1/PBPMS msec intervals */
1550 /* from 25 msec (40Hz) to 100msec (10 Hz) */
1551 mincv = 1e48, maxcv = -1e48;
1552 for (i = 0; i < NPER; i++) {
1553 int poff = PERMIN + i; /* Offset to corresponding sample */
1554
1555 corr[i] = 0;
1556 for (k = 0; (k + poff) < nbins; k++)
1557 corr[i] += bins[k] * bins[k + poff];
1558 corr[i] /= (double)k; /* Normalize */
1559
1560 if (corr[i] > maxcv)
1561 maxcv = corr[i];
1562 if (corr[i] < mincv)
1563 mincv = corr[i];
1564 }
1565 /* Free the bins */
1566 free(bins);
1567
1568 #else /* !FREQ_SLOW_PRECISE Fast - autocorrellate then filter */
1569
1570 /* Do point by point correllation of samples */
1571 for (i = 0; i < NPER; i++) {
1572 tcorr[i] = 0.0;
1573 ntcorr[i] = 0;
1574 }
1575
1576 for (j = 0; j < (nfsamps-1); j++) {
1577
1578 for (k = j+1; k < nfsamps; k++) {
1579 double del, cor;
1580 int bix;
1581
1582 del = (k - j) * rsamp; /* Sample time delta */
1583 bix = (int)(del * 1000.0 * PBPMS + 0.5);
1584 if (bix < PERMIN)
1585 continue;
1586 if (bix > PERMAX)
1587 break;
1588 bix -= PERMIN;
1589
1590 cor = samp[j] * samp[k];
1591
1592 //printf("~1 j %d k %d, del %f bix %d cor %f\n",j,k,del,bix,cor);
1593 tcorr[bix] += cor;
1594 ntcorr[bix]++;
1595 }
1596 }
1597 /* Divide out count and linearly interpolate */
1598 j = 0;
1599 for (i = 0; i < NPER; i++) {
1600 if (ntcorr[i] > 0) {
1601 tcorr[i] /= ntcorr[i];
1602 if ((i - j) > 1) {
1603 if (j == 0) {
1604 for (k = j; k < i; k++)
1605 tcorr[k] = tcorr[i];
1606
1607 } else { /* Linearly interpolate from last value */
1608 double ww = (double)i-j;
1609 for (k = j+1; k < i; k++) {
1610 double bl = (k-j)/ww;
1611 tcorr[k] = (1.0 - bl) * tcorr[j] + bl * tcorr[i];
1612 }
1613 }
1614 }
1615 j = i;
1616 }
1617 }
1618 if (j < (NPER-1)) {
1619 for (k = j+1; k < NPER; k++) {
1620 tcorr[k] = tcorr[j];
1621 }
1622 }
1623
1624 #ifdef PLOT_REFRESH
1625 /* Plot unfiltered auto correlation */
1626 {
1627 double xx[NPER];
1628 double y1[NPER];
1629
1630 for (i = 0; i < NPER; i++) {
1631 xx[i] = (i + PERMIN) / (double)PBPMS; /* msec */
1632 y1[i] = tcorr[i];
1633 }
1634 printf("Unfiltered auto correlation (msec)\n");
1635 do_plot(xx, y1, NULL, NULL, NPER);
1636 }
1637 #endif /* PLOT_REFRESH */
1638
1639 /* Apply a gausian filter */
1640 #define FWIDTH 100
1641 {
1642 double gaus_[2 * FWIDTH * PBPMS + 1];
1643 double *gaus = &gaus_[FWIDTH * PBPMS];
1644 double bb = 1.0/pow(2, 5.0);
1645 double fw = rsamp * 1000.0;
1646 int ifw;
1647
1648 //printf("~1 sc = %f = %f msec\n",1.0/rsamp, fw);
1649 //printf("~1 fw = %f, ifw = %d\n",fw,ifw);
1650
1651 fw *= 0.9;
1652 ifw = (int)ceil(fw * PBPMS);
1653 if (ifw > FWIDTH * PBPMS)
1654 error("k10: Not enough space for lanczos 2 filter");
1655 for (j = -ifw; j <= ifw; j++) {
1656 double x, y;
1657 x = j/(PBPMS * fw);
1658 if (fabs(x) > 1.0)
1659 y = 0.0;
1660 else
1661 y = 1.0/pow(2, 5.0 * x * x) - bb;
1662 gaus[j] = y;
1663 //printf("~1 gaus[%d] = %f\n",j,y);
1664 }
1665
1666 for (i = 0; i < NPER; i++) {
1667 double sum = 0.0;
1668 double wght = 0.0;
1669
1670 for (j = -ifw; j <= ifw; j++) {
1671 double w;
1672 int ix = i + j;
1673 if (ix < 0)
1674 ix = -ix;
1675 if (ix > (NPER-1))
1676 ix = 2 * NPER-1 - ix;
1677 w = gaus[j];
1678 sum += w * tcorr[ix];
1679 wght += w;
1680 }
1681 //printf("~1 corr[%d] wgt = %f\n",i,wght);
1682 corr[i] = sum / wght;
1683 }
1684 }
1685
1686 /* Compute min & max */
1687 mincv = 1e48, maxcv = -1e48;
1688 for (i = 0; i < NPER; i++) {
1689 if (corr[i] > maxcv)
1690 maxcv = corr[i];
1691 if (corr[i] < mincv)
1692 mincv = corr[i];
1693 }
1694
1695 #endif /* !FREQ_SLOW_PRECISE Fast - autocorrellate then filter */
1696
1697 crange = maxcv - mincv;
1698 a1logd(p->log,3,"Correlation value range %f - %f = %f = %f%%\n",mincv, maxcv,crange, 100.0 * (maxcv-mincv)/maxcv);
1699
1700 #ifdef PLOT_REFRESH
1701 /* Plot this measuremnts auto correlation */
1702 {
1703 double xx[NPER];
1704 double y1[NPER];
1705
1706 for (i = 0; i < NPER; i++) {
1707 xx[i] = (i + PERMIN) / (double)PBPMS; /* msec */
1708 y1[i] = corr[i];
1709 }
1710 printf("Auto correlation (msec)\n");
1711 do_plot6(xx, y1, NULL, NULL, NULL, NULL, NULL, NPER);
1712 }
1713 #endif /* PLOT_REFRESH */
1714
1715 #define PFDB 4 // normally debug level 4
1716 /* If there is sufficient level and distict correlations */
1717 if (crange/maxcv >= 0.1) {
1718
1719 a1logd(p->log,PFDB,"Searching for peaks\n");
1720
1721 /* Locate all the peaks starting at the longest correllation */
1722 for (i = (NPER-1-PWIDTH); i >= 0 && npeaks < MAXPKS; i--) {
1723 double v1, v2, v3;
1724 v1 = corr[i];
1725 v2 = corr[i + PWIDTH/2]; /* Peak */
1726 v3 = corr[i + PWIDTH];
1727
1728 if (fabs(v3 - v1)/crange < 0.05
1729 && (v2 - v1)/crange > 0.025
1730 && (v2 - v3)/crange > 0.025
1731 && (v2 - mincv)/crange > 0.5) {
1732 double pkv; /* Peak value */
1733 int pki; /* Peak index */
1734 double ii, bl;
1735
1736 #ifdef PLOT_REFRESH
1737 a1logd(p->log,PFDB,"Max between %f and %f msec\n",
1738 (i + PERMIN)/(double)PBPMS,(i + PWIDTH + PERMIN)/(double)PBPMS);
1739 #endif
1740
1741 /* Locate the actual peak */
1742 pkv = -1.0;
1743 pki = 0;
1744 for (j = i; j < (i + PWIDTH); j++) {
1745 if (corr[j] > pkv) {
1746 pkv = corr[j];
1747 pki = j;
1748 }
1749 }
1750 #ifdef PLOT_REFRESH
1751 a1logd(p->log,PFDB,"Peak is at %f msec, %f corr\n", (pki + PERMIN)/(double)PBPMS, pkv);
1752 #endif
1753
1754 /* Interpolate the peak value for higher precision */
1755 /* j = bigest */
1756 if (corr[pki-1] > corr[pki+1]) {
1757 j = pki-1;
1758 k = pki+1;
1759 } else {
1760 j = pki+1;
1761 k = pki-1;
1762 }
1763 bl = (corr[pki] - corr[j])/(corr[pki] - corr[k]);
1764 bl = (bl + 1.0)/2.0;
1765 ii = bl * pki + (1.0 - bl) * j;
1766 pval = (ii + PERMIN)/(double)PBPMS;
1767 #ifdef PLOT_REFRESH
1768 a1logd(p->log,PFDB,"Interpolated peak is at %f msec\n", pval);
1769 #endif
1770 peaks[npeaks] = pval;
1771 peakh[npeaks] = corr[pki];
1772 npeaks++;
1773
1774 i -= PWIDTH;
1775 }
1776 #ifdef NEVER
1777 if (v2 > v1 && v2 > v3) {
1778 printf("Peak rejected:\n");
1779 printf("(v3 - v1)/crange = %f < 0.05 ?\n",fabs(v3 - v1)/crange);
1780 printf("(v2 - v1)/crange = %f > 0.025 ?\n",(v2 - v1)/crange);
1781 printf("(v2 - v3)/crange = %f > 0.025 ?\n",(v2 - v3)/crange);
1782 printf("(v2 - mincv)/crange = %f > 0.5 ?\n",(v2 - mincv)/crange);
1783 }
1784 #endif
1785 }
1786 a1logd(p->log,3,"Number of peaks located = %d\n",npeaks);
1787
1788 } else {
1789 a1logd(p->log,3,"All rejected, crange/maxcv = %f < 0.06\n",crange/maxcv);
1790 }
1791 #undef PFDB
1792
1793 a1logd(p->log,3,"Number of peaks located = %d\n",npeaks);
1794
1795 if (npeaks > 1) { /* Compute aparent refresh rate */
1796 int nfails;
1797 double div, avg, ano;
1798
1799 /* Try and locate a common divisor amongst all the peaks. */
1800 /* This is likely to be the underlying refresh rate. */
1801 for (k = 0; k < npeaks; k++) {
1802 for (j = 1; j < 25; j++) {
1803 avg = ano = 0.0;
1804 div = peaks[k]/(double)j;
1805 if (div < 5.0)
1806 continue; /* Skip anything higher than 200Hz */
1807 //printf("~1 trying %f Hz\n",1000.0/div);
1808 for (nfails = i = 0; i < npeaks; i++) {
1809 double rem, cnt;
1810
1811 rem = peaks[i]/div;
1812 cnt = floor(rem + 0.5);
1813 rem = fabs(rem - cnt);
1814
1815 #ifdef PLOT_REFRESH
1816 a1logd(p->log, 3, "remainder for peak %d = %f\n",i,rem);
1817 #endif
1818 if (rem > 0.06) {
1819 if (++nfails > 2)
1820 break; /* Fail this divisor */
1821 } else {
1822 avg += peaks[i]; /* Already weighted by cnt */
1823 ano += cnt;
1824 }
1825 }
1826
1827 if (nfails == 0 || (nfails <= 2 && npeaks >= 6))
1828 break; /* Sucess */
1829 /* else go and try a different divisor */
1830 }
1831 if (j < 25)
1832 break; /* Success - found common divisor */
1833 }
1834 if (k >= npeaks) {
1835 a1logd(p->log,3,"Failed to locate common divisor\n");
1836
1837 } else {
1838 pval = 1000.0 * ano/avg;
1839 if (pval > srate) {
1840 a1logd(p->log,3,"Discarding frequency %f > sample rate %f\n",pval, srate);
1841 } else {
1842 rfreq = pval;
1843 a1logd(p->log,3,"Located frequency %f sum %f dif %f\n",pval, pval + srate, fabs(pval - srate));
1844 tix++;
1845 }
1846 }
1847 }
1848
1849 if (tix) {
1850
1851 /* The Klein samples so fast, we don't have to deal with */
1852 /* sub Nyquist aliases. */
1853
1854 if (ref_rate != NULL)
1855 *ref_rate = rfreq;
1856
1857 /* Error against my 85Hz CRT - GWG */
1858 a1logd(p->log, 1, "Refresh rate %f Hz, error = %.4f%%\n",rfreq,100.0 * fabs(rfreq - 85.0)/(85.0));
1859 free(samp);
1860 return k10_interp_code(p, K10_OK);
1861
1862 } else {
1863 a1logd(p->log, 3, "Refresh rate was unclear\n");
1864 }
1865
1866 free(samp);
1867
1868 return k10_interp_code(p, K10_NOREFR_FOUND);
1869 }
1870 #undef NFSAMPS
1871 #undef PBPMS
1872 #undef PERMIN
1873 #undef PERMAX
1874 #undef NPER
1875 #undef PWIDTH
1876
1877 /* Read an emissive refresh rate */
1878 static inst_code
k10_read_refrate(inst * pp,double * ref_rate)1879 k10_read_refrate(
1880 inst *pp,
1881 double *ref_rate
1882 ) {
1883 kleink10 *p = (kleink10 *)pp;
1884 char buf[MAX_MES_SIZE];
1885 double refrate;
1886 inst_code rv;
1887
1888 if (!p->gotcoms)
1889 return inst_no_coms;
1890 if (!p->inited)
1891 return inst_no_init;
1892
1893 if (ref_rate != NULL)
1894 *ref_rate = 0.0;
1895
1896 if ((rv = k10_imp_measure_refresh(p, &refrate)) != inst_ok) {
1897 return rv;
1898 }
1899
1900 if (refrate == 0.0)
1901 return inst_misread;
1902
1903 if (ref_rate != NULL)
1904 *ref_rate = refrate;
1905
1906 return inst_ok;
1907 }
1908
1909 /* - - - - - - - - - - - - - - - - */
1910 /* Measure a display update delay. It is assumed that */
1911 /* white_stamp(init) has been called, and then a */
1912 /* white to black change has been made to the displayed color, */
1913 /* and this will measure the time it took for the update to */
1914 /* be noticed by the instrument, up to 2.0 seconds. */
1915 /* (It is assumed that white_change() will be called at the time the patch */
1916 /* changes color.) */
1917 /* inst_misread will be returned on failure to find a transition to black. */
1918
1919 #define NDSAMPS 40 /* Maximum samples */
1920 #define NDMXTIME 2.0 /* Maximum time to take */
1921
k10_meas_delay(inst * pp,int * pdispmsec,int * pinstmsec)1922 static inst_code k10_meas_delay(
1923 inst *pp,
1924 int *pdispmsec, /* Return display update delay in msec */
1925 int *pinstmsec) { /* Return instrument reaction time in msec */
1926 kleink10 *p = (kleink10 *)pp;
1927 inst_code ev;
1928 char mes[MAX_MES_SIZE];
1929 int bread;
1930 int i, j, k;
1931 double sutime, putime, cutime, eutime;
1932 struct {
1933 double sec;
1934 double xyz[3];
1935 } samp[NDSAMPS];
1936 int ndsamps;
1937 double stot, etot, del, thr;
1938 double stime, etime;
1939 int isdeb;
1940 int avgsampsp;
1941 int dispmsec, instmsec;
1942
1943 if (pinstmsec != NULL)
1944 *pinstmsec = -230;
1945
1946 if (!p->gotcoms)
1947 return inst_no_coms;
1948
1949 if (!p->inited)
1950 return inst_no_init;
1951
1952 if (usec_time() < 0.0) {
1953 a1loge(p->log, inst_internal_error, "k10_imp_meas_delay: No high resolution timers\n");
1954 return inst_internal_error;
1955 }
1956
1957 /* Turn debug off so that they doesn't intefere with measurement timing */
1958 isdeb = p->log->debug;
1959 p->icom->log->debug = 0;
1960
1961 /* Read the samples */
1962 putime = usec_time() / 1000000.0;
1963 amutex_lock(p->lock);
1964 for (i = 0; i < NDSAMPS; i++) {
1965
1966 /* Take a measurement to get ranges ? */
1967 if ((ev = k10_command(p, "N5\r", mes, MAX_MES_SIZE, &bread, 15, ec_ec, 2.0)) != inst_ok) {
1968 amutex_unlock(p->lock);
1969 p->log->debug = isdeb;
1970 a1logd(p->log, 1, "k10_meas_delay: measurement failed\n");
1971 return ev;
1972 }
1973
1974 if ((ev = decodeN5(p, samp[i].xyz, NULL, mes, bread)) != inst_ok) {
1975 amutex_unlock(p->lock);
1976 p->log->debug = isdeb;
1977 a1logd(p->log, 1, "k10_meas_delay: measurement decode failed\n");
1978 return ev;
1979 }
1980
1981 cutime = usec_time() / 1000000.0;
1982 // samp[i].sec = 0.5 * (putime + cutime); /* Mean of before and after stamp ? */
1983 samp[i].sec = cutime; /* Assume took until measure was received */
1984 // samp[i].sec = putime; /* Assume sampled at time triggered */
1985 putime = cutime;
1986 if (cutime > NDMXTIME)
1987 break;
1988 }
1989 ndsamps = i;
1990 amutex_unlock(p->lock);
1991
1992 /* Average sample spacing in msec */
1993 avgsampsp = (int)(1000.0 * (samp[i-1].sec - samp[0].sec)/(i-1.0) + 0.5);
1994
1995 /* Restore debugging */
1996 p->log->debug = isdeb;
1997
1998 if (ndsamps == 0) {
1999 a1logd(p->log, 1, "k10_meas_delay: No measurement samples returned in time\n");
2000 return inst_internal_error;
2001 }
2002
2003 if (p->whitestamp < 0.0) {
2004 a1logd(p->log, 1, "k10_meas_delay: White transition wasn't timestamped\n");
2005 return inst_internal_error;
2006 }
2007
2008 /* Set the times to be white transition relative */
2009 for (i = 0; i < ndsamps; i++)
2010 samp[i].sec -= p->whitestamp / 1000000.0;
2011
2012 /* Over the first 100msec, locate the maximum value */
2013 stime = samp[0].sec;
2014 stot = -1e9;
2015 for (i = 0; i < ndsamps; i++) {
2016 if (samp[i].xyz[1] > stot)
2017 stot = samp[i].xyz[1];
2018 if ((samp[i].sec - stime) > 0.1)
2019 break;
2020 }
2021
2022 /* Over the last 100msec, locate the maximum value */
2023 etime = samp[ndsamps-1].sec;
2024 etot = -1e9;
2025 for (i = ndsamps-1; i >= 0; i--) {
2026 if (samp[i].xyz[1] > etot)
2027 etot = samp[i].xyz[1];
2028 if ((etime - samp[i].sec) > 0.1)
2029 break;
2030 }
2031
2032 del = etot - stot;
2033 thr = etot - 0.10 * del; /* 10% of transition threshold */
2034
2035 #ifdef PLOT_UPDELAY
2036 a1logd(p->log, 0, "k10_meas_delay: start tot %f end tot %f del %f, thr %f\n", stot, etot, del, thr);
2037 #endif
2038
2039 #ifdef PLOT_UPDELAY
2040 /* Plot the raw sensor values */
2041 {
2042 double xx[NDSAMPS];
2043 double y1[NDSAMPS];
2044 double y2[NDSAMPS];
2045 double y3[NDSAMPS];
2046
2047 for (i = 0; i < ndsamps; i++) {
2048 xx[i] = samp[i].sec;
2049 y1[i] = samp[i].xyz[0];
2050 y2[i] = samp[i].xyz[1];
2051 y3[i] = samp[i].xyz[2];
2052 }
2053 printf("Display update delay measure sensor values and time (sec)\n");
2054 do_plot(xx, y1, y2, y3, ndsamps);
2055 }
2056 #endif
2057
2058 /* Check that there has been a transition */
2059 if (del < (0.7 * etot)) {
2060 a1logd(p->log, 1, "k10_meas_delay: can't detect change from black to white\n");
2061 return inst_misread;
2062 }
2063
2064 /* Working from the start, locate the time at which the level was above the threshold */
2065 for (i = 0; i < (ndsamps-1); i++) {
2066 if (samp[i].xyz[1] > thr)
2067 break;
2068 }
2069
2070 a1logd(p->log, 2, "k10_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec);
2071
2072 /* Compute overall delay */
2073 dispmsec = (int)(samp[i].sec * 1000.0 + 0.5);
2074
2075 /* The 20 Hz filter is probably a FIR which introduces a delay in */
2076 /* the samples being measured, creating both a settling delay and */
2077 /* a look ahead. A negative inst. reaction time value will cause the */
2078 /* patch_delay to be extended by that amount of time. */
2079 /* We assume 2 samples times to settle, but round up the patch */
2080 /* delay conservatively. */
2081 instmsec = -2 * avgsampsp;
2082
2083 #ifdef PLOT_UPDELAY
2084 a1logd(p->log, 0, "k10_meas_delay: raw %d & %d msec\n",dispmsec,instmsec);
2085 #endif
2086
2087 dispmsec += instmsec; /* Account for lookahead */
2088
2089 if (dispmsec < 0) /* This can happen if the patch generator delays it's return */
2090 dispmsec = 0;
2091
2092 /* Round the patch delay to to next highest avgsampsp */
2093 dispmsec = (int)((1.0 + floor((double)dispmsec/(double)avgsampsp)) * avgsampsp + 0.5);
2094
2095 if (pdispmsec != NULL)
2096 *pdispmsec = dispmsec;
2097
2098 if (pinstmsec != NULL)
2099 *pinstmsec = instmsec;
2100
2101 #ifdef PLOT_UPDELAY
2102 a1logd(p->log, 0, "k10_meas_delay: returning %d & %d msec\n",dispmsec,instmsec);
2103 #endif
2104
2105 return inst_ok;
2106 }
2107 #undef NDSAMPS
2108 #undef DINTT
2109 #undef NDMXTIME
2110
2111
2112 /* Timestamp the white patch change during meas_delay() */
k10_white_change(inst * pp,int init)2113 static inst_code k10_white_change(
2114 inst *pp,
2115 int init) {
2116 kleink10 *p = (kleink10 *)pp;
2117 inst_code ev;
2118
2119 if (init)
2120 p->whitestamp = -1.0;
2121 else {
2122 if ((p->whitestamp = usec_time()) < 0.0) {
2123 a1loge(p->log, inst_internal_error, "k10_wite_changeO: No high resolution timers\n");
2124 return inst_internal_error;
2125 }
2126 }
2127
2128 return inst_ok;
2129 }
2130
2131 /* - - - - - - - - - - - - - - - - */
2132
2133 /* Do a black calibration */
2134 static inst_code
k10_do_black_cal(kleink10 * p)2135 k10_do_black_cal(
2136 kleink10 *p
2137 ) {
2138 inst_code ev;
2139 char mes[MAX_MES_SIZE];
2140 unsigned char *umes = (unsigned char *)mes;
2141 int bread;
2142 int i, j, k;
2143 int val, th1, th2;
2144 int bvals[6][3]; /* Black values for range 1 to 6 */
2145 int thermal; /* Thermal value */
2146
2147 amutex_lock(p->lock);
2148
2149 /* First get the Measure Count to check that TH1 and TH2 are between 50 and 200 */
2150 /* (Don't know why or what these mean - something to do with temperature compensation */
2151 /* values not being setup ?) */
2152 if ((ev = k10_command(p, "M6\r", mes, MAX_MES_SIZE, &bread, 20, ec_e, 2.0)) != inst_ok) {
2153 amutex_unlock(p->lock);
2154 a1logd(p->log, 1, "k10_do_black_cal: M6 failed\n");
2155 return ev;
2156 }
2157
2158 if (bread < 17) {
2159 amutex_unlock(p->lock);
2160 a1logd(p->log, 1, "k10_do_black_cal: not enough bytes returned from M6 (%d)\n",bread);
2161 return inst_protocol_error;
2162 }
2163
2164 th1 = umes[14];
2165 th2 = umes[15];
2166
2167 if (th1 < 50 || th1 > 200
2168 || th2 < 50 || th2 > 200) {
2169 amutex_unlock(p->lock);
2170 a1logd(p->log, 1, "th1 %d or th2 %d is out of range 50-200\n",th1,th2);
2171 return k10_interp_code(p, K10_BLACK_CAL_INIT);
2172 }
2173
2174 /* Do the black calibration */
2175 if ((ev = k10_command(p, "B9\r", mes, MAX_MES_SIZE, &bread, 43, ec_ec, 15.0)) != inst_ok) {
2176 a1logd(p->log, 1, "k10_do_black_cal: B9 failed\n");
2177 amutex_unlock(p->lock);
2178 return ev;
2179 }
2180
2181 if (bread < 40) {
2182 amutex_unlock(p->lock);
2183 a1logd(p->log, 1, "k10_do_black_cal: not enough bytes returned from B9 (%d)\n",bread);
2184 return inst_protocol_error;
2185 }
2186
2187 /* Parse the black values that resulted */
2188 for (k = i = 0; i < 6; i++) {
2189 for (j = 0; j < 3; j++, k++) {
2190 val = umes[2 + 2 * k] * 256 + umes[2 + 2 * k + 1];
2191 if (val < 500 || val > 2500) {
2192 amutex_unlock(p->lock);
2193 a1logd(p->log, 1, "k10_do_black_cal: B9 black result value out of range\n");
2194 return k10_interp_code(p, K10_BLACK_CAL_FAIL);
2195 }
2196 bvals[i][j] = val;
2197 }
2198 }
2199 val = umes[2 + 2 * k] * 256 + umes[2 + 2 * k + 1];
2200 if (val < 500 || val > 2500) {
2201 amutex_unlock(p->lock);
2202 a1logd(p->log, 1, "k10_do_black_cal: B9 black thermal result value out of range\n");
2203 return k10_interp_code(p, K10_BLACK_CAL_FAIL);
2204 }
2205 thermal = val;
2206
2207 if (p->log->debug >= 4) {
2208 for (i = 0; i < 6; i++)
2209 a1logd(p->log, 4, "Black cal. Range %d XYZ = %d %d %d\n",
2210 i+1, bvals[i][0], bvals[i][1], bvals[i][2]);
2211 a1logd(p->log, 4, "Thermal %d\n",thermal);
2212 }
2213
2214 /* All looks well - copy into Flash ROM */
2215 if ((ev = k10_command(p, "B7\r", mes, MAX_MES_SIZE, &bread, 2, ec_c, 2.0)) != inst_ok) {
2216 amutex_unlock(p->lock);
2217 a1logd(p->log, 1, "k10_do_black_cal: B7 failed\n");
2218 return ev;
2219 }
2220
2221 /* Send verification code and get error code*/
2222 if ((ev = k10_command(p, "{00000000}@%#\r", mes, MAX_MES_SIZE, &bread, 3, ec_e, 2.0)) != inst_ok) {
2223 amutex_unlock(p->lock);
2224 a1logd(p->log, 1, "k10_do_black_cal: B7 followup failed\n");
2225 return ev;
2226 }
2227 amutex_unlock(p->lock);
2228
2229 a1logd(p->log, 4, "k10_do_black_cal: Done\n");
2230
2231 return inst_ok;
2232 }
2233
2234 /* - - - - - - - - - - - - - - - - */
2235
2236 /* Return needed and available inst_cal_type's */
k10_get_n_a_cals(inst * pp,inst_cal_type * pn_cals,inst_cal_type * pa_cals)2237 static inst_code k10_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
2238 kleink10 *p = (kleink10 *)pp;
2239 inst_cal_type n_cals = inst_calt_none;
2240 inst_cal_type a_cals = inst_calt_none;
2241
2242 /* Can do a black cal, but not required */
2243 a_cals |= inst_calt_emis_offset;
2244
2245 if (pn_cals != NULL)
2246 *pn_cals = n_cals;
2247
2248 if (pa_cals != NULL)
2249 *pa_cals = a_cals;
2250
2251 return inst_ok;
2252 }
2253
2254 /* Request an instrument calibration. */
k10_calibrate(inst * pp,inst_cal_type * calt,inst_cal_cond * calc,inst_calc_id_type * idtype,char id[CALIDLEN])2255 inst_code k10_calibrate(
2256 inst *pp,
2257 inst_cal_type *calt, /* Calibration type to do/remaining */
2258 inst_cal_cond *calc, /* Current condition/desired condition */
2259 inst_calc_id_type *idtype, /* Condition identifier type */
2260 char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */
2261 ) {
2262 kleink10 *p = (kleink10 *)pp;
2263 inst_code ev;
2264 inst_cal_type needed, available;
2265
2266 if (!p->gotcoms)
2267 return inst_no_coms;
2268
2269 if (!p->inited)
2270 return inst_no_init;
2271
2272 *idtype = inst_calc_id_none;
2273 id[0] = '\000';
2274
2275 if ((ev = k10_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok)
2276 return ev;
2277
2278 /* Translate inst_calt_all/needed into something specific */
2279 if (*calt == inst_calt_all
2280 || *calt == inst_calt_needed
2281 || *calt == inst_calt_available) {
2282 if (*calt == inst_calt_all)
2283 *calt = (needed & inst_calt_n_dfrble_mask) | inst_calt_ap_flag;
2284 else if (*calt == inst_calt_needed)
2285 *calt = needed & inst_calt_n_dfrble_mask;
2286 else if (*calt == inst_calt_available)
2287 *calt = available & inst_calt_n_dfrble_mask;
2288
2289 a1logd(p->log,4,"k10_calibrate: doing calt 0x%x\n",calt);
2290
2291 if ((*calt & inst_calt_n_dfrble_mask) == 0) /* Nothing todo */
2292 return inst_ok;
2293 }
2294
2295 /* See if it's a calibration we understand */
2296 if (*calt & ~available & inst_calt_all_mask) {
2297 return inst_unsupported;
2298 }
2299
2300 /* Do the appropriate calibration */
2301 if (*calt & inst_calt_emis_offset) {
2302
2303 if ((*calc & inst_calc_cond_mask) != inst_calc_man_em_dark) {
2304 *calc = inst_calc_man_em_dark;
2305 return inst_cal_setup;
2306 }
2307
2308 /* Do black offset calibration */
2309 if ((ev = k10_do_black_cal(p)) != inst_ok)
2310 return ev;
2311
2312 *calt &= ~inst_calc_man_em_dark;
2313 }
2314 return inst_ok;
2315 }
2316
2317 /* Error codes interpretation */
2318 static char *
k10_interp_error(inst * pp,int ec)2319 k10_interp_error(inst *pp, int ec) {
2320 kleink10 *p = (kleink10 *)pp;
2321 ec &= inst_imask;
2322 switch (ec) {
2323 case K10_INTERNAL_ERROR:
2324 return "Internal software error";
2325 case K10_TIMEOUT:
2326 return "Communications timeout";
2327 case K10_COMS_FAIL:
2328 return "Communications failure";
2329 case K10_UNKNOWN_MODEL:
2330 return "Not a Klein K10";
2331 case K10_DATA_PARSE_ERROR:
2332 return "Data from kleink10 didn't parse as expected";
2333 // case K10_SPOS_EMIS:
2334 // return "Ambient filter should be removed";
2335 // case K10_SPOS_AMB:
2336 // return "Ambient filter should be used";
2337
2338 case K10_OK:
2339 return "No device error";
2340
2341 case K10_CMD_VERIFY:
2342 return "Instrument didn't echo command code";
2343 case K10_BAD_RETVAL:
2344 return "Unable to parse return instruction return code";
2345
2346 case K10_FIRMWARE:
2347 return "Firmware error";
2348
2349 case K10_BLACK_EXCESS:
2350 return "Black Excessive";
2351 case K10_BLACK_OVERDRIVE:
2352 return "Black Overdrive";
2353 case K10_BLACK_ZERO:
2354 return "Black Zero";
2355
2356 case K10_OVER_HIGH_RANGE:
2357 return "Over High Range";
2358 case K10_TOP_OVER_RANGE:
2359 return "Top over range";
2360 case K10_BOT_UNDER_RANGE:
2361 return "Bottom under range";
2362 case K10_AIMING_LIGHTS:
2363 return "Aiming lights on when measuring";
2364
2365 case K10_UNKNOWN:
2366 return "Unknown error from instrument";
2367
2368 case K10_INT_MALLOC:
2369 return "Memory allocation failure";
2370
2371 case K10_NOREFR_FOUND:
2372 return "No refresh rate detected or failed to measure it";
2373
2374 case K10_NOTRANS_FOUND:
2375 return "No delay measurment transition found";
2376
2377 case K10_RANGE_CHANGE:
2378 return "Range changed during measurement";
2379
2380 case K10_BLACK_CAL_INIT:
2381 return "Instrument hasn't been setup for black calibration";
2382 case K10_BLACK_CAL_FAIL:
2383 return "Black calibration failed";
2384
2385 default:
2386 return "Unknown error code";
2387 }
2388 }
2389
2390
2391 /* Convert a machine specific error code into an abstract dtp code */
2392 static inst_code
k10_interp_code(kleink10 * p,int ec)2393 k10_interp_code(kleink10 *p, int ec) {
2394
2395 ec &= inst_imask;
2396 switch (ec) {
2397
2398 case K10_OK:
2399 return inst_ok;
2400
2401 case K10_INTERNAL_ERROR:
2402 case K10_AIMING_LIGHTS:
2403 case K10_UNKNOWN:
2404 case K10_INT_MALLOC:
2405 return inst_internal_error | ec;
2406
2407 case K10_TIMEOUT:
2408 case K10_COMS_FAIL:
2409 return inst_coms_fail | ec;
2410
2411 case K10_UNKNOWN_MODEL:
2412 return inst_unknown_model | ec;
2413
2414 case K10_CMD_VERIFY:
2415 case K10_BAD_RETVAL:
2416 case K10_DATA_PARSE_ERROR:
2417 return inst_protocol_error | ec;
2418
2419 case K10_FIRMWARE:
2420 case K10_BLACK_EXCESS: // ?
2421 case K10_BLACK_OVERDRIVE: // ?
2422 case K10_BLACK_ZERO: // ?
2423 case K10_BLACK_CAL_INIT:
2424 return inst_hardware_fail | ec;
2425
2426 case K10_OVER_HIGH_RANGE:
2427 case K10_TOP_OVER_RANGE:
2428 case K10_BOT_UNDER_RANGE:
2429 case K10_NOREFR_FOUND:
2430 case K10_NOTRANS_FOUND:
2431 case K10_RANGE_CHANGE:
2432 case K10_BLACK_CAL_FAIL:
2433 return inst_misread | ec;
2434
2435 }
2436 return inst_other_error | ec;
2437 }
2438
2439 /* Destroy ourselves */
2440 static void
k10_del(inst * pp)2441 k10_del(inst *pp) {
2442 if (pp != NULL) {
2443 kleink10 *p = (kleink10 *)pp;
2444 if (p->icom != NULL)
2445 p->icom->del(p->icom);
2446 amutex_del(p->lock);
2447 p->vdel(pp);
2448 free(p);
2449 }
2450 }
2451
2452 /* Return the instrument mode capabilities */
k10_capabilities(inst * pp,inst_mode * pcap1,inst2_capability * pcap2,inst3_capability * pcap3)2453 static void k10_capabilities(inst *pp,
2454 inst_mode *pcap1,
2455 inst2_capability *pcap2,
2456 inst3_capability *pcap3) {
2457 kleink10 *p = (kleink10 *)pp;
2458 inst_mode cap1 = 0;
2459 inst2_capability cap2 = 0;
2460
2461 cap1 |= inst_mode_emis_tele
2462 | inst_mode_emis_spot
2463 | inst_mode_ambient /* But cc matrix is up to user */
2464 | inst_mode_emis_nonadaptive
2465 | inst_mode_colorimeter
2466 ;
2467
2468 /* can inst2_has_sensmode, but not report it asynchronously */
2469 cap2 |= inst2_prog_trig
2470 | inst2_user_trig
2471 | inst2_disptype
2472 | inst2_has_target /* Has target lights */
2473 | inst2_ccmx
2474 | inst2_emis_refr_meas
2475 | inst2_meas_disp_update
2476 ;
2477
2478
2479 if (pcap1 != NULL)
2480 *pcap1 = cap1;
2481 if (pcap2 != NULL)
2482 *pcap2 = cap2;
2483 if (pcap3 != NULL)
2484 *pcap3 = inst3_none;
2485 }
2486
2487 /* Check device measurement mode */
k10_check_mode(inst * pp,inst_mode m)2488 static inst_code k10_check_mode(inst *pp, inst_mode m) {
2489 inst_mode cap;
2490
2491 if (!pp->gotcoms)
2492 return inst_no_coms;
2493 if (!pp->inited)
2494 return inst_no_init;
2495
2496 pp->capabilities(pp, &cap, NULL, NULL);
2497
2498 /* Simple test */
2499 if (m & ~cap)
2500 return inst_unsupported;
2501
2502 if (!IMODETST(m, inst_mode_emis_spot)
2503 && !IMODETST(m, inst_mode_emis_tele)
2504 && !IMODETST(m, inst_mode_emis_ambient)) {
2505 return inst_unsupported;
2506 }
2507
2508 return inst_ok;
2509 }
2510
2511 /* Set device measurement mode */
k10_set_mode(inst * pp,inst_mode m)2512 static inst_code k10_set_mode(inst *pp, inst_mode m) {
2513 kleink10 *p = (kleink10 *)pp;
2514 int refrmode;
2515 inst_code ev;
2516
2517 if ((ev = k10_check_mode(pp, m)) != inst_ok)
2518 return ev;
2519
2520 p->mode = m;
2521
2522 return inst_ok;
2523 }
2524
2525 /* This table gets extended on initialisation */
2526 /* There is 1 factory + 96 programmable + end marker */
2527 static inst_disptypesel k10_disptypesel[98] = {
2528 {
2529 inst_dtflags_default | inst_dtflags_mtx, /* flags */
2530 1, /* cbid */
2531 "F", /* sel */
2532 "Factory Default", /* desc */
2533 0, /* refr */
2534 disptech_unknown, /* disptype */
2535 0 /* ix */
2536 },
2537 {
2538 inst_dtflags_end,
2539 0,
2540 "",
2541 "",
2542 0,
2543 disptech_none,
2544 0
2545 }
2546 };
2547
2548 /* Get mode and option details */
k10_get_disptypesel(inst * pp,int * pnsels,inst_disptypesel ** psels,int allconfig,int recreate)2549 static inst_code k10_get_disptypesel(
2550 inst *pp,
2551 int *pnsels, /* Return number of display types */
2552 inst_disptypesel **psels, /* Return the array of display types */
2553 int allconfig, /* nz to return list for all configs, not just current. */
2554 int recreate /* nz to re-check for new ccmx & ccss files */
2555 ) {
2556 kleink10 *p = (kleink10 *)pp;
2557 inst_code rv = inst_ok;
2558
2559 /* Create/Re-create a current list of available display types */
2560 if (p->dtlist == NULL || recreate) {
2561 if ((rv = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist,
2562 k10_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
2563 return rv;
2564 }
2565
2566 if (pnsels != NULL)
2567 *pnsels = p->ndtlist;
2568
2569 if (psels != NULL)
2570 *psels = p->dtlist;
2571
2572 return inst_ok;
2573 }
2574
2575 /* Given a display type entry, setup calibration from that type */
set_disp_type(kleink10 * p,inst_disptypesel * dentry)2576 static inst_code set_disp_type(kleink10 *p, inst_disptypesel *dentry) {
2577
2578 /* If aninbuilt matrix hasn't been read from the instrument, */
2579 /* read it now. */
2580 if ((dentry->flags & inst_dtflags_mtx)
2581 && (dentry->flags & inst_dtflags_ld) == 0) {
2582 inst_code rv;
2583 if ((rv = k10_read_cal_matrix(p, dentry, dentry->ix)) != inst_ok)
2584 return rv;
2585 }
2586
2587 if (dentry->flags & inst_dtflags_ccmx) {
2588 if (dentry->cc_cbid != 1) {
2589 a1loge(p->log, 1, "k10: matrix must use cbid 1!\n",dentry->cc_cbid);
2590 return inst_wrong_setup;
2591 }
2592
2593 p->dtech = dentry->dtech;
2594 icmCpy3x3(p->ccmat, dentry->mat);
2595 p->cbid = 0; /* Can't be a base type now */
2596
2597 } else {
2598 p->dtech = dentry->dtech;
2599 icmCpy3x3(p->ccmat, dentry->mat);
2600 p->cbid = dentry->cbid;
2601 p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */
2602 }
2603
2604 if (p->log->debug >= 4) {
2605 a1logd(p->log,4,"ccmat = %f %f %f\n",
2606 p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
2607 a1logd(p->log,4," %f %f %f\n",
2608 p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]);
2609 a1logd(p->log,4," %f %f %f\n\n",
2610 p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
2611 a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid);
2612 a1logd(p->log,4,"\n");
2613 }
2614
2615 return inst_ok;
2616 }
2617
2618 /* Setup the default display type */
set_default_disp_type(kleink10 * p)2619 static inst_code set_default_disp_type(kleink10 *p) {
2620 inst_code ev;
2621 int i;
2622
2623 if (p->dtlist == NULL) {
2624 if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
2625 k10_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
2626 return ev;
2627 }
2628
2629 for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) {
2630 if (p->dtlist[i].flags & inst_dtflags_default)
2631 break;
2632 }
2633 if (p->dtlist[i].flags & inst_dtflags_end) {
2634 a1loge(p->log, 1, "set_default_disp_type: failed to find type!\n");
2635 return inst_internal_error;
2636 }
2637 if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) {
2638 return ev;
2639 }
2640
2641 return inst_ok;
2642 }
2643
2644 /* Set the display type */
k10_set_disptype(inst * pp,int ix)2645 static inst_code k10_set_disptype(inst *pp, int ix) {
2646 kleink10 *p = (kleink10 *)pp;
2647 inst_code ev;
2648 inst_disptypesel *dentry;
2649
2650 if (!p->gotcoms)
2651 return inst_no_coms;
2652 if (!p->inited)
2653 return inst_no_init;
2654
2655 if (p->dtlist == NULL) {
2656 if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
2657 k10_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
2658 return ev;
2659 }
2660
2661 if (ix < 0 || ix >= p->ndtlist)
2662 return inst_unsupported;
2663
2664 dentry = &p->dtlist[ix];
2665
2666 if ((ev = set_disp_type(p, dentry)) != inst_ok) {
2667 return ev;
2668 }
2669
2670 return inst_ok;
2671 }
2672
2673 /* Get the disptech and other corresponding info for the current */
2674 /* selected display type. Returns disptype_unknown by default. */
2675 /* Because refrmode can be overridden, it may not match the refrmode */
2676 /* of the dtech. (Pointers may be NULL if not needed) */
k10_get_disptechi(inst * pp,disptech * dtech,int * refrmode,int * cbid)2677 static inst_code k10_get_disptechi(
2678 inst *pp,
2679 disptech *dtech,
2680 int *refrmode,
2681 int *cbid) {
2682 kleink10 *p = (kleink10 *)pp;
2683 if (dtech != NULL)
2684 *dtech = p->dtech;
2685 if (refrmode != NULL)
2686 *refrmode = disptech_get_id(disptech_unknown)->refr;
2687 if (cbid != NULL)
2688 *cbid = p->cbid;
2689 return inst_ok;
2690 }
2691
2692 /* Insert a colorimetric correction matrix in the instrument XYZ readings */
2693 /* This is only valid for colorimetric instruments. */
2694 /* To remove the matrix, pass NULL for the filter filename */
k10_col_cor_mat(inst * pp,disptech dtech,int cbid,double mtx[3][3])2695 inst_code k10_col_cor_mat(
2696 inst *pp,
2697 disptech dtech, /* Use disptech_unknown if not known */ \
2698 int cbid, /* Calibration display type base ID, 1 if unknown */\
2699 double mtx[3][3]
2700 ) {
2701 kleink10 *p = (kleink10 *)pp;
2702
2703 if (!p->gotcoms)
2704 return inst_no_coms;
2705 if (!p->inited)
2706 return inst_no_init;
2707
2708 /* We don't have to set the base type since the instrument always returns factory */
2709 if (cbid != 1) {
2710 a1loge(p->log, 1, "k10: matrix must use cbid 1!\n",cbid);
2711 return inst_wrong_setup;
2712 }
2713
2714 if (mtx == NULL) {
2715 icmSetUnity3x3(p->ccmat);
2716 } else {
2717 icmCpy3x3(p->ccmat, mtx);
2718 }
2719
2720 p->dtech = dtech;
2721 p->cbid = 0;
2722
2723 if (p->log->debug >= 4) {
2724 a1logd(p->log,4,"ccmat = %f %f %f\n",
2725 p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
2726 a1logd(p->log,4," %f %f %f\n",
2727 p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]);
2728 a1logd(p->log,4," %f %f %f\n\n",
2729 p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
2730 a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid);
2731 a1logd(p->log,4,"\n");
2732 }
2733
2734 return inst_ok;
2735 }
2736
2737 /*
2738 * set or reset an optional mode
2739 *
2740 * Some options talk to the instrument, and these will
2741 * error if it hasn't been initialised.
2742 */
2743 static inst_code
k10_get_set_opt(inst * pp,inst_opt_type m,...)2744 k10_get_set_opt(inst *pp, inst_opt_type m, ...) {
2745 kleink10 *p = (kleink10 *)pp;
2746 char buf[MAX_MES_SIZE];
2747 int se;
2748
2749 a1logd(p->log, 5, "k10_get_set_opt: opt type 0x%x\n",m);
2750
2751 /* Record the trigger mode */
2752 if (m == inst_opt_trig_prog
2753 || m == inst_opt_trig_user) {
2754 p->trig = m;
2755 return inst_ok;
2756 }
2757
2758 if (!p->gotcoms)
2759 return inst_no_coms;
2760 if (!p->inited)
2761 return inst_no_init;
2762
2763 /* Get target light state */
2764 if (m == inst_opt_get_target_state) {
2765 va_list args;
2766 int *pstate, lstate = 0;
2767
2768 va_start(args, m);
2769 pstate = va_arg(args, int *);
2770 va_end(args);
2771
2772 if (pstate != NULL)
2773 *pstate = p->lights;
2774
2775 return inst_ok;
2776
2777 /* Set target light state */
2778 } else if (m == inst_opt_set_target_state) {
2779 inst_code ev;
2780 va_list args;
2781 int state = 0;
2782
2783 va_start(args, m);
2784 state = va_arg(args, int);
2785 va_end(args);
2786
2787 amutex_lock(p->lock);
2788
2789 if (state == 2) { /* Toggle */
2790 if (p->lights)
2791 state = 0;
2792 else
2793 state = 1;
2794 }
2795
2796 if (state == 1) { /* Turn on */
2797 if ((ev = k10_command(p, "L1\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 0.5)) != inst_ok
2798 /* Strangely the L0/1 command mat return irrelevant error codes... */
2799 && (ev & inst_imask) != K10_UNKNOWN
2800 && (ev & inst_imask) != K10_BLACK_EXCESS
2801 && (ev & inst_imask) != K10_BLACK_OVERDRIVE
2802 && (ev & inst_imask) != K10_BLACK_ZERO
2803 && (ev & inst_imask) != K10_OVER_HIGH_RANGE
2804 && (ev & inst_imask) != K10_TOP_OVER_RANGE
2805 && (ev & inst_imask) != K10_BOT_UNDER_RANGE) {
2806 amutex_unlock(p->lock);
2807 a1logd(p->log, 1, "k10_get_set_opt: L1 failed\n");
2808 return ev;
2809 }
2810 p->lights = 1;
2811 } else if (state == 0) { /* Turn off */
2812 if ((ev = k10_command(p, "L0\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 0.5)) != inst_ok
2813 /* Strangely the L0/1 command mat return irrelevant error codes... */
2814 && (ev & inst_imask) != K10_UNKNOWN
2815 && (ev & inst_imask) != K10_BLACK_EXCESS
2816 && (ev & inst_imask) != K10_BLACK_OVERDRIVE
2817 && (ev & inst_imask) != K10_BLACK_ZERO
2818 && (ev & inst_imask) != K10_OVER_HIGH_RANGE
2819 && (ev & inst_imask) != K10_TOP_OVER_RANGE
2820 && (ev & inst_imask) != K10_BOT_UNDER_RANGE) {
2821 amutex_unlock(p->lock);
2822 a1logd(p->log, 1, "k10_get_set_opt: L0 failed\n");
2823 return ev;
2824 }
2825 p->lights = 0;
2826 }
2827 amutex_unlock(p->lock);
2828 return inst_ok;
2829 }
2830
2831 /* Use default implementation of other inst_opt_type's */
2832 {
2833 inst_code rv;
2834 va_list args;
2835
2836 va_start(args, m);
2837 rv = inst_get_set_opt_def(pp, m, args);
2838 va_end(args);
2839
2840 return rv;
2841 }
2842 }
2843
2844 /* Constructor */
new_kleink10(icoms * icom,instType itype)2845 extern kleink10 *new_kleink10(icoms *icom, instType itype) {
2846 kleink10 *p;
2847 if ((p = (kleink10 *)calloc(sizeof(kleink10),1)) == NULL) {
2848 a1loge(icom->log, 1, "new_kleink10: malloc failed!\n");
2849 return NULL;
2850 }
2851
2852 p->log = new_a1log_d(icom->log);
2853
2854 p->init_coms = k10_init_coms;
2855 p->init_inst = k10_init_inst;
2856 p->capabilities = k10_capabilities;
2857 p->check_mode = k10_check_mode;
2858 p->set_mode = k10_set_mode;
2859 p->get_disptypesel = k10_get_disptypesel;
2860 p->set_disptype = k10_set_disptype;
2861 p->get_disptechi = k10_get_disptechi;
2862 p->get_set_opt = k10_get_set_opt;
2863 p->read_sample = k10_read_sample;
2864 p->read_refrate = k10_read_refrate;
2865 p->get_n_a_cals = k10_get_n_a_cals;
2866 p->calibrate = k10_calibrate;
2867 p->col_cor_mat = k10_col_cor_mat;
2868 p->meas_delay = k10_meas_delay;
2869 p->white_change = k10_white_change;
2870 p->interp_error = k10_interp_error;
2871 p->del = k10_del;
2872
2873 p->icom = icom;
2874 p->itype = itype;
2875 p->dtech = disptech_unknown;
2876
2877 amutex_init(p->lock);
2878
2879 /* Attempt to get the calibration list */
2880 k10_init_coms((inst *)p, baud_nc, fc_nc, 0.0);
2881
2882 return p;
2883 }
2884
2885