1437 lines
39 KiB
C
1437 lines
39 KiB
C
/*
|
|
* Argyll Color Management System
|
|
*
|
|
* Xrite DTP92/94 related functions
|
|
*
|
|
* Author: Graeme W. Gill
|
|
* Date: 5/10/1996
|
|
*
|
|
* Copyright 1996 - 2014, Graeme W. Gill
|
|
* All rights reserved.
|
|
*
|
|
* This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
|
|
* see the License2.txt file for licencing details.
|
|
*/
|
|
|
|
/*
|
|
If you make use of the instrument driver code here, please note
|
|
that it is the author(s) of the code who are responsibility
|
|
for its operation. Any problems or queries regarding driving
|
|
instruments with the Argyll drivers, should be directed to
|
|
the Argyll's author(s), and not to any other party.
|
|
|
|
If there is some instrument feature or function that you
|
|
would like supported here, it is recommended that you
|
|
contact Argyll's author(s) first, rather than attempt to
|
|
modify the software yourself, if you don't have firm knowledge
|
|
of the instrument communicate protocols. There is a chance
|
|
that an instrument could be damaged by an incautious command
|
|
sequence, and the instrument companies generally cannot and
|
|
will not support developers that they have not qualified
|
|
and agreed to support.
|
|
*/
|
|
|
|
/*
|
|
TTBD:
|
|
Should make DTP92 switch trigger a reading.
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <stdarg.h>
|
|
#ifndef SALONEINSTLIB
|
|
#include "copyright.h"
|
|
#include "aconfig.h"
|
|
#include "numlib.h"
|
|
#else /* !SALONEINSTLIB */
|
|
#include "sa_config.h"
|
|
#include "numsup.h"
|
|
#endif /* !SALONEINSTLIB */
|
|
#include "cgats.h"
|
|
#include "xspect.h"
|
|
#include "insttypes.h"
|
|
#include "conv.h"
|
|
#include "icoms.h"
|
|
#include "dtp92.h"
|
|
|
|
/* Default flow control */
|
|
#define DEFFC fc_None
|
|
|
|
#define DEF_TIMEOUT 0.5
|
|
#define MED_TIMEOUT 2.5
|
|
#define MEAS_TIMEOUT 10.0
|
|
|
|
#define IGNORE_NEEDS_OFFSET_DRIFT_CAL_ERR
|
|
|
|
static inst_code dtp92_interp_code(inst *pp, int ec);
|
|
|
|
#define MAX_MES_SIZE 500 /* Maximum normal message reply size */
|
|
#define MAX_RD_SIZE 5000 /* Maximum reading messagle reply size */
|
|
|
|
/* Extract an error code from a reply string */
|
|
/* Return -1 if no error code can be found */
|
|
static int
|
|
extract_ec(char *s) {
|
|
char *p;
|
|
char tt[3];
|
|
int rv;
|
|
p = s + strlen(s);
|
|
/* Find the trailing '>' */
|
|
for (p--; p >= s;p--) {
|
|
if (*p == '>')
|
|
break;
|
|
}
|
|
if ( (p-3) < s
|
|
|| p[0] != '>'
|
|
|| p[-3] != '<')
|
|
return -1;
|
|
tt[0] = p[-2];
|
|
tt[1] = p[-1];
|
|
tt[2] = '\000';
|
|
if (sscanf(tt,"%x",&rv) != 1)
|
|
return -1;
|
|
return rv;
|
|
}
|
|
|
|
/* Interpret an icoms error into a DTP92 error */
|
|
static int icoms2dtp92_err(int se) {
|
|
if (se != ICOM_OK) {
|
|
if (se & ICOM_TO)
|
|
return DTP92_TIMEOUT;
|
|
return DTP92_COMS_FAIL;
|
|
}
|
|
return DTP92_OK;
|
|
}
|
|
|
|
/* Do a full featured command/response echange with the dtp92 */
|
|
/* Return the dtp error code. End on the specified number */
|
|
/* of specified characters, or expiry if the specified timeout */
|
|
/* Assume standard error code if tc = '>' and ntc = 1 */
|
|
/* Return a DTP92 error code */
|
|
static int
|
|
dtp92_fcommand(
|
|
struct _dtp92 *p,
|
|
char *in, /* In string */
|
|
char *out, /* Out string buffer */
|
|
int bsize, /* Out buffer size */
|
|
char *tc, /* Terminating characters */
|
|
int ntc, /* Number of terminating characters */
|
|
double to) { /* Timeout in seconds */
|
|
int rv, se;
|
|
|
|
if ((se = p->icom->write_read(p->icom, in, 0, out, bsize, NULL, tc, ntc, to)) != 0) {
|
|
a1logd(p->log, 1, "dtp92_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in));
|
|
return icoms2dtp92_err(se);
|
|
}
|
|
rv = DTP92_OK;
|
|
if (tc != NULL && tc[0] == '>' && ntc >= 1) {
|
|
rv = extract_ec(out);
|
|
|
|
#ifdef NEVER
|
|
if (strcmp(in, "0PR\r") == 0)
|
|
rv = DTP92_NEEDS_OFFSET_DRIFT_CAL; /* Emulate 1B error */
|
|
#endif /* NEVER */
|
|
|
|
if (rv > 0) {
|
|
rv &= inst_imask;
|
|
if (rv != DTP92_OK) { /* Clear the error */
|
|
char buf[MAX_MES_SIZE];
|
|
p->icom->write_read(p->icom, "CE\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.5);
|
|
}
|
|
}
|
|
}
|
|
a1logd(p->log, 4, "dtp92_fcommand: command '%s' returned '%s', value 0x%x\n",
|
|
icoms_fix(in), icoms_fix(out),rv);
|
|
|
|
#ifdef IGNORE_NEEDS_OFFSET_DRIFT_CAL_ERR
|
|
if ((strcmp(in, "0PR\r") == 0
|
|
|| strcmp(in, "EFC\r") == 0) && rv == DTP92_NEEDS_OFFSET_DRIFT_CAL) {
|
|
static int warned = 0;
|
|
if (!warned) {
|
|
a1logw(p->log,"dtp92: Got error NEEDS_OFFSET_DRIFT_CAL on instrument reset - being ignored.");
|
|
warned = 1;
|
|
}
|
|
rv = 0;
|
|
}
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
/* Do a standard command/response echange with the dtp92 */
|
|
/* Return the dtp error code */
|
|
static inst_code
|
|
dtp92_command(dtp92 *p, char *in, char *out, int bsize, double to) {
|
|
int rv = dtp92_fcommand(p, in, out, bsize, ">", 1, to);
|
|
return dtp92_interp_code((inst *)p, rv);
|
|
}
|
|
|
|
/* Establish communications with a DTP92 */
|
|
/* If it's a serial port, use the baud rate given, and timeout in to secs */
|
|
/* Return DTP92_COMS_FAIL on failure to establish communications */
|
|
static inst_code
|
|
dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
|
|
dtp92 *p = (dtp92 *) pp;
|
|
static char buf[MAX_MES_SIZE];
|
|
baud_rate brt[5] = { baud_9600, baud_19200, baud_4800, baud_2400, baud_1200 };
|
|
char *brc[5] = { "30BR\r", "60BR\r", "18BR\r", "0CBR\r", "06BR\r" };
|
|
char *fcc;
|
|
unsigned int etime;
|
|
instType dtype = pp->dtype;
|
|
int ci, bi, i, se;
|
|
inst_code ev = inst_ok;
|
|
|
|
if (p->icom->port_type(p->icom) == icomt_usb) {
|
|
#ifdef ENABLE_USB
|
|
int wr_ep, rd_ep;
|
|
|
|
a1logd(p->log, 2, "dtp92_init_coms: About to init USB\n");
|
|
|
|
/* Note that the 92Q and 94 have a different */
|
|
/* arrangement of end points: */
|
|
/* */
|
|
/* 92Q 94 */
|
|
/* --- -- */
|
|
/* */
|
|
/* 0x81 i Bulk 0x81 i Intr. */
|
|
/* 0x01 o Bulk */
|
|
/* 0x82 i Intr. */
|
|
/* 0x02 o Bulk 0x02 o Intr. */
|
|
/* */
|
|
/* Set config, interface, write end point, read end point, read quanta */
|
|
|
|
if (dtype == instDTP94) {
|
|
wr_ep = 0x02;
|
|
rd_ep = 0x81;
|
|
} else {
|
|
wr_ep = 0x01;
|
|
rd_ep = 0x81;
|
|
}
|
|
|
|
/* The DTP94 will babble if a measure is interrupted, so reset it */
|
|
/* on close to make sure it restarts correctly. */
|
|
if ((se = p->icom->set_usb_port(p->icom, 1, wr_ep, rd_ep, icomuf_reset_before_close,
|
|
0, NULL)) != ICOM_OK) {
|
|
a1logd(p->log, 1, "dtp92_init_coms: set_usb_port failed ICOM err 0x%x\n",se);
|
|
return dtp92_interp_code((inst *)p, icoms2dtp92_err(se));
|
|
}
|
|
|
|
/* Blind reset it twice - it seems to sometimes hang up */
|
|
/* otherwise under OSX */
|
|
dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, DEF_TIMEOUT);
|
|
dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, DEF_TIMEOUT);
|
|
#else /* !ENABLE_USB */
|
|
a1logd(p->log, 1, "dtp92: Failed to find USB connection to instrument\n");
|
|
return inst_coms_fail;
|
|
#endif /* !ENABLE_USB */
|
|
|
|
} else if (p->icom->port_type(p->icom) == icomt_serial) {
|
|
|
|
#ifdef ENABLE_SERIAL
|
|
a1logd(p->log, 2, "dtp92_init_coms: About to init Serial I/O\n");
|
|
|
|
/* Deal with flow control setting */
|
|
if (fc == fc_nc)
|
|
fc = DEFFC;
|
|
if (fc == fc_XonXOff) {
|
|
fcc = "0304CF\r";
|
|
} else if (fc == fc_Hardware) {
|
|
fcc = "0104CF\r";
|
|
} else {
|
|
fc = fc_None;
|
|
fcc = "0004CF\r";
|
|
}
|
|
|
|
/* Figure DTP92 baud rate being asked for */
|
|
for (bi = 0; bi < 5; bi++) {
|
|
if (brt[bi] == br)
|
|
break;
|
|
}
|
|
if (bi >= 5)
|
|
bi = 0;
|
|
|
|
/* Figure current icoms baud rate */
|
|
for (ci = 0; ci < 5; ci++) {
|
|
if (brt[ci] == p->icom->br)
|
|
break;
|
|
}
|
|
if (ci >= 5)
|
|
ci = bi;
|
|
|
|
/* The tick to give up on */
|
|
etime = msec_time() + (long)(1000.0 * tout + 0.5);
|
|
|
|
/* Until we time out, find the correct baud rate */
|
|
for (i = ci; msec_time() < etime;) {
|
|
a1logd(p->log, 4, "dtp92_init_coms: Trying %s baud, %d msec to go\n",
|
|
baud_rate_to_str(brt[i]), etime- msec_time());
|
|
if ((se = p->icom->set_ser_port(p->icom, fc_None, brt[i], parity_none,
|
|
stop_1, length_8)) != ICOM_OK) {
|
|
a1logd(p->log, 1, "dtp92_init_coms: set_ser_port failed ICOM err 0x%x\n",se);
|
|
return dtp92_interp_code((inst *)p, icoms2dtp92_err(se));
|
|
}
|
|
|
|
/* Throw one response away, to work around some USB<->Serial quirks */
|
|
p->icom->write_read(p->icom, "\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.1);
|
|
|
|
if (((ev = dtp92_command(p, "\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) & inst_mask)
|
|
!= inst_coms_fail)
|
|
goto got_coms; /* We've got coms or user abort */
|
|
|
|
/* Check for user abort */
|
|
if (p->uicallback != NULL) {
|
|
inst_code ev;
|
|
if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) {
|
|
a1logd(p->log, 1, "dtp92_init_coms: user aborted\n");
|
|
return inst_user_abort;
|
|
}
|
|
}
|
|
|
|
if (++i >= 5)
|
|
i = 0;
|
|
}
|
|
/* We haven't established comms */
|
|
return inst_coms_fail;
|
|
|
|
got_coms:;
|
|
|
|
/* Throw one response away, to work around some USB<->Serial quirks */
|
|
p->icom->write_read(p->icom, "\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.1);
|
|
|
|
/* Set the handshaking */
|
|
if ((ev = dtp92_command(p, fcc, buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Change the baud rate to the rate we've been told */
|
|
if ((se = p->icom->write_read(p->icom, brc[bi], 0, buf, MAX_MES_SIZE, NULL, ">", 1, .2)) != 0) {
|
|
if (extract_ec(buf) != DTP92_OK)
|
|
return inst_coms_fail;
|
|
}
|
|
|
|
/* Configure our baud rate and handshaking as well */
|
|
if ((se = p->icom->set_ser_port(p->icom, fc, brt[bi], parity_none,
|
|
stop_1, length_8)) != ICOM_OK) {
|
|
a1logd(p->log, 1, "dtp92_init_coms: set_ser_port failed ICOM err 0x%x\n",se);
|
|
return dtp92_interp_code((inst *)p, icoms2dtp92_err(se));
|
|
}
|
|
|
|
/* Loose a character (not sure why) */
|
|
p->icom->write_read(p->icom, "\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.1);
|
|
|
|
#else /* !ENABLE_SERIAL */
|
|
a1logd(p->log, 1, "dtp92: Failed to find serial connection to instrument\n");
|
|
return inst_coms_fail;
|
|
#endif /* !ENABLE_SERIAL */
|
|
} else {
|
|
a1logd(p->log, 1, "dtp92: wrong communications type for device!\n");
|
|
return inst_coms_fail;
|
|
}
|
|
|
|
/* Check instrument is responding, and reset it again. */
|
|
if ((ev = dtp92_command(p, "\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok
|
|
|| (ev = dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, MED_TIMEOUT)) != inst_ok) {
|
|
|
|
a1logd(p->log, 1, "dtp92_init_coms: failed with ICOM 0x%x\n",ev);
|
|
|
|
#ifdef NEVER /* Experimental code for fixing 0x1B "Offset Drift invalid" error */
|
|
if ((ev & inst_mask) == inst_hardware_fail) {
|
|
/* Could be a checksum type error. Try resetting to factory settings */
|
|
warning("Got error %s (%s) on attempting to communicate with instrument.",
|
|
p->inst_interp_error((inst *)p, ev), p->interp_error((inst *)p, ev));
|
|
if ((ev & inst_imask) == DTP92_NEEDS_OFFSET_DRIFT_CAL) {
|
|
char tbuf[100];
|
|
double odv = 0.0;
|
|
printf("Got Offset Drift Cal error. Will try re-writing it\n");
|
|
if ((ev = dtp92_command(p, "SD\r", buf, MAX_MES_SIZE, MED_TIMEOUT)) != inst_ok)
|
|
error("Reading current offset drift value failed");
|
|
if (sscanf(buf, "%lf<", &odv) != 1)
|
|
error("Unable to parse offset drift value");
|
|
printf("Read current offset drift value of %f\n", odv);
|
|
if (odv > 0.5 || odv < 0.05)
|
|
error("offset drift value seems like nonsense - aborting");
|
|
sprintf(tbuf,"%05dSD\r",(int)(odv * 100000));
|
|
printf("Command about to be sent is '%s', OK ? (Y/N)\n",icoms_fix(tbuf));
|
|
if (getchar() == 'Y') {
|
|
if ((ev = dtp92_command(p, tbuf, buf, MAX_MES_SIZE, 6.0)) != inst_ok)
|
|
error("Writing offset drift value failed");
|
|
else
|
|
printf("Writing offset drift value succeeded!\n");
|
|
} else {
|
|
printf("No command written\n");
|
|
}
|
|
printf("Now try re-running program\n");
|
|
}
|
|
}
|
|
#endif /* NEVER */
|
|
|
|
// p->icom->del(p->icom); /* Since caller may not clean up */
|
|
// p->icom = NULL;
|
|
return inst_coms_fail;
|
|
}
|
|
|
|
a1logd(p->log, 2, "dtp92_init_coms: init coms has succeeded\n");
|
|
|
|
p->gotcoms = 1;
|
|
return inst_ok;
|
|
}
|
|
|
|
static inst_code set_default_disp_type(dtp92 *p);
|
|
|
|
/* Initialise the DTP92 */
|
|
/* return non-zero on an error, with dtp error code */
|
|
static inst_code
|
|
dtp92_init_inst(inst *pp) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
static char buf[MAX_MES_SIZE];
|
|
inst_code ev = inst_ok;
|
|
|
|
a1logd(p->log, 2, "dtp92_init_inst: called\n");
|
|
|
|
if (p->gotcoms == 0)
|
|
return inst_internal_error; /* Must establish coms before calling init */
|
|
|
|
/* Reset it ( without disconnecting USB or resetting baud rate etc.) */
|
|
if ((ev = dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, MED_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Get the model and version number */
|
|
if ((ev = dtp92_command(p, "SV\r", buf, MAX_MES_SIZE, MED_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Check that it is a DTP92, DTP92Q or DTP94 */
|
|
if ( strlen(buf) < 12
|
|
|| (strncmp(buf,"X-Rite DTP92",12) != 0
|
|
&& strncmp(buf,"X-Rite DTP94",12) != 0))
|
|
return inst_unknown_model;
|
|
|
|
if (strncmp(buf,"X-Rite DTP94",12) == 0)
|
|
p->dtype = instDTP94;
|
|
else
|
|
p->dtype = instDTP92;
|
|
|
|
if (p->dtype == instDTP92) {
|
|
/* Turn echoing of characters off */
|
|
if ((ev = dtp92_command(p, "DEC\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
}
|
|
|
|
if (p->dtype == instDTP92) {
|
|
/* Set decimal point on */
|
|
if ((ev = dtp92_command(p, "0106CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
}
|
|
|
|
/* Set color data separator to TAB */
|
|
if ((ev = dtp92_command(p, "0207CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Set delimeter to CR */
|
|
if ((ev = dtp92_command(p, "0008CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Set extra digit resolution (X10) */
|
|
if ((ev = dtp92_command(p, "010ACF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
if (p->dtype == instDTP92) {
|
|
/* Set absolute (luminance) calibration */
|
|
if ((ev = dtp92_command(p, "0118CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
}
|
|
|
|
/* Set no black point subtraction */
|
|
if ((ev = dtp92_command(p, "0019CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Set to factory calibration */
|
|
if ((ev = dtp92_command(p, "EFC\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
if (p->dtype == instDTP94) {
|
|
/* Compensate for offset drift */
|
|
if ((ev = dtp92_command(p, "0117CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
}
|
|
|
|
if (p->dtype == instDTP92) {
|
|
/* Enable ABS mode (in case firmware doesn't default to this after EFC) */
|
|
if ((ev = dtp92_command(p, "0118CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Disable -BLK mode (in case firmware doesn't default to this after EFC) */
|
|
if ((ev = dtp92_command(p, "0019CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Set to transmit just the colorimetric data */
|
|
if ((ev = dtp92_command(p, "0120CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Transmit colorimetric data as XYZ */
|
|
if ((ev = dtp92_command(p, "0221CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Disable Luminance group */
|
|
if ((ev = dtp92_command(p, "0022CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Disable Frequency group */
|
|
if ((ev = dtp92_command(p, "0023CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Disable color temperature group */
|
|
if ((ev = dtp92_command(p, "0024CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Disable RGB group */
|
|
if ((ev = dtp92_command(p, "0025CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Disable pushbutton */
|
|
if ((ev = dtp92_command(p, "DPB\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Set sample size to 16 (default is 16) */
|
|
if ((ev = dtp92_command(p, "10SS\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
}
|
|
p->trig = inst_opt_trig_user;
|
|
|
|
/* Set the default display type */
|
|
if ((ev = set_default_disp_type(p)) != inst_ok)
|
|
return ev;
|
|
|
|
/* ??? Need to set CTYP appropriately ??? */
|
|
|
|
#ifdef NEVER /* Debug code */
|
|
if (p->dtype == instDTP94) {
|
|
int i, val;
|
|
char tb[50];
|
|
|
|
for (i = 0; i < 0x800; i++) {
|
|
sprintf(tb,"%04xRN\r",i);
|
|
if ((ev = dtp92_command(p, tb, buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
if (sscanf(buf,"%x",&val) != 1)
|
|
return inst_coms_fail;
|
|
tb[0] = val;
|
|
tb[1] = '\000';
|
|
a1logd(p->log, 0, "Mem location 0x%04x = 0x%02x '%s'\n",i,val,icoms_fix(tb));
|
|
}
|
|
}
|
|
#endif /* NEVER */
|
|
|
|
if (p->log->verb) {
|
|
int i, j;
|
|
if ((ev = dtp92_command(p, "GI\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) {
|
|
a1logd(p->log, 1, "dtp92: GI command failed with ICOM err 0x%x\n",ev);
|
|
return ev;
|
|
}
|
|
for (j = i = 0; ;i++) {
|
|
if (buf[i] == '<' || buf[i] == '\000')
|
|
break;
|
|
if (buf[i] == '\r') {
|
|
buf[i] = '\000';
|
|
a1logv(p->log, 1, " %s\n",&buf[j]);
|
|
if (buf[i+1] == '\n')
|
|
i++;
|
|
j = i+1;
|
|
}
|
|
}
|
|
}
|
|
|
|
p->inited = 1;
|
|
a1logd(p->log, 2, "dtp92_init_inst: instrument inited OK\n");
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* The DTP92 seems to have a bug whereby it adds a spurious */
|
|
/* digit after the 'Z' of the Z value. Try and discard this. */
|
|
|
|
/* Read a single sample */
|
|
/* Return the dtp error code */
|
|
static inst_code
|
|
dtp92_read_sample(
|
|
inst *pp,
|
|
char *name, /* Strip name (7 chars) */
|
|
ipatch *val, /* Pointer to instrument patch value */
|
|
instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */
|
|
dtp92 *p = (dtp92 *)pp;
|
|
char buf[MAX_RD_SIZE];
|
|
int tries;
|
|
int user_trig = 0;
|
|
inst_code rv = inst_protocol_error;
|
|
|
|
if (!p->gotcoms)
|
|
return inst_no_coms;
|
|
if (!p->inited)
|
|
return inst_no_init;
|
|
|
|
/* Could change SS to suite level expected. */
|
|
#ifdef NEVER
|
|
if (p->dtype == instDTP92) {
|
|
/* Set sample size to 31 (default is 16) for low level readings */
|
|
if ((rv = dtp92_command(p, "1fSS\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
if (p->trig == inst_opt_trig_user) {
|
|
|
|
if (p->uicallback == NULL) {
|
|
a1logd(p->log, 1, "dtp92: inst_opt_trig_user but no uicallback function set!\n");
|
|
return inst_unsupported;
|
|
}
|
|
|
|
for (;;) {
|
|
if ((rv = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
|
|
if (rv == inst_user_abort)
|
|
return rv; /* Abort */
|
|
if (rv == inst_user_trig) {
|
|
user_trig = 1;
|
|
break; /* Trigger */
|
|
}
|
|
}
|
|
msec_sleep(200);
|
|
}
|
|
/* Notify of trigger */
|
|
if (p->uicallback)
|
|
p->uicallback(p->uic_cntx, inst_triggered);
|
|
|
|
/* Progromatic Trigger */
|
|
} else {
|
|
/* Check for abort */
|
|
if (p->uicallback != NULL
|
|
&& (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort)
|
|
return rv; /* Abort */
|
|
}
|
|
|
|
/* Until we get a valid return */
|
|
for (tries = 0; tries < 2; tries++) {
|
|
|
|
/* Take a reading */
|
|
/* (DTP94 has optional parameters, but the default is what we want, XYZ in cd/m^2) */
|
|
if ((rv = dtp92_command(p, "RM\r", buf, MAX_RD_SIZE, MEAS_TIMEOUT)) != inst_ok) {
|
|
if ((rv & inst_imask) == DTP92_NEEDS_OFFSET_CAL)
|
|
p->need_offset_cal = 1;
|
|
else if ((rv & inst_imask) == DTP92_NEEDS_RATIO_CAL) /* DTP92 only */
|
|
p->need_ratio_cal = 1;
|
|
return rv;
|
|
}
|
|
|
|
if (sscanf(buf, " X%*c %lf\t Y%*c %lf\t Z%*c %lf ",
|
|
&val->XYZ[0], &val->XYZ[1], &val->XYZ[2]) == 3) {
|
|
|
|
/* Hmm. The DTP92 seems to return strange X & Z values if the light */
|
|
/* level is zero, and it's black level is out of calibration */
|
|
/* (i.e. internally it has -ve Y ?) */
|
|
if (val->XYZ[1] == 0.0 && val->XYZ[0] == 999.99)
|
|
val->XYZ[0] = 0.0;
|
|
if (val->XYZ[1] == 0.0 && val->XYZ[2] == 999.99)
|
|
val->XYZ[2] = 0.0;
|
|
|
|
/* Apply the colorimeter correction matrix */
|
|
icmMulBy3x3(val->XYZ, p->ccmat, val->XYZ);
|
|
|
|
/* This may not change anything since instrument may clamp */
|
|
if (clamp)
|
|
icmClamp3(val->XYZ, val->XYZ);
|
|
val->loc[0] = '\000';
|
|
val->mtype = inst_mrt_emission;
|
|
val->mcond = inst_mrc_none;
|
|
val->XYZ_v = 1; /* These are absolute XYZ readings */
|
|
val->sp.spec_n = 0;
|
|
val->duration = 0.0;
|
|
rv = inst_ok;
|
|
break;
|
|
} else {
|
|
a1logd(p->log, 1, "dtp92_read_sample: failed to parse string '%s'\n",buf);
|
|
rv = inst_coms_fail;
|
|
}
|
|
}
|
|
|
|
#ifdef NEVER
|
|
if (p->dtype == instDTP92) {
|
|
/* Set sample size back to 16 */
|
|
if ((rv = dtp92_command(p, "10SS\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
if (user_trig)
|
|
return inst_user_trig;
|
|
return rv;
|
|
}
|
|
|
|
/* Read an emissive refresh rate */
|
|
static inst_code
|
|
dtp92_read_refrate(
|
|
inst *pp,
|
|
double *ref_rate
|
|
) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
char buf[MAX_RD_SIZE];
|
|
char buf2[MAX_RD_SIZE];
|
|
double refrate = 0.0;
|
|
inst_code rv;
|
|
|
|
if (!p->gotcoms)
|
|
return inst_no_coms;
|
|
if (!p->inited)
|
|
return inst_no_init;
|
|
|
|
if (ref_rate != NULL)
|
|
*ref_rate = 0.0;
|
|
|
|
/* Measure the refresh rate */
|
|
rv = dtp92_command(p, "00103RM\r", buf, MAX_RD_SIZE, 5.0);
|
|
|
|
if (rv != inst_ok) {
|
|
if ((rv & inst_imask) == DTP92_NO_DATA_AVAILABLE)
|
|
rv = inst_misread;
|
|
return rv;
|
|
}
|
|
|
|
if (sscanf(buf, "Hz %lf ", &refrate) != 1) {
|
|
a1logd(p->log, 1, "dtp92_read_refrate rate: failed to parse string '%s'\n",buf);
|
|
return inst_misread;
|
|
}
|
|
|
|
if (refrate == 0.0)
|
|
return inst_misread;
|
|
|
|
|
|
if (ref_rate != NULL)
|
|
*ref_rate = refrate;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
static inst_code set_base_disp_type(dtp92 *p, int cbid);
|
|
|
|
/* Insert a colorimetric correction matrix in the instrument XYZ readings */
|
|
/* This is only valid for colorimetric instruments. */
|
|
/* To remove the matrix, pass NULL for the filter filename */
|
|
static inst_code dtp92_col_cor_mat(
|
|
inst *pp,
|
|
disptech dtech, /* Use disptech_unknown if not known */ \
|
|
int cbid, /* Calibration display type base ID, 1 if unknown */\
|
|
double mtx[3][3]
|
|
) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
inst_code ev;
|
|
|
|
if (!p->gotcoms)
|
|
return inst_no_coms;
|
|
if (!p->inited)
|
|
return inst_no_init;
|
|
|
|
if ((ev = set_base_disp_type(p, cbid)) != inst_ok)
|
|
return ev;
|
|
if (mtx == NULL)
|
|
icmSetUnity3x3(p->ccmat);
|
|
else
|
|
icmCpy3x3(p->ccmat, mtx);
|
|
|
|
p->dtech = dtech;
|
|
p->refrmode = disptech_get_id(dtech)->refr;
|
|
p->cbid = 0; /* Can't be base type now */
|
|
|
|
if (p->log->debug >= 4) {
|
|
a1logd(p->log,4,"ccmat = %f %f %f\n",
|
|
p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
|
|
a1logd(p->log,4," %f %f %f\n",
|
|
p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]);
|
|
a1logd(p->log,4," %f %f %f\n\n",
|
|
p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
|
|
a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid);
|
|
a1logd(p->log,4,"\n");
|
|
}
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
|
|
/* Return needed and available inst_cal_type's */
|
|
static inst_code dtp92_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
inst_cal_type n_cals = inst_calt_none;
|
|
inst_cal_type a_cals = inst_calt_none;
|
|
|
|
if (p->dtype == instDTP92) {
|
|
if (p->need_ratio_cal)
|
|
n_cals |= inst_calt_emis_ratio;
|
|
a_cals |= inst_calt_emis_ratio;
|
|
}
|
|
|
|
if (p->need_offset_cal)
|
|
n_cals |= inst_calt_emis_offset;
|
|
a_cals |= inst_calt_emis_offset;
|
|
|
|
if (pn_cals != NULL)
|
|
*pn_cals = n_cals;
|
|
|
|
if (pa_cals != NULL)
|
|
*pa_cals = a_cals;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Request an instrument calibration. */
|
|
/* This is use if the user decides they want to do a calibration */
|
|
/* in anticipation of a calibration (needs_calibration()) to avoid */
|
|
/* requiring one during measurement, or in response to measuring */
|
|
/* returning inst_needs_cal. Initially use an inst_cal_cond of inst_calc_none, */
|
|
/* and then be prepared to setup the right conditions, or ask the */
|
|
/* user to do so, each time the error inst_cal_setup is returned. */
|
|
static inst_code dtp92_calibrate(
|
|
inst *pp,
|
|
inst_cal_type *calt, /* Calibration type to do/remaining */
|
|
inst_cal_cond *calc, /* Current condition/desired condition */
|
|
inst_calc_id_type *idtype, /* Condition identifier type */
|
|
char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */
|
|
) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
char buf[MAX_RD_SIZE];
|
|
inst_code ev = inst_ok;
|
|
inst_cal_type needed, available;
|
|
|
|
if (!p->gotcoms)
|
|
return inst_no_coms;
|
|
if (!p->inited)
|
|
return inst_no_init;
|
|
|
|
*idtype = inst_calc_id_none;
|
|
id[0] = '\000';
|
|
|
|
if ((ev = dtp92_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Translate inst_calt_all/needed into something specific */
|
|
if (*calt == inst_calt_all
|
|
|| *calt == inst_calt_needed
|
|
|| *calt == inst_calt_available) {
|
|
if (*calt == inst_calt_all)
|
|
*calt = (needed & inst_calt_n_dfrble_mask) | inst_calt_ap_flag;
|
|
else if (*calt == inst_calt_needed)
|
|
*calt = needed & inst_calt_n_dfrble_mask;
|
|
else if (*calt == inst_calt_available)
|
|
*calt = available & inst_calt_n_dfrble_mask;
|
|
|
|
a1logd(p->log,4,"dtp92_calibrate: doing calt 0x%x\n",calt);
|
|
|
|
if ((*calt & inst_calt_n_dfrble_mask) == 0) /* Nothing todo */
|
|
return inst_ok;
|
|
}
|
|
|
|
/* See if it's a calibration we understand */
|
|
if (*calt & ~available & inst_calt_all_mask) {
|
|
return inst_unsupported;
|
|
}
|
|
|
|
if (*calt & inst_calt_emis_offset) { /* Dark offset calibration */
|
|
|
|
if ((*calc & inst_calc_cond_mask) != inst_calc_man_em_dark) {
|
|
*calc = inst_calc_man_em_dark;
|
|
return inst_cal_setup;
|
|
}
|
|
|
|
/* Do offset calibration */
|
|
if ((ev = dtp92_command(p, "CO\r", buf, MAX_RD_SIZE, 12.0)) != inst_ok)
|
|
return ev;
|
|
|
|
*calt &= inst_calt_emis_offset;
|
|
}
|
|
|
|
if (*calt & inst_calt_emis_ratio) { /* Cell ratio calibration */
|
|
|
|
if ((*calc & inst_calc_cond_mask) != inst_calc_emis_grey
|
|
&& (*calc & inst_calc_cond_mask) != inst_calc_emis_grey_darker
|
|
&& (*calc & inst_calc_cond_mask) != inst_calc_emis_grey_ligher) {
|
|
*calc = inst_calc_emis_grey;
|
|
return inst_cal_setup;
|
|
}
|
|
|
|
/* Do ratio calibration */
|
|
if ((ev = dtp92_command(p, "CR\r", buf, MAX_RD_SIZE, 25.0)) != inst_ok) {
|
|
|
|
if ((ev & inst_imask) == DTP92_TOO_MUCH_LIGHT) {
|
|
*calc = inst_calc_emis_grey_darker;
|
|
return inst_cal_setup;
|
|
} else if ((ev & inst_imask) == DTP92_NOT_ENOUGH_LIGHT) {
|
|
*calc = inst_calc_emis_grey_ligher;
|
|
return inst_cal_setup;
|
|
}
|
|
return ev; /* Error */
|
|
}
|
|
*calt &= inst_calt_emis_ratio;
|
|
}
|
|
|
|
return ev;
|
|
}
|
|
|
|
/* Return the last reading refresh rate in Hz. */
|
|
static inst_code dtp92_get_refr_rate(inst *pp,
|
|
double *ref_rate
|
|
) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
char buf[MAX_RD_SIZE];
|
|
char buf2[MAX_RD_SIZE];
|
|
double refrate = 0.0;
|
|
inst_code rv;
|
|
|
|
if (!p->gotcoms)
|
|
return inst_no_coms;
|
|
if (!p->inited)
|
|
return inst_no_init;
|
|
|
|
if (ref_rate != NULL)
|
|
*ref_rate = 0.0;
|
|
|
|
/* Get the last readings refresh rate */
|
|
rv = dtp92_command(p, "10103RM\r", buf, MAX_RD_SIZE, 5.0);
|
|
|
|
if (rv != inst_ok) {
|
|
if ((rv & inst_imask) == DTP92_NO_DATA_AVAILABLE)
|
|
rv = inst_misread;
|
|
return rv;
|
|
}
|
|
|
|
if (sscanf(buf, "Hz %lf ", &refrate) != 1) {
|
|
a1logd(p->log, 1, "dtp92_read_refresh rate: failed to parse string '%s'\n",buf);
|
|
return inst_misread;
|
|
}
|
|
if (refrate == 0.0)
|
|
return inst_misread;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Error codes interpretation */
|
|
static char *
|
|
dtp92_interp_error(inst *pp, int ec) {
|
|
// dtp92 *p = (dtp92 *)pp;
|
|
ec &= inst_imask;
|
|
switch (ec) {
|
|
case DTP92_INTERNAL_ERROR:
|
|
return "Internal software error";
|
|
case DTP92_COMS_FAIL:
|
|
return "Communications failure";
|
|
case DTP92_UNKNOWN_MODEL:
|
|
return "Not a DTP92 or DTP94";
|
|
case DTP92_DATA_PARSE_ERROR:
|
|
return "Data from DTP didn't parse as expected";
|
|
|
|
case DTP92_OK:
|
|
return "No device error";
|
|
|
|
case DTP92_BAD_COMMAND:
|
|
return "Unrecognized command";
|
|
case DTP92_PRM_RANGE:
|
|
return "Command parameter out of range";
|
|
case DTP92_MEMORY_OVERFLOW:
|
|
return "Memory bounds error";
|
|
case DTP92_INVALID_BAUD_RATE:
|
|
return "Invalid baud rate";
|
|
case DTP92_TIMEOUT:
|
|
return "Receive timeout";
|
|
case DTP92_SYNTAX_ERROR:
|
|
return "Badly formed parameter";
|
|
case DTP92_NO_DATA_AVAILABLE:
|
|
return "No data available";
|
|
case DTP92_MISSING_PARAMETER:
|
|
return "Parameter is missing";
|
|
case DTP92_CALIBRATION_DENIED:
|
|
return "Invalid calibration enable code";
|
|
case DTP92_NEEDS_OFFSET_CAL:
|
|
return "Offset calibration checksum failed";
|
|
case DTP92_NEEDS_RATIO_CAL:
|
|
return "Ratio calibration checksum failed";
|
|
case DTP92_NEEDS_LUMINANCE_CAL:
|
|
return "Luminance calibration checksum failed";
|
|
case DTP92_NEEDS_WHITE_POINT_CAL:
|
|
return "White point calibration checksum failed";
|
|
case DTP92_NEEDS_BLACK_POINT_CAL:
|
|
return "Black point calibration checksum failed";
|
|
case DTP92_NEEDS_OFFSET_DRIFT_CAL:
|
|
return "Offset drift calibration checksum failed";
|
|
case DTP92_INVALID_READING:
|
|
return "Unable to take a reading";
|
|
case DTP92_BAD_COMP_TABLE:
|
|
return "Bad compensation table";
|
|
case DTP92_TOO_MUCH_LIGHT:
|
|
return "Too much light entering instrument";
|
|
case DTP92_NOT_ENOUGH_LIGHT:
|
|
return "Not enough light to complete operation";
|
|
|
|
case DTP92_BAD_SERIAL_NUMBER:
|
|
return "New serial number is invalid";
|
|
|
|
case DTP92_NO_MODULATION:
|
|
return "No refresh modulation detected";
|
|
|
|
case DTP92_EEPROM_FAILURE:
|
|
return "EEprom write failure";
|
|
case DTP92_FLASH_WRITE_FAILURE:
|
|
return "Flash memory write failure";
|
|
case DTP92_BAD_CONFIGURATION:
|
|
return "Configuration data checksum failed";
|
|
case DTP92_INST_INTERNAL_ERROR:
|
|
return "Internal instrument error";
|
|
default:
|
|
return "Unknown error code";
|
|
}
|
|
}
|
|
|
|
|
|
/* Convert a machine specific error code into an abstract dtp code */
|
|
static inst_code
|
|
dtp92_interp_code(inst *pp, int ec) {
|
|
|
|
ec &= inst_imask;
|
|
switch (ec) {
|
|
|
|
case DTP92_OK:
|
|
return inst_ok;
|
|
|
|
case DTP92_INTERNAL_ERROR:
|
|
return inst_internal_error | ec;
|
|
|
|
case DTP92_TIMEOUT:
|
|
case DTP92_COMS_FAIL:
|
|
return inst_coms_fail | ec;
|
|
|
|
case DTP92_UNKNOWN_MODEL:
|
|
return inst_unknown_model | ec;
|
|
|
|
case DTP92_DATA_PARSE_ERROR:
|
|
return inst_protocol_error | ec;
|
|
|
|
case DTP92_NO_MODULATION: /* ?? */
|
|
case DTP92_TOO_MUCH_LIGHT:
|
|
case DTP92_NOT_ENOUGH_LIGHT:
|
|
case DTP92_INVALID_READING:
|
|
return inst_misread | ec;
|
|
|
|
case DTP92_NEEDS_OFFSET_CAL:
|
|
return inst_needs_cal | ec;
|
|
|
|
case DTP92_NEEDS_RATIO_CAL:
|
|
return inst_needs_cal | ec;
|
|
|
|
case DTP92_NEEDS_LUMINANCE_CAL:
|
|
case DTP92_NEEDS_WHITE_POINT_CAL:
|
|
case DTP92_NEEDS_BLACK_POINT_CAL:
|
|
case DTP92_NEEDS_OFFSET_DRIFT_CAL:
|
|
return inst_hardware_fail | ec;
|
|
}
|
|
return inst_other_error | ec;
|
|
}
|
|
|
|
/* Destroy ourselves */
|
|
static void
|
|
dtp92_del(inst *pp) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
if (p->icom != NULL)
|
|
p->icom->del(p->icom);
|
|
inst_del_disptype_list(p->dtlist, p->ndtlist);
|
|
p->vdel(pp);
|
|
free(p);
|
|
}
|
|
|
|
/* Return the instrument mode capabilities */
|
|
static void dtp92_capabilities(inst *pp,
|
|
inst_mode *pcap1,
|
|
inst2_capability *pcap2,
|
|
inst3_capability *pcap3) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
inst_mode cap1 = 0;
|
|
inst2_capability cap2 = 0;
|
|
|
|
cap1 |= inst_mode_emis_spot
|
|
| inst_mode_colorimeter
|
|
;
|
|
|
|
cap2 |= inst2_prog_trig
|
|
| inst2_user_trig
|
|
| inst2_ccmx
|
|
; /* The '92 does have a switch, but we're not currently supporting it */
|
|
|
|
if (p->dtype == instDTP94) {
|
|
cap2 |= inst2_disptype;
|
|
} else {
|
|
cap2 |= inst2_get_refresh_rate;
|
|
cap2 |= inst2_emis_refr_meas;
|
|
}
|
|
|
|
if (pcap1 != NULL)
|
|
*pcap1 = cap1;
|
|
if (pcap2 != NULL)
|
|
*pcap2 = cap2;
|
|
if (pcap3 != NULL)
|
|
*pcap3 = inst3_none;
|
|
}
|
|
|
|
/* Check device measurement mode */
|
|
static inst_code dtp92_check_mode(inst *pp, inst_mode m) {
|
|
inst_mode cap;
|
|
|
|
if (!pp->gotcoms)
|
|
return inst_no_coms;
|
|
if (!pp->inited)
|
|
return inst_no_init;
|
|
|
|
pp->capabilities(pp, &cap, NULL, NULL);
|
|
|
|
/* Simple test */
|
|
if (m & ~cap)
|
|
return inst_unsupported;
|
|
|
|
/* Only display emission mode supported */
|
|
if (!IMODETST(m, inst_mode_emis_spot)) {
|
|
return inst_unsupported;
|
|
}
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Set device measurement mode */
|
|
static inst_code dtp92_set_mode(inst *pp, inst_mode m) {
|
|
inst_code ev;
|
|
|
|
if ((ev = dtp92_check_mode(pp, m)) != inst_ok)
|
|
return ev;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
static inst_disptypesel dtp92_disptypesel[2] = {
|
|
{
|
|
inst_dtflags_default, /* flags */
|
|
2, /* cbid */
|
|
"c", /* sel */
|
|
"CRT display", /* desc */
|
|
1, /* refr */
|
|
disptech_crt, /* disptype */
|
|
0 /* ix */
|
|
},
|
|
{
|
|
inst_dtflags_end,
|
|
0,
|
|
"",
|
|
"",
|
|
0,
|
|
disptech_none,
|
|
0
|
|
}
|
|
};
|
|
|
|
static inst_disptypesel dtp94_disptypesel[4] = {
|
|
{
|
|
inst_dtflags_default,
|
|
1,
|
|
"l",
|
|
"LCD display",
|
|
0,
|
|
disptech_lcd,
|
|
2
|
|
},
|
|
{
|
|
inst_dtflags_none, /* flags */
|
|
2, /* cbid */
|
|
"c", /* sel */
|
|
"CRT display", /* desc */
|
|
1, /* refr */
|
|
disptech_crt, /* disptype */
|
|
1 /* ix */
|
|
},
|
|
{
|
|
inst_dtflags_none,
|
|
3,
|
|
"g",
|
|
"Generic display",
|
|
0, /* Might be auto refresh detect ? */
|
|
disptech_unknown,
|
|
0
|
|
},
|
|
{
|
|
inst_dtflags_end,
|
|
0,
|
|
"",
|
|
"",
|
|
0,
|
|
disptech_none,
|
|
0
|
|
}
|
|
};
|
|
|
|
static void set_base_disptype_list(dtp92 *p) {
|
|
if (p->dtype == instDTP94) {
|
|
p->_dtlist = dtp94_disptypesel;
|
|
} else {
|
|
p->_dtlist = dtp92_disptypesel;
|
|
}
|
|
}
|
|
|
|
/* Get mode and option details */
|
|
static inst_code dtp92_get_disptypesel(
|
|
inst *pp,
|
|
int *pnsels, /* Return number of display types */
|
|
inst_disptypesel **psels, /* Return the array of display types */
|
|
int allconfig, /* nz to return list for all configs, not just current. */
|
|
int recreate /* nz to re-check for new ccmx & ccss files */
|
|
) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
inst_code rv = inst_ok;
|
|
|
|
/* Create/Re-create a current list of available display types */
|
|
if (p->dtlist == NULL || recreate) {
|
|
if ((rv = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist,
|
|
p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
|
|
return rv;
|
|
}
|
|
|
|
if (pnsels != NULL)
|
|
*pnsels = p->ndtlist;
|
|
|
|
if (psels != NULL)
|
|
*psels = p->dtlist;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Given a display type entry, setup for that type */
|
|
static inst_code set_disp_type(dtp92 *p, inst_disptypesel *dentry) {
|
|
|
|
if (dentry->flags & inst_dtflags_ccmx) {
|
|
inst_code ev;
|
|
if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok)
|
|
return ev;
|
|
icmCpy3x3(p->ccmat, dentry->mat);
|
|
p->dtech = dentry->dtech;
|
|
p->cbid = 0; /* Can't be a base type */
|
|
|
|
} else { /* Native */
|
|
p->icx = dentry->ix;
|
|
p->dtech = dentry->dtech;
|
|
p->cbid = dentry->cbid;
|
|
p->ucbid = dentry->cbid; /* is underying base if dentry is base selection */
|
|
|
|
if (p->dtype == instDTP92) {
|
|
if (p->icx != 0)
|
|
return inst_unsupported;
|
|
|
|
} else { /* DTP94 */
|
|
static char buf[MAX_MES_SIZE];
|
|
inst_code ev;
|
|
|
|
if (p->icx == 0) { /* Generic/Non-specific */
|
|
if ((ev = dtp92_command(p, "0016CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
} else if (p->icx == 1) { /* CRT */
|
|
if ((ev = dtp92_command(p, "0116CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
} else if (p->icx == 2) { /* LCD */
|
|
if ((ev = dtp92_command(p, "0216CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok)
|
|
return ev;
|
|
} else {
|
|
return inst_unsupported;
|
|
}
|
|
}
|
|
icmSetUnity3x3(p->ccmat);
|
|
}
|
|
p->refrmode = dentry->refr;
|
|
|
|
if (p->log->debug >= 4) {
|
|
a1logd(p->log,4,"ccmat = %f %f %f\n",
|
|
p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]);
|
|
a1logd(p->log,4," %f %f %f\n",
|
|
p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]);
|
|
a1logd(p->log,4," %f %f %f\n\n",
|
|
p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]);
|
|
a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid);
|
|
a1logd(p->log,4,"\n");
|
|
}
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Set the display type */
|
|
static inst_code dtp92_set_disptype(inst *pp, int ix) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
inst_code ev;
|
|
inst_disptypesel *dentry;
|
|
|
|
if (!p->gotcoms)
|
|
return inst_no_coms;
|
|
if (!p->inited)
|
|
return inst_no_init;
|
|
|
|
if (p->dtlist == NULL) {
|
|
if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist,
|
|
p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
|
|
return ev;
|
|
}
|
|
|
|
if (ix < 0 || ix >= p->ndtlist)
|
|
return inst_unsupported;
|
|
|
|
dentry = &p->dtlist[ix];
|
|
|
|
if ((ev = set_disp_type(p, dentry)) != inst_ok) {
|
|
return ev;
|
|
}
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Setup the default display type */
|
|
static inst_code set_default_disp_type(dtp92 *p) {
|
|
inst_code ev;
|
|
int i;
|
|
|
|
if (p->dtlist == NULL) {
|
|
if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
|
|
p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
|
|
return ev;
|
|
}
|
|
|
|
for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) {
|
|
if (p->dtlist[i].flags & inst_dtflags_default)
|
|
break;
|
|
}
|
|
if (p->dtlist[i].flags & inst_dtflags_end) {
|
|
a1loge(p->log, 1, "set_default_disp_type: failed to find type!\n");
|
|
return inst_internal_error;
|
|
}
|
|
if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) {
|
|
return ev;
|
|
}
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Setup the display type to the given base type */
|
|
static inst_code set_base_disp_type(dtp92 *p, int cbid) {
|
|
inst_code ev;
|
|
int i;
|
|
|
|
if (cbid == 0) {
|
|
a1loge(p->log, 1, "dtp92 set_base_disp_type: can't set base display type of 0\n");
|
|
return inst_wrong_setup;
|
|
}
|
|
if (p->dtlist == NULL) {
|
|
if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist,
|
|
dtp92_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok)
|
|
return ev;
|
|
}
|
|
|
|
for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) {
|
|
if (!(p->dtlist[i].flags & inst_dtflags_ccmx) /* Prevent infinite recursion */
|
|
&& p->dtlist[i].cbid == cbid)
|
|
break;
|
|
}
|
|
if (p->dtlist[i].flags & inst_dtflags_end) {
|
|
a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid);
|
|
return inst_wrong_setup;
|
|
}
|
|
if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) {
|
|
return ev;
|
|
}
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Get the disptech and other corresponding info for the current */
|
|
/* selected display type. Returns disptype_unknown by default. */
|
|
/* Because refrmode can be overridden, it may not match the refrmode */
|
|
/* of the dtech. (Pointers may be NULL if not needed) */
|
|
static inst_code dtp92_get_disptechi(
|
|
inst *pp,
|
|
disptech *dtech,
|
|
int *refrmode,
|
|
int *cbid) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
if (dtech != NULL)
|
|
*dtech = p->dtech;
|
|
if (refrmode != NULL)
|
|
*refrmode = p->refrmode;
|
|
if (cbid != NULL)
|
|
*cbid = p->cbid;
|
|
return inst_ok;
|
|
}
|
|
|
|
/*
|
|
* set or reset an optional mode
|
|
*
|
|
* Some options talk to the instrument, and these will
|
|
* error if it hasn't been initialised.
|
|
*/
|
|
static inst_code
|
|
dtp92_get_set_opt(inst *pp, inst_opt_type m, ...) {
|
|
dtp92 *p = (dtp92 *)pp;
|
|
char buf[MAX_MES_SIZE];
|
|
inst_code ev = inst_ok;
|
|
|
|
/* Record the trigger mode */
|
|
if (m == inst_opt_trig_prog
|
|
|| m == inst_opt_trig_user) {
|
|
p->trig = m;
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Use default implementation of other inst_opt_type's */
|
|
{
|
|
inst_code rv;
|
|
va_list args;
|
|
|
|
va_start(args, m);
|
|
rv = inst_get_set_opt_def(pp, m, args);
|
|
va_end(args);
|
|
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
/* Constructor */
|
|
extern dtp92 *new_dtp92(icoms *icom, instType dtype) {
|
|
dtp92 *p;
|
|
if ((p = (dtp92 *)calloc(sizeof(dtp92),1)) == NULL) {
|
|
a1loge(icom->log, 1, "new_dtp92: malloc failed!\n");
|
|
return NULL;
|
|
}
|
|
|
|
p->log = new_a1log_d(icom->log);
|
|
|
|
p->init_coms = dtp92_init_coms;
|
|
p->init_inst = dtp92_init_inst;
|
|
p->capabilities = dtp92_capabilities;
|
|
p->check_mode = dtp92_check_mode;
|
|
p->set_mode = dtp92_set_mode;
|
|
p->get_disptypesel = dtp92_get_disptypesel;
|
|
p->set_disptype = dtp92_set_disptype;
|
|
p->get_disptechi = dtp92_get_disptechi;
|
|
p->get_set_opt = dtp92_get_set_opt;
|
|
p->read_sample = dtp92_read_sample;
|
|
p->read_refrate = dtp92_read_refrate;
|
|
p->get_n_a_cals = dtp92_get_n_a_cals;
|
|
p->calibrate = dtp92_calibrate;
|
|
p->col_cor_mat = dtp92_col_cor_mat;
|
|
p->get_refr_rate = dtp92_get_refr_rate;
|
|
p->interp_error = dtp92_interp_error;
|
|
p->del = dtp92_del;
|
|
|
|
p->icom = icom;
|
|
p->dtype = dtype;
|
|
|
|
icmSetUnity3x3(p->ccmat); /* Set the colorimeter correction matrix to do nothing */
|
|
set_base_disptype_list(p);
|
|
p->dtech = disptech_unknown;
|
|
|
|
return p;
|
|
}
|
|
|