2962 lines
94 KiB
C
2962 lines
94 KiB
C
|
|
/*
|
|
* Argyll Color Management 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 <ctype.h>
|
|
#include "copyright.h"
|
|
#include "aconfig.h"
|
|
#include "numlib.h"
|
|
#include "cgats.h"
|
|
#include "xspect.h"
|
|
#include "insttypes.h"
|
|
#include "conv.h"
|
|
#include "icoms.h"
|
|
#include "inst.h"
|
|
#include "spyd2.h"
|
|
#include "dispwin.h"
|
|
#include "webwin.h"
|
|
#include "dummywin.h"
|
|
#include "ccast.h"
|
|
#include "ccwin.h"
|
|
#ifdef NT
|
|
# include "madvrwin.h"
|
|
#endif
|
|
|
|
#include "dispsup.h"
|
|
#include "instappsup.h"
|
|
|
|
#undef DEBUG
|
|
|
|
#undef SIMPLE_MODEL /* Make fake device well behaved */
|
|
/* else has offsets, quantization, noise etc. */
|
|
|
|
#define DRIFT_IPERIOD 40 /* [40] Number of samples between drift interpolation measurements */
|
|
#define DRIFT_EPERIOD 20 /* [20] Number of samples between drift extrapolation measurements */
|
|
#define DRIFT_MAXSECS 200 /* [200] Number of seconds to time out previous drift value */
|
|
//#define DRIFT_IPERIOD 6 /* Test values */
|
|
//#define DRIFT_EPERIOD 3
|
|
|
|
#ifdef SIMPLE_MODEL
|
|
# undef FAKE_NOISE /* Add noise to _fake_ devices XYZ */
|
|
# undef FAKE_UNPREDIC /* Initialise random unpredictably */
|
|
# 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_UNPREDIC /* Initialise random unpredictably */
|
|
# define FAKE_BITS 9 /* Number of bits of significance of fake device */
|
|
#endif
|
|
#undef FAKE_WDRIFT /* Drift the white point by increment every measurement */
|
|
/* - useful over about 200 measurements */
|
|
|
|
#if defined(DEBUG)
|
|
# pragma message("######### DEBUG enabled !!!!! ########")
|
|
# define DRIFT_IPERIOD 10
|
|
# define DBG(xxx) fprintf xxx ;
|
|
# define dbgo stderr
|
|
#else
|
|
# define DBG(xxx)
|
|
#endif /* DEBUG */
|
|
|
|
/* -------------------------------------------------------- */
|
|
/* 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 with calc = 0x%x\n",calc);
|
|
|
|
switch (calc & inst_calc_cond_mask) {
|
|
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:
|
|
case inst_calc_emis_80pc:
|
|
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, 0, NULL, NULL, dwi->icalmax, dwi->out_tvenc,
|
|
dwi->fullscreen, 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->ccid != NULL) {
|
|
if ((dwi->_dw = new_ccwin(dwi->ccid, dwi->hpatsize, dwi->vpatsize,
|
|
dwi->ho, dwi->vo, 0, 0, NULL, NULL, dwi->icalmax, dwi->out_tvenc,
|
|
dwi->fullscreen, 0, 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;
|
|
}
|
|
#ifdef NT
|
|
} else if (dwi->madvrdisp != 0) {
|
|
if ((dwi->_dw = new_madvrwin(dwi->hpatsize, dwi->vpatsize,
|
|
dwi->ho, dwi->vo, 0, 0, NULL, NULL, dwi->icalmax, dwi->out_tvenc,
|
|
dwi->fullscreen, 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;
|
|
}
|
|
#endif /* NT */
|
|
} else if (dwi->dummydisp != 0) {{
|
|
if ((dwi->_dw = new_dummywin(dwi->hpatsize, dwi->vpatsize,
|
|
dwi->ho, dwi->vo, 0, 0, NULL, NULL, dwi->icalmax, dwi->out_tvenc,
|
|
dwi->fullscreen, 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;
|
|
}
|
|
}
|
|
} else {
|
|
if ((dwi->_dw = new_dispwin(dwi->disp, dwi->hpatsize, dwi->vpatsize,
|
|
dwi->ho, dwi->vo, 0, 0, NULL, NULL, dwi->icalmax, dwi->out_tvenc,
|
|
dwi->fullscreen, 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("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;
|
|
}
|
|
|
|
/* Set display rise & fall time more optimally */
|
|
{
|
|
disptech dtech;
|
|
disptech_info *tinfo;
|
|
p->get_disptechi(p, &dtech, NULL, NULL);
|
|
tinfo = disptech_get_id(dtech);
|
|
dwi->dw->set_settling_delay(dwi->dw, tinfo->rise_time, tinfo->fall_time, -1.0);
|
|
}
|
|
|
|
if ((calc & inst_calc_cond_mask) == inst_calc_emis_white) {
|
|
p->cal_gy_level = 1.0;
|
|
dwi->_dw->set_color(dwi->_dw, p->cal_gy_level * dwi->_dw->icalmax,
|
|
p->cal_gy_level * dwi->_dw->icalmax,
|
|
p->cal_gy_level * dwi->_dw->icalmax);
|
|
|
|
} else if ((calc & inst_calc_cond_mask) == inst_calc_emis_80pc) {
|
|
p->cal_gy_level = 0.8;
|
|
dwi->_dw->set_color(dwi->_dw, p->cal_gy_level * dwi->_dw->icalmax,
|
|
p->cal_gy_level * dwi->_dw->icalmax,
|
|
p->cal_gy_level * dwi->_dw->icalmax);
|
|
|
|
} else {
|
|
if ((calc & inst_calc_cond_mask) == inst_calc_emis_grey) {
|
|
p->cal_gy_level = 0.6;
|
|
p->cal_gy_count = 0;
|
|
} else if ((calc & inst_calc_cond_mask) == inst_calc_emis_grey_darker) {
|
|
p->cal_gy_level *= 0.7;
|
|
p->cal_gy_count++;
|
|
} else if ((calc & inst_calc_cond_mask) == 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 * dwi->_dw->icalmax,
|
|
p->cal_gy_level * dwi->_dw->icalmax,
|
|
p->cal_gy_level * dwi->_dw->icalmax);
|
|
}
|
|
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 ditype, /* Display type selection character(s) */
|
|
int sditype, /* Spectro ditype, use ditype if -1 */
|
|
int docbid, /* NZ to only allow cbid ditypes */
|
|
int tele, /* NZ for tele mode, falls back to spot mode */
|
|
int ambient, /* NZ for ambient 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 */
|
|
ccast_id *ccid, /* non-NULL for ChromeCast */
|
|
#ifdef NT
|
|
int madvrdisp, /* NZ for MadVR display */
|
|
#endif
|
|
int dummydisp, /* NZ for dummy display */
|
|
double icalmax, /* Scale inst. cal. test values by this (0.0 .. 1.0) */
|
|
int out_tvenc, /* 1 = use RGB Video Level encoding */
|
|
int fullscreen, /* 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.ccid = ccid;
|
|
#ifdef NT
|
|
dwi.madvrdisp = madvrdisp;
|
|
#endif
|
|
dwi.dummydisp = dummydisp;
|
|
dwi.disp = disp;
|
|
dwi.icalmax = icalmax;
|
|
dwi.out_tvenc = out_tvenc;
|
|
dwi.fullscreen = fullscreen;
|
|
dwi.override = override;
|
|
dwi.hpatsize = hpatsize;
|
|
dwi.vpatsize = vpatsize;
|
|
dwi.ho = ho;
|
|
dwi.vo = vo;
|
|
|
|
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;
|
|
}
|
|
|
|
p->log = new_a1log_d(log);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
// ~~~~9999 we should call config_inst_displ(p) instead of badly duplicating
|
|
// the instrument setup here ???
|
|
|
|
itype = p->get_itype(p); /* Actual type */
|
|
p->capabilities(p, &cap, &cap2, &cap3);
|
|
|
|
if (tele)
|
|
ambient = 0;
|
|
|
|
if (tele && p->check_mode(p, inst_mode_emis_tele) != inst_ok) {
|
|
printf("Want telephoto measurement capability but instrument doesn't support it\n");
|
|
printf("so falling back to emissive spot mode.\n");
|
|
tele = 0;
|
|
}
|
|
|
|
if (ambient && p->check_mode(p, inst_mode_emis_ambient) != inst_ok) {
|
|
printf("Want ambient measurement capability but instrument doesn't support it\n");
|
|
printf("so falling back to emissive spot mode.\n");
|
|
ambient = 0;
|
|
}
|
|
|
|
if (!tele && !ambient && p->check_mode(p, inst_mode_emis_spot) != inst_ok) {
|
|
printf("Want emissive spot measurement capability but instrument doesn't support it\n");
|
|
printf("so switching to telephoto spot mode.\n");
|
|
tele = 1;
|
|
}
|
|
|
|
if (( tele && !ambient && p->check_mode(p, inst_mode_emis_tele) != inst_ok)
|
|
|| (!tele && ambient && p->check_mode(p, inst_mode_emis_ambient) != inst_ok)
|
|
|| (!tele && !ambient && p->check_mode(p, inst_mode_emis_spot) != inst_ok)) {
|
|
printf("Need %s emissive measurement capability,\n", tele ? "telephoto" : ambient ? "ambient" : "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",
|
|
tele ? "telephoto" : ambient ? "ambient" : "spot");
|
|
p->del(p);
|
|
return -1;
|
|
}
|
|
|
|
/* Set to emission mode to read a display */
|
|
if (tele)
|
|
mode = inst_mode_emis_tele;
|
|
else if (ambient)
|
|
mode = inst_mode_emis_ambient;
|
|
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);
|
|
|
|
/* If this is a spectral instrument, and a different */
|
|
/* spectral inst. ditype is supplied, then use it */
|
|
if (IMODETST(cap, inst_mode_spectral) && sditype >= 0)
|
|
ditype = sditype;
|
|
|
|
/* Set the display type or calibration mode */
|
|
if (ditype != 0) { /* Given selection character */
|
|
if (cap2 & inst2_disptype) {
|
|
int ix;
|
|
if ((ix = inst_get_disptype_index(p, ditype, docbid)) < 0) {
|
|
a1logd(log,1,"Display type selection '%s' is not valid for instrument\n",
|
|
inst_distr(ditype));
|
|
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 selection\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, 0);
|
|
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;
|
|
}
|
|
|
|
/* The i1pro/munki seems to get into trouble if we do a meas_delay */
|
|
/* on another thread - some conflict with the button monotitor thread. */
|
|
|
|
/* Set color to white after 200 msec delay */
|
|
int del_set_white(void *cx) {
|
|
disprd *p = (disprd *)cx;
|
|
inst_code ev;
|
|
int rv;
|
|
|
|
msec_sleep(200);
|
|
|
|
/* Start the patch change */
|
|
/* This function may return some time before or after */
|
|
/* the change actually arrives at the instrument. */
|
|
if ((rv = p->dw->set_color(p->dw, p->dw->icalmax, p->dw->icalmax, p->dw->icalmax)) != 0) {
|
|
a1logd(p->log,1,"set_color() returned %d\n",rv);
|
|
return 3;
|
|
}
|
|
|
|
/* Signal instrument that we've returned from the patch change function */
|
|
/* (We're interested in any delay from this point in time until the */
|
|
/* change arrives at the instrument, since this is the point that we */
|
|
/* add extra delay at the end of the set_color() functions) */
|
|
if ((ev = p->it->white_change(p->it, 0)) != inst_ok) {
|
|
a1logd(p->log,1,"white_change() returned 0x%x\n",ev);
|
|
return 3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef NEVER
|
|
/* Implement scallout, which reports the XYZ measured */
|
|
static void do_scallout(disprd *p, double *xyz) {
|
|
char *cmd;
|
|
|
|
if (p->scallout == NULL)
|
|
return;
|
|
|
|
if ((cmd = malloc(strlen(p->scallout) + 200)) == NULL)
|
|
error("Malloc of command string failed");
|
|
|
|
sprintf(cmd, "%s %f %f %f",p->scallout, xyz[0], xyz[1], xyz[2]);
|
|
if ((rv = system(cmd)) != 0)
|
|
error("System command '%s' failed with %d",cmd,rv);
|
|
free(cmd);
|
|
}
|
|
#endif
|
|
|
|
/* Take a series of readings from the display - implementation */
|
|
/* Return nz on fail/abort - see dispsup.h */
|
|
/* 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 tc, /* If nz, termination key */
|
|
instClamping clamp, /* NZ if clamp XYZ/Lab to be +ve */
|
|
int noinc /* If nz, don't increment the count */
|
|
) {
|
|
int j, rv;
|
|
int patch;
|
|
ipatch val; /* Return value */
|
|
int ch; /* Character */
|
|
int cal_type;
|
|
inst_calc_id_type idtype;
|
|
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) {
|
|
athread *th;
|
|
inst_code ev;
|
|
int mdelay; /* Display update delay*/
|
|
int idelay = 0; /* Instrument reaction time */
|
|
|
|
/* Disable the update delay */
|
|
p->dw->enable_update_delay(p->dw, 0);
|
|
|
|
/* Set black */
|
|
if ((rv = p->dw->set_color(p->dw, 0.0, 0.0, 0.0)) != 0) {
|
|
a1logd(p->log,1,"set_color() returned %d\n",rv);
|
|
p->dw->enable_update_delay(p->dw, 1);
|
|
return 3;
|
|
}
|
|
/* Wait for it to settle */
|
|
msec_sleep(600);
|
|
|
|
/* Init the white change stamp */
|
|
p->it->white_change(p->it, 1);
|
|
|
|
/* Set black 200 msec after this call and call white_change() */
|
|
if ((th = new_athread(del_set_white, (void *)p)) == NULL) {
|
|
a1logd(p->log,1,"failed to create thread to set_color()\n");
|
|
p->dw->enable_update_delay(p->dw, 1);
|
|
return 3;
|
|
}
|
|
|
|
/* If the test window is adding extra known delay after the patch */
|
|
/* update, don't start looking for the transition too soon, */
|
|
/* or we may exaust our 2.0 second scan window */
|
|
if (p->dw->extra_update_delay > 0.2) {
|
|
a1logd(p->log,1,"update delay cal waiting %f secs to start scan\n",
|
|
p->dw->extra_update_delay - 0.2);
|
|
msec_sleep((int)((p->dw->extra_update_delay - 0.2) * 1000));
|
|
}
|
|
|
|
/* Measure the delay */
|
|
if ((rv = p->it->meas_delay(p->it, &mdelay, &idelay)) != 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));
|
|
mdelay = PATCH_UPDATE_DELAY;
|
|
/* idelay will be set to a default by meas_delay() */
|
|
} else {
|
|
a1logv(p->log, 1, "Measured display update delay of %d msec",mdelay);
|
|
mdelay += mdelay/3 + 100; /* Margin - +30% + 100msec */
|
|
}
|
|
p->dw->set_update_delay(p->dw, mdelay, idelay);
|
|
a1logv(p->log, 1, ", using delay of %d msec & %d msec inst reaction\n",mdelay, idelay);
|
|
p->update_delay_set = 1;
|
|
|
|
/* Re-enable update delay */
|
|
p->dw->enable_update_delay(p->dw, 1);
|
|
|
|
th->del(th);
|
|
}
|
|
|
|
/* 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 != 0.8 || cols[0].g != 0.8 || cols[0].b != 0.8)) {
|
|
col tc;
|
|
inst_cal_type calt = inst_calt_ref_freq;
|
|
inst_cal_cond calc = inst_calc_emis_80pc;
|
|
|
|
/* Hmm. Should really ask the instrument what sort of calc it needs !!! */
|
|
if ((rv = p->dw->set_color(p->dw, 0.8 * p->dw->icalmax, 0.8 * p->dw->icalmax,
|
|
0.8 * p->dw->icalmax)) != 0) {
|
|
a1logd(p->log,1,"set_color() returned %d\n",rv);
|
|
return 3;
|
|
}
|
|
/* Do calibrate, but ignore return code. Press on regardless. */
|
|
if ((rv = p->it->calibrate(p->it, &calt, &calc, &idtype, 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 * p->dw->icalmax, 1.0 * p->dw->icalmax,
|
|
1.0 * p->dw->icalmax)) != 0) {
|
|
a1logd(p->log,1,"set_color() returned %d\n",rv);
|
|
return 3;
|
|
}
|
|
/* Do calibrate, but ignore return code. Press on regardless. */
|
|
if ((rv = p->it->calibrate(p->it, &calt, &calc, &idtype, 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->mcond = inst_mrc_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);
|
|
if (p->dw->set_pinfo != NULL)
|
|
p->dw->set_pinfo(p->dw, 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 */
|
|
/* (dispwin will apply any tvenc needed) */
|
|
if ((p->native & 1) && 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 %d\n",rv);
|
|
return 3;
|
|
}
|
|
|
|
/* Until we give up retrying */
|
|
for (;;) {
|
|
val.mtype = inst_mrt_none;
|
|
val.mcond = inst_mrc_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:%s",fl_end); do_fflush();
|
|
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, 0);
|
|
setup_display_calibrate(p->it, inst_calc_none, &dwi);
|
|
if (rv != inst_ok) { /* Abort or fatal error */
|
|
return 1;
|
|
}
|
|
|
|
printf("Place instrument back on test window.\n");
|
|
printf("Hit Esc or Q to give up, any other key to continue:%s",fl_end); do_fflush();
|
|
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
|
|
printf("\n");
|
|
return 1;
|
|
}
|
|
printf("\n");
|
|
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:%s",fl_end); do_fflush();
|
|
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:%s",fl_end); do_fflush();
|
|
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:%s",fl_end); do_fflush();
|
|
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)
|
|
&& !(p->it->icom->port_attr(p->it->icom) & icomt_fastserial)) {
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
printf("\nSample read failed with unhandled error.\n");
|
|
return 2;
|
|
} 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, transferring to col\n",
|
|
val.XYZ[0], val.XYZ[1], val.XYZ[2]);
|
|
|
|
scb->mtype = val.mtype;
|
|
scb->mcond = val.mcond;
|
|
|
|
/* 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 - see dispsup.h */
|
|
/* 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 noinc /* Ignored */
|
|
) {
|
|
int rv, i, j, e;
|
|
double fper, foff;
|
|
int off, poff;
|
|
int boff, eoff, dno; /* b&w offsets and count */
|
|
|
|
DBG((dbgo,"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->wdrift == 0) { /* Must be just bdrift */
|
|
boff = eoff = 0;
|
|
dno = 1;
|
|
} else { /* Must be both */
|
|
boff = 0;
|
|
eoff = 1;
|
|
dno = 2;
|
|
}
|
|
|
|
/* Make sure these have been initialised */
|
|
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 the last drift readings are invalid or too old, */
|
|
/* or if we will use interpolation, read b&w */
|
|
#ifdef DEBUG
|
|
DBG((dbgo,"last_bw_v = %d (%d)\n",p->last_bw_v,p->last_bw_v == 0))
|
|
DBG((dbgo,"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)))
|
|
DBG((dbgo,"serno - lastserno %d > %d (%d)\n",p->serno - p->last_bw[eoff].serno, DRIFT_EPERIOD,(p->serno - p->last_bw[eoff].serno) > DRIFT_EPERIOD))
|
|
DBG((dbgo,"msec %d - last %d = %d > %d (%d)\n",msec_time(),p->last_bw[boff].msec,msec_time() - p->last_bw[boff].msec,DRIFT_MAXSECS * 1000, (msec_time() - p->last_bw[eoff].msec) > (DRIFT_MAXSECS * 1000)))
|
|
#endif /* DEBUG */
|
|
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) > (DRIFT_MAXSECS * 1000)) { /* The current is too long ago */
|
|
|
|
DBG((dbgo,"Reading a beginning set of %d b/w drift compensation patches\n",dno))
|
|
a1logd(p->log,2, "Reading a beginning set of %d b/w drift compensation patches\n",dno);
|
|
|
|
/* Read the black and/or white drift patch */
|
|
if ((rv = p->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 */
|
|
/* (These will be used as the black drift targets) */
|
|
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; /* Drift samples */
|
|
|
|
/* Figure out the number of drift samples we need */
|
|
ndrift += (npat-1)/DRIFT_IPERIOD;
|
|
DBG((dbgo,"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);
|
|
DBG((dbgo,"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;
|
|
DBG((dbgo,"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 */
|
|
DBG((dbgo,"Reading another set of %d b/w drift compensation patches\n",dno))
|
|
a1logd(p->log,2, "Reading another set of %d b/w drift compensation patches\n",dno);
|
|
if ((rv = p->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 = p->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 */
|
|
DBG((dbgo,"Reading an end set of %d b/w drift compensation patches\n",dno))
|
|
a1logd(p->log,2, "Reading an end set of %d b/w drift compensation patches\n",dno);
|
|
if ((rv = p->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 series */
|
|
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 of the series */
|
|
p->targ_w = p->last_bw[1];
|
|
p->targ_w_v = 1;
|
|
|
|
DBG((dbgo,"ref b = %f %f %f\n", p->ref_bw[0].XYZ[0], p->ref_bw[0].XYZ[1], p->ref_bw[0].XYZ[2]))
|
|
DBG((dbgo,"ref w = %f %f %f\n", p->ref_bw[1].XYZ[0], p->ref_bw[1].XYZ[1], p->ref_bw[1].XYZ[2]))
|
|
DBG((dbgo,"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]))
|
|
DBG((dbgo,"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 earlier value */
|
|
col bb, ww; /* Interpolated black and white */
|
|
#ifdef DEBUG
|
|
double uXYZ[3];
|
|
icmCpy3(uXYZ, cols[k].XYZ);
|
|
DBG((dbgo,"patch %d = %f %f %f\n", k, cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2]))
|
|
#endif
|
|
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];
|
|
}
|
|
DBG((dbgo,"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];
|
|
}
|
|
DBG((dbgo,"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];
|
|
}
|
|
DBG((dbgo,"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];
|
|
}
|
|
DBG((dbgo,"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];
|
|
}
|
|
DBG((dbgo,"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];
|
|
}
|
|
}
|
|
}
|
|
DBG((dbgo,"%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 {
|
|
|
|
DBG((dbgo,"doing small number of readings\n"))
|
|
/* Read the small number of patches */
|
|
if ((rv = p->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;
|
|
DBG((dbgo,"set white drift target\n"))
|
|
}
|
|
|
|
DBG((dbgo,"ref b = %f %f %f\n", p->ref_bw[0].XYZ[0], p->ref_bw[0].XYZ[1], p->ref_bw[0].XYZ[2]))
|
|
DBG((dbgo,"ref w = %f %f %f\n", p->ref_bw[1].XYZ[0], p->ref_bw[1].XYZ[1], p->ref_bw[1].XYZ[2]))
|
|
DBG((dbgo,"last b = %f %f %f\n", p->last_bw[0].XYZ[0], p->last_bw[0].XYZ[1], p->last_bw[0].XYZ[2]))
|
|
DBG((dbgo,"last w = %f %f %f\n", p->last_bw[1].XYZ[0], p->last_bw[1].XYZ[1], p->last_bw[1].XYZ[2]))
|
|
DBG((dbgo,"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 */
|
|
|
|
#ifdef DEBUG
|
|
double uXYZ[3];
|
|
DBG((dbgo,"patch %d = %f %f %f\n", j, cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2]))
|
|
icmCpy3(uXYZ, cols[j].XYZ);
|
|
#endif
|
|
|
|
if (p->bdrift) {
|
|
bb = p->last_bw[0];
|
|
DBG((dbgo,"bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2]))
|
|
}
|
|
if (p->wdrift) {
|
|
ww = p->last_bw[1];
|
|
DBG((dbgo,"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];
|
|
}
|
|
DBG((dbgo,"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];
|
|
}
|
|
DBG((dbgo,"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];
|
|
}
|
|
DBG((dbgo,"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];
|
|
}
|
|
}
|
|
}
|
|
DBG((dbgo,"%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 - see dispsup.h */
|
|
/* 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 noinc /* Ignored */
|
|
) {
|
|
int rv, i;
|
|
|
|
if (p->bdrift || p->wdrift) {
|
|
if ((rv = disprd_read_drift(p, cols,npat,spat,tpat,acr,tc,clamp, 0)) != 0)
|
|
return rv;
|
|
} else {
|
|
if ((rv = disprd_read_imp(p, cols,npat,spat,tpat,acr,tc,clamp, 0)) != 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 - see dispsup.h */
|
|
/* 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 (p->it->check_mode(p->it, inst_mode_emis_ambient) != inst_ok) {
|
|
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 available 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.mcond = inst_mrc_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: %s",fl_end);
|
|
else
|
|
printf("Hit ESC or Q to exit, any other key to take a reading: %s",fl_end);
|
|
do_fflush();
|
|
|
|
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:%s",fl_end); do_fflush();
|
|
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, 0);
|
|
setup_display_calibrate(p->it,inst_calc_none, &dwi);
|
|
if (rv != inst_ok) { /* Abort or fatal error */
|
|
return ((rv & inst_mask) == inst_user_abort) ? 1 : 2;
|
|
}
|
|
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:%s",fl_end); do_fflush();
|
|
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:%s",fl_end); do_fflush();
|
|
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:%s",fl_end); do_fflush();
|
|
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)
|
|
&& !(p->it->icom->port_attr(p->it->icom) & icomt_fastserial)) {
|
|
/* 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");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Test without a spectrometer using a fake device */
|
|
/* Return nz on fail/abort - see dispsup.h */
|
|
/* 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 */
|
|
int noinc /* Ignored */
|
|
) {
|
|
static int drift = 0; /* Drift white offset multiplier */
|
|
static unsigned int msec = 0;
|
|
double white[3]; /* White point */
|
|
double red[3]; /* Red colorant */
|
|
double green[3]; /* Green colorant */
|
|
double blue[3]; /* 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[0] = br * 0.955; /* Somewhere between D50 and D65 */
|
|
white[1] = br * 1.00;
|
|
white[2] = br * 0.97;
|
|
red[0] = br * 0.41;
|
|
red[1] = br * 0.21;
|
|
red[2] = br * 0.02;
|
|
green[0] = br * 0.30;
|
|
green[1] = br * 0.55;
|
|
green[2] = br * 0.15;
|
|
blue[0] = br * 0.15;
|
|
blue[1] = br * 0.10;
|
|
blue[2] = 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 added to RGB, equivalent to RGB offsets having various values */
|
|
doff[0] = -0.03;
|
|
doff[1] = 0.07;
|
|
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
|
|
|
|
#ifdef FAKE_WDRIFT
|
|
white[0] += (double)drift * -0.005;
|
|
white[1] += (double)drift * 0.02;
|
|
white[2] += (double)drift * 0.01;
|
|
drift++;
|
|
#endif
|
|
|
|
if (icmRGBXYZprim2matrix(red, green, blue, white, 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:%s",fl_end); do_fflush();
|
|
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->native & 1) { /* Apply soft calibration to display value */
|
|
if ((rv = p->dw->set_color(p->dw, crgb[0], crgb[1], crgb[2])) != 0) {
|
|
a1logd(p->log,1,"set_color() returned %d\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 %d\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;
|
|
cols[patch].mcond = inst_mrc_none;
|
|
|
|
cols[patch].serno = p->serno++;
|
|
cols[patch].msec = msec++;
|
|
}
|
|
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 - see dispsup.h */
|
|
/* 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 noinc /* Ignored */
|
|
) {
|
|
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:%s",fl_end); do_fflush();
|
|
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 %d\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_fwd(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;
|
|
cols[patch].mcond = inst_mrc_none;
|
|
}
|
|
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 - see duspsup.h */
|
|
/* 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 noinc /* Ignored */
|
|
) {
|
|
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:%s",fl_end); do_fflush();
|
|
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 %d\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;
|
|
cols[patch].mcond = inst_mrc_none;
|
|
|
|
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;
|
|
}
|
|
|
|
/* Use without a direct connect spectrometer by manual user input */
|
|
/* Return nz on fail/abort - see duspsup.h */
|
|
/* Use disprd_err() to interpret it */
|
|
static int disprd_fake_read_manual(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 noinc /* Ignored */
|
|
) {
|
|
int patch, i, j;
|
|
int ttpat = tpat;
|
|
|
|
if (ttpat < npat)
|
|
ttpat = npat;
|
|
|
|
/* Continue until the user is done or quits */
|
|
for (patch = 0;;) {
|
|
char buf[200], *bp = NULL, *ep = NULL;
|
|
char ch = 0;
|
|
int allread;
|
|
double rgb[3];
|
|
int rv;
|
|
char *cmd;
|
|
FILE *fp;
|
|
|
|
/* Check if all the patches have been read */
|
|
for (allread = i = 0; i < npat; i++) {
|
|
if (cols[i].XYZ_v == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (i >= npat)
|
|
allread = 1;
|
|
|
|
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;
|
|
p->dw->enable_update_delay(p->dw, 0);
|
|
if ((rv = p->dw->set_color(p->dw, rgb[0], rgb[1], rgb[2])) != 0) {
|
|
a1logd(p->log,1,"set_color() returned %d\n",rv);
|
|
return 3;
|
|
}
|
|
p->dw->enable_update_delay(p->dw, 1);
|
|
}
|
|
|
|
/* If we have a RAMDAC, apply it to the requested 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);
|
|
}
|
|
}
|
|
|
|
printf("\nReady to read patch %d of %d RGB %.1f %.1f %.1f%s\n",
|
|
spat+patch,tpat, rgb[0] * 255.0, rgb[1] * 255.0, rgb[2] * 255.0,
|
|
allread ? " (All patches read!)" :
|
|
cols[patch].XYZ_v ? " (Already read)" : "");
|
|
|
|
printf("Enter %s value (separated by spaces), or\n",
|
|
p->xtern == 1 ? "L*a*b*" : "XYZ");
|
|
printf(" 'f' to move forward\n");
|
|
printf(" 'b' to move back\n");
|
|
printf(" 'd' when done, 'q' to abort : %s",fl_end);
|
|
do_fflush();
|
|
|
|
/* Read in the next line from stdin. */
|
|
if (con_fgets(buf, 200) == NULL) {
|
|
printf("Error - unrecognised input\n");
|
|
continue;
|
|
}
|
|
/* Skip whitespace */
|
|
for (bp = buf; *bp != '\000' && isspace(*bp); bp++)
|
|
;
|
|
|
|
ch = *bp; /* First character */
|
|
if (ch == '\000') {
|
|
printf("Error - unrecognised input\n");
|
|
continue;
|
|
}
|
|
|
|
/* If it looks like a number */
|
|
if (isdigit(ch) || ch == '-' || ch == '+' || ch == '.') {
|
|
|
|
/* For each input number */
|
|
for (i = 0; *bp != '\000' && i < 3; i++) {
|
|
char *tp, *nbp;
|
|
|
|
/* Find the start of the number */
|
|
while(*bp != '\000' && !isdigit(*bp)
|
|
&& *bp != '-' && *bp != '+' && *bp != '.')
|
|
bp++;
|
|
if (!isdigit(*bp) && *bp != '-' && *bp != '+' && *bp != '.')
|
|
break;
|
|
|
|
/* Find the end of the number */
|
|
for (tp = bp+1; isdigit(*tp) || *tp == 'e' || *tp == 'E'
|
|
|| *tp == '-' || *tp == '+' || *tp == '.'; tp++)
|
|
;
|
|
if (*tp != '\000')
|
|
nbp = tp+1;
|
|
else
|
|
nbp = tp;
|
|
*tp = '\000';
|
|
|
|
/* Read the number */
|
|
cols[patch].XYZ[i] = atof(bp);
|
|
|
|
bp = nbp;
|
|
}
|
|
if (i < 3) { /* Didn't find 3 numbers */
|
|
printf("Error - unrecognised input\n");
|
|
continue;
|
|
}
|
|
if (p->xtern == 1) {
|
|
icmLab2XYZ(&icmD50, cols[patch].XYZ, cols[patch].XYZ);
|
|
cols[patch].XYZ[0] *= 100.0;
|
|
cols[patch].XYZ[1] *= 100.0;
|
|
cols[patch].XYZ[2] *= 100.0;
|
|
}
|
|
|
|
if (clamp)
|
|
icmClamp3(cols[patch].XYZ, cols[patch].XYZ);
|
|
cols[patch].XYZ_v = 1;
|
|
cols[patch].mtype = inst_mrt_emission;
|
|
cols[patch].mcond = inst_mrc_none;
|
|
printf(" Got XYZ value %f %f %f\n",cols[patch].XYZ[0], cols[patch].XYZ[1], cols[patch].XYZ[2]);
|
|
/* Advance to next patch. */
|
|
patch++;
|
|
if (patch >= npat)
|
|
patch = 0;
|
|
continue;
|
|
|
|
/* Assume it's a command */
|
|
} else if (ch == 'f' || ch == 'F') {
|
|
patch++;
|
|
if (patch >= npat)
|
|
patch = 0;
|
|
continue;
|
|
|
|
} else if (ch == 'b' || ch == 'B') {
|
|
patch--;
|
|
if (patch < 0)
|
|
patch = npat-1;
|
|
continue;
|
|
|
|
} else if (ch == 'd' || ch == 'D') {
|
|
for (allread = i = 0; i < npat; i++) {
|
|
if (cols[i].XYZ_v == 0)
|
|
break;
|
|
}
|
|
if (i >= npat)
|
|
allread = 1;
|
|
|
|
if (allread)
|
|
break;
|
|
|
|
/* Not all patches have been read */
|
|
empty_con_chars();
|
|
printf("\nDone ? - At least one unread patch (%d), Are you sure [y/n]: %s", spat+i, fl_end);
|
|
do_fflush();
|
|
if ((ch = next_con_char()) == 0x1b) {
|
|
printf("\n");
|
|
return 1;
|
|
}
|
|
printf("\n");
|
|
if (ch == 'y' || ch == 'Y')
|
|
break;
|
|
continue;
|
|
|
|
} else if (ch == 'q' || ch == 'Q' || ch == 0x1b) {
|
|
empty_con_chars();
|
|
printf("\nAbort ? - Are you sure ? [y/n]:%s",fl_end); do_fflush();
|
|
if ((ch = next_con_char()) == 'y' || ch == 'Y') {
|
|
printf("\n");
|
|
return 1;
|
|
}
|
|
printf("\n");
|
|
|
|
} else {
|
|
printf("Error - unrecognised input '%c'\n",ch);
|
|
continue;
|
|
}
|
|
}
|
|
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 oeminst ?)";
|
|
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 "Video encoding requested using nonlinear current calibration curves";
|
|
case 13:
|
|
return "Video encoding requested for MadVR display - use MadVR to set video encoding";
|
|
case 14:
|
|
return "Instrument has no set refresh rate capability";
|
|
case 15:
|
|
return "Unknown calibration display type selection";
|
|
case 16:
|
|
return "Must use BASE calibration display type selection";
|
|
}
|
|
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) {
|
|
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 ditype = p->ditype;
|
|
int rv;
|
|
|
|
p->it->capabilities(p->it, &cap, &cap2, &cap3);
|
|
|
|
if (p->tele && p->it->check_mode(p->it, inst_mode_emis_tele) != inst_ok) {
|
|
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->ambient && p->it->check_mode(p->it, inst_mode_emis_ambient) != inst_ok) {
|
|
printf("Want ambient 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->ambient = 0;
|
|
}
|
|
|
|
if (!p->tele && !p->ambient && p->it->check_mode(p->it, inst_mode_emis_spot) != inst_ok) {
|
|
printf("Want emissive spot measurement capability but instrument doesn't support it\n");
|
|
printf("so switching to telephoto spot mode.\n");
|
|
p->tele = 1;
|
|
}
|
|
|
|
if (( p->tele && !p->ambient && p->it->check_mode(p->it, inst_mode_emis_tele) != inst_ok)
|
|
|| (!p->tele && p->ambient && p->it->check_mode(p->it, inst_mode_emis_spot) != inst_ok)
|
|
|| (!p->tele && !p->ambient && p->it->check_mode(p->it, inst_mode_emis_spot) != inst_ok)) {
|
|
printf("Need %s emissive measurement capability,\n", p->tele ? "telephoto" : p->ambient ? "ambient" : "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" : p->ambient ? "ambient" : "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 if (p->ambient) {
|
|
mode = inst_mode_emis_ambient;
|
|
} 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;
|
|
}
|
|
|
|
/* If this is a spectral instrument, and a different */
|
|
/* spectral inst. ditype is supplied, then use it */
|
|
if (IMODETST(cap, inst_mode_spectral) && p->sditype >= 0)
|
|
ditype = p->sditype;
|
|
|
|
/* Set the display type or calibration mode */
|
|
if (ditype != 0) {
|
|
if (cap2 & inst2_disptype) {
|
|
int ix;
|
|
if ((ix = inst_get_disptype_index(p->it, ditype, p->docbid)) < 0) {
|
|
a1logd(p->log,1,"Display type selection '%s' is not valid for instrument\n",
|
|
inst_distr(ditype));
|
|
if (p->docbid)
|
|
return 16;
|
|
return 15;
|
|
}
|
|
if ((rv = p->it->set_disptype(p->it, ix)) != inst_ok) {
|
|
a1logd(p->log,1,"Setting display type failed with '%s' (%s)\n",
|
|
p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv));
|
|
return 15;
|
|
}
|
|
} else
|
|
printf("Display type ignored - instrument doesn't support display type selection\n");
|
|
}
|
|
|
|
/* 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 ((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);
|
|
|
|
/* Check and update mode for any mode dependent capabilities. */
|
|
/* (Could set this without check, or use get_set_opt() afterwards) */
|
|
if (p->highres) {
|
|
if (IMODETST(cap, inst_mode_highres)) {
|
|
mode |= inst_mode_highres;
|
|
|
|
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;
|
|
}
|
|
|
|
} else {
|
|
printf("high resolution ignored - instrument doesn't support high res. mode\n");
|
|
p->highres = 0;
|
|
}
|
|
}
|
|
|
|
/* Set calibration matrix */
|
|
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->cc_dtech, p->cc_cbid, 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;
|
|
}
|
|
}
|
|
|
|
/* Get the refresh mode and cbid */
|
|
p->it->get_disptechi(p->it, NULL, &p->refrmode, &p->cbid);
|
|
|
|
/* Observer */
|
|
if ((cap2 & inst2_ccss) != 0 && p->obType != icxOT_default) {
|
|
|
|
if ((cap2 & inst2_ccss) == 0) {
|
|
a1logd(p->log,1,"Instrument doesn't support ccss calibration and we need it\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;
|
|
}
|
|
}
|
|
|
|
/* Custom CCSS */
|
|
if (p->sets != NULL) {
|
|
|
|
if ((cap2 & inst2_ccss) == 0) {
|
|
a1logd(p->log,1,"Instrument doesn't support ccss calibration and we need it\n");
|
|
return 11;
|
|
}
|
|
|
|
if ((rv = p->it->col_cal_spec_set(p->it, p->cc_dtech, 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;
|
|
}
|
|
}
|
|
|
|
if (p->refrate > 0.0) {
|
|
if (!(cap2 & inst2_set_refresh_rate)) {
|
|
a1logd(p->log,1,"Instrument doesn't support setting refresh rate\n");
|
|
return 11;
|
|
} else {
|
|
if ((rv = p->it->set_refr_rate(p->it, p->refrate)) != inst_ok) {
|
|
a1logd(p->log,1,"set_refr_rate %f Hz returned '%s' (%s)\n",
|
|
p->refrate, 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 succeeded\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Create a display reading object. */
|
|
/* Return NULL if error */
|
|
/* Set *errc to code. See dispsup.h */
|
|
/* 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 ditype, /* Display type selection character(s) */
|
|
int sditype, /* Spectro ditype, use ditype if -1 */
|
|
int docbid, /* NZ to only allow cbid ditypes */
|
|
int tele, /* NZ for tele mode. Falls back to display mode */
|
|
int ambient, /* NZ for ambient mode */
|
|
int nadaptive, /* NZ for non-adaptive mode */
|
|
int noinitcal, /* No initial instrument calibration */
|
|
int noinitplace, /* Don't wait for user to place instrument on screen */
|
|
int highres, /* Use high res mode if available */
|
|
double refrate, /* If != 0.0, set display refresh rate calibration */
|
|
int native, /* X0 = use current per channel calibration curve */
|
|
/* X1 = set native linear output and use ramdac high precn. */
|
|
/* 0X = use current color management cLut (MadVR) */
|
|
/* 1X = disable color management cLUT (MadVR) */
|
|
int *noramdac, /* Return nz if no ramdac access. native is set to X0 */
|
|
int *nocm, /* Return nz if no CM cLUT access. native is set to 0X */
|
|
double cal[3][MAX_CAL_ENT], /* Calibration (cal == NULL or cal[0][0] < 0.0 if not valid) */
|
|
int ncal, /* Number of cal[] entries */
|
|
disppath *disp, /* Display to calibrate. NULL if fake and no dispwin */
|
|
double icalmax, /* Scale instrument calibration test values by this (0.0 .. 1.0) */
|
|
int out_tvenc, /* 1 = use RGB Video Level encoding */
|
|
int fullscreen, /* 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 */
|
|
ccast_id *ccid, /* non-NULL for ChromeCast */
|
|
#ifdef NT
|
|
int madvrdisp, /* NZ for MadVR display */
|
|
#endif
|
|
int dummydisp, /* NZ for dummy display */
|
|
char *ccallout, /* Shell callout on set color */
|
|
char *mcallout, /* Shell callout on measure color (forced fake) */
|
|
//char *scallout, /* Shell callout on results of measure color */
|
|
int xtern, /* Use external (user supplied) values rather than instument read */
|
|
/* 1 = Lab, 2 = XYZ */
|
|
double hpatsize, /* Size of dispwin */
|
|
double vpatsize,
|
|
double ho, /* Horizontal offset */
|
|
double vo, /* Vertical offset */
|
|
disptech cc_dtech, /* Display tech to go with ccmtx or sets */
|
|
int cc_cbid, /* cbid to go with ccmtx or sets */
|
|
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, /* If not NULL, use ICC profile for fake device response */
|
|
a1log *log /* Verb, debug & error log */
|
|
) {
|
|
disprd *p = NULL;
|
|
int ch;
|
|
inst_code rv;
|
|
int uout_tvenc = out_tvenc; /* tvenc to use with dispwin value setting */
|
|
|
|
if (errc != NULL) *errc = 0; /* default return code = no error */
|
|
|
|
/* 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->meas_ambient = disprd_ambient;
|
|
p->fake_name = fake_name;
|
|
|
|
p->cc_dtech = cc_dtech;
|
|
p->cc_cbid = cc_cbid;
|
|
p->ccmtx = ccmtx; /* CCMX */
|
|
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->ditype = ditype;
|
|
p->sditype = sditype;
|
|
p->docbid = docbid;
|
|
p->refrmode = -1; /* Unknown */
|
|
p->cbid = 0; /* Unknown */
|
|
p->tele = tele;
|
|
if (tele)
|
|
ambient = 0;
|
|
p->ambient = ambient;
|
|
p->nadaptive = nadaptive;
|
|
p->noinitcal = noinitcal;
|
|
p->noinitplace = noinitplace;
|
|
p->highres = highres;
|
|
p->refrate = refrate;
|
|
if (mcallout != NULL || xtern != 0)
|
|
ipath = &icomFakeDevice; /* Force fake device */
|
|
p->mcallout = mcallout;
|
|
// p->scallout = scallout;
|
|
p->xtern = xtern;
|
|
p->ipath = ipath;
|
|
p->br = baud_19200;
|
|
p->fc = fc;
|
|
|
|
p->native = native;
|
|
|
|
#ifdef FAKE_NOISE
|
|
# ifdef FAKE_UNPREDIC
|
|
{
|
|
char *eval;
|
|
if ((eval = getenv("ARGYLL_DISPLAY_FAKE_RAND_SEED")) != NULL) {
|
|
unsigned int sv = (unsigned int)atoi(eval);
|
|
if (sv == 0)
|
|
sv = 1;
|
|
rand32(sv);
|
|
} else {
|
|
rand32(time(NULL));
|
|
}
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
/* Save this so we can return current cal, or */
|
|
/* 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;
|
|
} else {
|
|
p->cal[0][0] = -1.0;
|
|
p->ncal = 0;
|
|
}
|
|
|
|
/* Default is to use real instrument read implementation */
|
|
p->read_imp = disprd_read_imp;
|
|
|
|
/* If non-real instrument */
|
|
if (ipath == &icomFakeDevice) {
|
|
icmErr err = { 0, { '\000'} };
|
|
|
|
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->xtern == 0 && p->fake_name != NULL
|
|
&& (p->fake_fp = new_icmFileStd_name(&err, p->fake_name,"r")) != NULL) {
|
|
if ((p->fake_icc = new_icc(&err)) != NULL) {
|
|
if (p->fake_icc->read(p->fake_icc,p->fake_fp,0) == 0) {
|
|
icmCSInfo ini;
|
|
p->fake_lu = (icmLuSpace *)p->fake_icc->get_luobj(p->fake_icc, icmFwd, icAbsoluteColorimetric,
|
|
icSigXYZData, icmLuOrdNorm);
|
|
p->fake_lu->spaces(p->fake_lu, &ini, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
if (ini.sig != icSigRgbData) {
|
|
p->fake_lu->del(p->fake_lu);
|
|
p->fake_lu = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Call drift comp. with fake read implementation */
|
|
if (p->bdrift || p->wdrift) {
|
|
p->read = disprd_read_drift;
|
|
|
|
if (p->fake_lu != NULL) {
|
|
a1logv(p->log, 1, "Using profile '%s' rather than real device\n",p->fake_name);
|
|
p->read_imp = 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_imp = disprd_fake_read_co;
|
|
} else if (p->xtern != 0) {
|
|
a1logv(p->log, 1, "Using manual input rather than real device\n");
|
|
p->read_imp = disprd_fake_read_manual;
|
|
} else
|
|
p->read_imp = disprd_fake_read;
|
|
|
|
/* Call fake read implementation */
|
|
} else {
|
|
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 if (p->xtern != 0) {
|
|
a1logv(p->log, 1, "Using manual input rather than real device\n");
|
|
p->read = disprd_fake_read_manual;
|
|
} 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, 0.0, 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 - no black bg since we assume window only */
|
|
if ((p->dw = new_webwin(webdisp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm,
|
|
icalmax, uout_tvenc, 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;
|
|
}
|
|
} else if (ccid != NULL) {
|
|
if ((p->dw = new_ccwin(ccid, hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm,
|
|
icalmax, uout_tvenc, 0, 0, p->log->verb, p->log->debug)) == NULL) {
|
|
a1logd(log,1,"new_disprd failed because new_ccwin('%s') failed\n",ccid->name);
|
|
p->del(p);
|
|
if (errc != NULL) *errc = 3;
|
|
return NULL;
|
|
}
|
|
#ifdef NT
|
|
} else if (madvrdisp != 0) {
|
|
if (out_tvenc) {
|
|
a1logd(log,1,"new_disprd failed because tv_enc & MadVR window\n");
|
|
p->del(p);
|
|
if (errc != NULL) *errc = 13;
|
|
return NULL;
|
|
}
|
|
|
|
if ((p->dw = new_madvrwin(hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm, icalmax, out_tvenc,
|
|
0, p->log->verb, p->log->debug)) == NULL) {
|
|
a1logd(log,1,"new_disprd failed because new_madvrwin failed\n");
|
|
p->del(p);
|
|
if (errc != NULL) *errc = 3;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
} else if (dummydisp != 0) {
|
|
if ((p->dw = new_dummywin(hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm, icalmax, out_tvenc,
|
|
0, p->log->verb, p->log->debug)) == NULL) {
|
|
a1logd(log,1,"new_disprd failed because new_dummywin failed\n");
|
|
p->del(p);
|
|
if (errc != NULL) *errc = 3;
|
|
return NULL;
|
|
}
|
|
} else {
|
|
/* Don't do tvenc on test values if we are going to do it with RAMDAC curve */
|
|
if (out_tvenc && (p->native & 1) == 0 && p->cal[0][0] >= 0.0) {
|
|
uout_tvenc = 0;
|
|
}
|
|
|
|
/* Open display window for positioning (no fullscreen) */
|
|
if ((p->dw = new_dispwin(disp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm,
|
|
icalmax, uout_tvenc, 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 TV encoding using existing RAMDAC and it is not linear, */
|
|
/* error out, as TV encoding probably won't work properly. */
|
|
if (out_tvenc && (native & 1) == 0 && p->cal[0][0] < 0.0) {
|
|
ramdac *cr;
|
|
if ((cr = p->dw->get_ramdac(p->dw)) != NULL) {
|
|
int i, j;
|
|
for (i = 0; i < cr->nent; i++) {
|
|
double val = i/(cr->nent-1.0);
|
|
for (j = 0; j < 3; j++) {
|
|
if (fabs(val - cr->v[j][i]) > 1e-5)
|
|
break;
|
|
}
|
|
if (j < 3)
|
|
break;
|
|
}
|
|
if (i < cr->nent) {
|
|
a1logd(log,1,"new_disprd failed because tvenc and nonlinear RAMDAC");
|
|
cr->del(cr);
|
|
p->del(p);
|
|
if (errc != NULL) *errc = 12;
|
|
return NULL;
|
|
}
|
|
cr->del(cr);
|
|
}
|
|
}
|
|
|
|
#ifdef NEVER // Don't do this - dispread doesn't save calibration when using existing.
|
|
/* If using exiting RAMDAC, return it */
|
|
if ((native & 1) == 0 && p->cal[0][0] < 0.0 && cal != NULL) {
|
|
ramdac *r;
|
|
|
|
if ((r = p->dw->get_ramdac(p->dw)) != NULL) {
|
|
int j, i;
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (p->it != NULL) {
|
|
/* Do an instrument 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, 0);
|
|
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 = ((rv & inst_mask) == inst_user_abort) ? 1 : 2;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->it != NULL && !p->noinitplace) {
|
|
inst2_capability cap2;
|
|
|
|
p->it->capabilities(p->it, NULL, &cap2, NULL);
|
|
|
|
/* Turn target on */
|
|
if (cap2 & inst2_has_target)
|
|
p->it->get_set_opt(p->it, inst_opt_set_target_state, 1);
|
|
|
|
/* 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:%s",fl_end); do_fflush();
|
|
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");
|
|
if (cap2 & inst2_has_target) /* Turn target off */
|
|
p->it->get_set_opt(p->it, inst_opt_set_target_state, 0);
|
|
p->del(p);
|
|
if (errc != NULL) *errc = 1;
|
|
return NULL;
|
|
}
|
|
|
|
/* Turn target off */
|
|
if (cap2 & inst2_has_target)
|
|
p->it->get_set_opt(p->it, inst_opt_set_target_state, 0);
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
/* All except web disp can have a full screen black backround when running tests */
|
|
if (webdisp == 0 && fullscreen) {
|
|
|
|
if (p->dw->set_fc(p->dw, fullscreen)) { /* Have to re-create window */
|
|
|
|
/* Close the positioning window */
|
|
if (p->dw != NULL) {
|
|
p->dw->del(p->dw);
|
|
}
|
|
|
|
/* We happen to know that only the default dispwin needs re-creating */
|
|
|
|
/* Open display window again for measurement */
|
|
if ((p->dw = new_dispwin(disp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm,
|
|
icalmax, uout_tvenc, fullscreen, 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 display rise & fall time more optimally */
|
|
if (p->it != NULL) {
|
|
disptech dtech;
|
|
disptech_info *tinfo;
|
|
p->it->get_disptechi(p->it, &dtech, NULL, NULL);
|
|
tinfo = disptech_get_id(dtech);
|
|
p->dw->set_settling_delay(p->dw, tinfo->rise_time, tinfo->fall_time, -1.0);
|
|
}
|
|
|
|
/* Set color change callout */
|
|
if (ccallout) {
|
|
p->dw->set_callout(p->dw, ccallout);
|
|
}
|
|
|
|
/* If we have a calibration to set using the RAMDAC */
|
|
/* (This is only typically the case for disread) */
|
|
if ((p->native & 1) == 0 && p->cal[0][0] >= 0.0) {
|
|
|
|
/* Save current RAMDAC so that we can restore it */
|
|
if (p->dw->r == NULL) {
|
|
warning("Unable to read or set display RAMDAC - switching to softcal");
|
|
p->native |= 1;
|
|
|
|
/* Set the given RAMDAC so we can characterise through it */
|
|
} else {
|
|
int j, i;
|
|
|
|
/* Set the ramdac contents. */
|
|
/* We linearly interpolate from cal[ncal] to RAMDAC[nent] resolution */
|
|
for (i = 0; i < p->dw->r->nent; i++) {
|
|
double val, w;
|
|
unsigned int ix;
|
|
|
|
val = (ncal-1.0) * i/(p->dw->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 = p->cal[j][ix];
|
|
p->dw->r->v[j][i] = val + w * (p->cal[j][ix+1] - val);
|
|
/* Implement tvenc here rather than dispwin, so that the order is correct */
|
|
if (out_tvenc) {
|
|
p->dw->r->v[j][i] = ((235.0 - 16.0) * p->dw->r->v[j][i] + 16.0)/255.0;
|
|
|
|
/* For video encoding the extra bits of precision are created by bit */
|
|
/* shifting rather than scaling, so we need to scale the fp value to */
|
|
/* account for this. */
|
|
if (p->dw->edepth > 8)
|
|
p->dw->r->v[j][i] =
|
|
(p->dw->r->v[j][i] * 255 * (1 << (p->dw->edepth - 8)))
|
|
/((1 << p->dw->edepth) - 1.0);
|
|
}
|
|
}
|
|
}
|
|
if (p->dw->set_ramdac(p->dw, 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");
|
|
if (errc != NULL) *errc = 4;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
a1logd(log,1,"new_disprd succeeded\n");
|
|
return p;
|
|
}
|
|
|