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