summaryrefslogtreecommitdiff
path: root/spectro/inst.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/inst.c')
-rw-r--r--spectro/inst.c1359
1 files changed, 1359 insertions, 0 deletions
diff --git a/spectro/inst.c b/spectro/inst.c
new file mode 100644
index 0000000..d57a84a
--- /dev/null
+++ b/spectro/inst.c
@@ -0,0 +1,1359 @@
+
+ /* Abstract instrument class implemenation */
+
+/*
+ * Argyll Color Correction System
+ *
+ * Author: Graeme W. Gill
+ * Date: 10/3/2001
+ *
+ * Copyright 2001 - 2013 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
+ * see the License2.txt file for licencing details.
+ */
+
+/*
+ If you make use of the instrument driver code here, please note
+ that it is the author(s) of the code who take responsibility
+ for its operation. Any problems or queries regarding driving
+ instruments with the Argyll drivers, should be directed to
+ the Argyll's author(s), and not to any other party.
+
+ If there is some instrument feature or function that you
+ would like supported here, it is recommended that you
+ contact Argyll's author(s) first, rather than attempt to
+ modify the software yourself, if you don't have firm knowledge
+ of the instrument communicate protocols. There is a chance
+ that an instrument could be damaged by an incautious command
+ sequence, and the instrument companies generally cannot and
+ will not support developers that they have not qualified
+ and agreed to support.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#ifndef SALONEINSTLIB
+#include "copyright.h"
+#include "aconfig.h"
+#else
+#include "sa_config.h"
+#endif /* !SALONEINSTLIB */
+#include "numsup.h"
+#include "xspect.h"
+#include "ccmx.h"
+#include "ccss.h"
+#include "conv.h"
+#include "insttypes.h"
+
+#include "icoms.h"
+#include "inst.h"
+#include "insttypeinst.h"
+
+#include "sort.h"
+
+#ifdef ENABLE_SERIAL
+static instType ser_inst_type(icoms *p,
+inst_code (*uicallback)(void *cntx, inst_ui_purp purp), void *cntx);
+#endif /* ENABLE_SERIAL */
+
+/* ------------------------------------ */
+/* Default methods for instrument class */
+
+/* Establish communications at the indicated baud rate. */
+/* Timout in to seconds, and return non-zero error code */
+static inst_code init_coms(
+inst *p,
+baud_rate br, /* Baud rate */
+flow_control fc, /* Flow control */
+double tout) { /* Timeout */
+ return inst_unsupported;
+}
+
+/* Initialise or re-initialise the INST */
+/* return non-zero on an error, with inst error code */
+static inst_code init_inst(
+inst *p) {
+ return inst_unsupported;
+}
+
+/* Return the instrument type */
+static instType get_itype(inst *p) {
+ if (p != NULL)
+ return p->itype;
+ return instUnknown;
+}
+
+/* Return the instrument serial number. */
+/* (This will be an empty string if there is no serial no) */
+static char *get_serial_no(inst *p) {
+ return "";
+}
+
+/* Return the instrument mode capabilities */
+static void capabilities(inst *p, inst_mode *cap1,
+ inst2_capability *cap2, inst3_capability *cap3) {
+ if (cap1 != NULL)
+ *cap1 = inst_mode_none;
+ if (cap2 != NULL)
+ *cap2 = inst2_none;
+ if (cap3 != NULL)
+ *cap3 = inst3_none;
+}
+
+/* Return current or given configuration available measurement modes. */
+/* This default routine assumed one confiration which will be */
+/* all the measurement modes returned by capabilities(). */
+static inst_code meas_config(inst *p,
+inst_mode *mmodes, /* Return all the valid measurement modes */
+ /* for the current configuration */
+inst_cal_cond *cconds, /* Return the valid calibration conditions */
+int *conf_ix) { /* Request mode for given configuration, and */
+ /* return the index of the configuration returned */
+ if (mmodes != NULL) {
+ *mmodes = inst_mode_none;
+
+ /* Available measurement modes is current capabilities */
+ p->capabilities(p, mmodes, NULL, NULL);
+ }
+
+ if (cconds != NULL)
+ *cconds = inst_calc_unknown;
+
+ if (conf_ix != NULL)
+ *conf_ix = 0;
+
+ return inst_ok;
+}
+
+/* Check the device measurement mode */
+static inst_code check_mode(
+inst *p,
+inst_mode m) { /* Requested mode */
+ return inst_unsupported;
+}
+
+/* Set the device measurement mode */
+static inst_code set_mode(
+inst *p,
+inst_mode m) { /* Requested mode */
+ return inst_unsupported;
+}
+
+/* Return array of display type selectors */
+static inst_code get_disptypesel(
+inst *pp,
+int *pnsels, /* Return number of display types */
+inst_disptypesel **psels, /* Return the array of display types */
+int allconfig, /* nz to return list for all configs, not just current. */
+int recreate /* nz to re-check for new ccmx & ccss files */
+) {
+ return inst_unsupported;
+}
+
+/* Set the display type */
+static inst_code set_disptype(
+inst *pp,
+int ix /* index into the inst_disptypesel[] */
+) {
+ return inst_unsupported;
+}
+
+/* Get a status or set or get an option (default implementation) */
+inst_code inst_get_set_opt_def(
+inst *p,
+inst_opt_type m, /* Requested option type */
+va_list args) { /* Option parameters */
+
+ return inst_unsupported;
+}
+
+/* Get a status or set or get an option */
+static inst_code get_set_opt(
+inst *p,
+inst_opt_type m, /* Requested status type */
+...) { /* Status parameters */
+ inst_code rv;
+ va_list args;
+
+ va_start(args, m);
+ rv = inst_get_set_opt_def(p, m, args); /* Call the above */
+ va_end(args);
+
+ return rv;
+}
+
+/* Read a full test chart composed of multiple sheets */
+/* DOESN'T use the trigger mode */
+/* Return the inst error code */
+static inst_code read_chart(
+inst *p,
+int npatch, /* Total patches/values in chart */
+int pich, /* Passes (rows) in chart */
+int sip, /* Steps in each pass (patches in each row) */
+int *pis, /* Passes in each strip (rows in each sheet) */
+int chid, /* Chart ID number */
+ipatch *vals) { /* Pointer to array of values */
+
+ return inst_unsupported;
+}
+
+/* For an xy instrument, release the paper */
+/* (if cap has inst_xy_holdrel) */
+/* Return the inst error code */
+static inst_code xy_sheet_release(
+inst *p) {
+ return inst_unsupported;
+}
+
+/* For an xy instrument, hold the paper */
+/* (if cap has inst_xy_holdrel) */
+/* Return the inst error code */
+static inst_code xy_sheet_hold(
+inst *p) {
+ return inst_unsupported;
+}
+
+/* For an xy instrument, allow the user to locate a point */
+/* (if cap has inst_xy_locate) */
+/* Return the inst error code */
+static inst_code xy_locate_start(
+inst *p) {
+ return inst_unsupported;
+}
+
+/* For an xy instrument, read back the location */
+/* (if cap has inst_xy_locate) */
+/* Return the inst error code */
+static inst_code xy_get_location(
+inst *p,
+double *x, double *y) {
+ return inst_unsupported;
+}
+
+/* For an xy instrument, end allowing the user to locate a point */
+/* (if cap has inst_xy_locate) */
+/* Return the inst error code */
+static inst_code xy_locate_end(
+inst *p) {
+ return inst_unsupported;
+}
+
+/* For an xy instrument, move the measurement point */
+/* (if cap has inst_xy_position) */
+/* Return the inst error code */
+static inst_code xy_position(
+inst *p,
+int measure, /* nz for measure point, z for locate point */
+double x, double y) {
+ return inst_unsupported;
+}
+
+/* For an xy instrument, try and clear the table after an abort */
+/* Return the inst error code */
+static inst_code xy_clear(
+inst *p) {
+ return inst_unsupported;
+}
+
+/* Read a sheet full of patches using xy mode */
+/* DOESN'T use the trigger mode */
+/* Return the inst error code */
+static inst_code read_xy(
+inst *p,
+int pis, /* Passes in strips (letters in sheet) */
+int sip, /* Steps in pass (numbers in sheet) */
+int npatch, /* Total patches in strip (skip in last pass) */
+char *pname, /* Starting pass name (' A' to 'ZZ') */
+char *sname, /* Starting step name (' 1' to '99') */
+double ox, double oy, /* Origin of first patch */
+double ax, double ay, /* pass increment */
+double aax, double aay, /* pass offset for odd patches */
+double px, double py, /* step/patch increment */
+ipatch *vals) { /* Pointer to array of values */
+ return inst_unsupported;
+}
+
+/* Read a set of strips (applicable to strip reader) */
+/* Obeys the trigger mode set */
+/* Return the inst error code */
+static inst_code read_strip(
+inst *p,
+char *name, /* Strip name (up to 7 chars) */
+int npatch, /* Number of patches in each pass */
+char *pname, /* Pass name (3 chars) */
+int sguide, /* Guide number (decrements by 5) */
+double pwid, /* Patch width in mm (For DTP20/DTP41) */
+double gwid, /* Gap width in mm (For DTP20/DTP41) */
+double twid, /* Trailer width in mm (For DTP41T) */
+ipatch *vals) { /* Pointer to array of values */
+ return inst_unsupported;
+}
+
+/* Read a single sample (applicable to spot instruments) */
+/* Obeys the trigger mode set */
+/* Return the inst error code */
+static inst_code read_sample(
+inst *p,
+char *name, /* Patch identifier (up to 7 chars) */
+ipatch *val, /* Pointer to value to be returned */
+instClamping clamp) { /* NZ to clamp XYZ to be +ve */
+ return inst_unsupported;
+}
+
+/* Measure the emissive refresh rate in Hz. */
+static inst_code read_refrate(
+inst *p,
+double *ref_rate) {
+ if (ref_rate != NULL)
+ *ref_rate = 0.0;
+ return inst_unsupported;
+}
+
+/* Determine if a calibration is needed. Returns inst_calt_none if not */
+/* or unknown, or a mask of the needed calibrations. */
+/* Default implementation - call through to get_n_a_cals() */
+static inst_cal_type needs_calibration(
+inst *p) {
+ inst_cal_type n_cals;
+
+ if (p->get_n_a_cals(p, &n_cals, NULL) != inst_ok)
+ return inst_calt_none;
+
+ return n_cals;
+}
+
+/* Return combined mask of of needed or available inst_cal_type's */
+/* for the current mode. */
+inst_code inst_get_n_a_cals(
+struct _inst *p,
+inst_cal_type *n_cals,
+inst_cal_type *a_cals) {
+
+ if (n_cals != NULL)
+ *n_cals = inst_calc_none;
+
+ if (a_cals != NULL)
+ *a_cals = inst_mode_none;
+
+ return inst_ok;
+}
+
+/* Request an instrument calibration. */
+/* DOESN'T use the trigger mode */
+/* Return inst_unsupported if calibration type is not appropriate. */
+static inst_code calibrate(
+inst *p,
+inst_cal_type *calt, /* Calibration type to do/remaining */
+inst_cal_cond *calc, /* Current condition/desired condition */
+char id[CALIDLEN]) { /* Condition identifier (ie. white reference ID, filter ID) */
+ return inst_unsupported;
+}
+
+/* Measure a display update delay. It is assumed that a */
+/* White to black change has been made to the displayed color, */
+/* and this will measure the time it took for the update to */
+/* be noticed by the instrument, up to 1.0 seconds. */
+/* inst_misread will be returned on failure to find a transition. */
+static inst_code meas_delay(
+inst *p,
+int *msecdelay) { /* Return the number of msec */
+ return inst_unsupported;
+}
+
+/* Return the last calibrated refresh rate in Hz. Returns: */
+/* inst_unsupported - if this instrument doesn't suport a refresh mode */
+/* or is unable to retrieve the refresh rate */
+/* inst_needs_cal - if the refresh rate value is not valid */
+/* inst_misread - if no refresh rate could be determined */
+/* inst_ok - on returning a valid reading */
+static inst_code get_refr_rate(
+struct _inst *p,
+double *ref_rate) {
+ if (ref_rate != NULL)
+ *ref_rate = 0.0;
+ return inst_unsupported;
+}
+
+/* Set the calibrated refresh rate in Hz. */
+/* Set refresh rate to 0.0 to mark it as invalid */
+/* Rates outside the range 5.0 to 150.0 Hz will return an error */
+static inst_code set_refr_rate(
+struct _inst *p,
+double ref_rate) {
+ return inst_unsupported;
+}
+
+/* Insert a compensation filter in the instrument readings */
+/* This is typically needed if an adapter is being used, that alters */
+/* the spectrum of the light reaching the instrument */
+/* To remove the filter, pass NULL for the filter filename */
+static inst_code comp_filter(
+inst *p,
+char *filtername) { /* File containing compensating filter */
+ return inst_unsupported;
+}
+
+/* Insert a colorimetric correction matrix in the instrument XYZ readings */
+/* This is only valid for colorimetric instruments. */
+/* To remove the matrix, pass NULL for the matrix */
+static inst_code col_cor_mat(
+struct _inst *p,
+double mtx[3][3]) { /* XYZ matrix */
+ return inst_unsupported;
+}
+
+/* Use a Colorimeter Calibration Spectral Set to set the */
+/* instrumen calibration. */
+/* This is only valid for colorimetric instruments. */
+/* To set calibration back to default, pass NULL for ccss. */
+static inst_code col_cal_spec_set(
+inst *pp,
+xspect *sets,
+int no_sets) {
+ return inst_unsupported;
+}
+
+/* Supply a user interaction callback function. */
+static void set_uicallback(
+inst *pp,
+inst_code (*uicallback)(void *cntx, inst_ui_purp purp),
+void *cntx) {
+ pp->uicallback = uicallback;
+ pp->uic_cntx = cntx;
+}
+
+/* Supply an asynchronous event callback function. */
+static void set_event_callback(
+inst *pp,
+void (*eventcallback)(void *cntx, inst_event_type event),
+void *cntx) {
+ pp->eventcallback = eventcallback;
+ pp->event_cntx = cntx;
+}
+
+/* Generic inst error codes interpretation */
+static char *inst_interp_error(inst *p, inst_code ec) {
+ switch(ec & inst_mask) {
+ case inst_ok:
+ return "No error";
+ case inst_notify:
+ return "Notification";
+ case inst_warning:
+ return "Warning";
+ case inst_no_coms:
+ return "Internal error - communications needed but not established";
+ case inst_no_init:
+ return "Internal error - initialisation needed but not done";
+ case inst_internal_error:
+ return "Internal software error";
+ case inst_coms_fail:
+ return "Communications failure";
+ case inst_unknown_model:
+ return "Not expected instrument model";
+ case inst_protocol_error:
+ return "Communication protocol breakdown";
+ case inst_user_abort:
+ return "User hit Abort Key";
+ case inst_user_trig:
+ return "User hit Trigger Key";
+ case inst_misread:
+ return "Measurement misread";
+ case inst_nonesaved:
+ return "No saved data to read";
+ case inst_nochmatch:
+ return "Chart being read doesn't match chart expected";
+ case inst_needs_cal:
+ return "Instrument needs calibration";
+ case inst_cal_setup:
+ return "Instrument needs to be setup for calibration";
+ case inst_unsupported:
+ return "Unsupported function";
+ case inst_unexpected_reply:
+ return "Unexpected Reply";
+ case inst_wrong_config:
+ return "Wrong Sensor Position";
+ case inst_wrong_setup:
+ return "Wrong or conflicting setup";
+ case inst_bad_parameter:
+ return "Bad Parameter Value";
+ case inst_hardware_fail:
+ return "Hardware Failure";
+ case inst_other_error:
+ return "Non-specific error";
+ }
+ return "Unknown inst error code";
+}
+
+/* Instrument specific error codes interpretation */
+static char *interp_error(inst *p, int ec) {
+ return "interp_error is uninplemented in base class!";
+}
+
+/* Return the last serial communication error code */
+/* (This is used for deciding fallback/retry strategies) */
+static int last_scomerr(inst *p) {
+ return p->icom->lserr;
+}
+
+/* Convert instrument specific inst_wrong_config error to inst_config enum */
+static inst_config config_enum(inst *p, int ec) {
+ return inst_conf_unknown;
+}
+
+/* ---------------------------------------------- */
+/* Virtual constructor. */
+/* Return NULL for unknown instrument, */
+/* or serial instrument if nocoms == 0. */
+extern inst *new_inst(
+icompath *path, /* Device path to instrument */
+int nocoms, /* Don't open if communications are needed to establish inst type */
+a1log *log, /* log to use */
+inst_code (*uicallback)(void *cntx, inst_ui_purp purp), /* optional uicallback */
+void *cntx /* Context for callback */
+) {
+ instType itype = instUnknown; /* Actual type */
+ icoms *icom;
+ inst *p = NULL;
+
+ if ((icom = new_icoms(path, log)) == NULL) {
+ return NULL;
+ }
+
+ /* Set instrument type from USB port, if not specified */
+ itype = icom->itype; /* Instrument type if its known from usb/hid */
+#ifdef ENABLE_SERIAL
+ if (itype == instUnknown && !nocoms)
+ itype = ser_inst_type(icom, uicallback, cntx); /* Else type from serial */
+#endif /* ENABLE_SERIAL */
+
+
+#ifdef ENABLE_SERIAL
+ if (itype == instDTP22)
+ p = (inst *)new_dtp22(icom, itype);
+ else if (itype == instDTP41)
+ p = (inst *)new_dtp41(icom, itype);
+ else if (itype == instDTP51)
+ p = (inst *)new_dtp51(icom, itype);
+ else if (itype == instDTP92)
+ p = (inst *)new_dtp92(icom, itype);
+ else if ((itype == instSpectrolino ) ||
+ (itype == instSpectroScan ) ||
+ (itype == instSpectroScanT))
+ p = (inst *)new_ss(icom, itype);
+/* NYI
+ else if (itype == instSpectrocam)
+ p = (inst *)new_spc(icom, itype);
+*/
+#endif /* ENABLE_SERIAL */
+
+#ifdef ENABLE_USB
+ if (itype == instDTP94)
+ p = (inst *)new_dtp92(icom, itype);
+ else if (itype == instDTP20)
+ p = (inst *)new_dtp20(icom, itype);
+ else if (itype == instI1Disp1 ||
+ itype == instI1Disp2)
+ p = (inst *)new_i1disp(icom, itype);
+ else if (itype == instI1Disp3)
+ p = (inst *)new_i1d3(icom, itype);
+ else if (itype == instI1Monitor)
+ p = (inst *)new_i1pro(icom, itype);
+ else if ((itype == instI1Pro) ||
+ (itype == instI1Pro2))
+ p = (inst *)new_i1pro(icom, itype);
+ else if (itype == instColorMunki)
+ p = (inst *)new_munki(icom, itype);
+ else if (itype == instHCFR)
+ p = (inst *)new_hcfr(icom, itype);
+ else if (itype == instSpyder2)
+ p = (inst *)new_spyd2(icom, itype);
+ else if (itype == instSpyder3)
+ p = (inst *)new_spyd2(icom, itype);
+ else if (itype == instSpyder4)
+ p = (inst *)new_spyd2(icom, itype);
+ else if (itype == instHuey)
+ p = (inst *)new_huey(icom, itype);
+ else if (itype == instSmile)
+ p = (inst *)new_i1disp(icom, itype);
+ else if (itype == instColorHug)
+ p = (inst *)new_colorhug(icom, itype);
+#endif /* ENABLE_USB */
+
+ /* Nothing matched */
+ if (p == NULL)
+ return NULL;
+
+ /* Add default methods if constructor did not supply them */
+ if (p->init_coms == NULL)
+ p->init_coms = init_coms;
+ if (p->init_inst == NULL)
+ p->init_inst = init_inst;
+ if (p->get_itype == NULL)
+ p->get_itype = get_itype;
+ if (p->get_serial_no == NULL)
+ p->get_serial_no = get_serial_no;
+ if (p->capabilities == NULL)
+ p->capabilities = capabilities;
+ if (p->meas_config == NULL)
+ p->meas_config = meas_config;
+ if (p->set_mode == NULL)
+ p->set_mode = set_mode;
+ if (p->get_disptypesel == NULL)
+ p->get_disptypesel = get_disptypesel;
+ if (p->set_disptype == NULL)
+ p->set_disptype = set_disptype;
+ if (p->get_set_opt == NULL)
+ p->get_set_opt = get_set_opt;
+ if (p->read_chart == NULL)
+ p->read_chart = read_chart;
+ if (p->xy_sheet_release == NULL)
+ p->xy_sheet_release = xy_sheet_release;
+ if (p->xy_sheet_hold == NULL)
+ p->xy_sheet_hold = xy_sheet_hold;
+ if (p->xy_locate_start == NULL)
+ p->xy_locate_start = xy_locate_start;
+ if (p->xy_get_location == NULL)
+ p->xy_get_location = xy_get_location;
+ if (p->xy_locate_end == NULL)
+ p->xy_locate_end = xy_locate_end;
+ if (p->xy_position == NULL)
+ p->xy_position = xy_position;
+ if (p->xy_clear == NULL)
+ p->xy_clear = xy_clear;
+ if (p->read_xy == NULL)
+ p->read_xy = read_xy;
+ if (p->read_strip == NULL)
+ p->read_strip = read_strip;
+ if (p->read_sample == NULL)
+ p->read_sample = read_sample;
+ if (p->read_refrate == NULL)
+ p->read_refrate = read_refrate;
+ if (p->needs_calibration == NULL)
+ p->needs_calibration = needs_calibration;
+ if (p->get_n_a_cals == NULL)
+ p->get_n_a_cals = inst_get_n_a_cals;
+ if (p->calibrate == NULL)
+ p->calibrate = calibrate;
+ if (p->meas_delay == NULL)
+ p->meas_delay = meas_delay;
+ if (p->get_refr_rate == NULL)
+ p->get_refr_rate = get_refr_rate;
+ if (p->set_refr_rate == NULL)
+ p->set_refr_rate = set_refr_rate;
+ if (p->comp_filter == NULL)
+ p->comp_filter = comp_filter;
+ if (p->col_cor_mat == NULL)
+ p->col_cor_mat = col_cor_mat;
+ if (p->col_cal_spec_set == NULL)
+ p->col_cal_spec_set = col_cal_spec_set;
+ if (p->set_uicallback == NULL)
+ p->set_uicallback = set_uicallback;
+ if (p->set_event_callback == NULL)
+ p->set_event_callback = set_event_callback;
+ if (p->inst_interp_error == NULL)
+ p->inst_interp_error = inst_interp_error;
+ if (p->interp_error == NULL)
+ p->interp_error = interp_error;
+ if (p->last_scomerr == NULL)
+ p->last_scomerr = last_scomerr;
+ if (p->config_enum == NULL)
+ p->config_enum = config_enum;
+
+ /* Set the provided user interaction callback */
+ p->set_uicallback(p, uicallback, cntx);
+
+ return p;
+}
+
+/* --------------------------------------------------- */
+/* Display type list support */
+
+/* Default display type UI selector characters:
+
+ g Generic
+ n Non-refresh (Generic)
+ r Refresh (Generic)
+
+ c CRT
+ l LCD (Generic or CCFL Backlight)
+ f LCD, CCFL Backlight
+ b LCD, RGB LED Backlight
+ L LCD, Wide Gamut, CCFL Backlight
+ B LCD, Wide Gamut, RGB LED Backlight
+ e LCD, White LED Backlight
+ o OLED
+ a AMOLED
+ p Projector (Generic)
+ m Plasma
+
+ F Factory base calibration
+ R Raw sensor values
+ */
+
+
+/* Free a display type list */
+void inst_del_disptype_list(inst_disptypesel *list, int no) {
+
+ if (list != NULL) {
+ int i;
+ for (i = 0; i < no; i++) {
+ if (list[i].path != NULL)
+ free(list[i].path);
+ if (list[i].sets != NULL)
+ free(list[i].sets);
+ }
+ free(list);
+ }
+}
+
+/* Ensure that the list is large enough for n+1 entries (+1 == end marker) */
+/* Return NULL on malloc error */
+static inst_disptypesel *expand_dlist(inst_disptypesel *list, int nlist, int *nalist) {
+
+ if ((nlist+1) > *nalist) {
+ int xnalist = (2 * nlist + 6);
+ inst_disptypesel *xlist;
+
+ if ((xlist = realloc(list, xnalist * sizeof(inst_disptypesel))) == NULL) {
+ inst_del_disptype_list(list, nlist);
+ *nalist = 0;
+ return NULL;
+ }
+ *nalist = xnalist;
+ list = xlist;
+ }
+
+ /* Set end marker */
+ list[nlist].flags = inst_dtflags_end;
+ list[nlist].cbid = 0;
+ list[nlist].sel[0] = '\000';
+ list[nlist].desc[0] = '\000';
+ list[nlist].refr = 0;
+ list[nlist].ix = 0;
+ list[nlist].path = NULL;
+ list[nlist].sets = NULL;
+ list[nlist].no_sets = 0;
+
+ return list;
+}
+
+/* For each selector we need to:
+
+ check each selector char
+ if already used,
+ remove it.
+ if no selector remain,
+ allocate a free one.
+ mark all used selectors
+*/
+
+/* Set the selection characters */
+/* return NZ if we have run out */
+static int set_sel(char *sel, char *usels, int *k, char *asels) {
+ char *d, *s;
+
+ /* First remove any used chars from selector */
+ for (d = s = sel; *s != '\000'; s++) {
+ if (usels[*s] == 0)
+ *d++ = *s;
+ }
+ *d = '\000';
+
+ /* Add a selector if we need one */
+ if (sel[0] == '\000') {
+
+ /* Locate the next unused selector */
+ for (;;) {
+ if (asels[*k] == '\000') /* Run out of selectors */
+ return 1;
+ if (usels[*k] == 0)
+ break;
+ (*k)++;
+ }
+ sel[0] = asels[*k];
+ sel[1] = '\000';
+ usels[sel[0]] = 1;
+ (*k)++;
+ }
+
+ return 0;
+}
+
+/* Create the display type list */
+inst_code inst_creat_disptype_list(inst *p,
+int *pndtlist, /* Number in returned list */
+inst_disptypesel **pdtlist, /* Returned list */
+inst_disptypesel *sdtlist, /* Static list */
+int doccss, /* Add installed ccss files */
+int doccmx /* Add matching installed ccmx files */
+) {
+ inst_disptypesel *list = NULL;
+ int i, j, k, nlist = 0, nalist = 0;
+ char usels[256]; /* Used selectors */
+ static char *asels = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ /* free the old list */
+ inst_del_disptype_list(*pdtlist, *pndtlist);
+ *pdtlist = NULL;
+ *pndtlist = 0;
+
+ for (i = 0; i < 256; i++)
+ usels[i] = 0;
+ k = 0;
+
+ /* Add entries from the static list */
+ /* Count the number in the static list */
+ for (i = 0; !(sdtlist[i].flags & inst_dtflags_end); i++) {
+
+ if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL)
+ return inst_internal_error;
+
+ list[nlist-1] = sdtlist[i]; /* Struct copy */
+
+ if (set_sel(list[nlist-1].sel, usels, &k, asels)) {
+ a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n");
+ break;
+ }
+ }
+
+ k = 0; /* Next selector index */
+
+ /* Add any ccss's */
+ if (doccss) {
+ iccss *ss_list;
+ if ((ss_list = list_iccss(NULL)) == NULL) {
+ free(list);
+ return inst_internal_error;
+ }
+
+ for (i = 0; ss_list[i].path != NULL; i++) {
+
+ if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL)
+ return inst_internal_error;
+
+ list[nlist-1].flags = inst_dtflags_ccss;
+
+ if (ss_list[i].sel != NULL) {
+ strncpy(list[nlist-1].sel, ss_list[i].sel, INST_DTYPE_SEL_LEN);
+ list[nlist-1].sel[INST_DTYPE_SEL_LEN-1] = '\000';
+ }
+ if (set_sel(list[nlist-1].sel, usels, &k, asels)) {
+ a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n");
+ break;
+ }
+
+ strncpy(list[nlist-1].desc, ss_list[i].desc, INST_DTYPE_DESC_LEN);
+ list[nlist-1].desc[INST_DTYPE_DESC_LEN-1] = '\000';
+ list[nlist-1].refr = ss_list[i].refr;
+ list[nlist-1].ix = 0;
+ list[nlist-1].path = ss_list[i].path; ss_list[i].path = NULL;
+ list[nlist-1].sets = ss_list[i].sets; ss_list[i].sets = NULL;
+ list[nlist-1].no_sets = ss_list[i].no_sets; ss_list[i].no_sets = 0;
+ }
+ }
+
+ /* Add any ccmx's */
+ if (doccmx) {
+ iccmx *ss_list;
+
+ /* Just ccmx's for this instrument */
+ if ((ss_list = list_iccmx(inst_name(p->itype), NULL)) == NULL) {
+ free(list);
+ return inst_internal_error;
+ }
+
+ for (i = 0; ss_list[i].path != NULL; i++) {
+
+ /* Check that we can find the matching base calibation */
+ for (j = 0; j < nlist; j++) {
+ if (list[j].cbid == ss_list[i].cbid)
+ break;
+ }
+ if (j >= nlist) {
+ a1loge(p->log, 1, "inst_creat_disptype_list can't find cbid %d for '%s'\n",list[j].cbid, list[j].path);
+ continue;
+ }
+
+ if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL)
+ return inst_internal_error;
+
+ list[nlist-1].flags = inst_dtflags_ccmx;
+
+ if (ss_list[i].sel != NULL) {
+ strncpy(list[nlist-1].sel, ss_list[i].sel, INST_DTYPE_SEL_LEN);
+ list[nlist-1].sel[INST_DTYPE_SEL_LEN-1] = '\000';
+ }
+ if (set_sel(list[nlist-1].sel, usels, &k, asels)) {
+ a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n");
+ break;
+ }
+ strncpy(list[nlist-1].desc, ss_list[i].desc, INST_DTYPE_DESC_LEN);
+ list[nlist-1].desc[INST_DTYPE_DESC_LEN-1] = '\000';
+ list[nlist-1].refr = ss_list[i].refr;
+ list[nlist-1].ix = list[j].ix; /* Copy underlying cal selection from base */
+ list[nlist-1].path = ss_list[i].path; ss_list[i].path = NULL;
+ icmCpy3x3(list[nlist-1].mat, ss_list[i].mat);
+ }
+ }
+
+ if (pndtlist != NULL)
+ *pndtlist = nlist;
+ if (pdtlist != NULL)
+ *pdtlist = list;
+
+ return inst_ok;
+}
+
+/* ============================================================= */
+/* CCMX location support */
+
+/* return a list of installed ccmx files. */
+/* if inst != NULL, return those that match the given instrument. */
+/* The list is sorted by description and terminated by a NULL entry. */
+/* If no is != NULL, return the number in the list */
+/* Return NULL and -1 if there is a malloc error */
+iccmx *list_iccmx(char *inst, int *no) {
+ int i, j;
+ iccmx *rv;
+
+ char **paths = NULL;
+ int npaths = 0;
+
+
+ npaths = xdg_bds(NULL, &paths, xdg_data, xdg_read, xdg_user,
+ "ArgyllCMS/\052.ccmx" XDG_FUDGE "color/\052.ccmx"
+ );
+
+ if ((rv = malloc(sizeof(iccmx) * (npaths + 1))) == NULL) {
+ a1loge(g_log, 1, "list_iccmx: malloc of paths failed\n");
+ xdg_free(paths, npaths);
+ if (no != NULL) *no = -1;
+ return NULL;
+ }
+
+ for (i = j = 0; i < npaths; i++) {
+ ccmx *cs;
+ int len;
+ char *pp;
+ char *tech, *disp;
+ int cbid, refr;
+
+ if ((cs = new_ccmx()) == NULL) {
+ a1loge(g_log, 1, "list_iccmx: new_ccmx failed\n");
+ for (--j; j>= 0; j--) {
+ free(rv[j].path);
+ free(rv[j].desc);
+ }
+ xdg_free(paths, npaths);
+ if (no != NULL) *no = -1;
+ return NULL;
+ }
+ if (cs->read_ccmx(cs, paths[i])) {
+ cs->del(cs);
+ continue; /* Skip any unreadable ccmx's */
+ }
+
+ /* Skip any that don't match */
+ if (inst != NULL && cs->inst != NULL && strcmp(inst, cs->inst) != 0)
+ continue;
+
+ if ((tech = cs->tech) == NULL)
+ tech = "";
+ if ((disp = cs->disp) == NULL)
+ disp = "";
+ cbid = cs->cbid;
+ refr = cs->refrmode;
+ len = strlen(tech) + strlen(disp) + 4;
+ if ((pp = malloc(len)) == NULL) {
+ a1loge(g_log, 1, "list_iccmx: malloc failed\n");
+ for (--j; j >= 0; j--) {
+ free(rv[j].path);
+ free(rv[j].desc);
+ }
+ cs->del(cs);
+ free(rv);
+ xdg_free(paths, npaths);
+ if (no != NULL) *no = -1;
+ return NULL;
+ }
+ if ((rv[j].path = strdup(paths[i])) == NULL) {
+ a1loge(g_log, 1, "list_iccmx: strdup failed\n");
+ for (--j; j >= 0; j--) {
+ free(rv[j].path);
+ free(rv[j].desc);
+ }
+ cs->del(cs);
+ free(rv);
+ free(pp);
+ xdg_free(paths, npaths);
+ if (no != NULL) *no = -1;
+ return NULL;
+ }
+ strcpy(pp, tech);
+ strcat(pp, " (");
+ strcat(pp, disp);
+ strcat(pp, ")");
+ rv[j].desc = pp;
+ rv[j].cbid = cbid;
+ rv[j].refr = refr;
+ rv[j].sel = cs->sel; cs->sel = NULL;
+ icmCpy3x3(rv[j].mat, cs->matrix);
+ cs->del(cs);
+ j++;
+ }
+ xdg_free(paths, npaths);
+ rv[j].path = NULL;
+ rv[j].desc = NULL;
+ rv[j].cbid = 0;
+ rv[j].refr = -1;
+ rv[j].sel = NULL;
+ if (no != NULL)
+ *no = j;
+
+ /* Sort the list */
+#define HEAP_COMPARE(A,B) (strcmp(A.desc, B.desc) < 0)
+ HEAPSORT(iccmx, rv, j)
+#undef HEAP_COMPARE
+
+ return rv;
+}
+
+/* Free up a iccmx list */
+void free_iccmx(iccmx *list) {
+ int i;
+
+ if (list != NULL) {
+ for (i = 0; list[i].path != NULL || list[i].desc != NULL; i++) {
+ if (list[i].path != NULL)
+ free(list[i].path);
+ if (list[i].desc != NULL)
+ free(list[i].desc);
+ if (list[i].sel != NULL)
+ free(list[i].sel);
+ }
+ free(list);
+ }
+}
+
+/* ============================================================= */
+/* CCSS location support */
+
+/* return a list of installed ccss files. */
+/* The list is sorted by description and terminated by a NULL entry. */
+/* If no is != NULL, return the number in the list */
+/* Return NULL and -1 if there is a malloc error */
+iccss *list_iccss(int *no) {
+ int i, j;
+ iccss *rv;
+
+ char **paths = NULL;
+ int npaths = 0;
+
+
+ npaths = xdg_bds(NULL, &paths, xdg_data, xdg_read, xdg_user,
+ "ArgyllCMS/\052.ccss" XDG_FUDGE "color/\052.ccss"
+ );
+
+ if ((rv = malloc(sizeof(iccss) * (npaths + 1))) == NULL) {
+ a1loge(g_log, 1, "list_iccss: malloc of paths failed\n");
+ xdg_free(paths, npaths);
+ if (no != NULL) *no = -1;
+ return NULL;
+ }
+
+ for (i = j = 0; i < npaths; i++) {
+ ccss *cs;
+ int len;
+ char *pp;
+ char *tech, *disp;
+ int refr;
+
+ if ((cs = new_ccss()) == NULL) {
+ a1loge(g_log, 1, "list_iccss: new_ccss failed\n");
+ for (--j; j>= 0; j--) {
+ free(rv[j].path);
+ free(rv[j].desc);
+ }
+ xdg_free(paths, npaths);
+ if (no != NULL) *no = -1;
+ return NULL;
+ }
+ if (cs->read_ccss(cs, paths[i])) {
+ cs->del(cs);
+ continue; /* Skip any unreadable ccss's */
+ }
+
+ if ((tech = cs->tech) == NULL)
+ tech = "";
+ if ((disp = cs->disp) == NULL)
+ disp = "";
+ refr = cs->refrmode;
+ len = strlen(tech) + strlen(disp) + 4;
+ if ((pp = malloc(len)) == NULL) {
+ a1loge(g_log, 1, "list_iccss: malloc failed\n");
+ for (--j; j >= 0; j--) {
+ free(rv[j].path);
+ free(rv[j].desc);
+ }
+ cs->del(cs);
+ free(rv);
+ xdg_free(paths, npaths);
+ if (no != NULL) *no = -1;
+ return NULL;
+ }
+ if ((rv[j].path = strdup(paths[i])) == NULL) {
+ a1loge(g_log, 1, "list_iccss: strdup failed\n");
+ for (--j; j >= 0; j--) {
+ free(rv[j].path);
+ free(rv[j].desc);
+ }
+ cs->del(cs);
+ free(rv);
+ free(pp);
+ xdg_free(paths, npaths);
+ if (no != NULL) *no = -1;
+ return NULL;
+ }
+ strcpy(pp, tech);
+ strcat(pp, " (");
+ strcat(pp, disp);
+ strcat(pp, ")");
+ rv[j].desc = pp;
+ rv[j].refr = refr;
+ rv[j].sel = cs->sel; cs->sel = NULL;
+ rv[j].sets = cs->samples; cs->samples = NULL;
+ rv[j].no_sets = cs->no_samp; cs->no_samp = 0;
+ cs->del(cs);
+ j++;
+ }
+ xdg_free(paths, npaths);
+ rv[j].path = NULL;
+ rv[j].desc = NULL;
+ rv[j].refr = -1;
+ rv[j].sel = NULL;
+ rv[j].sets = NULL;
+ rv[j].no_sets = 0;
+ if (no != NULL)
+ *no = j;
+
+ /* Sort the list */
+#define HEAP_COMPARE(A,B) (strcmp(A.desc, B.desc) < 0)
+ HEAPSORT(iccss, rv, j)
+#undef HEAP_COMPARE
+
+ return rv;
+}
+
+/* Free up a iccss list */
+void free_iccss(iccss *list) {
+ int i;
+
+ if (list != NULL) {
+ for (i = 0; list[i].path != NULL || list[i].desc != NULL; i++) {
+ if (list[i].path != NULL)
+ free(list[i].path);
+ if (list[i].desc != NULL)
+ free(list[i].desc);
+ if (list[i].sel != NULL)
+ free(list[i].sel);
+ if (list[i].sets != NULL)
+ free(list[i].sets);
+ }
+ free(list);
+ }
+}
+
+/* ============================================================= */
+
+#ifdef ENABLE_SERIAL
+static void hex2bin(char *buf, int len);
+
+/* Heuristicly determine the instrument type for */
+/* a serial connection, and instUnknown if not serial. */
+/* Set it in icoms and also return it. */
+static instType ser_inst_type(
+ icoms *p,
+ inst_code (*uicallback)(void *cntx, inst_ui_purp purp), /* optional uicallback */
+ void *cntx /* Context for callback */
+) {
+ instType rv = instUnknown;
+ char buf[100];
+ baud_rate brt[] = { baud_9600, baud_19200, baud_4800, baud_2400,
+ baud_1200, baud_38400, baud_57600, baud_115200,
+ baud_600, baud_300, baud_110, baud_nc };
+ unsigned int etime;
+ unsigned int bi, i;
+ int se, len;
+ int xrite = 0;
+ int ss = 0;
+ int so = 0;
+
+#ifdef ENABLE_USB
+ if (p->usbd != NULL || p->hidd != NULL)
+ return p->itype;
+#endif /* ENABLE_USB */
+
+ bi = 0;
+
+ /* The tick to give up on */
+ etime = msec_time() + (long)(1000.0 * 20.0 + 0.5);
+
+ a1logd(p->log, 1, "ser_inst_type: Trying different baud rates (%u msec to go)\n",etime - msec_time());
+
+ /* Until we time out, find the correct baud rate */
+ for (i = bi; msec_time() < etime; i++) {
+ if (brt[i] == baud_nc)
+ i = 0;
+ if ((se = p->set_ser_port(p, fc_none, brt[i], parity_none,
+ stop_1, length_8)) != ICOM_OK) {
+ a1logd(p->log, 5, "ser_inst_type: set_ser_port failed with 0x%x\n",se);
+ return instUnknown; /* Give up */
+ }
+
+// a1logd(p->log, 5, "brt = %d\n",brt[i]);
+ if ((se = p->write_read(p, ";D024\r\n", buf, 100, '\r', 1, 0.5)) != inst_ok) {
+ /* Check for user abort */
+ if (uicallback != NULL) {
+ inst_code ev;
+ if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) {
+ a1logd(p->log, 5, "ser_inst_type: User aborted\n");
+ return instUnknown;
+ }
+ }
+ }
+ len = strlen(buf);
+
+// a1logd(p->log, 5, "len = %d\n",len);
+ if (len < 4)
+ continue;
+
+ /* Is this an X-Rite error value such as "<01>" ? */
+ if (buf[0] == '<' && isdigit(buf[1]) && isdigit(buf[2]) && buf[3] == '>') {
+// a1logd(p->log, 5, "xrite\n");
+ xrite = 1;
+ break;
+ }
+
+ /* Is this a Spectrolino error resonse ? */
+ if (len >= 5 && strncmp(buf, ":26", 3) == 0) {
+// a1logd(p->log, 5, "spectrolino\n");
+ so = 1;
+ break;
+ }
+ /* Is this a SpectroScan response ? */
+ if (len >= 7 && strncmp(buf, ":D183", 5) == 0) {
+// a1logd(p->log, 5, "spectroscan\n");
+ ss = 1;
+ break;
+ }
+ }
+
+ if (msec_time() >= etime) { /* We haven't established comms */
+ a1logd(p->log, 5, "ser_inst_type: Failed to establish coms\n");
+ return instUnknown;
+ }
+
+ a1logd(p->log, 5, "ser_inst_type: Got coms with instrument\n");
+
+ /* Spectrolino */
+ if (so) {
+ rv = instSpectrolino;
+ }
+
+ /* SpectroScan */
+ if (ss) {
+ rv = instSpectroScan;
+ if ((se = p->write_read(p, ";D030\r\n", buf, 100, '\n', 1, 1.5)) == 0) {
+ if (strlen(buf) >= 41) {
+ hex2bin(&buf[5], 12);
+// a1logd(p->log, 5, "spectroscan type = '%s'\n",buf);
+ if (strncmp(buf, ":D190SpectroScanT", 17) == 0)
+ rv = instSpectroScanT;
+ }
+ }
+ }
+ if (xrite) {
+
+ /* Get the X-Rite model and version number */
+ if ((se = p->write_read(p, "SV\r\n", buf, 100, '>', 1, 2.5)) != 0)
+ return instUnknown;
+
+ if (strlen(buf) >= 12) {
+ if (strncmp(buf,"X-Rite DTP22",12) == 0)
+ rv = instDTP22;
+ if (strncmp(buf,"X-Rite DTP41",12) == 0)
+ rv = instDTP41;
+ if (strncmp(buf,"X-Rite DTP42",12) == 0)
+ rv = instDTP41;
+ if (strncmp(buf,"X-Rite DTP51",12) == 0)
+ rv = instDTP51;
+ if (strncmp(buf,"X-Rite DTP52",12) == 0)
+ rv = instDTP51;
+ if (strncmp(buf,"X-Rite DTP92",12) == 0)
+ rv = instDTP92;
+ if (strncmp(buf,"X-Rite DTP94",12) == 0)
+ rv = instDTP94;
+ }
+ }
+
+ a1logd(p->log, 5, "ser_inst_type: Instrument type is '%s'\n", inst_name(rv));
+
+ p->close_port(p); /* Or should we leave it open ?? */
+
+ p->itype = rv;
+
+ return rv;
+}
+
+/* Convert an ASCII Hex character to an integer. */
+static int h2b(char c) {
+ if (c >= '0' && c <= '9')
+ return (c-(int)'0');
+ if (c >= 'A' && c <= 'F')
+ return (10 + c-(int)'A');
+ if (c >= 'a' && c <= 'f')
+ return (10 + c-(int)'a');
+ return 0;
+}
+
+/* Convert a Hex encoded buffer into binary. */
+/* len is number of bytes out */
+static void hex2bin(char *buf, int len) {
+ int i;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (char)((h2b(buf[2 * i + 0]) << 4)
+ | (h2b(buf[2 * i + 1]) << 0));
+ }
+}
+
+#endif /* ENABLE_SERIAL */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+