summaryrefslogtreecommitdiff
path: root/spectro/dispsup.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/dispsup.c')
-rw-r--r--spectro/dispsup.c2343
1 files changed, 2343 insertions, 0 deletions
diff --git a/spectro/dispsup.c b/spectro/dispsup.c
new file mode 100644
index 0000000..0dc29ad
--- /dev/null
+++ b/spectro/dispsup.c
@@ -0,0 +1,2343 @@
+
+
+/*
+ * Argyll Color Correction System
+ * Common display patch reading support.
+ *
+ * Author: Graeme W. Gill
+ * Date: 2/11/2005
+ *
+ * Copyright 1998 - 2013 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/*
+ TTBD:
+
+ If verb, report white/black drift
+
+ Should add option to ask for a black cal every N readings, for spectro's
+ */
+
+#ifdef __MINGW32__
+# define WINVER 0x0500
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/types.h>
+#include <time.h>
+#include <string.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include "numlib.h"
+#include "xspect.h"
+#include "insttypes.h"
+#include "conv.h"
+#include "icoms.h"
+#include "inst.h"
+#include "spyd2.h"
+#include "dispwin.h"
+#include "dispsup.h"
+#include "webwin.h"
+#include "instappsup.h"
+
+#undef SIMPLE_MODEL /* Make fake device well behaved */
+ /* else has offsets, quantization, noise etc. */
+
+#define DRIFT_IPERIOD 40 /* Number of samples between drift interpolation measurements */
+#define DRIFT_EPERIOD 20 /* Number of samples between drift extrapolation measurements */
+//#define DRIFT_IPERIOD 6 /* Test values */
+//#define DRIFT_EPERIOD 3
+
+#ifdef SIMPLE_MODEL
+# undef FAKE_NOISE /* Add noise to _fake_ devices XYZ */
+# undef FAKE_BITS /* Number of bits of significance of fake device */
+#else
+# define FAKE_NOISE 0.01 /* Add noise to _fake_ devices XYZ */
+# define FAKE_BITS 9 /* Number of bits of significance of fake device */
+#endif
+
+
+/* -------------------------------------------------------- */
+/* A default callback that can be provided as an argument to */
+/* inst_handle_calibrate() to handle the display part of a */
+/* calibration callback. */
+/* Call this again with calc = inst_calc_none to cleanup afterwards. */
+inst_code setup_display_calibrate(
+ inst *p,
+ inst_cal_cond calc, /* Current condition. inst_calc_none for not setup */
+ disp_win_info *dwi /* Information to be able to open a display test patch */
+) {
+ inst_code rv = inst_ok, ev;
+ dispwin *dw; /* Display window to display test patches on, NULL if none. */
+ a1logd(p->log,1,"setup_display_calibrate called\n");
+
+ switch (calc) {
+ case inst_calc_none: /* Use this as a cleanup flag */
+ if (dwi->dw == NULL && dwi->_dw != NULL) {
+ dwi->_dw->del(dwi->_dw);
+ dwi->_dw = NULL;
+ }
+ break;
+
+ case inst_calc_emis_white:
+ if (dwi->dw == NULL) {
+ if (dwi->webdisp != 0) {
+ if ((dwi->_dw = new_webwin(dwi->webdisp, dwi->hpatsize, dwi->vpatsize,
+ dwi->ho, dwi->vo, 0, dwi->blackbg,
+ p->log->verb, p->log->debug)) == NULL) {
+ a1logd(p->log,1,"inst_handle_calibrate failed to create test window 0x%x\n",inst_other_error);
+ return inst_other_error;
+ }
+ } else {
+ if ((dwi->_dw = new_dispwin(dwi->disp, dwi->hpatsize, dwi->vpatsize,
+ dwi->ho, dwi->vo, 0, 0, NULL, dwi->blackbg,
+ dwi->override, p->log->debug)) == NULL) {
+ a1logd(p->log,1,"inst_handle_calibrate failed to create test window 0x%x\n",inst_other_error);
+ return inst_other_error;
+ }
+ }
+ printf("Frequency calibration, Place instrument on test window.\n");
+ printf(" Hit any key to continue,\n");
+ printf(" or hit Esc or Q to abort:");
+ } else {
+ dwi->_dw = dwi->dw;
+ }
+ p->cal_gy_level = 1.0;
+ dwi->_dw->set_color(dwi->_dw, 1.0, 1.0, 1.0);
+ break;
+
+ case inst_calc_emis_grey:
+ case inst_calc_emis_grey_darker:
+ case inst_calc_emis_grey_ligher:
+ if (dwi->dw == NULL) {
+ if (dwi->webdisp != 0) {
+ if ((dwi->_dw = new_webwin(dwi->webdisp, dwi->hpatsize, dwi->vpatsize,
+ dwi->ho, dwi->vo, 0, dwi->blackbg,
+ p->log->verb, p->log->debug)) == NULL) {
+ a1logd(p->log,1,"inst_handle_calibrate failed to create test window 0x%x\n",inst_other_error);
+ return inst_other_error;
+ }
+ } else {
+ if ((dwi->_dw = new_dispwin(dwi->disp, dwi->hpatsize, dwi->vpatsize,
+ dwi->ho, dwi->vo, 0, 0, NULL, dwi->blackbg,
+ dwi->override, p->log->debug)) == NULL) {
+ a1logd(p->log,1,"inst_handle_calibrate failed to create test window 0x%x\n",inst_other_error);
+ return inst_other_error;
+ }
+ }
+ printf("Cell ratio calibration, Place instrument on test window.\n");
+ printf(" Hit any key to continue,\n");
+ printf(" or hit Esc or Q to abort:");
+ } else {
+ dwi->_dw = dwi->dw;
+ }
+ if (calc == inst_calc_emis_grey) {
+ p->cal_gy_level = 0.6;
+ p->cal_gy_count = 0;
+ } else if (calc == inst_calc_emis_grey_darker) {
+ p->cal_gy_level *= 0.7;
+ p->cal_gy_count++;
+ } else if (calc == inst_calc_emis_grey_ligher) {
+ p->cal_gy_level *= 1.4;
+ if (p->cal_gy_level > 1.0)
+ p->cal_gy_level = 1.0;
+ p->cal_gy_count++;
+ }
+ if (p->cal_gy_count > 4) {
+ printf("Cell ratio calibration failed - too many tries at setting grey level.\n");
+ a1logd(p->log,1,"inst_handle_calibrate too many tries at setting grey level 0x%x\n",inst_internal_error);
+ return inst_internal_error;
+ }
+ dwi->_dw->set_color(dwi->_dw, p->cal_gy_level, p->cal_gy_level, p->cal_gy_level);
+ break;
+
+ default:
+ /* Something isn't being handled */
+ a1logd(p->log,1,"inst_handle_calibrate unhandled calc case 0x%x, err 0x%x\n",calc,inst_internal_error);
+ return inst_internal_error;
+ }
+ return inst_ok;
+}
+
+/* -------------------------------------------------------- */
+/* User requested calibration of the display instrument */
+/* Do all available non-deferrable calibrations */
+/* Should add an argument to be able to select type of calibration, */
+/* rather than guessing what the user wants ? */
+int disprd_calibration(
+icompath *ipath, /* Instrument path to open, &icomFakeDevice == fake */
+flow_control fc, /* Serial flow control */
+int dtype, /* Display type selection character */
+int docbid, /* NZ to only allow cbid dtypes */
+int tele, /* NZ for tele mode, falls back to spot mode */
+int nadaptive, /* NZ for non-adaptive mode */
+int noinitcal, /* NZ to disable initial instrument calibration */
+disppath *disp, /* display to calibrate. */
+int webdisp, /* If nz, port number for web display */
+int blackbg, /* NZ if whole screen should be filled with black */
+int override, /* Override_redirect on X11 */
+double hpatsize, /* Size of dispwin */
+double vpatsize,
+double ho, double vo, /* Position of dispwin */
+a1log *log /* Verb, debug & error log */
+) {
+ instType itype = instUnknown;
+ inst *p = NULL;
+ int c;
+ inst_code rv;
+ baud_rate br = baud_19200;
+ disp_win_info dwi;
+ inst_cal_type calt = inst_calt_needed;
+ inst_mode cap;
+ inst2_capability cap2;
+ inst3_capability cap3;
+ inst_mode mode = 0;
+
+ memset((void *)&dwi, 0, sizeof(disp_win_info));
+ dwi.webdisp = webdisp;
+ dwi.disp = disp;
+ dwi.blackbg = blackbg;
+ dwi.override = override;
+ dwi.hpatsize = hpatsize;
+ dwi.vpatsize = vpatsize;
+ dwi.ho = ho;
+ dwi.vo = vo;
+
+ p->log = new_a1log_d(log);
+
+ a1logv(log, 1, "Setting up the instrument\n");
+
+ if ((p = new_inst(ipath, 0, log, DUIH_FUNC_AND_CONTEXT)) == NULL) {
+ a1logd(log, 1, "new_inst failed\n");
+ return -1;
+ }
+
+ /* Establish communications */
+ if ((rv = p->init_coms(p, br, fc, 15.0)) != inst_ok) {
+ a1logd(p->log, 1, "init_coms returned '%s' (%s)\n",
+ p->inst_interp_error(p, rv), p->interp_error(p, rv));
+ p->del(p);
+ return -1;
+ }
+
+ /* Initialise the instrument */
+ if ((rv = p->init_inst(p)) != inst_ok) {
+ a1logd(log,1,"init_inst returned '%s' (%s)\n",
+ p->inst_interp_error(p, rv), p->interp_error(p, rv));
+ p->del(p);
+ return -1;
+ }
+
+ itype = p->get_itype(p); /* Actual type */
+ p->capabilities(p, &cap, &cap2, &cap3);
+
+ if (tele && !IMODETST(cap, inst_mode_emis_tele)) {
+ printf("Want telephoto measurement capability but instrument doesn't support it\n");
+ printf("so falling back to emissive spot mode.\n");
+ tele = 0;
+ }
+
+ /* Set to emission mode to read a display */
+ if (tele)
+ mode = inst_mode_emis_tele;
+ else
+ mode = inst_mode_emis_spot;
+ if (nadaptive)
+ mode |= inst_mode_emis_nonadaptive;
+
+ /* (We're assuming spectral doesn't affect calibration ?) */
+
+ if ((rv = p->set_mode(p, mode)) != inst_ok) {
+ a1logd(log,1,"Set_mode failed with '%s' (%s)\n",
+ p->inst_interp_error(p, rv), p->interp_error(p, rv));
+ return -1;
+ }
+ p->capabilities(p, &cap, &cap2, &cap3);
+
+ /* Set the display type */
+ if (dtype != 0) { /* Given selection character */
+ if (cap2 & inst2_disptype) {
+ int ix;
+ if ((ix = inst_get_disptype_index(p, dtype, docbid)) < 0) {
+ a1logd(log,1,"Display type selection '%c' is not valid for instrument\n",dtype);
+ p->del(p);
+ return -1;
+ }
+
+ if ((rv = p->set_disptype(p, ix)) != inst_ok) {
+ a1logd(log,1,"Setting display type failed failed with '%s' (%s)\n",
+ p->inst_interp_error(p, rv), p->interp_error(p, rv));
+ p->del(p);
+ return -1;
+ }
+ } else
+ printf("Display type ignored - instrument doesn't support display type\n");
+ }
+
+ /* Disable initial calibration of machine if selected */
+ if (noinitcal != 0) {
+ if ((rv = p->get_set_opt(p,inst_opt_noinitcalib, 0)) != inst_ok) {
+ a1logd(log,1,"Setting no-initail calibrate failed with '%s' (%s)\n",
+ p->inst_interp_error(p, rv), p->interp_error(p, rv));
+ printf("Disable initial-calibrate not supported\n");
+ }
+ }
+
+ /* Do the calibration */
+ rv = inst_handle_calibrate(p, inst_calt_available, inst_calc_none, setup_display_calibrate, &dwi);
+ setup_display_calibrate(p,inst_calc_none, &dwi);
+ if (rv == inst_unsupported) {
+ printf("No calibration available for instrument in this mode\n");
+ } else if (rv != inst_ok) { /* Abort or fatal error */
+ printf("Calibrate failed with '%s' (%s)\n",
+ p->inst_interp_error(p, rv), p->interp_error(p, rv));
+ p->del(p);
+ return -1;
+ }
+
+ /* clean up */
+ p->del(p);
+ a1logv(log, 1, "Finished setting up the instrument\n");
+
+ return 0;
+}
+
+/* Take a series of readings from the display - implementation */
+/* Return nz on fail/abort */
+/* 1 = user aborted */
+/* 2 = instrument access failed */
+/* 3 = window access failed */
+/* 4 = user hit terminate key */
+/* 5 = system error */
+/* Use disprd_err() to interpret it */
+static int disprd_read_imp(
+ disprd *p,
+ col *cols, /* Array of patch colors to be tested */
+ int npat, /* Number of patches to be tested */
+ int spat, /* Start patch index for "verb", 0 if not used */
+ int tpat, /* Total patch index for "verb", 0 if not used */
+ int acr, /* If nz, do automatic final carriage return */
+ int noinc, /* If nz, don't increment the count */
+ int tc, /* If nz, termination key */
+ instClamping clamp /* NZ if clamp XYZ/Lab to be +ve */
+) {
+ int j, rv;
+ int patch;
+ ipatch val; /* Return value */
+ int ch; /* Character */
+ int cal_type;
+ char id[CALIDLEN];
+ inst2_capability cap2;
+
+ /* Setup the keyboard trigger to return our commands */
+ inst_set_uih(0x0, 0xff, DUIH_TRIG);
+ inst_set_uih('q', 'q', DUIH_ABORT);
+ inst_set_uih('Q', 'Q', DUIH_ABORT);
+ inst_set_uih(0x03, 0x03, DUIH_ABORT); /* ^c */
+ inst_set_uih(0x1b, 0x1b, DUIH_ABORT); /* Esc */
+
+ /* Setup user termination character */
+ inst_set_uih(tc, tc, DUIH_TERM);
+
+ p->it->capabilities(p->it, NULL, &cap2, NULL);
+
+ /* See if we should calibrate the display update */
+ if (!p->update_delay_set && (cap2 & inst2_meas_disp_update) != 0) {
+ int cdelay, mdelay;
+
+ /* Set white with a normal delay */
+ if ((rv = p->dw->set_color(p->dw, 1.0, 1.0, 1.0)) != 0) {
+ a1logd(p->log,1,"set_color() returned %s\n",rv);
+ return 3;
+ }
+
+ /* Set a zero delay */
+ cdelay = p->dw->set_update_delay(p->dw, 0);
+
+ /* Set black with zero delay */
+ if ((rv = p->dw->set_color(p->dw, 0.0, 0.0, 0.0)) != 0) {
+ a1logd(p->log,1,"set_color() returned %s\n",rv);
+ return 3;
+ }
+
+ /* Measure the delay */
+ if ((rv = p->it->meas_delay(p->it, &mdelay)) != inst_ok) {
+ a1logd(p->log,1,"warning, measure display update delay failed with '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ p->dw->set_update_delay(p->dw, cdelay); /* Restore the default */
+ } else {
+ a1logv(p->log, 1, "Measured display update delay of %d msec",mdelay);
+ mdelay += mdelay/2 + 50;
+ p->dw->set_update_delay(p->dw, mdelay);
+ a1logv(p->log, 1, ", using delay of %d msec\n",mdelay);
+ }
+ p->update_delay_set = 1;
+ }
+
+ /* See if we should do a frequency calibration or display integration time cal. first */
+ if (p->it->needs_calibration(p->it) & inst_calt_ref_freq
+ && npat > 0
+ && (cols[0].r != 1.0 || cols[0].g != 1.0 || cols[0].b != 1.0)) {
+ col tc;
+ inst_cal_type calt = inst_calt_ref_freq;
+ inst_cal_cond calc = inst_calc_emis_white;
+
+ if ((rv = p->dw->set_color(p->dw, 1.0, 1.0, 1.0)) != 0) {
+ a1logd(p->log,1,"set_color() returned %s\n",rv);
+ return 3;
+ }
+ /* Do calibrate, but ignore return code. Press on regardless. */
+ if ((rv = p->it->calibrate(p->it, &calt, &calc, id)) != inst_ok) {
+ a1logd(p->log,1,"warning, frequency calibrate failed with '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ }
+ }
+
+ /* See if we should do a display integration calibration first */
+ if (p->it->needs_calibration(p->it) & inst_calt_emis_int_time) {
+ col tc;
+ inst_cal_type calt = inst_calt_emis_int_time;
+ inst_cal_cond calc = inst_calc_emis_white;
+
+ if ((rv = p->dw->set_color(p->dw, 1.0, 1.0, 1.0)) != 0) {
+ a1logd(p->log,1,"set_color() returned %s\n",rv);
+ return 3;
+ }
+ /* Do calibrate, but ignore return code. Press on regardless. */
+ if ((rv = p->it->calibrate(p->it, &calt, &calc, id)) != inst_ok) {
+ a1logd(p->log,1,"warning, display integration calibrate failed with '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ }
+ }
+
+ for (patch = 0; patch < npat; patch++) {
+ col *scb = &cols[patch];
+ double rgb[3];
+
+ scb->mtype = inst_mrt_none;
+ scb->XYZ_v = 0; /* No readings are valid */
+ scb->sp.spec_n = 0;
+ scb->duration = 0.0;
+
+ if (spat != 0 && tpat != 0)
+ a1logv(p->log, 1, "%cpatch %d of %d",cr_char,spat + (noinc != 0 ? 0 : patch),tpat);
+ a1logd(p->log,1,"About to read patch %d\n",patch);
+
+ rgb[0] = scb->r;
+ rgb[1] = scb->g;
+ rgb[2] = scb->b;
+
+ /* If we are doing a soft cal, apply it to the test color */
+ if (p->softcal && p->cal[0][0] >= 0.0) {
+ int j;
+ double inputEnt_1 = (double)(p->ncal-1);
+
+ for (j = 0; j < 3; j++) {
+ unsigned int ix;
+ double val, w;
+ val = rgb[j] * inputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ } else if (val > inputEnt_1) {
+ val = inputEnt_1;
+ }
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > (p->ncal-2))
+ ix = (p->ncal-2);
+ w = val - (double)ix; /* weight */
+ val = p->cal[j][ix];
+ rgb[j] = val + w * (p->cal[j][ix+1] - val);
+ }
+ }
+ if ((rv = p->dw->set_color(p->dw, rgb[0], rgb[1], rgb[2])) != 0) {
+ a1logd(p->log,1,"set_color() returned %s\n",rv);
+ return 3;
+ }
+
+ /* Until we give up retrying */
+ for (;;) {
+ val.mtype = inst_mrt_none;
+ val.XYZ_v = 0; /* No readings are valid */
+ val.sp.spec_n = 0;
+ val.duration = 0.0;
+
+ if ((rv = p->it->read_sample(p->it, scb->id, &val, 1)) != inst_ok
+ && (rv & inst_mask) != inst_user_trig) {
+ a1logd(p->log,1,"read_sample returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+
+ /* deal with a user terminate or abort */
+ if ((rv & inst_mask) == inst_user_abort) {
+ int keyc = inst_get_uih_char();
+
+ /* Deal with a user terminate */
+ if (keyc & DUIH_TERM) {
+ return 4;
+
+ /* Deal with a user abort */
+ } else if (keyc & DUIH_ABORT) {
+ empty_con_chars();
+ printf("\nSample read stopped at user request!\n");
+ printf("Hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ continue;
+ }
+
+ /* Deal with needing calibration */
+ } else if ((rv & inst_mask) == inst_needs_cal) {
+ disp_win_info dwi;
+ dwi.dw = p->dw; /* Set window to use */
+ printf("\nSample read failed because instruments needs calibration\n");
+ rv = inst_handle_calibrate(p->it, inst_calt_needed, inst_calc_none,
+ setup_display_calibrate, &dwi);
+ setup_display_calibrate(p->it, inst_calc_none, &dwi);
+ if (rv != inst_ok) { /* Abort or fatal error */
+ return 1;
+ }
+ continue;
+
+ /* Deal with a bad sensor position */
+ } else if ((rv & inst_mask) == inst_wrong_config) {
+ empty_con_chars();
+ printf("\n\nSpot read failed due to the sensor being in the wrong position\n");
+ printf("(%s)\n",p->it->interp_error(p->it, rv));
+ printf("Correct position then hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ continue;
+
+ /* Deal with a misread */
+ } else if ((rv & inst_mask) == inst_misread) {
+ empty_con_chars();
+ printf("\nSample read failed due to misread\n");
+ printf("Hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ continue;
+
+ /* Deal with a communications error */
+ } else if ((rv & inst_mask) == inst_coms_fail) {
+ empty_con_chars();
+ printf("\nSample read failed due to communication problem.\n");
+ printf("Hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ if (p->it->icom->port_type(p->it->icom) == icomt_serial) {
+ /* Allow retrying at a lower baud rate */
+ int tt = p->it->last_scomerr(p->it);
+ if (tt & (ICOM_BRK | ICOM_FER | ICOM_PER | ICOM_OER)) {
+ if (p->br == baud_19200) p->br = baud_9600;
+ else if (p->br == baud_9600) p->br = baud_4800;
+ else if (p->br == baud_2400) p->br = baud_1200;
+ else p->br = baud_1200;
+ }
+ if ((rv = p->it->init_coms(p->it, p->br, p->fc, 15.0)) != inst_ok) {
+ a1logd(p->log,1,"init_coms returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+ }
+ continue;
+ }
+ } else {
+ break; /* Sucesful reading */
+ }
+ }
+ /* We only fall through with a valid reading */
+ scb->serno = p->serno++;
+ scb->msec = msec_time();
+
+ a1logd(p->log,1, "got reading %f %f %f, transfering to col\n",
+ val.XYZ[0], val.XYZ[1], val.XYZ[2]);
+
+ scb->mtype = val.mtype;
+
+ /* Copy XYZ */
+ if (val.XYZ_v != 0) {
+ for (j = 0; j < 3; j++)
+ scb->XYZ[j] = val.XYZ[j];
+ scb->XYZ_v = 1;
+ }
+
+ /* Copy spectral */
+ if (p->spectral && val.sp.spec_n > 0) {
+ scb->sp = val.sp;
+ }
+ a1logd(p->log,1,"on to next reading\n");
+ }
+ /* Final return. */
+ if (acr && spat != 0 && tpat != 0 && (spat+patch-1) == tpat)
+ a1logv(p->log, 1, "\n");
+ return 0;
+}
+
+/* A structure to hold info about drift samples */
+typedef struct {
+ col dcols[2]; /* Black and white readings */
+
+ int off, count; /* Offset and count into cols */
+
+} dsamples;
+
+/* Take a series of readings from the display - drift compensation */
+/* Return nz on fail/abort */
+/* 1 = user aborted */
+/* 2 = instrument access failed */
+/* 3 = window access failed */
+/* 4 = user hit terminate key */
+/* 5 = system error */
+/* Use disprd_err() to interpret it */
+static int disprd_read_drift(
+ disprd *p,
+ col *cols, /* Array of patch colors to be tested */
+ int npat, /* Number of patches to be tested */
+ int spat, /* Start patch index for "verb", 0 if not used */
+ int tpat, /* Total patch index for "verb", 0 if not used */
+ int acr, /* If nz, do automatic final carriage return */
+ int tc, /* If nz, termination key */
+ instClamping clamp /* NZ if clamp XYZ/Lab to be +ve */
+) {
+ int rv, i, j, e;
+ double fper, foff;
+ int off, poff;
+ int boff, eoff, dno; /* b&w offsets and count */
+
+//printf("~1 DRIFT_EPERIOD %d, DRIFT_IPERIOD %d, npat %d\n",DRIFT_EPERIOD,DRIFT_IPERIOD,npat);
+
+ /* Figure number and offset for b&w */
+ if (p->bdrift == 0) { /* Must be just wdrift */
+ boff = eoff = 1;
+ dno = 1;
+ } else if (p->bdrift == 0) { /* Must be just bdrift */
+ boff = eoff = 0;
+ dno = 1;
+ } else { /* Must be both */
+ boff = eoff = 0;
+ dno = 2;
+ }
+
+ /* If the last drift readings are invalid or too old, */
+ /* or if we will use interpolation, read b&w */
+#ifdef NEVER
+ printf("last_bw_v = %d (%d)\n",p->last_bw_v,p->last_bw_v == 0);
+ printf("npat = %d > %d, serno %d, last serno %d (%d)\n",npat, DRIFT_EPERIOD, p->serno,p->last_bw[eoff].serno,(npat > DRIFT_EPERIOD && p->serno > p->last_bw[eoff].serno));
+ printf("serno - lastserno %d > %d (%d)\n",p->serno - p->last_bw[eoff].serno, DRIFT_EPERIOD,(p->serno - p->last_bw[eoff].serno) > DRIFT_EPERIOD);
+ printf("msec %d - last %d = %d > 10000 (%d)\n",msec_time(),p->last_bw[boff].msec,msec_time() - p->last_bw[boff].msec,(msec_time() - p->last_bw[eoff].msec) > 10000);
+#endif /* NEVER */
+ if (p->last_bw_v == 0 /* There are none */
+ || (npat > DRIFT_EPERIOD && p->serno > p->last_bw[eoff].serno) /* We will interpolate */
+ || (p->serno - p->last_bw[eoff].serno - dno) > DRIFT_EPERIOD /* extrapolate would be too far */
+ || (msec_time() - p->last_bw[eoff].msec) > 10000) { /* The current is too long ago */
+
+//printf("~1 refreshing last bw\n");
+ /* Read the black and/or white drift patch */
+ p->last_bw[0].r =
+ p->last_bw[0].g =
+ p->last_bw[0].b = 0.0;
+ p->last_bw[1].r =
+ p->last_bw[1].g =
+ p->last_bw[1].b = 1.0;
+ if ((rv = disprd_read_imp(p, &p->last_bw[boff], dno, spat, tpat, 0, 1, tc, 0)) != 0) {
+ return rv;
+ }
+ p->last_bw_v = 1;
+
+ /* If there is no reference b&w, set them from this first b&w */
+ if (p->ref_bw_v == 0) {
+ p->ref_bw[0] = p->last_bw[0];
+ p->ref_bw[1] = p->last_bw[1];
+ p->ref_bw_v = 1;
+ }
+ }
+
+ /* If there are enough patches to bracket with drift readings */
+ if (npat > DRIFT_EPERIOD) {
+ int ndrift = 2; /* Number of drift records */
+ dsamples *dss;
+
+ /* Figure out the number of drift samples we need */
+ ndrift += (npat-1)/DRIFT_IPERIOD;
+//printf("~1 spat %d, npat = %d, tpat %d, ndrift = %d\n",spat,npat,tpat,ndrift);
+
+ if ((dss = (dsamples *)calloc(sizeof(dsamples), ndrift)) == NULL) {
+ a1logd(p->log,1, "malloc of %d dsamples failed\n",ndrift);
+ return 5;
+ }
+
+ /* Set up bookeeping */
+ fper = (double)npat/(ndrift-1.0);
+//printf("~1 fper = %f\n",fper);
+ foff = 0.0;
+ for (poff = off = i = 0; i < ndrift; i++) {
+ dss[i].off = off;
+ foff += fper;
+ off = (int)floor(foff + 0.5);
+ if (i < (ndrift-1))
+ dss[i].count = off - poff;
+ else
+ dss[i].count = 0;
+ poff = off;
+//printf("~1 dss[%d] off = %d, count = %d\n",i, dss[i].off,dss[i].count);
+
+ dss[i].dcols[0].r =
+ dss[i].dcols[0].g =
+ dss[i].dcols[0].b = 0.0;
+ dss[i].dcols[1].r =
+ dss[i].dcols[1].g =
+ dss[i].dcols[1].b = 1.0;
+ }
+
+ /* For each batch of patches bracketed by drift patch readings */
+ for (i = 0; i < (ndrift-1); i++) {
+
+ if (i == 0) { /* Already done very first drift patches, so use them */
+ /* Use last b&w */
+ dss[i].dcols[0] = p->last_bw[0];
+ dss[i].dcols[1] = p->last_bw[1];
+ } else {
+ /* Read the black and/or white drift patchs before next batch */
+ if ((rv = disprd_read_imp(p, &dss[i].dcols[boff], dno, spat+dss[i].off, tpat, 0, 1, tc, 0)) != 0) {
+ free(dss);
+ return rv;
+ }
+ }
+ /* Read this batch of patches */
+ if ((rv = disprd_read_imp(p, &cols[dss[i].off], dss[i].count,spat+dss[i].off,tpat,0,0,tc, 0)) != 0) {
+ free(dss);
+ return rv;
+ }
+ }
+ /* Read the black and/or white drift patchs after last batch */
+ if ((rv = disprd_read_imp(p, &dss[i].dcols[boff], dno, spat+dss[i].off-1, tpat, 0, 1, tc, 0)) != 0) {
+ free(dss);
+ return rv;
+ }
+ /* Remember the last for next time */
+ p->last_bw[0] = dss[i].dcols[0];
+ p->last_bw[1] = dss[i].dcols[1];
+ p->last_bw_v = 1;
+
+ /* Set the white drift target to be the last one for batch */
+ p->targ_w = p->last_bw[1];
+ p->targ_w_v = 1;
+
+//printf("~1 ref b = %f %f %f\n", p->ref_bw[0].XYZ[0], p->ref_bw[0].XYZ[1], p->ref_bw[0].XYZ[2]);
+//printf("~1 ref w = %f %f %f\n", p->ref_bw[1].XYZ[0], p->ref_bw[1].XYZ[1], p->ref_bw[1].XYZ[2]);
+//printf("~1 ndrift-1 b = %f %f %f\n", dss[ndrift-1].dcols[0].XYZ[0], dss[ndrift-1].dcols[0].XYZ[1], dss[ndrift-1].dcols[0].XYZ[2]);
+//printf("~1 ndrift-1 w = %f %f %f\n", dss[ndrift-1].dcols[1].XYZ[0], dss[ndrift-1].dcols[1].XYZ[1], dss[ndrift-1].dcols[1].XYZ[2]);
+
+ /* Apply the drift compensation using interpolation */
+ for (i = 0; i < (ndrift-1); i++) {
+
+ for (j = 0; j < dss[i].count; j++) {
+ int k = dss[i].off + j;
+ double we; /* Interpolation weight of eairlier value */
+ col bb, ww; /* Interpolated black and white */
+//double uXYZ[3];
+//icmCpy3(uXYZ, cols[k].XYZ);
+//printf("~1 patch %d = %f %f %f\n", k, cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2]);
+ if (p->bdrift) {
+ we = (double)(dss[i+1].dcols[0].msec - cols[k].msec)/
+ (double)(dss[i+1].dcols[0].msec - dss[i].dcols[0].msec);
+
+ if (cols[k].XYZ_v) {
+ for (e = 0; e < 3; e++) {
+ bb.XYZ[e] = we * dss[i].dcols[0].XYZ[e]
+ + (1.0 - we) * dss[i+1].dcols[0].XYZ[e];
+ }
+//printf("~1 bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2]);
+ }
+ if (cols[k].sp.spec_n > 0) {
+ for (e = 0; e < cols[k].sp.spec_n; e++) {
+ bb.sp.spec[e] = we * dss[i].dcols[0].sp.spec[e]
+ + (1.0 - we) * dss[i+1].dcols[0].sp.spec[e];
+ }
+ }
+ }
+ if (p->wdrift) {
+ we = (double)(dss[i+1].dcols[1].msec - cols[k].msec)/
+ (double)(dss[i+1].dcols[1].msec - dss[i].dcols[1].msec);
+
+ if (cols[k].XYZ_v) {
+ for (e = 0; e < 3; e++) {
+ ww.XYZ[e] = we * dss[i].dcols[1].XYZ[e]
+ + (1.0 - we) * dss[i+1].dcols[1].XYZ[e];
+ }
+//printf("~1 ww = %f %f %f\n", ww.XYZ[0], ww.XYZ[1], ww.XYZ[2]);
+ }
+ if (cols[k].sp.spec_n > 0) {
+ for (e = 0; e < cols[k].sp.spec_n; e++) {
+ ww.sp.spec[e] = we * dss[i].dcols[1].sp.spec[e]
+ + (1.0 - we) * dss[i+1].dcols[1].sp.spec[e];
+ }
+ }
+ }
+
+ if (p->bdrift) {
+ /* Compensate interpolated black for any white change from ref. */
+ if (p->wdrift) {
+ if (cols[k].XYZ_v) {
+ for (e = 0; e < 3; e++) {
+ bb.XYZ[e] *= p->ref_bw[1].XYZ[e]/ww.XYZ[e];
+ }
+//printf("~1 wcomp bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2]);
+ }
+ if (cols[k].sp.spec_n > 0) {
+ for (e = 0; e < cols[k].sp.spec_n; e++) {
+ bb.sp.spec[e] *= p->ref_bw[1].sp.spec[e]/ww.sp.spec[e];
+ }
+ }
+ }
+
+ /* Compensate the reading for any black drift from ref. */
+ if (cols[k].XYZ_v) {
+ for (e = 0; e < 3; e++) {
+ cols[k].XYZ[e] += p->ref_bw[0].XYZ[e] - bb.XYZ[e];
+ }
+//printf("~1 bcomp patch = %f %f %f\n", cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2]);
+ }
+ if (cols[k].sp.spec_n > 0) {
+ for (e = 0; e < cols[k].sp.spec_n; e++) {
+ cols[k].sp.spec[e] += p->ref_bw[0].sp.spec[e] - bb.sp.spec[e];
+ }
+ }
+ }
+ /* Compensate the reading for any white drift to target white */
+ if (p->wdrift) {
+ if (cols[k].XYZ_v) {
+ for (e = 0; e < 3; e++) {
+ cols[k].XYZ[e] *= p->targ_w.XYZ[e]/ww.XYZ[e];
+ }
+//printf("~1 wcomp patch = %f %f %f\n", cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2]);
+ }
+ if (cols[k].sp.spec_n > 0) {
+ for (e = 0; e < cols[k].sp.spec_n; e++) {
+ cols[k].sp.spec[e] *= p->targ_w.sp.spec[e]/ww.sp.spec[e];
+ }
+ }
+ }
+//printf("~1 %d: drift change %f DE\n",k,icmXYZLabDE(&icmD50, uXYZ, cols[k].XYZ));
+ }
+ }
+ free(dss);
+
+ /* Else too small a batch, use extrapolation from the last b&w */
+ } else {
+
+//printf("~1 doing small number of readings\n");
+ /* Read the small number of patches */
+ if ((rv = disprd_read_imp(p, cols,npat,spat,tpat,acr,0,tc,0)) != 0)
+ return rv;
+
+ if (p->targ_w_v == 0) {
+ /* Set the white drift reference to be the last one */
+ p->targ_w = p->last_bw[1];
+ p->targ_w_v = 1;
+//printf("~1 set white drift target\n");
+ }
+
+//printf("~1 ref b = %f %f %f\n", p->ref_bw[0].XYZ[0], p->ref_bw[0].XYZ[1], p->ref_bw[0].XYZ[2]);
+//printf("~1 ref w = %f %f %f\n", p->ref_bw[1].XYZ[0], p->ref_bw[1].XYZ[1], p->ref_bw[1].XYZ[2]);
+//printf("~1 last b = %f %f %f\n", p->last_bw[0].XYZ[0], p->last_bw[0].XYZ[1], p->last_bw[0].XYZ[2]);
+//printf("~1 last w = %f %f %f\n", p->last_bw[1].XYZ[0], p->last_bw[1].XYZ[1], p->last_bw[1].XYZ[2]);
+//printf("~1 target w = %f %f %f\n", p->targ_w.XYZ[0], p->targ_w.XYZ[1], p->targ_w.XYZ[2]);
+ /* Apply the drift compensation using extrapolation */
+ for (j = 0; j < npat; j++) {
+ double we; /* Interpolation weight of eairlier value */
+ col bb, ww; /* Interpolated black and white */
+
+//printf("~1 patch %d = %f %f %f\n", j, cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2]);
+//double uXYZ[3];
+//icmCpy3(uXYZ, cols[j].XYZ);
+
+ if (p->bdrift) {
+ bb = p->last_bw[0];
+//printf("~1 bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2]);
+ }
+ if (p->wdrift) {
+ ww = p->last_bw[1];
+//printf("~1 ww = %f %f %f\n", ww.XYZ[0], ww.XYZ[1], ww.XYZ[2]);
+ }
+
+ if (p->bdrift) {
+ /* Compensate interpolated black for any white change from ref. */
+ if (p->wdrift) {
+ if (cols[j].XYZ_v) {
+ for (e = 0; e < 3; e++) {
+ bb.XYZ[e] *= p->ref_bw[1].XYZ[e]/ww.XYZ[e];
+ }
+//printf("~1 wcomp bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2]);
+ }
+ if (cols[j].sp.spec_n > 0) {
+ for (e = 0; e < cols[j].sp.spec_n; e++) {
+ bb.sp.spec[e] *= p->ref_bw[1].sp.spec[e]/ww.sp.spec[e];
+ }
+ }
+ }
+
+ /* Compensate the reading for any black drift from ref. */
+ if (cols[j].XYZ_v) {
+ for (e = 0; e < 3; e++) {
+ cols[j].XYZ[e] += p->ref_bw[0].XYZ[e] - bb.XYZ[e];
+ }
+//printf("~1 bcomp patch = %f %f %f\n", cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2]);
+ }
+ if (cols[j].sp.spec_n > 0) {
+ for (e = 0; e < cols[j].sp.spec_n; e++) {
+ cols[j].sp.spec[e] += p->ref_bw[0].sp.spec[e] - bb.sp.spec[e];
+ }
+ }
+ }
+ /* Compensate the reading for any white drift to target white */
+ if (p->wdrift) {
+ if (cols[j].XYZ_v) {
+ for (e = 0; e < 3; e++) {
+ cols[j].XYZ[e] *= p->targ_w.XYZ[e]/ww.XYZ[e];
+ }
+//printf("~1 wcomp patch = %f %f %f\n", cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2]);
+ }
+ if (cols[j].sp.spec_n > 0) {
+ for (e = 0; e < cols[j].sp.spec_n; e++) {
+ cols[j].sp.spec[e] *= p->targ_w.sp.spec[e]/ww.sp.spec[e];
+ }
+ }
+ }
+//printf("~1 %d: drift change %f DE\n",j,icmXYZLabDE(&icmD50, uXYZ, cols[j].XYZ));
+ }
+ }
+
+ if (acr && spat != 0 && tpat != 0 && (spat+npat-1) == tpat)
+ a1logv(p->log, 1, "\n");
+
+ if (clamp) {
+ for (j = 0; j < npat; j++) {
+ if (cols[j].XYZ_v)
+ icmClamp3(cols[j].XYZ, cols[j].XYZ);
+ }
+ }
+ return rv;
+}
+
+/* Reset the white drift target white value, for non-batch */
+/* readings when white drift comp. is enabled */
+static void disprd_reset_targ_w(disprd *p) {
+ p->targ_w_v = 0;
+}
+
+/* Change the black/white drift compensation options. */
+/* Note that this simply invalidates any reference readings, */
+/* and therefore will not make for good black compensation */
+/* if it is done a long time since the instrument calibration. */
+static void disprd_change_drift_comp(disprd *p,
+ int bdrift, /* Flag, nz for black drift compensation */
+ int wdrift /* Flag, nz for white drift compensation */
+) {
+ if (p->bdrift && !bdrift) { /* Turning black drift off */
+ p->bdrift = 0;
+ p->ref_bw_v = 0;
+ p->last_bw_v = 0;
+ p->targ_w_v = 0;
+
+ } else if (!p->bdrift && bdrift) { /* Turning black drift on */
+ p->bdrift = 1;
+ p->ref_bw_v = 0;
+ p->last_bw_v = 0;
+ p->targ_w_v = 0;
+ }
+
+ if (p->wdrift && !wdrift) { /* Turning white drift off */
+ p->wdrift = 0;
+ p->ref_bw_v = 0;
+ p->last_bw_v = 0;
+ p->targ_w_v = 0;
+
+ } else if (!p->wdrift && wdrift) { /* Turning white drift on */
+ p->wdrift = 1;
+ p->ref_bw_v = 0;
+ p->last_bw_v = 0;
+ p->targ_w_v = 0;
+ }
+}
+
+/* Take a series of readings from the display */
+/* Return nz on fail/abort */
+/* 1 = user aborted */
+/* 2 = instrument access failed */
+/* 3 = window access failed */
+/* 4 = user hit terminate key */
+/* 5 = system error */
+/* Use disprd_err() to interpret it */
+static int disprd_read(
+ disprd *p,
+ col *cols, /* Array of patch colors to be tested */
+ int npat, /* Number of patches to be tested */
+ int spat, /* Start patch index for "verb", 0 if not used */
+ int tpat, /* Total patch index for "verb", 0 if not used */
+ int acr, /* If nz, do automatic final carriage return */
+ int tc, /* If nz, termination key */
+ instClamping clamp /* NZ if clamp XYZ/Lab to be +ve */
+) {
+ int rv, i;
+
+ if (p->bdrift || p->wdrift) {
+ if ((rv = disprd_read_drift(p, cols,npat,spat,tpat,acr,tc,clamp)) != 0)
+ return rv;
+ } else {
+ if ((rv = disprd_read_imp(p, cols,npat,spat,tpat,acr,0,tc,clamp)) != 0)
+ return rv;
+ }
+
+ /* Use spectral if available. */
+ /* Since this is a display, assume that this is absolute spectral */
+ if (p->sp2cie != NULL) {
+ for (i = 0; i < npat; i++) {
+ if (cols[i].sp.spec_n > 0) {
+ p->sp2cie->convert(p->sp2cie, cols[i].XYZ, &cols[i].sp);
+ if (clamp)
+ icmClamp3(cols[i].XYZ, cols[i].XYZ);
+ cols[i].XYZ_v = 1;
+ }
+ }
+ }
+
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Return the refresh mode and cbid */
+static void disprd_get_disptype(disprd *p, int *refrmode, int *cbid) {
+ if (refrmode != NULL)
+ *refrmode = p->refrmode;
+ if (cbid != NULL)
+ *cbid = p->cbid;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+static int config_inst_displ(disprd *p);
+
+/* Take an ambient reading if the instrument has the capability. */
+/* return nz on fail/abort */
+/* 1 = user aborted */
+/* 2 = instrument access failed */
+/* 4 = user hit terminate key */
+/* 5 = system error */
+/* 8 = no ambient capability */
+/* Use disprd_err() to interpret it */
+int disprd_ambient(
+ struct _disprd *p,
+ double *ambient, /* return ambient in cd/m^2 */
+ int tc /* If nz, termination key */
+) {
+ inst_mode cap = 0;
+ inst2_capability cap2 = 0;
+ inst3_capability cap3 = 0;
+ inst_opt_type trigmode = inst_opt_unknown; /* Chosen trigger mode */
+ int uswitch = 0; /* Instrument switch is enabled */
+ ipatch val;
+ int rv;
+
+ if (p->it != NULL) { /* Not fake */
+ p->it->capabilities(p->it, &cap, &cap2, &cap3);
+ }
+
+ if (!IMODETST(cap, inst_mode_emis_ambient)) {
+ printf("Need ambient measurement capability,\n");
+ printf("but instrument doesn't support it\n");
+ return 8;
+ }
+
+ printf("\nPlease make sure the instrument is fitted with\n");
+ printf("the appropriate ambient light measuring head.\n");
+
+ if ((rv = p->it->set_mode(p->it, inst_mode_emis_ambient)) != inst_ok) {
+ a1logd(p->log,1,"set_mode returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+ if (p->it != NULL) { /* Not fake */
+ p->it->capabilities(p->it, &cap, &cap2, &cap3);
+ }
+
+ /* Select a reasonable trigger mode */
+ if (cap2 & inst2_user_switch_trig) {
+ trigmode = inst_opt_trig_user_switch;
+ uswitch = 1;
+
+ /* Or go for user via uicallback trigger */
+ } else if (cap2 & inst2_user_trig) {
+ trigmode = inst_opt_trig_user;
+
+ /* Or something is wrong with instrument capabilities */
+ } else {
+ printf("No reasonable trigger mode avilable for this instrument\n");
+ return 2;
+ }
+
+ if ((rv = p->it->get_set_opt(p->it, trigmode)) != inst_ok) {
+ printf("\nSetting trigger mode failed with error :'%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+
+ /* Setup the keyboard trigger to return our commands */
+ inst_set_uih(0x0, 0xff, DUIH_TRIG);
+ inst_set_uih('q', 'q', DUIH_ABORT);
+ inst_set_uih('Q', 'Q', DUIH_ABORT);
+ inst_set_uih(0x03, 0x03, DUIH_ABORT); /* ^c */
+ inst_set_uih(0x1b, 0x1b, DUIH_ABORT); /* Esc */
+
+ /* Setup user termination character */
+ inst_set_uih(tc, tc, DUIH_TERM);
+
+ /* Until we give up retrying */
+ for (;;) {
+ char ch;
+ val.mtype = inst_mrt_none;
+ val.XYZ_v = 0; /* No readings are valid */
+ val.sp.spec_n = 0;
+ val.duration = 0.0;
+
+ printf("\nPlace the instrument so as to measure ambient upwards, beside the display,\n");
+ if (uswitch)
+ printf("Hit ESC or Q to exit, instrument switch or any other key to take a reading: ");
+ else
+ printf("Hit ESC or Q to exit, any other key to take a reading: ");
+ fflush(stdout);
+
+ if ((rv = p->it->read_sample(p->it, "AMBIENT", &val, 1)) != inst_ok
+ && (rv & inst_mask) != inst_user_trig) {
+ a1logd(p->log,1,"read_sample returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+
+ /* deal with a user terminate or abort */
+ if ((rv & inst_mask) == inst_user_abort) {
+ int keyc = inst_get_uih_char();
+
+ /* Deal with a user terminate */
+ if (keyc & DUIH_TERM) {
+ return 4;
+
+ /* Deal with a user abort */
+ } else if (keyc & DUIH_ABORT) {
+ empty_con_chars();
+ printf("\nMeasure stopped at user request!\n");
+ printf("Hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ continue;
+ }
+
+ /* Deal with needs calibration */
+ } else if ((rv & inst_mask) == inst_needs_cal) {
+ disp_win_info dwi;
+ dwi.dw = p->dw; /* Set window to use */
+ printf("\nSample read failed because instruments needs calibration\n");
+ rv = inst_handle_calibrate(p->it, inst_calt_needed, inst_calc_none,
+ setup_display_calibrate, &dwi);
+ setup_display_calibrate(p->it,inst_calc_none, &dwi);
+ if (rv != inst_ok) { /* Abort or fatal error */
+ return 1;
+ }
+ continue;
+
+ /* Deal with a bad sensor position */
+ } else if ((rv & inst_mask) == inst_wrong_config) {
+ empty_con_chars();
+ printf("\n\nSpot read failed due to the sensor being in the wrong position\n");
+ printf("(%s)\n",p->it->interp_error(p->it, rv));
+ printf("Correct position then hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ continue;
+
+ /* Deal with a misread */
+ } else if ((rv & inst_mask) == inst_misread) {
+ empty_con_chars();
+ printf("\nMeasurement failed due to misread\n");
+ printf("Hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ continue;
+
+ /* Deal with a communications error */
+ } else if ((rv & inst_mask) == inst_coms_fail) {
+ empty_con_chars();
+ printf("\nMeasurement read failed due to communication problem.\n");
+ printf("Hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ if (p->it->icom->port_type(p->it->icom) == icomt_serial) {
+ /* Allow retrying at a lower baud rate */
+ int tt = p->it->last_scomerr(p->it);
+ if (tt & (ICOM_BRK | ICOM_FER | ICOM_PER | ICOM_OER)) {
+ if (p->br == baud_19200) p->br = baud_9600;
+ else if (p->br == baud_9600) p->br = baud_4800;
+ else if (p->br == baud_2400) p->br = baud_1200;
+ else p->br = baud_1200;
+ }
+ if ((rv = p->it->init_coms(p->it, p->br, p->fc, 15.0)) != inst_ok) {
+ a1logd(p->log,1,"init_coms returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+ }
+ continue;
+ }
+ } else {
+ break; /* Sucesful reading */
+ }
+ }
+ /* Convert spectral to XYZ */
+ if (p->sp2cie != NULL && val.sp.spec_n > 0) {
+ p->sp2cie->convert(p->sp2cie, val.XYZ, &val.sp);
+ icmClamp3(val.XYZ, val.XYZ);
+ val.XYZ_v = 1;
+ }
+
+ if (val.XYZ_v == 0) {
+ printf("Unexpected failure to get measurement\n");
+ return 2;
+ }
+
+ a1logd(p->log,1,"Measured ambient of %f\n",val.XYZ[1]);
+ if (ambient != NULL)
+ *ambient = val.XYZ[1];
+
+ /* Configure the instrument mode back to reading the display */
+ if ((rv = config_inst_displ(p)) != 0)
+ return rv;
+
+ printf("\nPlace the instrument back on the test window\n");
+ fflush(stdout);
+
+ return 0;
+}
+
+
+/* Test without a spectrometer using a fake device */
+/* Return nz on fail/abort */
+/* 1 = user aborted */
+/* 2 = instrument access failed */
+/* 3 = window access failed */
+/* 4 = user hit terminate key */
+/* 5 = system error */
+/* Use disprd_err() to interpret it */
+static int disprd_fake_read(
+ disprd *p,
+ col *cols, /* Array of patch colors to be tested */
+ int npat, /* Number of patches to be tested */
+ int spat, /* Start patch index for "verb", 0 if not used */
+ int tpat, /* Total patch index for "verb", 0 if not used */
+ int acr, /* If nz, do automatic final carriage return */
+ int tc, /* If nz, termination key */
+ instClamping clamp /* NZ if clamp XYZ/Lab to be +ve */
+) {
+ icmXYZNumber white; /* White point */
+ icmXYZNumber red; /* Red colorant */
+ icmXYZNumber green; /* Green colorant */
+ icmXYZNumber blue; /* Blue colorant */
+ double doff[3]; /* device offsets */
+ double mat[3][3]; /* Destination matrix */
+ double xmat[3][3]; /* Extra matrix */
+ double ooff[3]; /* XYZ offsets */
+ double rgb[3]; /* Given test values */
+ double crgb[3]; /* Calibrated test values */
+// double br = 35.4; /* Overall brightness */
+ double br = 120.0; /* Overall brightness */
+ int patch, j;
+ int ttpat = tpat;
+ inst_code (*uicallback)(void *, inst_ui_purp) = NULL;
+ void *uicontext = NULL;
+
+ if (ttpat < npat)
+ ttpat = npat;
+
+ /* Setup fake device */
+ white.X = br * 0.955; /* Somewhere between D50 and D65 */
+ white.Y = br * 1.00;
+ white.Z = br * 0.97;
+ red.X = br * 0.41;
+ red.Y = br * 0.21;
+ red.Z = br * 0.02;
+ green.X = br * 0.30;
+ green.Y = br * 0.55;
+ green.Z = br * 0.15;
+ blue.X = br * 0.15;
+ blue.Y = br * 0.10;
+ blue.Z = br * 0.97;
+#ifdef SIMPLE_MODEL
+ doff[0] = doff[1] = doff[2] = 0.0; /* Input offset */
+ ooff[0] = ooff[1] = ooff[2] = 0.0; /* Output offset */
+#else
+ /* Input offset, equivalent to RGB offsets having various values */
+ doff[0] = 0.10;
+ doff[1] = 0.06;
+ doff[2] = 0.08;
+ /* Output offset - equivalent to flare [range 0.0 - 1.0] */
+ ooff[0] = 0.03;
+ ooff[1] = 0.04;
+ ooff[2] = 0.09;
+#endif
+
+ if (icmRGBprim2matrix(white, red, green, blue, mat))
+ error("Fake read unexpectedly got singular matrix\n");
+
+ icmSetUnity3x3(xmat);
+
+ if (p->fake2 == 1) {
+ /* Target matrix */
+ xmat[0][0] = 1.0; xmat[0][1] = 0.01; xmat[0][2] = 0.02;
+ xmat[1][0] = 0.1; xmat[1][1] = 1.11; xmat[1][2] = 0.12;
+ xmat[2][0] = 0.2; xmat[2][1] = 0.2; xmat[2][2] = 1.22;
+ }
+
+ uicallback = inst_get_uicallback();
+ uicontext = inst_get_uicontext();
+
+ /* Setup the keyboard trigger to return our commands */
+ inst_set_uih(0x0, 0xff, DUIH_TRIG);
+ inst_set_uih('q', 'q', DUIH_ABORT);
+ inst_set_uih('Q', 'Q', DUIH_ABORT);
+ inst_set_uih(0x03, 0x03, DUIH_ABORT); /* ^c */
+ inst_set_uih(0x1b, 0x1b, DUIH_ABORT); /* Esc */
+
+ /* Setup user termination character */
+ inst_set_uih(tc, tc, DUIH_TERM);
+
+ for (patch = 0; patch < npat; patch++) {
+ if (spat != 0 && tpat != 0)
+ a1logv(p->log, 1, "%cpatch %d of %d",cr_char,spat+patch,tpat);
+ crgb[0] = rgb[0] = cols[patch].r;
+ crgb[1] = rgb[1] = cols[patch].g;
+ crgb[2] = rgb[2] = cols[patch].b;
+
+ /* deal with a user terminate or abort */
+ if (uicallback(uicontext, inst_measuring) == inst_user_abort) {
+ int ch, keyc = inst_get_uih_char();
+
+ /* Deal with a user terminate */
+ if (keyc & DUIH_TERM) {
+ return 4;
+
+ /* Deal with a user abort */
+ } else if (keyc & DUIH_ABORT) {
+ empty_con_chars();
+ printf("\nSample read stopped at user request!\n");
+ printf("Hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ }
+ }
+
+//printf("~1 patch %d RGB %f %f %f\n",patch,rgb[0], rgb[1], rgb[2]);
+ /* If we have a calibration, apply it to the color */
+ if (p->cal[0][0] >= 0.0) {
+ double inputEnt_1 = (double)(p->ncal-1);
+
+ for (j = 0; j < 3; j++) {
+ unsigned int ix;
+ double val, w;
+ val = rgb[j] * inputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ } else if (val > inputEnt_1) {
+ val = inputEnt_1;
+ }
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > (p->ncal-2))
+ ix = (p->ncal-2);
+ w = val - (double)ix; /* weight */
+ val = p->cal[j][ix];
+ crgb[j] = val + w * (p->cal[j][ix+1] - val);
+ }
+//printf("~1 patch %d cal RGB %f %f %f\n",patch, crgb[0], crgb[1], crgb[2]);
+ }
+
+ /* If we have a test window, display the patch color */
+ if (p->dw) {
+ inst_code rv;
+ if (p->softcal) { /* Display value with calibration */
+ if ((rv = p->dw->set_color(p->dw, crgb[0], crgb[1], crgb[2])) != 0) {
+ a1logd(p->log,1,"set_color() returned %s\n",rv);
+ return 3;
+ }
+ } else { /* Hardware will apply calibration */
+ if ((rv = p->dw->set_color(p->dw, rgb[0], rgb[1], rgb[2])) != 0) {
+ a1logd(p->log,1,"set_color() returned %s\n",rv);
+ return 3;
+ }
+ }
+ }
+
+ /* Apply the fake devices level of quantization */
+#ifdef FAKE_BITS
+ if (p->fake2 == 0) {
+ for (j = 0; j < 3; j++) {
+ int vv;
+ vv = (int) (crgb[j] * ((1 << FAKE_BITS) - 1.0) + 0.5);
+ crgb[j] = vv/((1 << FAKE_BITS) - 1.0);
+ }
+ }
+#endif
+
+ /* Apply device offset */
+ for (j = 0; j < 3; j++) {
+ if (crgb[j] < 0.0)
+ crgb[j] = 0.0;
+ else if (crgb[j] > 1.0)
+ crgb[j] = 1.0;
+ crgb[j] = doff[j] + (1.0 - doff[j]) * crgb[j];
+ }
+
+ /* Apply gamma */
+ for (j = 0; j < 3; j++) {
+ if (crgb[j] >= 0.0)
+ crgb[j] = pow(crgb[j], 2.5);
+ else
+ crgb[j] = 0.0;
+ }
+
+ /* Convert to XYZ */
+ icmMulBy3x3(cols[patch].XYZ, mat, crgb);
+
+ /* Apply XYZ offset */
+ for (j = 0; j < 3; j++)
+ cols[patch].XYZ[j] += ooff[j];
+
+ /* Apply extra matrix */
+ icmMulBy3x3(cols[patch].XYZ, xmat, cols[patch].XYZ);
+
+#ifdef FAKE_NOISE
+ if (p->fake2 == 0) {
+ cols[patch].XYZ[0] += 2.0 * FAKE_NOISE * d_rand(-1.0, 1.0);
+ cols[patch].XYZ[1] += FAKE_NOISE * d_rand(-1.0, 1.0);
+ cols[patch].XYZ[2] += 4.0 * FAKE_NOISE * d_rand(-1.0, 1.0);
+ }
+#endif
+ if (clamp)
+ icmClamp3(cols[patch].XYZ, cols[patch].XYZ);
+//printf("~1 patch %d XYZ %f %f %f\n",patch,cols[patch].XYZ[0], cols[patch].XYZ[1], cols[patch].XYZ[2]);
+ cols[patch].XYZ_v = 1;
+ cols[patch].sp.spec_n = 0;
+ cols[patch].mtype = inst_mrt_emission;
+ }
+ if (acr && spat != 0 && tpat != 0 && (spat+patch-1) == tpat)
+ a1logv(p->log, 1, "\n");
+ return 0;
+}
+
+/* Test without a spectrometer using a fake ICC profile device */
+/* Return nz on fail/abort */
+/* 1 = user aborted */
+/* 2 = instrument access failed */
+/* 3 = window access failed */
+/* 4 = user hit terminate key */
+/* 5 = system error */
+/* Use disprd_err() to interpret it */
+static int disprd_fake_read_lu(
+ disprd *p,
+ col *cols, /* Array of patch colors to be tested */
+ int npat, /* Number of patches to be tested */
+ int spat, /* Start patch index for "verb", 0 if not used */
+ int tpat, /* Total patch index for "verb", 0 if not used */
+ int acr, /* If nz, do automatic final carriage return */
+ int tc, /* If nz, termination key */
+ instClamping clamp /* NZ if clamp XYZ/Lab to be +ve */
+) {
+ int patch, j;
+ int ttpat = tpat;
+ double br = 120.4; /* Overall brightness */
+ inst_code (*uicallback)(void *, inst_ui_purp) = NULL;
+ void *uicontext = NULL;
+
+ if (ttpat < npat)
+ ttpat = npat;
+
+ uicallback = inst_get_uicallback();
+ uicontext = inst_get_uicontext();
+
+ /* Setup the keyboard trigger to return our commands */
+ inst_set_uih(0x0, 0xff, DUIH_TRIG);
+ inst_set_uih('q', 'q', DUIH_ABORT);
+ inst_set_uih('Q', 'Q', DUIH_ABORT);
+ inst_set_uih(0x03, 0x03, DUIH_ABORT); /* ^c */
+ inst_set_uih(0x1b, 0x1b, DUIH_ABORT); /* Esc */
+
+ /* Setup user termination character */
+ inst_set_uih(tc, tc, DUIH_TERM);
+
+ for (patch = 0; patch < npat; patch++) {
+ double rgb[3];;
+ if (spat != 0 && tpat != 0)
+ a1logv(p->log, 1, "%cpatch %d of %d",cr_char,spat+patch,tpat);
+ rgb[0] = cols[patch].r;
+ rgb[1] = cols[patch].g;
+ rgb[2] = cols[patch].b;
+
+ /* deal with a user terminate or abort */
+ if (uicallback(uicontext, inst_measuring) == inst_user_abort) {
+ int ch, keyc = inst_get_uih_char();
+
+ /* Deal with a user terminate */
+ if (keyc & DUIH_TERM) {
+ return 4;
+
+ /* Deal with a user abort */
+ } else if (keyc & DUIH_ABORT) {
+ empty_con_chars();
+ printf("\nSample read stopped at user request!\n");
+ printf("Hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ }
+ }
+
+ /* If we have a test window, display the patch color */
+ if (p->dw) {
+ inst_code rv;
+ if ((rv = p->dw->set_color(p->dw, rgb[0], rgb[1], rgb[2])) != 0) {
+ a1logd(p->log,1,"set_color() returned %s\n",rv);
+ return 3;
+ }
+ }
+
+ /* If we have a RAMDAC, apply it to the color */
+ if (p->cal[0][0] >= 0.0) {
+ double inputEnt_1 = (double)(p->ncal-1);
+
+ for (j = 0; j < 3; j++) {
+ unsigned int ix;
+ double val, w;
+
+ val = rgb[j] * inputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ } else if (val > inputEnt_1) {
+ val = inputEnt_1;
+ }
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > (p->ncal-2))
+ ix = (p->ncal-2);
+ w = val - (double)ix; /* weight */
+ val = p->cal[j][ix];
+ rgb[j] = val + w * (p->cal[j][ix+1] - val);
+ }
+ }
+
+ p->fake_lu->lookup(p->fake_lu, cols[patch].XYZ, rgb);
+ for (j = 0; j < 3; j++)
+ cols[patch].XYZ[j] *= br;
+#ifdef FAKE_NOISE
+ cols[patch].XYZ[0] += 2.0 * FAKE_NOISE * d_rand(-1.0, 1.0);
+ cols[patch].XYZ[1] += FAKE_NOISE * d_rand(-1.0, 1.0);
+ cols[patch].XYZ[2] += 4.0 * FAKE_NOISE * d_rand(-1.0, 1.0);
+#endif
+ if (clamp)
+ icmClamp3(cols[patch].XYZ, cols[patch].XYZ);
+ cols[patch].XYZ_v = 1;
+ cols[patch].sp.spec_n = 0;
+ cols[patch].mtype = inst_mrt_emission;
+ }
+ if (acr && spat != 0 && tpat != 0 && (spat+patch-1) == tpat)
+ a1logv(p->log, 1, "\n");
+ return 0;
+}
+
+/* Use without a direct connect spectrometer using shell callout */
+/* Return nz on fail/abort */
+/* 1 = user aborted */
+/* 2 = instrument access failed */
+/* 3 = window access failed */
+/* 4 = user hit terminate key */
+/* 5 = system error */
+/* Use disprd_err() to interpret it */
+static int disprd_fake_read_co(disprd *p,
+ col *cols, /* Array of patch colors to be tested */
+ int npat, /* Number of patches to be tested */
+ int spat, /* Start patch index for "verb", 0 if not used */
+ int tpat, /* Total patch index for "verb", 0 if not used */
+ int acr, /* If nz, do automatic final carriage return */
+ int tc, /* If nz, termination key */
+ instClamping clamp /* NZ if clamp XYZ/Lab to be +ve */
+) {
+ int patch, j;
+ int ttpat = tpat;
+ inst_code (*uicallback)(void *, inst_ui_purp) = NULL;
+ void *uicontext = NULL;
+
+ if (ttpat < npat)
+ ttpat = npat;
+
+ uicallback = inst_get_uicallback();
+ uicontext = inst_get_uicontext();
+
+ /* Setup the keyboard trigger to return our commands */
+ inst_set_uih(0x0, 0xff, DUIH_TRIG);
+ inst_set_uih('q', 'q', DUIH_ABORT);
+ inst_set_uih('Q', 'Q', DUIH_ABORT);
+ inst_set_uih(0x03, 0x03, DUIH_ABORT); /* ^c */
+ inst_set_uih(0x1b, 0x1b, DUIH_ABORT); /* Esc */
+
+ /* Setup user termination character */
+ inst_set_uih(tc, tc, DUIH_TERM);
+
+ for (patch = 0; patch < npat; patch++) {
+ double rgb[3];
+ int rv;
+ char *cmd;
+ FILE *fp;
+
+ /* deal with a user terminate or abort */
+ if (uicallback(uicontext, inst_measuring) == inst_user_abort) {
+ int ch, keyc = inst_get_uih_char();
+
+ /* Deal with a user terminate */
+ if (keyc & DUIH_TERM) {
+ return 4;
+
+ /* Deal with a user abort */
+ } else if (keyc & DUIH_ABORT) {
+ empty_con_chars();
+ printf("\nSample read stopped at user request!\n");
+ printf("Hit Esc or Q to give up, any other key to retry:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ return 1;
+ }
+ printf("\n");
+ }
+ }
+
+ if (spat != 0 && tpat != 0)
+ a1logv(p->log, 1, "%cpatch %d of %d",cr_char,spat+patch,tpat);
+ rgb[0] = cols[patch].r;
+ rgb[1] = cols[patch].g;
+ rgb[2] = cols[patch].b;
+
+ /* If we have a test window, display the patch color */
+ if (p->dw) {
+ inst_code rv;
+ if ((rv = p->dw->set_color(p->dw, rgb[0], rgb[1], rgb[2])) != 0) {
+ a1logd(p->log,1,"set_color() returned %s\n",rv);
+ return 3;
+ }
+ }
+
+ /* If we have a RAMDAC, apply it to the color */
+ if (p->cal[0][0] >= 0.0) {
+ double inputEnt_1 = (double)(p->ncal-1);
+
+ for (j = 0; j < 3; j++) {
+ unsigned int ix;
+ double val, w;
+
+ val = rgb[j] * inputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ } else if (val > inputEnt_1) {
+ val = inputEnt_1;
+ }
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > (p->ncal-2))
+ ix = (p->ncal-2);
+ w = val - (double)ix; /* weight */
+ val = p->cal[j][ix];
+ rgb[j] = val + w * (p->cal[j][ix+1] - val);
+ }
+ }
+
+ if ((cmd = malloc(strlen(p->mcallout) + 200)) == NULL)
+ error("Malloc of command string failed");
+
+ sprintf(cmd, "%s %d %d %d %f %f %f",p->mcallout,
+ (int)(rgb[0] * 255.0 + 0.5),
+ (int)(rgb[1] * 255.0 + 0.5),
+ (int)(rgb[2] * 255.0 + 0.5), rgb[0], rgb[1], rgb[2]);
+ if ((rv = system(cmd)) != 0)
+ error("System command '%s' failed with %d",cmd,rv);
+
+ /* Now read the XYZ result from the mcallout.meas file */
+ sprintf(cmd, "%s.meas",p->mcallout);
+ if ((fp = fopen(cmd,"r")) == NULL)
+ error("Unable to open measurement value file '%s'",cmd);
+
+ if (fscanf(fp, " %lf %lf %lf", &cols[patch].XYZ[0], &cols[patch].XYZ[1],
+ &cols[patch].XYZ[2]) != 3)
+ error("Unable to parse measurement value file '%s'",cmd);
+ fclose(fp);
+ free(cmd);
+
+ if (clamp)
+ icmClamp3(cols[patch].XYZ, cols[patch].XYZ);
+ cols[patch].XYZ_v = 1;
+ cols[patch].mtype = inst_mrt_emission;
+
+ a1logv(p->log, 2, "Read XYZ %f %f %f from '%s'\n", cols[patch].XYZ[0],
+ cols[patch].XYZ[1],cols[patch].XYZ[2], cmd);
+
+ }
+ if (acr && spat != 0 && tpat != 0 && (spat+patch-1) == tpat)
+ a1logv(p->log, 1, "\n");
+ return 0;
+}
+
+/* Return a string describing the error code */
+char *disprd_err(int en) {
+ switch(en) {
+ case 1:
+ return "User Aborted";
+ case 2:
+ return "Instrument Access Failed";
+ case 22:
+ return "Instrument Access Failed (No PLD Pattern - have you run spyd2en ?)";
+ case 3:
+ return "Window Access Failed";
+ case 4:
+ return "VideoLUT Access Failed";
+ case 5:
+ return "User Terminated";
+ case 6:
+ return "System Error";
+ case 7:
+ return "Either CRT or LCD must be selected";
+ case 8:
+ return "Instrument has no ambient measurement capability";
+ case 9:
+ return "Creating spectral conversion object failed";
+ case 10:
+ return "Instrument has no CCMX capability";
+ case 11:
+ return "Instrument has no CCSS capability";
+ case 12:
+ return "Internal: trying to set calibration when not using calibration";
+ }
+ return "Unknown";
+}
+
+static void disprd_del(disprd *p) {
+
+ if (p->log->verb >= 1 && p->bdrift && p->ref_bw_v && p->last_bw_v) {
+ icmXYZNumber w;
+ double de;
+ icmAry2XYZ(w, p->ref_bw[1].XYZ);
+ de = icmXYZLabDE(&w, p->ref_bw[0].XYZ, p->last_bw[0].XYZ);
+ a1logv(p->log, 1, "Black drift was %f DE\n",de);
+ }
+ if (p->log->verb >= 1 && p->wdrift && p->ref_bw_v && p->last_bw_v) {
+ icmXYZNumber w;
+ double de;
+ icmAry2XYZ(w, p->ref_bw[1].XYZ);
+ de = icmXYZLabDE(&w, p->ref_bw[1].XYZ, p->last_bw[1].XYZ);
+ a1logv(p->log, 1, "White drift was %f DE\n",de);
+ }
+
+ /* The user may remove the instrument */
+ if (p->dw != NULL)
+ printf("The instrument can be removed from the screen.\n");
+
+ if (p->fake_lu != NULL)
+ p->fake_lu->del(p->fake_lu);
+ if (p->fake_icc != NULL)
+ p->fake_icc->del(p->fake_icc);
+ if (p->fake_fp != NULL)
+ p->fake_fp->del(p->fake_fp);
+
+ if (p->it != NULL)
+ p->it->del(p->it);
+ if (p->dw != NULL) {
+ if (p->or != NULL)
+ p->dw->set_ramdac(p->dw,p->or, 0);
+ p->dw->del(p->dw);
+ }
+ if (p->sp2cie != NULL)
+ p->sp2cie->del(p->sp2cie);
+ p->log = del_a1log(p->log);
+ free(p);
+}
+
+/* Helper to configure the instrument mode ready */
+/* for reading the display */
+/* return new_disprd() error code */
+static int config_inst_displ(disprd *p) {
+ inst_mode cap;
+ inst2_capability cap2;
+ inst3_capability cap3;
+ inst_mode mode = 0;
+ int rv;
+
+ p->it->capabilities(p->it, &cap, &cap2, &cap3);
+
+ if (p->tele && !IMODETST(cap, inst_mode_emis_tele)) {
+ printf("Want telephoto measurement capability but instrument doesn't support it\n");
+ printf("so falling back to spot mode.\n");
+ a1logd(p->log,1,"No telephoto mode so falling back to spot mode.\n");
+ p->tele = 0;
+ }
+
+ if (( p->tele && !IMODETST(cap, inst_mode_emis_tele))
+ || (!p->tele && !IMODETST(cap, inst_mode_emis_spot))) {
+ printf("Need %s emissive measurement capability,\n", p->tele ? "telephoto" : "spot");
+ printf("but instrument doesn't support it\n");
+ a1logd(p->log,1,"Need %s emissive measurement capability but device doesn't support it,\n",
+ p->tele ? "telephoto" : "spot");
+ return 2;
+ }
+
+ if (p->nadaptive && !IMODETST(cap, inst_mode_emis_nonadaptive)) {
+ printf("Need non-adaptives measurement mode, but instrument doesn't support it\n");
+ a1logd(p->log,1, "Need non-adaptives measurement mode, but instrument doesn't support it\n");
+ return 2;
+ }
+
+ if (p->obType != icxOT_none
+ && p->obType != icxOT_default) {
+ if (!IMODETST(cap, inst_mode_spectral) && (cap2 & inst2_ccss) == 0) {
+ printf("A non-standard observer was requested,\n");
+ printf("but instrument doesn't support spectral or CCSS\n");
+ a1logd(p->log,1,"A non-standard observer was requested,\n"
+ "but instrument doesn't support spectral or CCSS\n");
+ return 2;
+ }
+ /* Make sure spectral is turned on if an observer is requested */
+ if (!p->spectral && (cap2 & inst2_ccss) == 0)
+ p->spectral = 1;
+ }
+
+ if (p->spectral && !IMODETST(cap,inst_mode_spectral)) {
+ if (p->spectral != 2) { /* Not soft */
+ printf("Spectral information was requested,\n");
+ printf("but instrument doesn't support it\n");
+ a1logd(p->log,1,"Spectral information was requested,\nbut instrument doesn't support it\n");
+ return 2;
+ }
+ p->spectral = 0;
+ }
+
+ if (p->tele) {
+ mode = inst_mode_emis_tele;
+ } else {
+ mode = inst_mode_emis_spot;
+ }
+ if (p->nadaptive)
+ mode |= inst_mode_emis_nonadaptive;
+
+ if (p->spectral) {
+ mode |= inst_mode_spectral;
+ p->spectral = 1;
+ } else {
+ p->spectral = 0;
+ }
+
+ /* Set the display type */
+
+ if (p->dtype != 0) {
+ if (cap2 & inst2_disptype) {
+ int ix;
+ if ((ix = inst_get_disptype_index(p->it, p->dtype, p->docbid)) < 0) {
+ a1logd(p->log,1,"Display type selection '%c' is not valid for instrument\n",p->dtype);
+ return 2;
+ }
+ if ((rv = p->it->set_disptype(p->it, ix)) != inst_ok) {
+ a1logd(p->log,1,"Setting display type failed failed with '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+ } else
+ printf("Display type ignored - instrument doesn't support display type\n");
+ }
+
+ /* Get the refresh mode and cbid */
+ if (cap2 & inst2_disptype) {
+ p->it->get_set_opt(p->it, inst_opt_get_dtinfo, &p->refrmode, &p->cbid);
+ }
+
+ /* Disable initcalibration of machine if selected */
+ if (p->noinitcal != 0) {
+ if ((rv = p->it->get_set_opt(p->it,inst_opt_noinitcalib, 0)) != inst_ok) {
+ a1logd(p->log,1,"Setting no-initial calibrate failed failed with '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ printf("Disable initial-calibrate not supported\n");
+ }
+ }
+
+ if (p->highres) {
+ if (IMODETST(cap, inst_mode_highres)) {
+ inst_code ev;
+ if ((ev = p->it->get_set_opt(p->it, inst_opt_highres)) != inst_ok) {
+ a1logd(p->log,1,"\nSetting high res mode failed with error :'%s' (%s)\n",
+ p->it->inst_interp_error(p->it, ev), p->it->interp_error(p->it, ev));
+ return 2;
+ }
+ } else {
+ a1logv(p->log, 1, "high resolution ignored - instrument doesn't support high res. mode\n");
+ }
+ }
+ if ((rv = p->it->set_mode(p->it, mode)) != inst_ok) {
+ a1logd(p->log,1,"set_mode returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+ p->it->capabilities(p->it, &cap, &cap2, &cap3);
+
+ if (p->ccmtx != NULL) {
+ if ((cap2 & inst2_ccmx) == 0) {
+ a1logd(p->log,1,"Instrument doesn't support ccmx correction\n");
+ return 10;
+ }
+ if ((rv = p->it->col_cor_mat(p->it, p->ccmtx)) != inst_ok) {
+ a1logd(p->log,1,"col_cor_mat returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+ }
+
+ if (p->sets != NULL
+ || ((cap2 & inst2_ccss) != 0 && p->obType != icxOT_default)) {
+
+ if ((cap2 & inst2_ccss) == 0) {
+ a1logd(p->log,1,"Instrument doesn't support ccss calibration\n");
+ return 11;
+ }
+ if ((rv = p->it->get_set_opt(p->it, inst_opt_set_ccss_obs, p->obType, p->custObserver))
+ != inst_ok) {
+ a1logd(p->log,1,"inst_opt_set_ccss_obs returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+ if ((rv = p->it->col_cal_spec_set(p->it, p->sets, p->no_sets)) != inst_ok) {
+ a1logd(p->log,1,"col_cal_spec_set returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+ }
+
+ /* Set the trigger mode to program triggered */
+ if ((rv = p->it->get_set_opt(p->it,inst_opt_trig_prog)) != inst_ok) {
+ a1logd(p->log,1,"Setting program trigger mode failed failed with '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ return 2;
+ }
+
+ /* Reset key meanings */
+ inst_reset_uih();
+
+ a1logd(p->log,1,"config_inst_displ suceeded\n");
+ return 0;
+}
+
+/* Create a display reading object. */
+/* Return NULL if error */
+/* Set *errc to code: */
+/* 0 = no error */
+/* 1 = user aborted */
+/* 2 = instrument access failed */
+/* 22 = instrument access failed - no PLD pattern */
+/* 3 = window access failed */
+/* 4 = RAMDAC access failed */
+/* 5 = user hit terminate key */
+/* 6 = system error */
+/* 7 = CRT or LCD must be selected */
+/* 9 = spectral conversion failed */
+/* 10 = no ccmx support */
+/* 11 = no ccss support */
+/* 12 = cal to set but native != 0 */
+/* Use disprd_err() to interpret *errc */
+disprd *new_disprd(
+int *errc, /* Error code. May be NULL (could use log for this instead?) */
+icompath *ipath, /* Instrument path to open, &icomFakeDevice == fake */
+flow_control fc, /* Flow control */
+int dtype, /* Display type selection character */
+int docbid, /* NZ to only allow cbid dtypes */
+int tele, /* NZ for tele mode. Falls back to display mode */
+int nadaptive, /* NZ for non-adaptive mode */
+int noinitcal, /* No initial instrument calibration */
+int highres, /* Use high res mode if available */
+int native, /* 0 = use current current or given calibration curve */
+ /* 1 = use native linear out & high precision */
+int *noramdac, /* Return nz if no ramdac access. native is set to 0 */
+double cal[3][MAX_CAL_ENT], /* Calibration set/return (cal[0][0] < 0.0 or NULL if not used) */
+ /* native must be 0 if cal is set */
+int ncal, /* Number of cal[] entries */
+int softcal, /* NZ if apply cal to readings rather than hardware */
+disppath *disp, /* Display to calibrate. NULL if fake and no dispwin */
+int blackbg, /* NZ if whole screen should be filled with black */
+int override, /* Override_redirect on X11 */
+int webdisp, /* If nz, port number for web color display */
+char *ccallout, /* Shell callout on set color */
+char *mcallout, /* Shell callout on measure color (forced fake) */
+double hpatsize, /* Size of dispwin */
+double vpatsize,
+double ho, /* Horizontal offset */
+double vo, /* Vertical offset */
+double ccmtx[3][3], /* Colorimeter Correction matrix, NULL if none */
+xspect *sets, /* CCSS Set of sample spectra, NULL if none */
+int no_sets, /* CCSS Number on set, 0 if none */
+int spectral, /* Generate spectral info flag */
+icxObserverType obType, /* Use alternate observer if spectral or CCSS and != icxOT_none */
+xspect custObserver[3], /* Optional custom observer */
+int bdrift, /* Flag, nz for black drift compensation */
+int wdrift, /* Flag, nz for white drift compensation */
+char *fake_name, /* Name of profile to use as a fake device */
+a1log *log /* Verb, debug & error log */
+) {
+ disprd *p = NULL;
+ int ch;
+ inst_code rv;
+
+ if (errc != NULL) *errc = 0; /* default return code = no error */
+
+ if (cal != NULL && cal[0][0] >= 0.0 && native != 0) {
+ if (errc != NULL) *errc = 12;
+ return NULL;
+ }
+
+ /* Allocate a disprd */
+ if ((p = (disprd *)calloc(sizeof(disprd), 1)) == NULL) {
+ a1logd(log, 1, "new_disprd failed due to malloc failure\n");
+ if (errc != NULL) *errc = 6;
+ return NULL;
+ }
+ p->log = new_a1log_d(log);
+ p->del = disprd_del;
+ p->read = disprd_read;
+ p->get_disptype = disprd_get_disptype;
+ p->reset_targ_w = disprd_reset_targ_w;
+ p->change_drift_comp = disprd_change_drift_comp;
+ p->ambient = disprd_ambient;
+ p->fake_name = fake_name;
+
+ p->ccmtx = ccmtx;
+ p->sets = sets;
+ p->no_sets = no_sets; /* CCSS */
+ p->spectral = spectral;
+ p->obType = obType; /* CCSS or spectral */
+ p->custObserver = custObserver;
+ p->bdrift = bdrift;
+ p->wdrift = wdrift;
+ p->dtype = dtype;
+ p->docbid = docbid;
+ p->refrmode = -1; /* Unknown */
+ p->cbid = 0; /* Unknown */
+ p->tele = tele;
+ p->nadaptive = nadaptive;
+ p->noinitcal = noinitcal;
+ p->highres = highres;
+ if (mcallout != NULL)
+ ipath = &icomFakeDevice; /* Force fake device */
+ p->mcallout = mcallout;
+ p->ipath = ipath;
+ p->br = baud_19200;
+ p->fc = fc;
+
+ /* Save this in case we are using a fake device */
+ if (cal != NULL && cal[0][0] >= 0.0) {
+ int j, i;
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < ncal; i++) {
+ p->cal[j][i] = cal[j][i];
+ }
+ }
+ p->ncal = ncal;
+ p->softcal = softcal;
+ } else {
+ p->cal[0][0] = -1.0;
+ p->ncal = 0;
+ p->softcal = 0;
+ }
+
+ /* If non-real instrument */
+ if (ipath == &icomFakeDevice) {
+ p->fake = 1;
+
+ p->fake_fp = NULL;
+ p->fake_icc = NULL;
+ p->fake_lu = NULL;
+
+ /* See if there is a profile we should use as the fake device */
+ if (p->mcallout == NULL && p->fake_name != NULL
+ && (p->fake_fp = new_icmFileStd_name(p->fake_name,"r")) != NULL) {
+ if ((p->fake_icc = new_icc()) != NULL) {
+ if (p->fake_icc->read(p->fake_icc,p->fake_fp,0) == 0) {
+ icColorSpaceSignature ins;
+ p->fake_lu = p->fake_icc->get_luobj(p->fake_icc, icmFwd, icAbsoluteColorimetric,
+ icSigXYZData, icmLuOrdNorm);
+ p->fake_lu->spaces(p->fake_lu, &ins, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (ins != icSigRgbData) {
+ p->fake_lu->del(p->fake_lu);
+ p->fake_lu = NULL;
+ }
+ }
+ }
+ }
+
+ if (p->fake_lu != NULL) {
+ a1logv(p->log, 1, "Using profile '%s' rather than real device\n",p->fake_name);
+ p->read = disprd_fake_read_lu;
+ } else if (p->mcallout != NULL) {
+ a1logv(p->log, 1, "Using shell callout '%s' rather than real device\n",p->mcallout);
+ p->read = disprd_fake_read_co;
+ } else
+ p->read = disprd_fake_read;
+
+ if (disp == NULL) {
+ a1logd(log,1,"new_disprd returning fake device\n");
+ return p;
+ }
+
+ /* Setup the instrument */
+ } else {
+
+ a1logv(p->log, 1, "Setting up the instrument\n");
+
+ if ((p->it = new_inst(ipath, 0, log, DUIH_FUNC_AND_CONTEXT)) == NULL) {
+ a1logd(p->log,1,"new_disprd failed because new_inst failed\n");
+ p->del(p);
+ if (errc != NULL) *errc = 2;
+ return NULL;
+ }
+
+ /* Establish communications */
+ if ((rv = p->it->init_coms(p->it, p->br, p->fc, 15.0)) != inst_ok) {
+ a1logd(log,1,"init_coms returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ a1logd(log,1,"new_disprd failed because init_coms failed\n");
+ p->del(p);
+ if (errc != NULL) *errc = 2;
+ return NULL;
+ }
+
+ /* Initialise the instrument */
+ if ((rv = p->it->init_inst(p->it)) != inst_ok) {
+ a1logd(log,1,"init_inst returned '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ a1logd(log,1,"new_disprd failed because init_inst failed\n");
+ p->del(p);
+ if (errc != NULL) {
+ *errc = 2;
+ if ((rv & inst_imask) == SPYD2_NO_PLD_PATTERN)
+ *errc = 22;
+ }
+ return NULL;
+ }
+
+ /* Configure the instrument mode for reading the display */
+ if ((rv = config_inst_displ(p)) != 0) {
+ a1logd(log,1,"new_disprd failed because config_inst_displ failed\n");
+ p->del(p);
+ if (errc != NULL) *errc = rv;
+ return NULL;
+ }
+ }
+
+ /* Create a spectral conversion object if needed */
+ if (p->spectral && p->obType != icxOT_none) {
+ if ((p->sp2cie = new_xsp2cie(icxIT_none, NULL, p->obType, custObserver, icSigXYZData, icxNoClamp))
+ == NULL) {
+ a1logd(log,1,"new_disprd failed because creation of spectral conversion object failed\n");
+ p->del(p);
+ if (errc != NULL) *errc = 9;
+ return NULL;
+ }
+ }
+
+ if (webdisp != 0) {
+ /* Open web display */
+ if ((p->dw = new_webwin(webdisp, hpatsize, vpatsize, ho, vo, 0, 0,
+ p->log->verb, p->log->debug)) == NULL) {
+ a1logd(log,1,"new_disprd failed because new_webwin failed\n");
+ p->del(p);
+ if (errc != NULL) *errc = 3;
+ return NULL;
+ }
+ if (noramdac != NULL)
+ *noramdac = 1;
+ } else {
+ /* Open display window for positioning (no blackbg) */
+ if ((p->dw = new_dispwin(disp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, 0,
+ override, p->log->debug)) == NULL) {
+ a1logd(log,1,"new_disprd failed because new_dispwin failed\n");
+ p->del(p);
+ if (errc != NULL) *errc = 3;
+ return NULL;
+ }
+ }
+
+ if (p->it != NULL) {
+ /* Do a calibration up front, so as not to get in the users way, */
+ /* but ignore a CRT frequency or display integration calibration, */
+ /* since these will be done automatically. */
+ if (p->it->needs_calibration(p->it) & inst_calt_n_dfrble_mask) {
+ disp_win_info dwi;
+ dwi.dw = p->dw; /* Set window to use */
+
+ rv = inst_handle_calibrate(p->it, inst_calt_needed, inst_calc_none,
+ setup_display_calibrate, &dwi);
+ setup_display_calibrate(p->it,inst_calc_none, &dwi);
+ printf("\n");
+ if (rv != inst_ok) { /* Abort or fatal error */
+ a1logd(log,1,"new_disprd failed because calibrate failed with '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ printf("Calibrate failed with '%s' (%s)\n",
+ p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
+ p->del(p);
+ if (errc != NULL) *errc = 2;
+ return NULL;
+ }
+ }
+ }
+
+ /* Ask user to put instrument on screen */
+ empty_con_chars();
+ printf("Place instrument on test window.\n");
+ printf("Hit Esc or Q to give up, any other key to continue:"); fflush(stdout);
+ if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
+ printf("\n");
+ a1logd(log,1,"new_disprd failed because user aborted when placing device\n");
+ p->del(p);
+ if (errc != NULL) *errc = 1;
+ return NULL;
+ }
+ printf("\n");
+
+ if (webdisp == 0) {
+ /* Close the positioning window */
+ if (p->dw != NULL) {
+ if (p->or != NULL)
+ p->dw->set_ramdac(p->dw,p->or, 0);
+ p->dw->del(p->dw);
+ }
+
+ /* Open display window again for measurement */
+ if ((p->dw = new_dispwin(disp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, blackbg,
+ override, p->log->debug)) == NULL) {
+ a1logd(log,1,"new_disprd failed new_dispwin failed\n");
+ p->del(p);
+ if (errc != NULL) *errc = 3;
+ return NULL;
+ }
+ }
+
+ /* Set color change callout */
+ if (ccallout) {
+ p->dw->set_callout(p->dw, ccallout);
+ }
+
+ /* If we have a calibration to set */
+ /* (This is only typically the case for disread) */
+ if (!p->softcal && cal != NULL && cal[0][0] >= 0.0) {
+
+ /* Save current RAMDAC so that we can restore it */
+ p->or = NULL;
+ if ((p->or = p->dw->get_ramdac(p->dw)) == NULL) {
+ warning("Unable to read or set display RAMDAC - switching to softcal");
+ p->softcal = softcal = 1;
+ }
+
+ /* Set the given RAMDAC so we can characterise through it */
+ if (p->or != NULL) {
+ ramdac *r;
+ int j, i;
+
+ r = p->or->clone(p->or);
+
+ /* Set the ramdac contents. */
+ /* We linearly interpolate from cal[ncal] to RAMDAC[nent] resolution */
+ for (i = 0; i < r->nent; i++) {
+ double val, w;
+ unsigned int ix;
+
+ val = (ncal-1.0) * i/(r->nent-1.0);
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > (ncal-2))
+ ix = (ncal-2);
+ w = val - (double)ix; /* weight */
+ for (j = 0; j < 3; j++) {
+ val = cal[j][ix];
+ r->v[j][i] = val + w * (cal[j][ix+1] - val);
+ }
+ }
+ if (p->dw->set_ramdac(p->dw, r, 0)) {
+ a1logd(log,1,"new_disprd failed becayse set_ramdac failed\n");
+ a1logv(p->log, 1, "Failed to set RAMDAC to desired calibration.\n");
+ a1logv(p->log, 1, "Perhaps the operating system is being fussy ?\n");
+ r->del(r);
+ p->del(p);
+ if (errc != NULL) *errc = 4;
+ return NULL;
+ }
+ r->del(r);
+ }
+ }
+
+ /* Return the ramdac being used */
+ if (p->or != NULL && cal != NULL) {
+ ramdac *r;
+ int j, i;
+
+ if ((r = p->dw->get_ramdac(p->dw)) == NULL) {
+ a1logd(log,1,"new_disprd failed becayse get_ramdac failed\n");
+ a1logv(p->log, 1, "Failed to read current RAMDAC\n");
+ p->del(p);
+ if (errc != NULL) *errc = 4;
+ return NULL;
+ }
+ /* Get the ramdac contents. */
+ /* We linearly interpolate from RAMDAC[nent] to cal[ncal] resolution */
+ for (i = 0; i < ncal; i++) {
+ double val, w;
+ unsigned int ix;
+
+ val = (r->nent-1.0) * i/(ncal-1.0);
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > (r->nent-2))
+ ix = (r->nent-2);
+ w = val - (double)ix; /* weight */
+ for (j = 0; j < 3; j++) {
+ val = r->v[j][ix];
+ cal[j][i] = val + w * (r->v[j][ix+1] - val);
+ }
+ }
+ r->del(r);
+ }
+
+ a1logd(log,1,"new_disprd succeeded\n");
+ return p;
+}
+