diff options
Diffstat (limited to 'spectro/dispsup.c')
-rw-r--r-- | spectro/dispsup.c | 2343 |
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; +} + |