1 
2 /*
3  * Argyll Color Correction System
4  *
5  * Gretag i1Monitor & i1Pro related functions
6  */
7 
8 /*
9  * Author: Graeme W. Gill
10  * Date:   24/11/2006
11  *
12  * Copyright 2006 - 2014, Graeme W. Gill
13  * All rights reserved.
14  *
15  * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
16  * see the License2.txt file for licencing details.
17  */
18 
19 /*
20    If you make use of the instrument driver code here, please note
21    that it is the author(s) of the code who take responsibility
22    for its operation. Any problems or queries regarding driving
23    instruments with the Argyll drivers, should be directed to
24    the Argyll's author(s), and not to any other party.
25 
26    If there is some instrument feature or function that you
27    would like supported here, it is recommended that you
28    contact Argyll's author(s) first, rather than attempt to
29    modify the software yourself, if you don't have firm knowledge
30    of the instrument communicate protocols. There is a chance
31    that an instrument could be damaged by an incautious command
32    sequence, and the instrument companies generally cannot and
33    will not support developers that they have not qualified
34    and agreed to support.
35  */
36 
37 /*
38 	TTBD
39 
40 	There may be a bug in HiRes emissive calibration - if you do an initial
41 	cal. in normal, then switch to HiRes, no white plate cal is performed.
42 	Is it using a previous cal result for this ?
43 
44 	Should add extra filter compensation support.
45 
46 	Should alias projector mode to display mode ??
47 
48  */
49 
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <ctype.h>
53 #include <string.h>
54 #include <time.h>
55 #include <stdarg.h>
56 #include <math.h>
57 #ifndef SALONEINSTLIB
58 #include "copyright.h"
59 #include "aconfig.h"
60 #include "numlib.h"
61 #include "rspl.h"
62 #else /* SALONEINSTLIB */
63 #include "sa_config.h"
64 #include "numsup.h"
65 #include "rspl1.h"
66 #endif /* SALONEINSTLIB */
67 #include "xspect.h"
68 #include "insttypes.h"
69 #include "conv.h"
70 #include "icoms.h"
71 #include "i1pro.h"
72 #include "i1pro_imp.h"
73 
74 #define MAX_MES_SIZE 500		/* Maximum normal message reply size */
75 #define MAX_RD_SIZE 5000		/* Maximum reading messagle reply size */
76 
77 /* Convert a machine specific error code into an abstract inst code */
78 static inst_code i1pro_interp_code(i1pro *p, i1pro_code ec);
79 
80 /* ------------------------------------------------------------------------ */
81 
82 /* Establish communications with a I1DISP */
83 /* If it's a serial port, use the baud rate given, and timeout in to secs */
84 /* Return I1PRO_COMS_FAIL on failure to establish communications */
85 static inst_code
i1pro_init_coms(inst * pp,baud_rate br,flow_control fc,double tout)86 i1pro_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
87 	i1pro *p = (i1pro *) pp;
88 	int rsize, se;
89 	icomuflags usbflags = icomuf_none;
90 #ifdef UNIX_APPLE
91 	/* If the X-Rite software has been installed, then there may */
92 	/* be a daemon process that has the device open. Kill that process off */
93 	/* so that we can open it here, before it re-spawns. */
94 	char *pnames[] = {
95 //			"i1iSisDeviceService",
96 			"i1ProDeviceService",
97 			NULL
98 	};
99 	int retries = 20;
100 #else /* !UNIX_APPLE */
101 	char **pnames = NULL;
102 	int retries = 0;
103 #endif /* !UNIX_APPLE */
104 
105 	a1logd(p->log, 2, "i1pro_init_coms: called\n");
106 
107 	if (p->icom->port_type(p->icom) != icomt_usb) {
108 		a1logd(p->log, 1, "i1pro_init_coms: wrong communications type for device!\n");
109 		return inst_coms_fail;
110 	}
111 
112 	a1logd(p->log, 2, "i1pro_init_coms: about to init USB\n");
113 
114 	/* Set config, interface, write end point, read end point, read quanta */
115 	/* ("serial" end points aren't used - the i1display uses USB control messages) */
116 	if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, usbflags, retries, pnames))
117 		                                                                    != ICOM_OK) {
118 		a1logd(p->log, 1, "i1pro_init_coms: failed ICOM err 0x%x\n",se);
119 		return i1pro_interp_code(p, icoms2i1pro_err(se));
120 	}
121 
122 	a1logd(p->log, 2, "i1pro_init_coms: init coms has suceeded\n");
123 
124 	p->gotcoms = 1;
125 	return inst_ok;
126 }
127 
128 static inst_code
i1pro_determine_capabilities(i1pro * p)129 i1pro_determine_capabilities(i1pro *p) {
130 
131 	/* Set the base Monitor/Pro capabilities mask */
132 	p->cap =  inst_mode_emis_spot
133 	       |  inst_mode_emis_tele
134 	       |  inst_mode_emis_strip		/* Also likely to be light table reading */
135 	       |  inst_mode_trans_spot		/* Support this manually using a light table */
136 	       |  inst_mode_trans_strip
137 	       |  inst_mode_emis_nonadaptive
138 	       |  inst_mode_colorimeter
139 	       |  inst_mode_spectral
140 	       ;
141 
142 	/* Set the Pro capabilities mask */
143 	if (p->itype == instI1Pro
144 	 || p->itype == instI1Pro2) {
145 		p->cap |= inst_mode_ref_spot
146 		       |  inst_mode_ref_strip
147 		       ;
148 	}
149 
150 	/* Set the Pro2 capabilities mask */
151 	if (p->itype == instI1Pro2) {
152 		p->cap |= inst_mode_ref_uv
153 		       ;
154 	}
155 
156 	if (i1pro_imp_highres(p))		/* This is static */
157 		p->cap |= inst_mode_highres;
158 
159 	if (i1pro_imp_ambient(p)) {		/* This depends on the instrument */
160 		p->cap |= inst_mode_emis_ambient
161 		       |  inst_mode_emis_ambient_flash
162 		       ;
163 	}
164 
165 	p->cap2 = inst2_prog_trig
166 			| inst2_user_trig
167 			| inst2_user_switch_trig
168 			| inst2_bidi_scan
169 			| inst2_has_scan_toll
170 			| inst2_no_feedback
171 	        ;
172 
173 	if (p->m != NULL) {
174 		i1proimp *m = (i1proimp *)p->m;
175 		i1pro_state *s = &m->ms[m->mmode];
176 		if (s->emiss) {
177 			p->cap2 |= inst2_meas_disp_update;
178 			p->cap2 |= inst2_emis_refr_meas;
179 		}
180 	}
181 
182 	p->cap3 = inst3_none;
183 
184 	return inst_ok;
185 }
186 
187 /* Initialise the I1PRO */
188 /* return non-zero on an error, with inst error code */
189 static inst_code
i1pro_init_inst(inst * pp)190 i1pro_init_inst(inst *pp) {
191 	i1pro *p = (i1pro *)pp;
192 	i1pro_code ev = I1PRO_OK;
193 
194 	a1logd(p->log, 2, "i1pro_init_inst: called\n");
195 
196 	if (p->gotcoms == 0)
197 		return i1pro_interp_code(p, I1PRO_INT_NO_COMS);	/* Must establish coms before calling init */
198 	if ((ev = i1pro_imp_init(p)) != I1PRO_OK) {
199 		a1logd(p->log, 1, "i1pro_init_inst: failed with 0x%x\n",ev);
200 		return i1pro_interp_code(p, ev);
201 	}
202 
203 	p->inited = 1;
204 	a1logd(p->log, 2, "i1pro_init_inst: instrument inited OK\n");
205 
206 	/* Now it's initied, we can get true capabilities */
207 	i1pro_determine_capabilities(p);
208 
209 	return i1pro_interp_code(p, ev);
210 }
211 
i1pro_get_serial_no(inst * pp)212 static char *i1pro_get_serial_no(inst *pp) {
213 	i1pro *p = (i1pro *)pp;
214 
215 	if (!pp->gotcoms)
216 		return "";
217 	if (!pp->inited)
218 		return "";
219 
220 	return i1pro_imp_get_serial_no(p);
221 }
222 
223 /* Read a set of strips */
224 static inst_code
i1pro_read_strip(inst * pp,char * name,int npatch,char * pname,int sguide,double pwid,double gwid,double twid,ipatch * vals)225 i1pro_read_strip(
226 inst *pp,
227 char *name,			/* Strip name (7 chars) */
228 int npatch,			/* Number of patches in the pass */
229 char *pname,		/* Pass name (3 chars) */
230 int sguide,			/* Guide number */
231 double pwid,		/* Patch length in mm (DTP41) */
232 double gwid,		/* Gap length in mm (DTP41) */
233 double twid,		/* Trailer length in mm (DTP41T) */
234 ipatch *vals) {		/* Pointer to array of instrument patch values */
235 	i1pro *p = (i1pro *)pp;
236 	i1pro_code rv;
237 
238 	if (!p->gotcoms)
239 		return inst_no_coms;
240 	if (!p->inited)
241 		return inst_no_init;
242 
243 	rv = i1pro_imp_measure(p, vals, npatch, instClamp);
244 
245 	return i1pro_interp_code(p, rv);
246 }
247 
248 /* Read a single sample */
249 static inst_code
i1pro_read_sample(inst * pp,char * name,ipatch * val,instClamping clamp)250 i1pro_read_sample(
251 inst *pp,
252 char *name,			/* Strip name (7 chars) */
253 ipatch *val,		/* Pointer to instrument patch value */
254 instClamping clamp) {		/* Clamp XYZ/Lab to be +ve */
255 	i1pro *p = (i1pro *)pp;
256 	i1pro_code rv;
257 
258 	if (!p->gotcoms)
259 		return inst_no_coms;
260 	if (!p->inited)
261 		return inst_no_init;
262 
263 	rv = i1pro_imp_measure(p, val, 1, clamp);
264 
265 	return i1pro_interp_code(p, rv);
266 }
267 
268 /* Read an emissive refresh rate */
269 static inst_code
i1pro_read_refrate(inst * pp,double * ref_rate)270 i1pro_read_refrate(
271 inst *pp,
272 double *ref_rate) {
273 	i1pro *p = (i1pro *)pp;
274 	i1pro_code rv;
275 
276 	if (!p->gotcoms)
277 		return inst_no_coms;
278 	if (!p->inited)
279 		return inst_no_init;
280 
281 	if (ref_rate != NULL)
282 		*ref_rate = 0.0;
283 
284 	rv = i1pro_imp_meas_refrate(p, ref_rate);
285 
286 
287 
288 	return i1pro_interp_code(p, rv);
289 }
290 
291 /* Read the display update delay */
292 static inst_code
i1pro_meas_delay(inst * pp,int * pdispmsec,int * pinstmsec)293 i1pro_meas_delay(
294 inst *pp,
295 int *pdispmsec,		/* Return display update delay in msec */
296 int *pinstmsec) {	/* Return instrument reaction time in msec */
297 	i1pro *p = (i1pro *)pp;
298 	i1pro_code rv;
299 
300 	if (!p->gotcoms)
301 		return inst_no_coms;
302 	if (!p->inited)
303 		return inst_no_init;
304 
305 	rv = i1pro_imp_meas_delay(p, pdispmsec, pinstmsec);
306 
307 	return i1pro_interp_code(p, rv);
308 }
309 
310 /* Timestamp the white patch change during meas_delay() */
i1pro_white_change(inst * pp,int init)311 static inst_code i1pro_white_change(
312 inst *pp,
313 int init) {
314 	i1pro *p = (i1pro *)pp;
315 
316 	return i1pro_imp_white_change(p, init);
317 }
318 
319 /* Return needed and available inst_cal_type's */
i1pro_get_n_a_cals(inst * pp,inst_cal_type * pn_cals,inst_cal_type * pa_cals)320 static inst_code i1pro_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
321 	i1pro *p = (i1pro *)pp;
322 	i1pro_code rv;
323 
324 	rv = i1pro_imp_get_n_a_cals(p, pn_cals, pa_cals);
325 	return i1pro_interp_code(p, rv);
326 }
327 
328 /* Request an instrument calibration. */
i1pro_calibrate(inst * pp,inst_cal_type * calt,inst_cal_cond * calc,inst_calc_id_type * idtype,char id[CALIDLEN])329 static inst_code i1pro_calibrate(
330 inst *pp,
331 inst_cal_type *calt,	/* Calibration type to do/remaining */
332 inst_cal_cond *calc,	/* Current condition/desired condition */
333 inst_calc_id_type *idtype,	/* Condition identifier type */
334 char id[CALIDLEN]		/* Condition identifier (ie. white reference ID) */
335 ) {
336 	i1pro *p = (i1pro *)pp;
337 	i1pro_code rv;
338 
339 	if (!p->gotcoms)
340 		return inst_no_coms;
341 	if (!p->inited)
342 		return inst_no_init;
343 
344 	rv = i1pro_imp_calibrate(p, calt, calc, idtype, id);
345 	return i1pro_interp_code(p, rv);
346 }
347 
348 /* Instrument specific error codes interpretation */
349 static char *
i1pro_interp_error(inst * pp,i1pro_code ec)350 i1pro_interp_error(inst *pp, i1pro_code ec) {
351 //	i1pro *p = (i1pro *)pp;
352 	ec &= inst_imask;
353 	switch (ec) {
354 		case I1PRO_INTERNAL_ERROR:
355 			return "Internal software error";
356 		case I1PRO_COMS_FAIL:
357 			return "Communications failure";
358 		case I1PRO_UNKNOWN_MODEL:
359 			return "Not an i1 Pro";
360 		case I1PRO_DATA_PARSE_ERROR:
361 			return "Data from i1 Display didn't parse as expected";
362 
363 		case I1PRO_USER_ABORT:
364 			return "User abort";
365 
366 		case I1PRO_USER_TRIG:
367 			return "User trigger";
368 
369 		case I1PRO_UNSUPPORTED:
370 			return "Unsupported function";
371 		case I1PRO_CAL_SETUP:
372 			return "Calibration retry with correct setup is needed";
373 
374 		case I1PRO_OK:
375 			return "No device error";
376 
377 		case I1PRO_DATA_COUNT:
378 			return "EEProm data count unexpectedly small";
379 		case I1PRO_DATA_BUFSIZE:
380 			return "EEProm data buffer too small";
381 		case I1PRO_DATA_MAKE_KEY:
382 			return "EEProm data creating key failed";
383 		case I1PRO_DATA_MEMORY:
384 			return "EEProm memory alloc failure";
385 		case I1PRO_DATA_KEYNOTFOUND:
386 			return "EEProm key value wasn't found";
387 		case I1PRO_DATA_WRONGTYPE:
388 			return "EEProm key is the wrong type";
389 		case I1PRO_DATA_KEY_CORRUPT:
390 			return "EEProm key table seems to be corrupted";
391 		case I1PRO_DATA_KEY_COUNT_SMALL:
392 			return "EEProm key table size is too small for expected number of keys";
393 		case I1PRO_DATA_KEY_COUNT_LARGE:
394 			return "EEProm key table size is too large for expected number of keys";
395 		case I1PRO_DATA_KEY_UNKNOWN:
396 			return "EEProm unknown key type";
397 		case I1PRO_DATA_KEY_MEMRANGE:
398 			return "EEProm key data is out of range of EEProm";
399 		case I1PRO_DATA_KEY_ENDMARK:
400 			return "EEProm end section marker was missing";
401 
402 		case I1PRO_HW_HIGHPOWERFAIL:
403 			return "Failed to switch to high power mode";
404 		case I1PRO_HW_EE_SIZE:
405 			return "EEProm size is too small";
406 		case I1PRO_HW_EE_SHORTREAD:
407 			return "Read less bytes for EEProm read than expected";
408 		case I1PRO_HW_EE_SHORTWRITE:
409 			return "Wrote less bytes for EEProm write than expected";
410 		case I1PRO_HW_ME_SHORTREAD:
411 			return "Read less bytes for measurement read than expected";
412 		case I1PRO_HW_ME_ODDREAD:
413 			return "Read a number of bytes not a multiple of 256";
414 		case I1PRO_HW_SW_SHORTREAD:
415 			return "Read less bytes for Switch read than expected";
416 		case I1PRO_HW_LED_SHORTWRITE:
417 			return "Wrote fewer LED sequence bytes than expected";
418 		case I1PRO_HW_UNEX_SPECPARMS:
419 			return "Instrument has unexpected spectral parameters";
420 		case I1PRO_HW_CALIBINFO:
421 			return "Instrument calibration info is missing or corrupted";
422 		case I1PRO_WL_TOOLOW:
423 			return "Wavelength calibration reading is too low";
424 		case I1PRO_WL_SHAPE:
425 			return "Wavelength calibration reading shape is incorrect";
426 		case I1PRO_WL_ERR2BIG:
427 			return "Wavelength calibration correction is excessive";
428 
429 		case I1PRO_RD_DARKREADINCONS:
430 			return "Dark calibration reading is inconsistent";
431 		case I1PRO_RD_SENSORSATURATED:
432 			return "Sensor is saturated";
433 		case I1PRO_RD_DARKNOTVALID:
434 			return "Dark reading is not valid (too light)";
435 		case I1PRO_RD_NEEDS_CAL:
436 			return "Mode needs calibration";
437 		case I1PRO_RD_WHITEREADINCONS:
438 			return "White calibration reading is inconsistent";
439 		case I1PRO_RD_WHITEREFERROR:
440 			return "White reference reading error";
441 		case I1PRO_RD_LIGHTTOOLOW:
442 			return "Light level is too low";
443 		case I1PRO_RD_LIGHTTOOHIGH:
444 			return "Light level is too high";
445 		case I1PRO_RD_SHORTMEAS:
446 			return "Reading is too short";
447 		case I1PRO_RD_READINCONS:
448 			return "Reading is inconsistent";
449 		case I1PRO_RD_TRANSWHITERANGE:
450 			return "Transmission white reference is out of range";
451 		case I1PRO_RD_NOTENOUGHPATCHES:
452 			return "Not enough patches";
453 		case I1PRO_RD_TOOMANYPATCHES:
454 			return "Too many patches";
455 		case I1PRO_RD_NOTENOUGHSAMPLES:
456 			return "Not enough samples per patch - Slow Down!";
457 		case I1PRO_RD_NOFLASHES:
458 			return "No flashes recognized";
459 		case I1PRO_RD_NOAMBB4FLASHES:
460 			return "No ambient found before first flash";
461 		case I1PRO_RD_NOREFR_FOUND:
462 			return "No refresh rate detected or failed to measure it";
463 		case I1PRO_RD_NOTRANS_FOUND:
464 			return "No delay calibration transition found";
465 
466 		case I1PRO_INT_NO_COMS:
467 			return "Communications hasn't been established";
468 		case I1PRO_INT_EETOOBIG:
469 			return "Read of EEProm is too big (> 65536)";
470 		case I1PRO_INT_ODDREADBUF:
471 			return "Measurement read buffer is not a multiple of reading size";
472 		case I1PRO_INT_SMALLREADBUF:
473 			return "Measurement read buffer is too small for initial measurement";
474 		case I1PRO_INT_INTTOOBIG:
475 			return "Integration time is too big";
476 		case I1PRO_INT_INTTOOSMALL:
477 			return "Integration time is too small";
478 		case I1PRO_INT_ILLEGALMODE:
479 			return "Illegal measurement mode selected";
480 		case I1PRO_INT_ZEROMEASURES:
481 			return "Number of measurements requested is zero";
482 		case I1PRO_INT_WRONGPATCHES:
483 			return "Number of patches to match is wrong";
484 		case I1PRO_INT_MEASBUFFTOOSMALL:
485 			return "Measurement exceeded read buffer";
486 		case I1PRO_INT_NOTIMPLEMENTED:
487 			return "Support not implemented";
488 		case I1PRO_INT_NOTCALIBRATED:
489 			return "Unexpectedely invalid calibration";
490 		case I1PRO_INT_NOINTERPDARK:
491 			return "Need interpolated dark and don't have it";
492 		case I1PRO_INT_THREADFAILED:
493 			return "Creation of thread failed";
494 		case I1PRO_INT_BUTTONTIMEOUT:
495 			return "Button status read timed out";
496 		case I1PRO_INT_CIECONVFAIL:
497 			return "Creating spectral to CIE converted failed";
498 		case I1PRO_INT_PREP_LOG_DATA:
499 			return "Error in preparing log data";
500 		case I1PRO_INT_MALLOC:
501 			return "Error in allocating memory";
502 		case I1PRO_INT_CREATE_EEPROM_STORE:
503 			return "Error in creating EEProm store";
504 		case I1PRO_INT_SAVE_SUBT_MODE:
505 			return "Can't save calibration if in subt mode";
506 		case I1PRO_INT_NO_CAL_TO_SAVE:
507 			return "No calibration data to save";
508 		case I1PRO_INT_EEPROM_DATA_MISSING:
509 			return "EEProm data is missing";
510 		case I1PRO_INT_NEW_RSPL_FAILED:
511 			return "Creating RSPL object faild";
512 		case I1PRO_INT_CAL_SAVE:
513 			return "Unable to save calibration to file";
514 		case I1PRO_INT_CAL_RESTORE:
515 			return "Unable to restore calibration from file";
516 		case I1PRO_INT_CAL_TOUCH:
517 			return "Unable to update calibration file modification time";
518 		case I1PRO_INT_ADARK_INVALID:
519 			return "Adaptive dark calibration is invalid";
520 		case I1PRO_INT_NO_HIGH_GAIN:
521 			return "Rev E mode doesn't have a high gain mode";
522 		case I1PRO_INT_ASSERT:
523 			return "Assert fail";
524 
525 		default:
526 			return "Unknown error code";
527 	}
528 }
529 
530 
531 /* Convert a machine specific error code into an abstract inst code */
532 static inst_code
i1pro_interp_code(i1pro * p,i1pro_code ec)533 i1pro_interp_code(i1pro *p, i1pro_code ec) {
534 
535 	ec &= inst_imask;
536 	switch (ec) {
537 
538 		case I1PRO_OK:
539 			return inst_ok;
540 
541 		case I1PRO_INTERNAL_ERROR:
542 			return inst_internal_error | ec;
543 
544 		case I1PRO_COMS_FAIL:
545 			return inst_coms_fail | ec;
546 
547 		case I1PRO_UNKNOWN_MODEL:
548 			return inst_unknown_model | ec;
549 
550 		case I1PRO_DATA_PARSE_ERROR:
551 			return inst_protocol_error | ec;
552 
553 		case I1PRO_USER_ABORT:
554 			return inst_user_abort;
555 
556 		case I1PRO_USER_TRIG:
557 			return inst_user_trig;
558 
559 		case I1PRO_UNSUPPORTED:
560 			return inst_unsupported | ec;
561 
562 		case I1PRO_CAL_SETUP:
563 			return inst_cal_setup | ec;
564 
565 		case I1PRO_DATA_COUNT:
566 		case I1PRO_DATA_BUFSIZE:
567 		case I1PRO_DATA_MAKE_KEY:
568 		case I1PRO_DATA_MEMORY:
569 		case I1PRO_DATA_KEYNOTFOUND:
570 		case I1PRO_DATA_WRONGTYPE:
571 		case I1PRO_DATA_KEY_CORRUPT:
572 		case I1PRO_DATA_KEY_COUNT_SMALL:
573 		case I1PRO_DATA_KEY_COUNT_LARGE:
574 		case I1PRO_DATA_KEY_UNKNOWN:
575 		case I1PRO_DATA_KEY_MEMRANGE:
576 		case I1PRO_DATA_KEY_ENDMARK:
577 
578 		case I1PRO_HW_HIGHPOWERFAIL:
579 		case I1PRO_HW_EE_SIZE:
580 		case I1PRO_HW_EE_SHORTREAD:
581 		case I1PRO_HW_EE_SHORTWRITE:
582 		case I1PRO_HW_ME_SHORTREAD:
583 		case I1PRO_HW_ME_ODDREAD:
584 		case I1PRO_HW_SW_SHORTREAD:
585 		case I1PRO_HW_LED_SHORTWRITE:
586 		case I1PRO_HW_UNEX_SPECPARMS:
587 		case I1PRO_HW_CALIBINFO:
588 		case I1PRO_WL_TOOLOW:
589 		case I1PRO_WL_SHAPE:
590 		case I1PRO_WL_ERR2BIG:
591 			return inst_hardware_fail | ec;
592 
593 		case I1PRO_RD_DARKREADINCONS:
594 		case I1PRO_RD_SENSORSATURATED:
595 		case I1PRO_RD_DARKNOTVALID:
596 		case I1PRO_RD_WHITEREADINCONS:
597 		case I1PRO_RD_WHITEREFERROR:
598 		case I1PRO_RD_LIGHTTOOLOW:
599 		case I1PRO_RD_LIGHTTOOHIGH:
600 		case I1PRO_RD_SHORTMEAS:
601 		case I1PRO_RD_READINCONS:
602 		case I1PRO_RD_TRANSWHITERANGE:
603 		case I1PRO_RD_NOTENOUGHPATCHES:
604 		case I1PRO_RD_TOOMANYPATCHES:
605 		case I1PRO_RD_NOTENOUGHSAMPLES:
606 		case I1PRO_RD_NOFLASHES:
607 		case I1PRO_RD_NOAMBB4FLASHES:
608 		case I1PRO_RD_NOREFR_FOUND:
609 		case I1PRO_RD_NOTRANS_FOUND:
610 			return inst_misread | ec;
611 
612 		case I1PRO_RD_NEEDS_CAL:
613 			return inst_needs_cal | ec;
614 
615 		case I1PRO_INT_NO_COMS:
616 		case I1PRO_INT_EETOOBIG:
617 		case I1PRO_INT_ODDREADBUF:
618 		case I1PRO_INT_SMALLREADBUF:
619 		case I1PRO_INT_INTTOOBIG:
620 		case I1PRO_INT_INTTOOSMALL:
621 		case I1PRO_INT_ILLEGALMODE:
622 		case I1PRO_INT_ZEROMEASURES:
623 		case I1PRO_INT_MEASBUFFTOOSMALL:
624 		case I1PRO_INT_NOTIMPLEMENTED:
625 		case I1PRO_INT_NOTCALIBRATED:
626 		case I1PRO_INT_NOINTERPDARK:
627 		case I1PRO_INT_THREADFAILED:
628 		case I1PRO_INT_BUTTONTIMEOUT:
629 		case I1PRO_INT_CIECONVFAIL:
630 		case I1PRO_INT_PREP_LOG_DATA:
631 		case I1PRO_INT_MALLOC:
632 		case I1PRO_INT_CREATE_EEPROM_STORE:
633 		case I1PRO_INT_SAVE_SUBT_MODE:
634 		case I1PRO_INT_NO_CAL_TO_SAVE:
635 		case I1PRO_INT_EEPROM_DATA_MISSING:
636 		case I1PRO_INT_NEW_RSPL_FAILED:
637 		case I1PRO_INT_CAL_SAVE:
638 		case I1PRO_INT_CAL_RESTORE:
639 		case I1PRO_INT_CAL_TOUCH:
640 		case I1PRO_INT_ADARK_INVALID:
641 		case I1PRO_INT_NO_HIGH_GAIN:
642 		case I1PRO_INT_ASSERT:
643 			return inst_internal_error | ec;
644 	}
645 	return inst_other_error | ec;
646 }
647 
648 /* Return the instrument capabilities */
i1pro_capabilities(inst * pp,inst_mode * pcap1,inst2_capability * pcap2,inst3_capability * pcap3)649 static void i1pro_capabilities(inst *pp,
650 inst_mode *pcap1,
651 inst2_capability *pcap2,
652 inst3_capability *pcap3) {
653 	i1pro *p = (i1pro *)pp;
654 
655 	if (pcap1 != NULL)
656 		*pcap1 = p->cap;
657 	if (pcap2 != NULL)
658 		*pcap2 = p->cap2;
659 	if (pcap3 != NULL)
660 		*pcap3 = p->cap3;
661 }
662 
663 /* Return the corresponding i1pro measurement mode, */
664 /* or i1p_no_modes if invalid */
i1pro_convert_mode(i1pro * p,inst_mode m)665 static i1p_mode i1pro_convert_mode(i1pro *p, inst_mode m) {
666 	i1p_mode mmode = 0;	/* Instrument measurement mode */
667 
668 	/* Simple test */
669 	if (m & ~p->cap) {
670 		return i1p_no_modes;
671 	}
672 
673 	if (IMODETST(m, inst_mode_ref_spot)) {
674 		mmode = i1p_refl_spot;
675 	} else if (IMODETST(m, inst_mode_ref_strip)) {
676 		mmode = i1p_refl_scan;
677 	} else if (IMODETST(m, inst_mode_trans_spot)) {
678 		mmode = i1p_trans_spot;
679 	} else if (IMODETST(m, inst_mode_trans_strip)) {
680 		mmode = i1p_trans_scan;
681 	} else if (IMODETST(m, inst_mode_emis_spot)
682 	        || IMODETST(m, inst_mode_emis_tele)) {
683 		if (IMODETST(m, inst_mode_emis_nonadaptive))
684 			mmode = i1p_emiss_spot_na;
685 		else
686 			mmode = i1p_emiss_spot;
687 	} else if (IMODETST(m, inst_mode_emis_strip)) {
688 		mmode = i1p_emiss_scan;
689 	} else if (IMODETST(m, inst_mode_emis_ambient)
690 	        && (p->cap & inst_mode_emis_ambient)) {
691 		mmode = i1p_amb_spot;
692 	} else if (IMODETST(m, inst_mode_emis_ambient_flash)
693 	        && (p->cap & inst_mode_emis_ambient_flash)) {
694 		mmode = i1p_amb_flash;
695 	} else {
696 		return i1p_no_modes;
697 	}
698 
699 	return mmode;
700 }
701 
702 /* Check device measurement mode */
i1pro_check_mode(inst * pp,inst_mode m)703 static inst_code i1pro_check_mode(inst *pp, inst_mode m) {
704 	i1pro *p = (i1pro *)pp;
705 	i1p_mode mmode = 0;	/* Instrument measurement mode */
706 
707 	if (!p->gotcoms)
708 		return inst_no_coms;
709 	if (!p->inited)
710 		return inst_no_init;
711 
712 	if (i1pro_convert_mode(p, m) == i1p_no_modes)
713 		return inst_unsupported;
714 
715 	return inst_ok;
716 }
717 
718 /* Set device measurement mode */
i1pro_set_mode(inst * pp,inst_mode m)719 static inst_code i1pro_set_mode(inst *pp, inst_mode m) {
720 	i1pro *p = (i1pro *)pp;
721 	i1p_mode mmode;		/* Instrument measurement mode */
722 	inst_code rv;
723 
724 	if (!p->gotcoms)
725 		return inst_no_coms;
726 	if (!p->inited)
727 		return inst_no_init;
728 
729 	if ((mmode = i1pro_convert_mode(p, m)) == i1p_no_modes)
730 		return inst_unsupported;
731 
732 	if ((rv = i1pro_interp_code(p, i1pro_imp_set_mode(p, mmode, m))) != inst_ok)
733 		return rv;
734 
735 	i1pro_determine_capabilities(p);
736 
737 	return inst_ok;
738 }
739 
740 /*
741  * set or reset an optional mode
742  *
743  * Some options talk to the instrument, and these will
744  * error if it hasn't been initialised.
745  */
746 static inst_code
i1pro_get_set_opt(inst * pp,inst_opt_type m,...)747 i1pro_get_set_opt(inst *pp, inst_opt_type m, ...) {
748 	i1pro *p = (i1pro *)pp;
749 
750 	if (m == inst_opt_initcalib) {		/* default */
751 		i1pro_set_noinitcalib(p, 0, 0);
752 		return inst_ok;
753 
754 	} else if (m == inst_opt_noinitcalib) {
755 		va_list args;
756 		int losecs = 0;
757 
758 		va_start(args, m);
759 		losecs = va_arg(args, int);
760 		va_end(args);
761 
762 		i1pro_set_noinitcalib(p, 1, losecs);
763 		return inst_ok;
764 
765 	/* Record the trigger mode */
766 	} else if (m == inst_opt_trig_prog
767 	 || m == inst_opt_trig_user
768 	 || m == inst_opt_trig_user_switch) {
769 		i1pro_set_trig(p, m);
770 		return inst_ok;
771 	}
772 
773 	if (m == inst_opt_scan_toll) {
774 		va_list args;
775 		double toll_ratio = 1.0;
776 
777 		va_start(args, m);
778 		toll_ratio = va_arg(args, double);
779 		va_end(args);
780 		return i1pro_interp_code(p, i1pro_set_scan_toll(p, toll_ratio));
781 	}
782 
783 	if (!p->gotcoms)
784 		return inst_no_coms;
785 	if (!p->inited)
786 		return inst_no_init;
787 
788 	/* Not sure if this can be set before init */
789 	if (m == inst_opt_highres) {
790 		return i1pro_interp_code(p, i1pro_set_highres(p));
791 	} else if (m == inst_opt_stdres) {
792 		return i1pro_interp_code(p, i1pro_set_stdres(p));
793 	}
794 
795 	/* Return the filter */
796 	if (m == inst_stat_get_filter) {
797 		i1proimp *imp = (i1proimp *)p->m;
798 		inst_opt_filter *filt;
799 		va_list args;
800 
801 		va_start(args, m);
802 		filt = va_arg(args, inst_opt_filter *);
803 		va_end(args);
804 
805 		*filt = inst_opt_filter_none;
806 
807 		if (imp->physfilt == 0x82)
808 			*filt = inst_opt_filter_UVCut;
809 
810 		return inst_ok;
811 	}
812 
813 	/* Set xcalstd */
814 	if (m == inst_opt_set_xcalstd) {
815 		i1proimp *imp = (i1proimp *)p->m;
816 		xcalstd standard;
817 		va_list args;
818 
819 		va_start(args, m);
820 		standard = va_arg(args, xcalstd);
821 		va_end(args);
822 
823 		imp->target_calstd = standard;
824 
825 		return inst_ok;
826 	}
827 
828 	/* Get the current effective xcalstd */
829 	if (m == inst_opt_get_xcalstd) {
830 		i1proimp *imp = (i1proimp *)p->m;
831 		xcalstd *standard;
832 		va_list args;
833 
834 		va_start(args, m);
835 		standard = va_arg(args, xcalstd *);
836 		va_end(args);
837 
838 		if (imp->target_calstd == xcalstd_native)
839 			*standard = imp->native_calstd;		/* If not overridden */
840 		else
841 			*standard = imp->target_calstd;		/* Overidden std. */
842 
843 		return inst_ok;
844 	}
845 
846 	/* Return the white calibration tile spectrum */
847 	/* (We always return the normal rez. reference values) */
848 	if (m == inst_opt_get_cal_tile_sp) {
849 		i1proimp *imp = (i1proimp *)p->m;
850 		xspect *sp;
851 		inst_code rv;
852 		va_list args;
853 		int i;
854 
855 		va_start(args, m);
856 		sp = va_arg(args, xspect *);
857 		va_end(args);
858 
859 		if (imp->white_ref[0] == NULL)
860 			return inst_no_init;
861 
862 		sp->spec_n = imp->nwav[0];
863 		sp->spec_wl_short = imp->wl_short[0];
864 		sp->spec_wl_long = imp->wl_long[0];
865 		sp->norm = 100.0;
866 
867 		for (i = 0; i < sp->spec_n; i++)
868 			sp->spec[i] = imp->white_ref[0][i] * 100.0;
869 
870 		return inst_ok;
871 	}
872 
873 	/* Lamp drift remediation */
874 	if (m == inst_opt_lamp_remediate) {
875 		i1pro_code ev;
876 		va_list args;
877 		double seconds;
878 
879 		va_start(args, m);
880 		seconds = va_arg(args, double);
881 		va_end(args);
882 
883 		ev = i1pro_imp_lamp_fix(p, seconds);
884 
885 		return i1pro_interp_code(p, ev);
886 	}
887 
888 	/* Use default implementation of other inst_opt_type's */
889 	{
890 		inst_code rv;
891 		va_list args;
892 
893 		va_start(args, m);
894 		rv = inst_get_set_opt_def(pp, m, args);
895 		va_end(args);
896 
897 		return rv;
898 	}
899 }
900 
901 /* Destroy ourselves */
902 static void
i1pro_del(inst * pp)903 i1pro_del(inst *pp) {
904 	i1pro *p = (i1pro *)pp;
905 
906 	del_i1proimp(p);
907 	if (p->icom != NULL)
908 		p->icom->del(p->icom);
909 	p->vdel(pp);
910 	free(p);
911 }
912 
913 /* Constructor */
new_i1pro(icoms * icom,instType itype)914 extern i1pro *new_i1pro(icoms *icom, instType itype) {
915 	i1pro *p;
916 	int rv;
917 	if ((p = (i1pro *)calloc(sizeof(i1pro),1)) == NULL) {
918 		a1loge(icom->log, 1, "new_i1pro: malloc failed!\n");
919 		return NULL;
920 	}
921 
922 	p->log = new_a1log_d(icom->log);
923 
924 	/* Inst methods */
925 	p->init_coms         = i1pro_init_coms;
926 	p->init_inst         = i1pro_init_inst;
927 	p->capabilities      = i1pro_capabilities;
928 	p->get_serial_no     = i1pro_get_serial_no;
929 	p->check_mode        = i1pro_check_mode;
930 	p->set_mode          = i1pro_set_mode;
931 	p->get_set_opt       = i1pro_get_set_opt;
932 	p->read_strip        = i1pro_read_strip;
933 	p->read_sample       = i1pro_read_sample;
934 	p->read_refrate      = i1pro_read_refrate;
935 	p->get_n_a_cals      = i1pro_get_n_a_cals;
936 	p->calibrate         = i1pro_calibrate;
937 	p->meas_delay        = i1pro_meas_delay;
938 	p->white_change      = i1pro_white_change;
939 	p->interp_error      = i1pro_interp_error;
940 	p->del               = i1pro_del;
941 
942 	p->icom = icom;
943 	p->itype = itype;
944 
945 	i1pro_determine_capabilities(p);
946 
947 	if ((rv = add_i1proimp(p)) != I1PRO_OK) {
948 		free(p);
949 		a1loge(icom->log, 1, "new_i1pro: error %d creating i1proimp\n",rv);
950 		return NULL;
951 	}
952 
953 	return p;
954 }
955 
956