1
2 /* Abstract instrument class implemenation */
3
4 /*
5 * Argyll Color Correction System
6 *
7 * Author: Graeme W. Gill
8 * Date: 10/3/2001
9 *
10 * Copyright 2001 - 2013 Graeme W. Gill
11 * All rights reserved.
12 *
13 * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
14 * see the License2.txt file for licencing details.
15 */
16
17 /*
18 If you make use of the instrument driver code here, please note
19 that it is the author(s) of the code who take responsibility
20 for its operation. Any problems or queries regarding driving
21 instruments with the Argyll drivers, should be directed to
22 the Argyll's author(s), and not to any other party.
23
24 If there is some instrument feature or function that you
25 would like supported here, it is recommended that you
26 contact Argyll's author(s) first, rather than attempt to
27 modify the software yourself, if you don't have firm knowledge
28 of the instrument communicate protocols. There is a chance
29 that an instrument could be damaged by an incautious command
30 sequence, and the instrument companies generally cannot and
31 will not support developers that they have not qualified
32 and agreed to support.
33 */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <ctype.h>
39 #include <string.h>
40 #include <time.h>
41 #ifndef SALONEINSTLIB
42 #include "copyright.h"
43 #include "aconfig.h"
44 #else
45 #include "sa_config.h"
46 #endif /* !SALONEINSTLIB */
47 #include "numsup.h"
48 #include "xspect.h"
49 #include "conv.h"
50
51 #include "insttypes.h"
52 #include "icoms.h"
53 #include "inst.h"
54 #include "rspec.h"
55 #include "insttypeinst.h"
56 #include "ccmx.h"
57 #include "ccss.h"
58 #include "sort.h"
59
60 #if defined(ENABLE_FAST_SERIAL)
61 instType fast_ser_inst_type(icoms *p, int tryhard,
62 inst_code (*uicallback)(void *cntx, inst_ui_purp purp), void *cntx);
63 # if defined(ENABLE_SERIAL)
64 static instType ser_inst_type(icoms *p,
65 inst_code (*uicallback)(void *cntx, inst_ui_purp purp), void *cntx);
66 # endif /* ENABLE_SERIAL */
67 #endif /* ENABLE_FAST_SERIAL */
68
69 /* ------------------------------------ */
70 /* Default methods for instrument class */
71
72 /* Establish communications at the indicated baud rate. */
73 /* Timout in to seconds, and return non-zero error code */
init_coms(inst * p,baud_rate br,flow_control fc,double tout)74 static inst_code init_coms(
75 inst *p,
76 baud_rate br, /* Baud rate */
77 flow_control fc, /* Flow control */
78 double tout) { /* Timeout */
79 return inst_unsupported;
80 }
81
82 /* Initialise or re-initialise the INST */
83 /* return non-zero on an error, with inst error code */
init_inst(inst * p)84 static inst_code init_inst(
85 inst *p) {
86 return inst_unsupported;
87 }
88
89 /* Return the instrument type */
get_itype(inst * p)90 static instType get_itype(inst *p) {
91 if (p != NULL)
92 return p->itype;
93 return instUnknown;
94 }
95
96 /* Return the instrument serial number. */
97 /* (This will be an empty string if there is no serial no) */
get_serial_no(inst * p)98 static char *get_serial_no(inst *p) {
99 return "";
100 }
101
102 /* Return the instrument mode capabilities */
capabilities(inst * p,inst_mode * cap1,inst2_capability * cap2,inst3_capability * cap3)103 static void capabilities(inst *p, inst_mode *cap1,
104 inst2_capability *cap2, inst3_capability *cap3) {
105 if (cap1 != NULL)
106 *cap1 = inst_mode_none;
107 if (cap2 != NULL)
108 *cap2 = inst2_none;
109 if (cap3 != NULL)
110 *cap3 = inst3_none;
111 }
112
113 /* Return current or given configuration available measurement modes. */
114 /* This default routine assumed one confiration which will be */
115 /* all the measurement modes returned by capabilities(). */
meas_config(inst * p,inst_mode * mmodes,inst_cal_cond * cconds,int * conf_ix)116 static inst_code meas_config(inst *p,
117 inst_mode *mmodes, /* Return all the valid measurement modes */
118 /* for the current configuration */
119 inst_cal_cond *cconds, /* Return the valid calibration conditions */
120 int *conf_ix) { /* Request mode for given configuration, and */
121 /* return the index of the configuration returned */
122 if (mmodes != NULL) {
123 *mmodes = inst_mode_none;
124
125 /* Available measurement modes is current capabilities */
126 p->capabilities(p, mmodes, NULL, NULL);
127 }
128
129 if (cconds != NULL)
130 *cconds = inst_calc_unknown;
131
132 if (conf_ix != NULL)
133 *conf_ix = 0;
134
135 return inst_ok;
136 }
137
138 /* Check the device measurement mode */
check_mode(inst * p,inst_mode m)139 static inst_code check_mode(
140 inst *p,
141 inst_mode m) { /* Requested mode */
142 return inst_unsupported;
143 }
144
145 /* Set the device measurement mode */
set_mode(inst * p,inst_mode m)146 static inst_code set_mode(
147 inst *p,
148 inst_mode m) { /* Requested mode */
149 return inst_unsupported;
150 }
151
152 /* Return array of display type selectors */
get_disptypesel(inst * pp,int * pnsels,inst_disptypesel ** psels,int allconfig,int recreate)153 static inst_code get_disptypesel(
154 inst *pp,
155 int *pnsels, /* Return number of display types */
156 inst_disptypesel **psels, /* Return the array of display types */
157 int allconfig, /* nz to return list for all configs, not just current. */
158 int recreate /* nz to re-check for new ccmx & ccss files */
159 ) {
160 return inst_unsupported;
161 }
162
163 /* Set the display type */
set_disptype(inst * pp,int ix)164 static inst_code set_disptype(
165 inst *pp,
166 int ix /* index into the inst_disptypesel[] */
167 ) {
168 return inst_unsupported;
169 }
170
171 /* Get the disptech and other corresponding info for the current */
172 /* selected display type. Returns disptype_unknown by default. */
173 /* Because refrmode can be overridden, it may not match the refrmode */
174 /* of the dtech. (Pointers may be NULL if not needed) */
get_disptechi(inst * pp,disptech * dtech,int * refrmode,int * cbid)175 static inst_code get_disptechi(
176 inst *pp,
177 disptech *dtech,
178 int *refrmode,
179 int *cbid) {
180 if (dtech != NULL)
181 *dtech = disptech_unknown;
182 if (refrmode != NULL)
183 *refrmode = disptech_get_id(disptech_unknown)->refr;
184 if (cbid != NULL)
185 *cbid = 0;
186 return inst_ok;
187 }
188
189 /* Get a status or set or get an option (default implementation) */
inst_get_set_opt_def(inst * p,inst_opt_type m,va_list args)190 inst_code inst_get_set_opt_def(
191 inst *p,
192 inst_opt_type m, /* Requested option type */
193 va_list args) { /* Option parameters */
194
195 return inst_unsupported;
196 }
197
198 /* Get a status or set or get an option */
get_set_opt(inst * p,inst_opt_type m,...)199 static inst_code get_set_opt(
200 inst *p,
201 inst_opt_type m, /* Requested status type */
202 ...) { /* Status parameters */
203 inst_code rv;
204 va_list args;
205
206 va_start(args, m);
207 rv = inst_get_set_opt_def(p, m, args); /* Call the above */
208 va_end(args);
209
210 return rv;
211 }
212
213 /* Read a full test chart composed of multiple sheets */
214 /* DOESN'T use the trigger mode */
215 /* Return the inst error code */
read_chart(inst * p,int npatch,int pich,int sip,int * pis,int chid,ipatch * vals)216 static inst_code read_chart(
217 inst *p,
218 int npatch, /* Total patches/values in chart */
219 int pich, /* Passes (rows) in chart */
220 int sip, /* Steps in each pass (patches in each row) */
221 int *pis, /* Passes in each strip (rows in each sheet) */
222 int chid, /* Chart ID number */
223 ipatch *vals) { /* Pointer to array of values */
224
225 return inst_unsupported;
226 }
227
228 /* For an xy instrument, release the paper */
229 /* (if cap has inst_xy_holdrel) */
230 /* Return the inst error code */
xy_sheet_release(inst * p)231 static inst_code xy_sheet_release(
232 inst *p) {
233 return inst_unsupported;
234 }
235
236 /* For an xy instrument, hold the paper */
237 /* (if cap has inst_xy_holdrel) */
238 /* Return the inst error code */
xy_sheet_hold(inst * p)239 static inst_code xy_sheet_hold(
240 inst *p) {
241 return inst_unsupported;
242 }
243
244 /* For an xy instrument, allow the user to locate a point */
245 /* (if cap has inst_xy_locate) */
246 /* Return the inst error code */
xy_locate_start(inst * p)247 static inst_code xy_locate_start(
248 inst *p) {
249 return inst_unsupported;
250 }
251
252 /* For an xy instrument, read back the location */
253 /* (if cap has inst_xy_locate) */
254 /* Return the inst error code */
xy_get_location(inst * p,double * x,double * y)255 static inst_code xy_get_location(
256 inst *p,
257 double *x, double *y) {
258 return inst_unsupported;
259 }
260
261 /* For an xy instrument, end allowing the user to locate a point */
262 /* (if cap has inst_xy_locate) */
263 /* Return the inst error code */
xy_locate_end(inst * p)264 static inst_code xy_locate_end(
265 inst *p) {
266 return inst_unsupported;
267 }
268
269 /* For an xy instrument, move the measurement point */
270 /* (if cap has inst_xy_position) */
271 /* Return the inst error code */
xy_position(inst * p,int measure,double x,double y)272 static inst_code xy_position(
273 inst *p,
274 int measure, /* nz for measure point, z for locate point */
275 double x, double y) {
276 return inst_unsupported;
277 }
278
279 /* For an xy instrument, try and clear the table after an abort */
280 /* Return the inst error code */
xy_clear(inst * p)281 static inst_code xy_clear(
282 inst *p) {
283 return inst_unsupported;
284 }
285
286 /* Read a sheet full of patches using xy mode */
287 /* DOESN'T use the trigger mode */
288 /* Return the inst error code */
read_xy(inst * p,int pis,int sip,int npatch,char * pname,char * sname,double ox,double oy,double ax,double ay,double aax,double aay,double px,double py,ipatch * vals)289 static inst_code read_xy(
290 inst *p,
291 int pis, /* Passes in strips (letters in sheet) */
292 int sip, /* Steps in pass (numbers in sheet) */
293 int npatch, /* Total patches in strip (skip in last pass) */
294 char *pname, /* Starting pass name (' A' to 'ZZ') */
295 char *sname, /* Starting step name (' 1' to '99') */
296 double ox, double oy, /* Origin of first patch */
297 double ax, double ay, /* pass increment */
298 double aax, double aay, /* pass offset for odd patches */
299 double px, double py, /* step/patch increment */
300 ipatch *vals) { /* Pointer to array of values */
301 return inst_unsupported;
302 }
303
304 /* Read a set of strips (applicable to strip reader) */
305 /* Obeys the trigger mode set */
306 /* Return the inst error code */
read_strip(inst * p,char * name,int npatch,char * pname,int sguide,double pwid,double gwid,double twid,ipatch * vals)307 static inst_code read_strip(
308 inst *p,
309 char *name, /* Strip name (up to 7 chars) */
310 int npatch, /* Number of patches in each pass */
311 char *pname, /* Pass name (3 chars) */
312 int sguide, /* Guide number (decrements by 5) */
313 double pwid, /* Patch width in mm (For DTP20/DTP41) */
314 double gwid, /* Gap width in mm (For DTP20/DTP41) */
315 double twid, /* Trailer width in mm (For DTP41T) */
316 ipatch *vals) { /* Pointer to array of values */
317 return inst_unsupported;
318 }
319
320 /* Read a single sample (applicable to spot instruments) */
321 /* Obeys the trigger mode set */
322 /* Return the inst error code */
read_sample(inst * p,char * name,ipatch * val,instClamping clamp)323 static inst_code read_sample(
324 inst *p,
325 char *name, /* Patch identifier (up to 7 chars) */
326 ipatch *val, /* Pointer to value to be returned */
327 instClamping clamp) { /* NZ to clamp XYZ to be +ve */
328 return inst_unsupported;
329 }
330
331 /* Measure the emissive refresh rate in Hz. */
read_refrate(inst * p,double * ref_rate)332 static inst_code read_refrate(
333 inst *p,
334 double *ref_rate) {
335 if (ref_rate != NULL)
336 *ref_rate = 0.0;
337 return inst_unsupported;
338 }
339
340 /* Determine if a calibration is needed. Returns inst_calt_none if not */
341 /* or unknown, or a mask of the needed calibrations. */
342 /* Default implementation - call through to get_n_a_cals() */
needs_calibration(inst * p)343 static inst_cal_type needs_calibration(
344 inst *p) {
345 inst_cal_type n_cals;
346
347 if (p->get_n_a_cals(p, &n_cals, NULL) != inst_ok)
348 return inst_calt_none;
349
350 return n_cals;
351 }
352
353 /* Return combined mask of of needed or available inst_cal_type's */
354 /* for the current mode. */
inst_get_n_a_cals(struct _inst * p,inst_cal_type * n_cals,inst_cal_type * a_cals)355 inst_code inst_get_n_a_cals(
356 struct _inst *p,
357 inst_cal_type *n_cals,
358 inst_cal_type *a_cals) {
359
360 if (n_cals != NULL)
361 *n_cals = inst_calt_none;
362
363 if (a_cals != NULL)
364 *a_cals = inst_calt_none;
365
366 return inst_ok;
367 }
368
369 /* Request an instrument calibration. */
370 /* DOESN'T use the trigger mode */
371 /* Return inst_unsupported if calibration type is not appropriate. */
calibrate(inst * p,inst_cal_type * calt,inst_cal_cond * calc,inst_calc_id_type * idtype,char id[CALIDLEN])372 static inst_code calibrate(
373 inst *p,
374 inst_cal_type *calt, /* Calibration type to do/remaining */
375 inst_cal_cond *calc, /* Current condition/desired condition */
376 inst_calc_id_type *idtype, /* Condition identifier type */
377 char id[CALIDLEN]) { /* Condition identifier (ie. white reference ID, filter ID) */
378 return inst_unsupported;
379 }
380
381 /* Return a description of the calibration type */
calt2str(inst_cal_type calt)382 char *calt2str(inst_cal_type calt) {
383 calt &= inst_calt_all_mask;
384
385 if (calt & inst_calt_all)
386 return "All";
387 if (calt & inst_calt_needed)
388 return "Needed";
389 if (calt & inst_calt_available)
390 return "Needed";
391 if (calt & inst_calt_wavelength)
392 return "Wavelength";
393 if (calt & inst_calt_ref_white)
394 return "Reflective White or Emissive Dark";
395 if (calt & inst_calt_ref_dark)
396 return "Reflective Light Trap (Black)";
397 if (calt & inst_calt_ref_dark_gl)
398 return "Reflective Gloss";
399 if (calt & inst_calt_emis_offset)
400 return "Emissive Offset";
401 if (calt & inst_calt_emis_ratio)
402 return "Emissive Ratio";
403 if (calt & inst_calt_em_dark)
404 return "Emissive Dark";
405 if (calt & inst_calt_trans_white)
406 return "Transmissive White";
407 if (calt & inst_calt_trans_vwhite)
408 return "Transmissive Variable White";
409 if (calt & inst_calt_trans_dark)
410 return "Transmissive Dark";
411 if (calt & inst_calt_emis_int_time)
412 return "Emissive Integration Time";
413 if (calt & inst_calt_ref_freq)
414 return "Display Refresh Rate";
415
416 return "None or Unknown";
417 }
418
419 /* Measure a display update delay. It is assumed that a */
420 /* black to white change has been made to the displayed color, */
421 /* and this will measure the time it took for the update to */
422 /* (It is assumed that white_change() will be called at the time the patch */
423 /* changes color.) */
424 /* be noticed by the instrument, up to 1.0 seconds. */
425 /* inst_misread will be returned on failure to find a transition. */
meas_delay(inst * p,int * pdispmsec,int * pinstmsec)426 static inst_code meas_delay(
427 inst *p,
428 int *pdispmsec,
429 int *pinstmsec) {
430 return inst_unsupported;
431 }
432
433 /* Call used by other thread to timestamp the transition. */
white_change(struct _inst * p,int init)434 static inst_code white_change(
435 struct _inst *p, int init) {
436 return inst_unsupported;
437 }
438 \
439 /* Return the last calibrated refresh rate in Hz. Returns: */
440 /* inst_unsupported - if this instrument doesn't suport a refresh mode */
441 /* or is unable to retrieve the refresh rate */
442 /* inst_needs_cal - if the refresh rate value is not valid */
443 /* inst_misread - if no refresh rate could be determined */
444 /* inst_ok - on returning a valid reading */
get_refr_rate(struct _inst * p,double * ref_rate)445 static inst_code get_refr_rate(
446 struct _inst *p,
447 double *ref_rate) {
448 if (ref_rate != NULL)
449 *ref_rate = 0.0;
450 return inst_unsupported;
451 }
452
453 /* Set the calibrated refresh rate in Hz. */
454 /* Set refresh rate to 0.0 to mark it as invalid */
455 /* Rates outside the range 5.0 to 150.0 Hz will return an error */
set_refr_rate(struct _inst * p,double ref_rate)456 static inst_code set_refr_rate(
457 struct _inst *p,
458 double ref_rate) {
459 return inst_unsupported;
460 }
461
462 /* Insert a compensation filter in the instrument readings */
463 /* This is typically needed if an adapter is being used, that alters */
464 /* the spectrum of the light reaching the instrument */
465 /* To remove the filter, pass NULL for the filter filename */
comp_filter(inst * p,char * filtername)466 static inst_code comp_filter(
467 inst *p,
468 char *filtername) { /* File containing compensating filter */
469 return inst_unsupported;
470 }
471
472 /* Insert a colorimetric correction matrix in the instrument XYZ readings */
473 /* This is only valid for colorimetric instruments. */
474 /* To remove the matrix, pass NULL for the matrix */
col_cor_mat(struct _inst * p,disptech dtech,int cbid,double mtx[3][3])475 static inst_code col_cor_mat(
476 struct _inst *p,
477 disptech dtech, /* Use disptech_unknown if not known */
478 int cbid, /* Calibration display type base ID needed, 1 if unknown */
479 double mtx[3][3]) { /* XYZ matrix */
480 return inst_unsupported;
481 }
482
483 /* Use a Colorimeter Calibration Spectral Set to set the */
484 /* instrumen calibration. */
485 /* This is only valid for colorimetric instruments. */
486 /* To set calibration back to default, pass NULL for ccss. */
col_cal_spec_set(inst * pp,disptech dtech,xspect * sets,int no_sets)487 static inst_code col_cal_spec_set(
488 inst *pp,
489 disptech dtech, /* Use disptech_unknown if not known */
490 xspect *sets,
491 int no_sets) {
492 return inst_unsupported;
493 }
494
495 /* Supply a user interaction callback function. */
set_uicallback(inst * pp,inst_code (* uicallback)(void * cntx,inst_ui_purp purp),void * cntx)496 static void set_uicallback(
497 inst *pp,
498 inst_code (*uicallback)(void *cntx, inst_ui_purp purp),
499 void *cntx) {
500 pp->uicallback = uicallback;
501 pp->uic_cntx = cntx;
502 }
503
504 /* Supply an asynchronous event callback function. */
set_event_callback(inst * pp,void (* eventcallback)(void * cntx,inst_event_type event),void * cntx)505 static void set_event_callback(
506 inst *pp,
507 void (*eventcallback)(void *cntx, inst_event_type event),
508 void *cntx) {
509 pp->eventcallback = eventcallback;
510 pp->event_cntx = cntx;
511 }
512
513 /* Generic inst error codes interpretation */
inst_interp_error(inst * p,inst_code ec)514 static char *inst_interp_error(inst *p, inst_code ec) {
515 switch(ec & inst_mask) {
516 case inst_ok:
517 return "No error";
518 case inst_notify:
519 return "Notification";
520 case inst_warning:
521 return "Warning";
522 case inst_no_coms:
523 return "Internal error - communications needed but not established";
524 case inst_no_init:
525 return "Internal error - initialisation needed but not done";
526 case inst_internal_error:
527 return "Internal software error";
528 case inst_coms_fail:
529 return "Communications failure";
530 case inst_unknown_model:
531 return "Not expected instrument model";
532 case inst_protocol_error:
533 return "Communication protocol breakdown";
534 case inst_user_abort:
535 return "User hit Abort Key";
536 case inst_user_trig:
537 return "User hit Trigger Key";
538 case inst_misread:
539 return "Measurement misread";
540 case inst_nonesaved:
541 return "No saved data to read";
542 case inst_nochmatch:
543 return "Chart being read doesn't match chart expected";
544 case inst_needs_cal:
545 return "Instrument needs calibration";
546 case inst_cal_setup:
547 return "Instrument needs to be setup for calibration";
548 case inst_unsupported:
549 return "Unsupported function";
550 case inst_unexpected_reply:
551 return "Unexpected Reply";
552 case inst_wrong_config:
553 return "Wrong Sensor Position";
554 case inst_wrong_setup:
555 return "Wrong or conflicting setup";
556 case inst_bad_parameter:
557 return "Bad Parameter Value";
558 case inst_hardware_fail:
559 return "Hardware Failure";
560 case inst_system_error:
561 return "Operating System Error";
562 case inst_other_error:
563 return "Non-specific error";
564 }
565 return "Unknown inst error code";
566 }
567
568 /* Instrument specific error codes interpretation */
interp_error(inst * p,int ec)569 static char *interp_error(inst *p, int ec) {
570 return "interp_error is uninplemented in base class!";
571 }
572
573 /* Return the last serial communication error code */
574 /* (This is used for deciding fallback/retry strategies) */
last_scomerr(inst * p)575 static int last_scomerr(inst *p) {
576 return p->icom->lserr;
577 }
578
579 /* Convert instrument specific inst_wrong_config error to inst_config enum */
config_enum(inst * p,int ec)580 static inst_config config_enum(inst *p, int ec) {
581 return inst_conf_unknown;
582 }
583
584 /* ---------------------------------------------- */
585
586 /* Delete things set/done by new_inst() */
virtual_del(inst * p)587 static inst_code virtual_del(inst *p) {
588
589 #if defined(UNIX_APPLE)
590 osx_latencycritical_end();
591 #endif
592
593 return inst_ok;
594 }
595
596
597 /* Virtual constructor. */
598 /* Return NULL for unknown instrument, */
599 /* or serial instrument if nocoms == 0. */
new_inst(icompath * path,int nocoms,a1log * log,inst_code (* uicallback)(void * cntx,inst_ui_purp purp),void * cntx)600 extern inst *new_inst(
601 icompath *path, /* Device path to instrument */
602 int nocoms, /* Don't open if communications are needed to establish inst type */
603 a1log *log, /* log to use */
604 inst_code (*uicallback)(void *cntx, inst_ui_purp purp), /* optional uicallback */
605 void *cntx /* Context for callback */
606 ) {
607 instType itype = instUnknown; /* Actual type */
608 icoms *icom;
609 inst *p = NULL;
610
611 if (path == NULL) {
612 a1logd(log, 2, "new_inst: got NULL path\n");
613 return NULL;
614 }
615
616 a1logd(log, 2, "new_inst: called with path '%s' type '%s'\n",path->name,inst_sname(path->itype));
617
618 if ((icom = new_icoms(path, log)) == NULL) {
619 a1logd(log, 2, "new_inst: new_icoms failed to open it\n");
620 return NULL;
621 }
622
623
624
625 /* Set instrument type from USB port, if not specified */
626 itype = icom->itype; /* Instrument type if its known from usb/hid */
627
628 #if defined(ENABLE_FAST_SERIAL)
629 if (itype == instUnknown && !nocoms && (icom->dctype & icomt_fastserial)) {
630 itype = fast_ser_inst_type(icom, 1, uicallback, cntx); /* Else type from serial */
631 icom->dctype = (icom->dctype & ~icomt_cat_mask) | inst_category(itype);
632 a1logd(log, 8, "new_inst: fast set '%s' dctype 0x%x\n",icom->name,icom->dctype);
633 }
634 #endif /* ENABLE_FAST_SERIAL */
635
636 #if defined(ENABLE_SERIAL)
637 if (itype == instUnknown && !nocoms) {
638 itype = ser_inst_type(icom, uicallback, cntx); /* Else type from serial */
639 icom->dctype = (icom->dctype & ~icomt_cat_mask) | inst_category(itype);
640 a1logd(log, 8, "new_inst: set '%s' dctype 0x%x\n",icom->name,icom->dctype);
641 }
642 #endif /* ENABLE_SERIAL */
643
644
645 #ifdef ENABLE_SERIAL
646 if (itype == instDTP22)
647 p = (inst *)new_dtp22(icom, itype);
648 else if (itype == instDTP41)
649 p = (inst *)new_dtp41(icom, itype);
650 else if (itype == instDTP51)
651 p = (inst *)new_dtp51(icom, itype);
652 else if (itype == instDTP92)
653 p = (inst *)new_dtp92(icom, itype);
654 else if ((itype == instSpectrolino ) ||
655 (itype == instSpectroScan ) ||
656 (itype == instSpectroScanT))
657 p = (inst *)new_ss(icom, itype);
658 /* NYI
659 else if (itype == instSpectrocam)
660 p = (inst *)new_spc(icom, itype);
661 */
662 #endif /* ENABLE_SERIAL */
663
664 #ifdef ENABLE_FAST_SERIAL
665 if (itype == instSpecbos1201
666 || itype == instSpecbos
667 || itype == instSpectraval)
668 p = (inst *)new_specbos(icom, itype);
669 if (itype == instKleinK10)
670 p = (inst *)new_kleink10(icom, itype);
671 #endif /* ENABLE_SERIAL */
672
673 #ifdef ENABLE_USB
674 if (itype == instDTP94)
675 p = (inst *)new_dtp92(icom, itype);
676 else if (itype == instDTP20)
677 p = (inst *)new_dtp20(icom, itype);
678 else if (itype == instI1Disp1 ||
679 itype == instI1Disp2)
680 p = (inst *)new_i1disp(icom, itype);
681 else if (itype == instI1Disp3)
682 p = (inst *)new_i1d3(icom, itype);
683 else if (itype == instI1Monitor)
684 p = (inst *)new_i1pro(icom, itype);
685 else if ((itype == instI1Pro) ||
686 (itype == instI1Pro2))
687 p = (inst *)new_i1pro(icom, itype);
688 else if (itype == instColorMunki)
689 p = (inst *)new_munki(icom, itype);
690 else if (itype == instSpyder1)
691 p = (inst *)new_spyd2(icom, itype);
692 else if (itype == instSpyder2)
693 p = (inst *)new_spyd2(icom, itype);
694 else if (itype == instSpyder3)
695 p = (inst *)new_spyd2(icom, itype);
696 else if (itype == instSpyder4)
697 p = (inst *)new_spyd2(icom, itype);
698 else if (itype == instSpyder5)
699 p = (inst *)new_spyd2(icom, itype);
700 else if (itype == instEX1)
701 p = (inst *)new_ex1(icom, itype);
702 if (itype == instHuey)
703 p = (inst *)new_huey(icom, itype);
704 else if (itype == instSmile)
705 p = (inst *)new_i1disp(icom, itype);
706 #ifdef ENABLE_FAST_SERIAL
707 else if (itype == instSMCube)
708 p = (inst *)new_smcube(icom, itype);
709 #endif
710 else if (itype == instHCFR)
711 p = (inst *)new_hcfr(icom, itype);
712 else if (itype == instColorHug
713 || itype == instColorHug2)
714 p = (inst *)new_colorhug(icom, itype);
715 #endif /* ENABLE_USB */
716
717
718
719 /* Nothing matched */
720 if (p == NULL) {
721 a1logd(log, 2, "new_inst: instrument type not recognised\n");
722 icom->del(icom);
723 return NULL;
724 }
725
726 p->vdel = virtual_del;
727
728 /* Add default methods if constructor did not supply them */
729 if (p->init_coms == NULL)
730 p->init_coms = init_coms;
731 if (p->init_inst == NULL)
732 p->init_inst = init_inst;
733 if (p->get_itype == NULL)
734 p->get_itype = get_itype;
735 if (p->get_serial_no == NULL)
736 p->get_serial_no = get_serial_no;
737 if (p->capabilities == NULL)
738 p->capabilities = capabilities;
739 if (p->meas_config == NULL)
740 p->meas_config = meas_config;
741 if (p->set_mode == NULL)
742 p->set_mode = set_mode;
743 if (p->get_disptypesel == NULL)
744 p->get_disptypesel = get_disptypesel;
745 if (p->set_disptype == NULL)
746 p->set_disptype = set_disptype;
747 if (p->get_disptechi == NULL)
748 p->get_disptechi = get_disptechi;
749 if (p->get_set_opt == NULL)
750 p->get_set_opt = get_set_opt;
751 if (p->read_chart == NULL)
752 p->read_chart = read_chart;
753 if (p->xy_sheet_release == NULL)
754 p->xy_sheet_release = xy_sheet_release;
755 if (p->xy_sheet_hold == NULL)
756 p->xy_sheet_hold = xy_sheet_hold;
757 if (p->xy_locate_start == NULL)
758 p->xy_locate_start = xy_locate_start;
759 if (p->xy_get_location == NULL)
760 p->xy_get_location = xy_get_location;
761 if (p->xy_locate_end == NULL)
762 p->xy_locate_end = xy_locate_end;
763 if (p->xy_position == NULL)
764 p->xy_position = xy_position;
765 if (p->xy_clear == NULL)
766 p->xy_clear = xy_clear;
767 if (p->read_xy == NULL)
768 p->read_xy = read_xy;
769 if (p->read_strip == NULL)
770 p->read_strip = read_strip;
771 if (p->read_sample == NULL)
772 p->read_sample = read_sample;
773 if (p->read_refrate == NULL)
774 p->read_refrate = read_refrate;
775 if (p->needs_calibration == NULL)
776 p->needs_calibration = needs_calibration;
777 if (p->get_n_a_cals == NULL)
778 p->get_n_a_cals = inst_get_n_a_cals;
779 if (p->calibrate == NULL)
780 p->calibrate = calibrate;
781 if (p->meas_delay == NULL)
782 p->meas_delay = meas_delay;
783 if (p->white_change == NULL)
784 p->white_change = white_change;
785 if (p->get_refr_rate == NULL)
786 p->get_refr_rate = get_refr_rate;
787 if (p->set_refr_rate == NULL)
788 p->set_refr_rate = set_refr_rate;
789 if (p->comp_filter == NULL)
790 p->comp_filter = comp_filter;
791 if (p->col_cor_mat == NULL)
792 p->col_cor_mat = col_cor_mat;
793 if (p->col_cal_spec_set == NULL)
794 p->col_cal_spec_set = col_cal_spec_set;
795 if (p->set_uicallback == NULL)
796 p->set_uicallback = set_uicallback;
797 if (p->set_event_callback == NULL)
798 p->set_event_callback = set_event_callback;
799 if (p->inst_interp_error == NULL)
800 p->inst_interp_error = inst_interp_error;
801 if (p->interp_error == NULL)
802 p->interp_error = interp_error;
803 if (p->last_scomerr == NULL)
804 p->last_scomerr = last_scomerr;
805 if (p->config_enum == NULL)
806 p->config_enum = config_enum;
807
808 /* Set the provided user interaction callback */
809 p->set_uicallback(p, uicallback, cntx);
810
811 #if defined(UNIX_APPLE)
812 osx_latencycritical_start();
813 #endif
814
815 return p;
816 }
817
818 /* --------------------------------------------------- */
819
820 /* Free a display type list */
inst_del_disptype_list(inst_disptypesel * list,int no)821 void inst_del_disptype_list(inst_disptypesel *list, int no) {
822
823 if (list != NULL) {
824 int i;
825 for (i = 0; i < no; i++) {
826 if (list[i].path != NULL)
827 free(list[i].path);
828 if (list[i].sets != NULL)
829 free(list[i].sets);
830 }
831 free(list);
832 }
833 }
834
835 /* Ensure that the list is large enough for n+1 entries (+1 == end marker) */
836 /* Return NULL on malloc error */
expand_dlist(inst_disptypesel * list,int nlist,int * nalist)837 static inst_disptypesel *expand_dlist(inst_disptypesel *list, int nlist, int *nalist) {
838
839 if ((nlist+1) > *nalist) {
840 int xnalist = (2 * nlist + 6);
841 inst_disptypesel *xlist;
842
843 if ((xlist = realloc(list, xnalist * sizeof(inst_disptypesel))) == NULL) {
844 inst_del_disptype_list(list, nlist);
845 *nalist = 0;
846 return NULL;
847 }
848 *nalist = xnalist;
849 list = xlist;
850 }
851
852 /* Set end marker */
853 list[nlist].flags = inst_dtflags_end;
854 list[nlist].cbid = 0;
855 list[nlist].sel[0] = '\000';
856 list[nlist].desc[0] = '\000';
857 list[nlist].refr = 0;
858 list[nlist].ix = 0;
859 list[nlist].cc_cbid = 0;
860 list[nlist].path = NULL;
861 list[nlist].sets = NULL;
862 list[nlist].no_sets = 0;
863
864 return list;
865 }
866
867 /* For each selector we need to:
868
869 check each selector char
870 if already used,
871 remove it.
872 if no selector remain,
873 allocate a free one from the fallback list.
874 mark all used selectors
875
876 We treat the first selector as more important
877 than any aliases that come after it, and the
878 aliases as more important than the fallback list,
879 so we need to do three passes through all the selections.
880 */
881
882 /* Create the display type list */
inst_creat_disptype_list(inst * p,int * pndtlist,inst_disptypesel ** pdtlist,inst_disptypesel * sdtlist,int doccss,int doccmx)883 inst_code inst_creat_disptype_list(inst *p,
884 int *pndtlist, /* Number in returned list */
885 inst_disptypesel **pdtlist, /* Returned list */
886 inst_disptypesel *sdtlist, /* Static list */
887 int doccss, /* Add installed ccss files */
888 int doccmx /* Add matching installed ccmx files */
889 ) {
890 inst_disptypesel *list = NULL;
891 int i, j, k, nlist = 0, nalist = 0;
892 char usels[256]; /* Used selectors */
893 static char *asels = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
894 int fail = 0;
895
896 /* free the old list */
897 inst_del_disptype_list(*pdtlist, *pndtlist);
898 *pdtlist = NULL;
899 *pndtlist = 0;
900
901 for (i = 0; i < 256; i++)
902 usels[i] = ((char)-1);
903 k = 0; /* Next selector index */
904
905 /* First create a list of calibrations and their desired selectors: */
906
907 /* Add entries from the static list and their primary selectors */
908 /* (We're currently assuming that calibrations that the instrument */
909 /* returns are not custom.) */
910 /* Count the number in the static list. */
911 for (i = 0; !(sdtlist[i].flags & inst_dtflags_end); i++) {
912
913 if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL)
914 return inst_internal_error;
915
916 list[nlist-1] = sdtlist[i]; /* Struct copy */
917 }
918
919 /* Add any OEM and custom ccss's */
920 if (doccss) {
921 iccss *ss_list;
922 if ((ss_list = list_iccss(NULL)) == NULL) {
923 free(list);
924 return inst_internal_error;
925 }
926
927 for (i = 0; ss_list[i].path != NULL; i++) {
928
929 if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) {
930 free_iccss(ss_list);
931 return inst_internal_error;
932 }
933
934 list[nlist-1].flags = inst_dtflags_ccss | inst_dtflags_ld | inst_dtflags_wr;
935 if (!ss_list[i].oem)
936 list[nlist-1].flags |= inst_dtflags_custom;
937
938 if (ss_list[i].sel != NULL) {
939 strncpy(list[nlist-1].sel, ss_list[i].sel, INST_DTYPE_SEL_LEN);
940 list[nlist-1].sel[INST_DTYPE_SEL_LEN-1] = '\000';
941 }
942 strncpy(list[nlist-1].desc, ss_list[i].desc, INST_DTYPE_DESC_LEN);
943 list[nlist-1].desc[INST_DTYPE_DESC_LEN-1] = '\000';
944 list[nlist-1].dtech = ss_list[i].dtech;
945 list[nlist-1].refr = ss_list[i].refr;
946 list[nlist-1].ix = 0;
947 list[nlist-1].path = ss_list[i].path; ss_list[i].path = NULL;
948 list[nlist-1].cbid = 0;
949 list[nlist-1].sets = ss_list[i].sets; ss_list[i].sets = NULL;
950 list[nlist-1].no_sets = ss_list[i].no_sets; ss_list[i].no_sets = 0;
951 }
952 free_iccss(ss_list);
953 }
954
955 /* Add any OEM and custom ccmx's */
956 if (doccmx) {
957 iccmx *ss_list;
958
959 /* Just ccmx's for this instrument */
960 if ((ss_list = list_iccmx(p->itype, NULL)) == NULL) {
961 free(list);
962 return inst_internal_error;
963 }
964
965 for (i = 0; ss_list[i].path != NULL; i++) {
966
967 /* Check that there is a matching base calibation */
968 for (j = 0; j < nlist; j++) {
969 if (ss_list[i].cc_cbid != 0
970 && list[j].cbid == ss_list[i].cc_cbid)
971 break;
972 }
973 if (j >= nlist) {
974 a1loge(p->log, 1, "inst_creat_disptype_list can't find cbid %d for '%s'\n",ss_list[i].cc_cbid, ss_list[i].path);
975 continue;
976 }
977
978 if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) {
979 free_iccmx(ss_list);
980 return inst_internal_error;
981 }
982
983 list[nlist-1].flags = inst_dtflags_ccmx | inst_dtflags_ld | inst_dtflags_wr;
984 if (!ss_list[i].oem)
985 list[nlist-1].flags |= inst_dtflags_custom;
986
987 if (ss_list[i].sel != NULL) {
988 strncpy(list[nlist-1].sel, ss_list[i].sel, INST_DTYPE_SEL_LEN);
989 list[nlist-1].sel[INST_DTYPE_SEL_LEN-1] = '\000';
990 }
991 strncpy(list[nlist-1].desc, ss_list[i].desc, INST_DTYPE_DESC_LEN);
992 list[nlist-1].desc[INST_DTYPE_DESC_LEN-1] = '\000';
993 list[nlist-1].dtech = ss_list[i].dtech;
994 list[nlist-1].refr = ss_list[i].refr;
995 list[nlist-1].ix = list[j].ix; /* Copy underlying cal selection from base */
996 list[nlist-1].path = ss_list[i].path; ss_list[i].path = NULL;
997 list[nlist-1].cbid = 0;
998 list[nlist-1].cc_cbid = ss_list[i].cc_cbid;
999 icmCpy3x3(list[nlist-1].mat, ss_list[i].mat);
1000 }
1001 free_iccmx(ss_list);
1002 }
1003
1004 /* Copy candidate selectors to private isel[] list */
1005 for (i = 0; i < nlist; i++) {
1006 strcpy(list[i].isel, list[i].sel);
1007 list[i].sel[0] = '\000';
1008 }
1009
1010 /* Then allocate a slector for each calibration: */
1011
1012 /* Set selectors from primary for cbid or custom first */
1013 for (i = 0; i < nlist; i++) {
1014 if (list[i].cbid > 0
1015 || (list[i].flags & inst_dtflags_custom) != 0) {
1016 disptechs_set_sel(0, i, list[i].sel, list[i].isel, usels, &k, asels);
1017 }
1018 }
1019
1020 /* Set selectors from primary for rest */
1021 for (i = 0; i < nlist; i++)
1022 disptechs_set_sel(0, i, list[i].sel, list[i].isel, usels, &k, asels);
1023
1024 /* Set remaining selectors from primaries or secondaries */
1025 for (i = 0; i < nlist; i++)
1026 disptechs_set_sel(1, i, list[i].sel, list[i].isel, usels, &k, asels);
1027
1028 /* Set remaining from fallback */
1029 for (i = 0; i < nlist; i++) {
1030 disptechs_set_sel(2, i, list[i].sel, list[i].isel, usels, &k, asels);
1031 if (list[i].sel[0] == '\000')
1032 fail = 1;
1033 }
1034
1035 /* Any calibrations that failed to find a character will be left as a nul string */
1036
1037 /* Add alternate selectors if they are free. */
1038 for (;;) {
1039 int more = 0;
1040 for (i = 0; i < nlist; i++) {
1041 /* Add unused secondaries */
1042 disptechs_set_sel(3, i, list[i].sel, list[i].isel, usels, &k, asels);
1043
1044 if (list[i].isel[0] != '\000') { /* Still more secondaries available */
1045 more = 1;
1046 }
1047 }
1048 if (!more)
1049 break;
1050 }
1051
1052 if (pndtlist != NULL)
1053 *pndtlist = nlist;
1054 if (pdtlist != NULL)
1055 *pdtlist = list;
1056
1057 if (fail) {
1058 a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n");
1059 return inst_internal_error;
1060 }
1061
1062 return inst_ok;
1063 }
1064
1065 /* --------------------------------------------------- */
1066
1067 /* Delayed scan-ready prompt handler */
delayed_scan_ready(void * pp)1068 static int delayed_scan_ready(void *pp) {
1069 inst *p = (inst *)pp;
1070
1071 msec_sleep(p->scan_ready_delay);
1072 a1logd(g_log,8, "delayed scan_ready activate\n");
1073
1074 if (p->eventcallback != NULL)
1075 p->eventcallback(p->event_cntx, inst_event_scan_ready);
1076 return 0;
1077 }
1078
1079 /* Issue an inst_event_scan_ready event/prompt after a delay */
issue_scan_ready(inst * p,int delay)1080 void issue_scan_ready(inst *p, int delay) {
1081 a1logd(g_log,8, "msec_scan_ready %d msec\n",delay);
1082
1083 if (p->eventcallback == NULL) /* Hmm. */
1084 return;
1085
1086 if (delay > 0) {
1087 if (p->scan_ready_thread != NULL)
1088 p->scan_ready_thread->del(p->scan_ready_thread);
1089 p->scan_ready_delay = delay;
1090 if ((p->scan_ready_thread = new_athread(delayed_scan_ready, (void *)p)) == NULL)
1091 a1logw(g_log, "msec_scan_ready: Delayed scan_ready failed to create thread\n");
1092 } else {
1093 a1logd(g_log,8, "msec_scan_ready activate\n");
1094 p->eventcallback(p->event_cntx, inst_event_scan_ready);
1095 }
1096 }
1097
1098 /* ============================================================= */
1099 /* CCMX location support */
1100
1101 /* return a list of installed ccmx files. */
1102 /* if itype != instUnknown, return those that match the given instrument. */
1103 /* The list is sorted by description and terminated by a NULL entry. */
1104 /* If no is != NULL, return the number in the list */
1105 /* Return NULL and -1 if there is a malloc error */
list_iccmx(instType itype,int * no)1106 iccmx *list_iccmx(instType itype, int *no) {
1107 int i, j;
1108 iccmx *rv;
1109
1110 char **paths = NULL;
1111 int npaths = 0;
1112
1113
1114 npaths = xdg_bds(NULL, &paths, xdg_data, xdg_read, xdg_user,
1115 "ArgyllCMS/\052.ccmx" XDG_FUDGE "color/\052.ccmx"
1116 );
1117
1118 if ((rv = malloc(sizeof(iccmx) * (npaths + 1))) == NULL) {
1119 a1loge(g_log, 1, "list_iccmx: malloc of paths failed\n");
1120 xdg_free(paths, npaths);
1121 if (no != NULL) *no = -1;
1122 return NULL;
1123 }
1124
1125 for (i = j = 0; i < npaths; i++) {
1126 ccmx *cs;
1127 int len;
1128 char *pp;
1129 disptech dtech;
1130 char *tech, *disp;
1131 int cc_cbid, refr;
1132
1133 if ((cs = new_ccmx()) == NULL) {
1134 a1loge(g_log, 1, "list_iccmx: new_ccmx failed\n");
1135 for (--j; j>= 0; j--) {
1136 free(rv[j].path);
1137 free(rv[j].desc);
1138 }
1139 xdg_free(paths, npaths);
1140 free(rv);
1141 if (no != NULL) *no = -1;
1142 return NULL;
1143 }
1144 if (cs->read_ccmx(cs, paths[i])) {
1145 cs->del(cs);
1146 continue; /* Skip any unreadable ccmx's */
1147 }
1148
1149 /* Skip any that don't match */
1150 if (itype != instUnknown && cs->inst != NULL && inst_enum(cs->inst) != itype) {
1151 cs->del(cs);
1152 continue;
1153 }
1154
1155 a1logd(g_log, 5, "Reading '%s'\n",paths[i]);
1156 if ((tech = cs->tech) == NULL)
1157 tech = "";
1158 if ((disp = cs->disp) == NULL)
1159 disp = "";
1160 cc_cbid = cs->cc_cbid;
1161 dtech = cs->dtech;
1162 refr = cs->refrmode;
1163 len = strlen(tech) + strlen(disp) + 4;
1164 if ((pp = malloc(len)) == NULL) {
1165 a1loge(g_log, 1, "list_iccmx: malloc failed\n");
1166 for (--j; j >= 0; j--) {
1167 free(rv[j].path);
1168 free(rv[j].desc);
1169 }
1170 cs->del(cs);
1171 free(rv);
1172 xdg_free(paths, npaths);
1173 if (no != NULL) *no = -1;
1174 return NULL;
1175 }
1176 if ((rv[j].path = strdup(paths[i])) == NULL) {
1177 a1loge(g_log, 1, "list_iccmx: strdup failed\n");
1178 for (--j; j >= 0; j--) {
1179 free(rv[j].path);
1180 free(rv[j].desc);
1181 }
1182 cs->del(cs);
1183 free(rv);
1184 free(pp);
1185 xdg_free(paths, npaths);
1186 if (no != NULL) *no = -1;
1187 return NULL;
1188 }
1189 strcpy(pp, tech);
1190 strcat(pp, " (");
1191 strcat(pp, disp);
1192 strcat(pp, ")");
1193 rv[j].desc = pp;
1194 rv[j].cc_cbid = cc_cbid;
1195 rv[j].dtech = dtech;
1196 rv[j].refr = refr;
1197 rv[j].sel = cs->sel; cs->sel = NULL;
1198 rv[j].oem = cs->oem;
1199 icmCpy3x3(rv[j].mat, cs->matrix);
1200 cs->del(cs);
1201 j++;
1202 }
1203 xdg_free(paths, npaths);
1204 rv[j].path = NULL;
1205 rv[j].desc = NULL;
1206 rv[j].cc_cbid = 0;
1207 rv[j].dtech = disptech_unknown;
1208 rv[j].refr = -1;
1209 rv[j].sel = NULL;
1210 rv[j].oem = 0;
1211 if (no != NULL)
1212 *no = j;
1213
1214 /* Sort the list */
1215 #define HEAP_COMPARE(A,B) (strcmp(A.desc, B.desc) < 0)
1216 HEAPSORT(iccmx, rv, j)
1217 #undef HEAP_COMPARE
1218
1219 return rv;
1220 }
1221
1222 /* Free up a iccmx list */
free_iccmx(iccmx * list)1223 void free_iccmx(iccmx *list) {
1224 int i;
1225
1226 if (list != NULL) {
1227 for (i = 0; list[i].path != NULL || list[i].desc != NULL; i++) {
1228 if (list[i].path != NULL)
1229 free(list[i].path);
1230 if (list[i].desc != NULL)
1231 free(list[i].desc);
1232 if (list[i].sel != NULL)
1233 free(list[i].sel);
1234 }
1235 free(list);
1236 }
1237 }
1238
1239 /* ============================================================= */
1240 /* CCSS location support */
1241
1242 /* return a list of installed ccss files. */
1243 /* The list is sorted by description and terminated by a NULL entry. */
1244 /* If no is != NULL, return the number in the list */
1245 /* Return NULL and -1 if there is a malloc error */
list_iccss(int * no)1246 iccss *list_iccss(int *no) {
1247 int i, j;
1248 iccss *rv;
1249
1250 char **paths = NULL;
1251 int npaths = 0;
1252
1253
1254 npaths = xdg_bds(NULL, &paths, xdg_data, xdg_read, xdg_user,
1255 "ArgyllCMS/\052.ccss" XDG_FUDGE "color/\052.ccss"
1256 );
1257
1258 if ((rv = malloc(sizeof(iccss) * (npaths + 1))) == NULL) {
1259 a1loge(g_log, 1, "list_iccss: malloc of paths failed\n");
1260 xdg_free(paths, npaths);
1261 if (no != NULL) *no = -1;
1262 return NULL;
1263 }
1264
1265 for (i = j = 0; i < npaths; i++) {
1266 ccss *cs;
1267 int len;
1268 char *pp;
1269 disptech dtech;
1270 char *tech, *disp;
1271 int refr;
1272
1273 if ((cs = new_ccss()) == NULL) {
1274 a1loge(g_log, 1, "list_iccss: new_ccss failed\n");
1275 for (--j; j>= 0; j--) {
1276 free(rv[j].path);
1277 free(rv[j].desc);
1278 }
1279 xdg_free(paths, npaths);
1280 if (no != NULL) *no = -1;
1281 return NULL;
1282 }
1283 if (cs->read_ccss(cs, paths[i])) {
1284 cs->del(cs);
1285 continue; /* Skip any unreadable ccss's */
1286 }
1287
1288 a1logd(g_log, 5, "Reading '%s'\n",paths[i]);
1289 if ((tech = cs->tech) == NULL)
1290 tech = "";
1291 if ((disp = cs->disp) == NULL)
1292 disp = "";
1293 dtech = cs->dtech;
1294 refr = cs->refrmode;
1295 len = strlen(tech) + strlen(disp) + 4;
1296 if ((pp = malloc(len)) == NULL) {
1297 a1loge(g_log, 1, "list_iccss: malloc failed\n");
1298 for (--j; j >= 0; j--) {
1299 free(rv[j].path);
1300 free(rv[j].desc);
1301 }
1302 cs->del(cs);
1303 free(rv);
1304 xdg_free(paths, npaths);
1305 if (no != NULL) *no = -1;
1306 return NULL;
1307 }
1308 if ((rv[j].path = strdup(paths[i])) == NULL) {
1309 a1loge(g_log, 1, "list_iccss: strdup failed\n");
1310 for (--j; j >= 0; j--) {
1311 free(rv[j].path);
1312 free(rv[j].desc);
1313 }
1314 cs->del(cs);
1315 free(rv);
1316 free(pp);
1317 xdg_free(paths, npaths);
1318 if (no != NULL) *no = -1;
1319 return NULL;
1320 }
1321 strcpy(pp, tech);
1322 strcat(pp, " (");
1323 strcat(pp, disp);
1324 strcat(pp, ")");
1325 rv[j].desc = pp;
1326 rv[j].dtech = dtech;
1327 rv[j].refr = refr;
1328 rv[j].sel = cs->sel; cs->sel = NULL;
1329 rv[j].oem = cs->oem;
1330 rv[j].sets = cs->samples; cs->samples = NULL;
1331 rv[j].no_sets = cs->no_samp; cs->no_samp = 0;
1332 cs->del(cs);
1333 j++;
1334 }
1335 xdg_free(paths, npaths);
1336 rv[j].path = NULL;
1337 rv[j].desc = NULL;
1338 rv[j].dtech = disptech_unknown;
1339 rv[j].refr = -1;
1340 rv[j].sel = NULL;
1341 rv[j].oem = 0;
1342 rv[j].sets = NULL;
1343 rv[j].no_sets = 0;
1344 if (no != NULL)
1345 *no = j;
1346
1347 /* Sort the list */
1348 #define HEAP_COMPARE(A,B) (strcmp(A.desc, B.desc) < 0)
1349 HEAPSORT(iccss, rv, j)
1350 #undef HEAP_COMPARE
1351
1352 return rv;
1353 }
1354
1355 /* Free up a iccss list */
free_iccss(iccss * list)1356 void free_iccss(iccss *list) {
1357 int i;
1358
1359 if (list != NULL) {
1360 for (i = 0; list[i].path != NULL || list[i].desc != NULL; i++) {
1361 if (list[i].path != NULL)
1362 free(list[i].path);
1363 if (list[i].desc != NULL)
1364 free(list[i].desc);
1365 if (list[i].sel != NULL)
1366 free(list[i].sel);
1367 if (list[i].sets != NULL)
1368 free(list[i].sets);
1369 }
1370 free(list);
1371 }
1372 }
1373
1374 /* ============================================================= */
1375 /* Detect fast serial instruments */
1376
1377 #ifdef ENABLE_FAST_SERIAL
1378 static void hex2bin(char *buf, int len);
1379
1380 /* Heuristicly determine the instrument type for */
1381 /* a fast serial connection, and instUnknown if not serial. */
1382 /* Set it in icoms and also return it. */
fast_ser_inst_type(icoms * p,int tryhard,inst_code (* uicallback)(void * cntx,inst_ui_purp purp),void * cntx)1383 instType fast_ser_inst_type(
1384 icoms *p,
1385 int tryhard,
1386 inst_code (*uicallback)(void *cntx, inst_ui_purp purp), /* optional uicallback */
1387 void *cntx /* Context for callback */
1388 ) {
1389 instType rv = instUnknown;
1390 #define BUFSZ (2048 + 10)
1391 char buf[BUFSZ];
1392 baud_rate brt1[] = { baud_9600, baud_921600, baud_115200,
1393 baud_38400, baud_nc }; /* HS - do K10/Spectrolino first */
1394 baud_rate brt2[] = { baud_115200, baud_nc }; /* Bluetooth */
1395
1396 baud_rate *brt = brt1;
1397 unsigned int etime;
1398 unsigned int i;
1399 int delayms = 0;
1400 int se, len;
1401 double tryto = 0.1; /* [0.1] Communication timout */
1402 // double tryto = 0.9; /* Communication timout (test) */
1403
1404 a1logd(p->log, 8, "fast_ser_inst_type: on '%s' dctype 0x%x\n",p->name,p->dctype);
1405
1406 if (!(p->dctype & icomt_seriallike)
1407 && !(p->dctype & icomt_fastserial)) {
1408 return p->itype;
1409 }
1410
1411 /* The tick to give up on */
1412 etime = msec_time() + (long)(2000.0 + 0.5);
1413 // etime = msec_time() + (long)(20000.0 + 0.5); /* (test) */
1414
1415 a1logd(p->log, 1, "fser_inst_type: Trying different baud rates (%u msec to go) Path %s%s\n",
1416 etime - msec_time(),p->spath,(p->dctype & icomt_btserial) ? " [Bluetooth]" : "");
1417
1418 if (p->dctype & icomt_btserial) {
1419 brt = brt2; /* Only try BT relevant baud rates. */
1420 delayms = 600; /* Spectraval locks up otherwise. */
1421 }
1422
1423 /* Until we time out, find the correct baud rate */
1424 for (i = 0; msec_time() < etime; i++) {
1425 if (brt[i] == baud_nc) {
1426 i = 0;
1427 if (!tryhard)
1428 break; /* try only once */
1429 }
1430 a1logd(p->log, 5, "Trying %s baud\n",baud_rate_to_str(brt[i]));
1431
1432 if ((se = p->set_ser_port_ex(p, fc_None, brt[i], parity_none,
1433 stop_1, length_8, delayms)) != ICOM_OK) {
1434 a1logd(p->log, 5, "fser_inst_type: set_ser_port failed with 0x%x\n",se);
1435 return instUnknown; /* Give up on port */
1436 }
1437
1438 /* Assume Klein K10 only uses 9600. */
1439 /* We need to also assume that we might be talking to a Spectrolino, */
1440 /* and avoid upsetting it. */
1441 if ((p->dctype & icomt_btserial) == 0 && brt[i] == baud_9600) {
1442 double sto = 0.2; /* Give 9600 a little more time */
1443 int bread, len;
1444
1445 /* Try a spectrolino/spectroscan command first */
1446 if (tryto > sto)
1447 sto = tryto;
1448 p->write_read_ex(p, ";D024\r\n", 0, buf, BUFSZ-1, &bread, "\r", 1, sto, 1);
1449
1450 if (bread == 0) {
1451 a1logd(p->log, 5, "fser_inst_type: Spectroino command returned nothing\n");
1452 goto not_k10;
1453 }
1454 buf[bread] = '\000';
1455 len = strlen(buf);
1456
1457 a1logd(p->log, 5, "fser_inst_type: got %d bytes\n",len);
1458
1459 if (len < 4) {
1460 a1logd(p->log, 5, "fser_inst_type: Reply was too short\n");
1461 goto not_k10; /* Not K10, X-Rite or Spectrolino */
1462 }
1463
1464 /* Is this a Spectrolino or Spectroscan error resonse ? */
1465 if (len >= 5 && strncmp(buf, ":26", 3) == 0
1466 || len >= 7 && strncmp(buf, ":D183", 5) == 0) {
1467 a1logd(p->log, 5, "fser_inst_type: Ignore Spectrolino\n");
1468 return instUnknown; /* Not doing Spectrolino detection here */
1469 }
1470
1471 /* Is this an X-Rite error value such as "<01>" ? */
1472 if (buf[0] == '<' && isdigit(buf[1]) && isdigit(buf[2]) && buf[3] == '>') {
1473 a1logd(p->log, 5, "fser_inst_type: Ignore X-Rite\n");
1474 return instUnknown; /* Not doing X-Rite detection here */
1475 }
1476
1477 /* The Klein K10 seems to respond with it's calibration list, preceeded by "D4" ? */
1478 if (buf[0] != 'D' || buf[1] != '4') {
1479 a1logd(p->log, 5, "fser_inst_type: Not Klein response\n");
1480 goto not_k10;;
1481 }
1482
1483 a1logd(p->log, 5, "fser_inst_type: Looks like it may be a Klein\n");
1484
1485 /* Confirm this is a Klein instrument */
1486
1487 /* The first response is the calibration list, and it may need flushing. */
1488 /* (write_read_ex doesn't cope with time it takes to dump this.) */
1489 for (;;) {
1490 bread = 0;
1491 p->read(p, buf, BUFSZ, &bread, NULL, BUFSZ, 0.1);
1492 if (bread == 0)
1493 break;
1494 }
1495
1496 if ((se = p->write_read_ex(p, "P0\r", 0, buf, BUFSZ, NULL, ">", 1, tryto, 1)) == inst_ok) {
1497
1498 /* Is this a Klein K1/K8/K10 response ? */
1499 if (strncmp(buf, "P0K-1 ", 6) == 0
1500 || strncmp(buf, "P0K-8 ", 6) == 0
1501 || strncmp(buf, "P0K-10", 6) == 0
1502 || strncmp(buf, "P0KV-10", 7) == 0) {
1503 a1logd(p->log, 5, "fser_inst_type: found Klein K1/K8/K10\n");
1504 rv = instKleinK10;
1505 break;
1506 }
1507 }
1508
1509 not_k10:;
1510
1511 /* Check for user abort */
1512 if (uicallback != NULL) {
1513 inst_code ev;
1514 if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) {
1515 a1logd(p->log, 5, "fser_inst_type: User aborted\n");
1516 return instUnknown;
1517 }
1518 }
1519 }
1520
1521 /* SwatchMate Cube only uses 38400 */
1522 if ((p->dctype & icomt_btserial) == 0 && brt[i] == baud_38400) {
1523 int bread;
1524
1525 /* See if it's a SwatchMate Cube. */
1526 /* The Cube uses RTS/CTS handshaking, but ignore this for identification. */
1527 buf[0] = 0x7e;
1528 buf[1] = 0x00;
1529 buf[2] = 0x02; /* Ping command */
1530 buf[3] = 0x00;
1531 if ((se = p->write_read_ex(p, buf, 4, buf, BUFSZ, &bread, NULL, 4, tryto, 1)) == inst_ok) {
1532 if (bread == 4) {
1533 if (buf[0] == 0x7e && buf[1] == 0x20 && buf[2] == 0x02 && buf[3] == 0x00) {
1534 a1logd(p->log, 5, "fser_inst_type: found SwatchMate Cube\n");
1535 rv = instSMCube;
1536 break;
1537 }
1538 }
1539 }
1540
1541 /* Check for user abort */
1542 if (uicallback != NULL) {
1543 inst_code ev;
1544 if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) {
1545 a1logd(p->log, 5, "fser_inst_type: User aborted\n");
1546 return instUnknown;
1547 }
1548 }
1549 }
1550
1551 /* JETI specbos or spectraval. */
1552 /* We are fudging the baud rate selection here - */
1553 /* the 1211 RS and BT can't handle 921600, */
1554 /* while the 15x1 can handle 230400 & 3000000, which we don't test for... */
1555 // if ((p->dctype & icomt_btserial) == 0 || brt[i] == baud_115200)
1556 if (brt[i] == baud_38400 || brt[i] == baud_115200 || brt[i] == baud_921600)
1557 {
1558 int bread;
1559
1560 /* See if it's a JETI specbos */
1561 p->write_read_ex(p, "*idn?\r", 0, buf, BUFSZ, &bread, "\r", 1, tryto, 1);
1562 if (bread > 0) {
1563 len = strlen(buf);
1564
1565 /* JETI specbos returns "JETI_SBXXXX", where XXXX is the instrument type, */
1566 /* except for the 1201 which returns "SB05" */
1567 /* The spectraval 1501 returns JETI_SDCM3 NNNNNNN */
1568
1569 /* Over Bluetooth, we get an erronious string "AT+JSCR\r\n" mixed in our output. */
1570 /* This would appear to be from the eBMU Bluetooth adapter AT command set. */
1571 if (len > 9 && strncmp(buf, "AT+JSCR\r\n", 9) == 0) {
1572 memmove(buf, buf+9, len-9);
1573 len -= 9;
1574 }
1575
1576 /* Is this a JETI specbos 1201 response ? */
1577 if (strncmp(buf, "SB05", 4) == 0) {
1578 a1logd(p->log, 5, "fser_inst_type: found JETI specbos 1201\n");
1579 rv = instSpecbos1201;
1580 break;
1581 }
1582 /* Is this a JETI specbos XXXX response ? */
1583 if (len >= 11 && strncmp(buf, "JETI_SB", 7) == 0) {
1584 a1logd(p->log, 5, "fser_inst_type: found JETI specbos\n");
1585 rv = instSpecbos;
1586 break;
1587 }
1588 /* Is this a JETI spectraval response ? */
1589 /* (Bluetooth returns "DCM3_JETI ... and other rubbish for some reason.) */
1590 if ((len >= 10 && strncmp(buf, "JETI_SDCM3", 10) == 0)
1591 || (len >= 9 && strncmp(buf, "DCM3_JETI", 9) == 0)
1592 || (len >= 17 && strncmp(buf, "PECFIRM_JETI_1501", 17) == 0)
1593 || (len >= 18 && strncmp(buf, "SPECFIRM_JETI_1501", 18) == 0)) {
1594 a1logd(p->log, 5, "fser_inst_type: found JETI spectraval\n");
1595 rv = instSpectraval;
1596 break;
1597 }
1598 }
1599 /* Check for user abort */
1600 if (uicallback != NULL) {
1601 inst_code ev;
1602 if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) {
1603 a1logd(p->log, 5, "fser_inst_type: User aborted\n");
1604 return instUnknown;
1605 }
1606 }
1607 }
1608 }
1609
1610 if (rv == instUnknown
1611 && msec_time() >= etime) { /* We haven't established comms */
1612 a1logd(p->log, 5, "fser_inst_type: Failed to establish coms\n");
1613 p->itype = rv;
1614 return instUnknown;
1615 }
1616
1617 a1logd(p->log, 5, "fser_inst_type: Instrument type is '%s'\n", inst_name(rv));
1618
1619 p->itype = rv;
1620
1621 return rv;
1622 }
1623 #undef BUFSZ
1624
1625 #endif /* ENABLE_FAST_SERIAL */
1626
1627 /* ============================================================= */
1628 /* Detect serial instruments */
1629
1630 #ifdef ENABLE_SERIAL
1631 static void hex2bin(char *buf, int len);
1632
1633 /* Heuristicly determine the instrument type for */
1634 /* a serial connection, and instUnknown if not serial. */
1635 /* Set it in icoms and also return it. */
ser_inst_type(icoms * p,inst_code (* uicallback)(void * cntx,inst_ui_purp purp),void * cntx)1636 static instType ser_inst_type(
1637 icoms *p,
1638 inst_code (*uicallback)(void *cntx, inst_ui_purp purp), /* optional uicallback */
1639 void *cntx /* Context for callback */
1640 ) {
1641 instType rv = instUnknown;
1642 #define BUFSZ (128 + 10)
1643 char buf[BUFSZ];
1644 baud_rate brt[] = { baud_9600, baud_19200, baud_4800, baud_2400,
1645 baud_1200, baud_38400, baud_57600, baud_115200,
1646 baud_600, baud_300, baud_110, baud_nc };
1647 unsigned int etime;
1648 unsigned int bi, i;
1649 int se, len, bread;
1650 int klein = 0;
1651 int xrite = 0;
1652 int ss = 0;
1653 int so = 0;
1654
1655 #ifdef ENABLE_USB
1656 if (p->usbd != NULL || p->hidd != NULL)
1657 return p->itype;
1658 #endif /* ENABLE_USB */
1659
1660 bi = 0;
1661
1662 /* The tick to give up on */
1663 etime = msec_time() + (long)(20.0 * 1000.0 + 0.5);
1664
1665 a1logd(p->log, 1, "ser_inst_type: Trying different baud rates (%u msec to go)\n",etime - msec_time());
1666
1667 /* Until we time out, find the correct baud rate */
1668 for (i = bi; msec_time() < etime; i++) {
1669 if (brt[i] == baud_nc)
1670 i = 0;
1671 if ((se = p->set_ser_port(p, fc_None, brt[i], parity_none,
1672 stop_1, length_8)) != ICOM_OK) {
1673 a1logd(p->log, 5, "ser_inst_type: set_ser_port failed with 0x%x\n",se);
1674 return instUnknown; /* Give up */
1675 }
1676
1677 a1logd(p->log, 5, "Trying %s baud\n",baud_rate_to_str(brt[i]));
1678 bread = 0;
1679
1680 /* Try a spectrolino/spectroscan command first */
1681 p->write_read_ex(p, ";D024\r\n", 0, buf, BUFSZ-1, &bread, "\r", 1, 0.5, 1);
1682
1683 if (bread == 0) {
1684 /* Check for user abort */
1685 if (uicallback != NULL) {
1686 inst_code ev;
1687 if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) {
1688 a1logd(p->log, 5, "ser_inst_type: User aborted\n");
1689 return instUnknown;
1690 }
1691 }
1692 continue;
1693 }
1694 buf[bread] = '\000';
1695 len = strlen(buf);
1696
1697 // a1logd(p->log, 5, "len = %d\n",len);
1698 if (len < 4)
1699 continue;
1700
1701 /* The Klein K10 seems to respond with it's calibration list, preceeded by "D4" ? */
1702 /* - don't know how reliable this is though. Another way may be to look for a */
1703 /* response len > 100 characters ?? */
1704 if (buf[0] == 'D' && buf[1] == '4') {
1705 // a1logd(p->log, 5, "klein\n");
1706 klein = 1;
1707 break;
1708 }
1709
1710 /* Is this an X-Rite error value such as "<01>" ? */
1711 if (buf[0] == '<' && isdigit(buf[1]) && isdigit(buf[2]) && buf[3] == '>') {
1712 // a1logd(p->log, 5, "xrite\n");
1713 xrite = 1;
1714 break;
1715 }
1716
1717 /* Is this a Spectrolino error resonse ? */
1718 if (len >= 5 && strncmp(buf, ":26", 3) == 0) {
1719 // a1logd(p->log, 5, "spectrolino\n");
1720 so = 1;
1721 break;
1722 }
1723 /* Is this a SpectroScan response ? */
1724 if (len >= 7 && strncmp(buf, ":D183", 5) == 0) {
1725 // a1logd(p->log, 5, "spectroscan\n");
1726 ss = 1;
1727 break;
1728 }
1729 }
1730
1731 if (rv == instUnknown
1732 && msec_time() >= etime) { /* We haven't established comms */
1733 a1logd(p->log, 5, "ser_inst_type: Failed to establish coms\n");
1734 return instUnknown;
1735 }
1736
1737 a1logd(p->log, 5, "ser_inst_type: Got coms with instrument\n");
1738
1739 /* Spectrolino */
1740 if (so) {
1741 rv = instSpectrolino;
1742 }
1743
1744 /* SpectroScan */
1745 if (ss) {
1746 rv = instSpectroScan;
1747 if ((se = p->write_read_ex(p, ";D030\r\n", 0, buf, BUFSZ, NULL, "\n", 1, 1.5, 1)) == 0) {
1748 if (strlen(buf) >= 41) {
1749 hex2bin(&buf[5], 12);
1750 // a1logd(p->log, 5, "spectroscan type = '%s'\n",buf);
1751 if (strncmp(buf, ":D190SpectroScanT", 17) == 0)
1752 rv = instSpectroScanT;
1753 }
1754 }
1755 }
1756 if (xrite) {
1757
1758 /* Get the X-Rite model and version number */
1759 if ((se = p->write_read_ex(p, "SV\r\n", 0, buf, BUFSZ, NULL, ">", 1, 2.5, 1)) != 0)
1760 return instUnknown;
1761
1762 if (strlen(buf) >= 12) {
1763 if (strncmp(buf,"X-Rite DTP22",12) == 0)
1764 rv = instDTP22;
1765 if (strncmp(buf,"X-Rite DTP41",12) == 0)
1766 rv = instDTP41;
1767 if (strncmp(buf,"X-Rite DTP42",12) == 0)
1768 rv = instDTP41;
1769 if (strncmp(buf,"X-Rite DTP51",12) == 0)
1770 rv = instDTP51;
1771 if (strncmp(buf,"X-Rite DTP52",12) == 0)
1772 rv = instDTP51;
1773 if (strncmp(buf,"X-Rite DTP92",12) == 0)
1774 rv = instDTP92;
1775 if (strncmp(buf,"X-Rite DTP94",12) == 0)
1776 rv = instDTP94;
1777 }
1778 }
1779
1780 if (klein) {
1781
1782 /* The first response is the calibration list, and it may need flushing. */
1783 /* (write_read_ex doesn't cope with time it takes to dump this.) */
1784 for (;;) {
1785 bread = 0;
1786 p->read(p, buf, BUFSZ, &bread, NULL, BUFSZ, 0.1);
1787 if (bread == 0)
1788 break;
1789 }
1790
1791 /* See if this is a Klein K10 or similar */
1792 if ((se = p->write_read_ex(p, "P0\r", 0, buf, BUFSZ, NULL, ">", 1, 1.5, 1)) != inst_ok)
1793 return instUnknown;
1794
1795 /* Is this a Klein K1/K8/K10 response ? */
1796 if (strncmp(buf, "P0K-1 ", 6) == 0
1797 || strncmp(buf, "P0K-8 ", 6) == 0
1798 || strncmp(buf, "P0K-10", 6) == 0
1799 || strncmp(buf, "P0KV-10", 7) == 0) {
1800 a1logd(p->log, 5, "fser_inst_type: found Klein K1/K8/K10\n");
1801 rv = instKleinK10;
1802 }
1803 }
1804
1805 a1logd(p->log, 5, "ser_inst_type: Instrument type is '%s'\n", inst_name(rv));
1806
1807 p->close_port(p); /* Or should we leave it open ?? */
1808
1809 p->itype = rv;
1810
1811 return rv;
1812 }
1813 #undef BUFSZ
1814
1815 #endif /* ENABLE_SERIAL */
1816
1817 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
1818
1819 /* Convert an ASCII Hex character to an integer. */
h2b(char c)1820 static int h2b(char c) {
1821 if (c >= '0' && c <= '9')
1822 return (c-(int)'0');
1823 if (c >= 'A' && c <= 'F')
1824 return (10 + c-(int)'A');
1825 if (c >= 'a' && c <= 'f')
1826 return (10 + c-(int)'a');
1827 return 0;
1828 }
1829
1830 /* Convert a Hex encoded buffer into binary. */
1831 /* len is number of bytes out */
hex2bin(char * buf,int len)1832 static void hex2bin(char *buf, int len) {
1833 int i;
1834
1835 for (i = 0; i < len; i++) {
1836 buf[i] = (char)((h2b(buf[2 * i + 0]) << 4)
1837 | (h2b(buf[2 * i + 1]) << 0));
1838 }
1839 }
1840
1841 #endif /* ENABLE_SERIAL */
1842
1843 /* ============================================================= */
1844 /* inst_mode persistent storage support */
1845
1846 /* Table listing and relating masks to symbols. */
1847 /* Need to keep this in sync with inst.h */
1848 struct {
1849 int mode; /* Mode bits */
1850 char *sym; /* 4 character symbol */
1851 } inst_mode_sym[] = {
1852 { inst_mode_reflection, inst_mode_reflection_sym },
1853 { inst_mode_s_reflection, inst_mode_s_reflection_sym },
1854 { inst_mode_transmission, inst_mode_transmission_sym },
1855 { inst_mode_emission, inst_mode_emission_sym },
1856
1857 { inst_mode_spot, inst_mode_spot_sym },
1858 { inst_mode_strip, inst_mode_strip_sym },
1859 { inst_mode_xy, inst_mode_xy_sym },
1860 { inst_mode_chart, inst_mode_chart_sym },
1861 { inst_mode_ambient, inst_mode_ambient_sym },
1862 { inst_mode_ambient_flash, inst_mode_ambient_flash_sym },
1863 { inst_mode_tele, inst_mode_tele_sym },
1864
1865 { inst_mode_emis_nonadaptive, inst_mode_emis_nonadaptive_sys },
1866 { inst_mode_ref_uv, inst_mode_ref_uv_sym },
1867 { inst_mode_emis_refresh_ovd, inst_mode_emis_refresh_ovd_sym },
1868 { inst_mode_emis_norefresh_ovd, inst_mode_emis_norefresh_ovd_sym },
1869
1870 { inst_mode_colorimeter, inst_mode_colorimeter_sym },
1871 { inst_mode_spectral, inst_mode_spectral_sym },
1872 { inst_mode_highres, inst_mode_highres_sym },
1873
1874 { inst_mode_calibration, inst_mode_calibration_sym },
1875
1876 { 0, NULL }
1877 };
1878
1879 /* Return a string with a symbolic encoding of the mode flags */
inst_mode_to_sym(char sym[MAX_INST_MODE_SYM_SZ],inst_mode mode)1880 void inst_mode_to_sym(char sym[MAX_INST_MODE_SYM_SZ], inst_mode mode) {
1881 int i;
1882 char *cp = sym;
1883
1884 for (i = 0; inst_mode_sym[i].mode != 0; i++) {
1885 if (mode & inst_mode_sym[i].mode) {
1886 if (cp != sym)
1887 *cp++ = '_';
1888 strncpy(cp, inst_mode_sym[i].sym, 4);
1889 cp += 4;
1890 }
1891 }
1892 *cp++ = '\000';
1893 }
1894
1895 /* Return a set of mode flags that correspond to the symbolic encoding */
1896 /* Return nz if a symbol wasn't recognized */
sym_to_inst_mode(inst_mode * mode,const char * sym)1897 int sym_to_inst_mode(inst_mode *mode, const char *sym) {
1898 int i;
1899 const char *cp = sym;
1900 int rv = 0;
1901
1902 for (*mode = 0;;) {
1903 if (cp[0] == '\000' || cp[1] == '\000' || cp[2] == '\000' || cp[3] == '\000')
1904 break;
1905
1906 for (i = 0; inst_mode_sym[i].mode != 0; i++) {
1907 if (strncmp(inst_mode_sym[i].sym, cp, 4) == 0) {
1908 *mode |= inst_mode_sym[i].mode;
1909 break;
1910 }
1911 }
1912 if (inst_mode_sym[i].mode == 0)
1913 rv = 1;
1914
1915 cp += 4;
1916
1917 if (*cp == '_')
1918 cp++;
1919 }
1920
1921 return rv;
1922 }
1923
1924 /* ============================================================= */
1925 /* Utilities */
1926
1927 /* Return a string for the xcalstd enum */
xcalstd2str(xcalstd std)1928 char *xcalstd2str(xcalstd std) {
1929 switch(std) {
1930 case xcalstd_native:
1931 return "NATIVE";
1932 case xcalstd_xrdi:
1933 return "XRDI";
1934 case xcalstd_gmdi:
1935 return "GMDI";
1936 case xcalstd_xrga:
1937 return "XRGA";
1938 default:
1939 break;
1940 }
1941 return "None";
1942 }
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965