2702 lines
75 KiB
C
2702 lines
75 KiB
C
|
|
/*
|
|
* Argyll Color Management System
|
|
*
|
|
* Gretag i1Display 1/2/Smile related functions
|
|
*
|
|
* Author: Graeme W. Gill
|
|
* Date: 18/10/2006
|
|
*
|
|
* Copyright 2006 - 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.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <stdarg.h>
|
|
#include <math.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 "i1disp.h"
|
|
|
|
static inst_code i1disp_interp_code(inst *pp, int ec);
|
|
static inst_code i1disp_do_fcal_setit(i1disp *p);
|
|
static inst_code i1disp_check_unlock(i1disp *p);
|
|
|
|
#define MAX_MES_SIZE 500 /* Maximum normal message reply size */
|
|
#define MAX_RD_SIZE 5000 /* Maximum reading messagle reply size */
|
|
|
|
#define CALFACTOR 3.428 /* Emissive magic calibration factor */
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/* Implementation */
|
|
|
|
/* Interpret an icoms error into a I1DISP error */
|
|
static int icoms2i1disp_err(int se) {
|
|
if (se != ICOM_OK)
|
|
return I1DISP_COMS_FAIL;
|
|
return I1DISP_OK;
|
|
}
|
|
|
|
/* i1Display command codes - number:X is argument count:return type */
|
|
/* B = byte (8bit), S = short (16bit), W = word (32bit), */
|
|
/* A = string (5 bytes total max) , - = none */
|
|
typedef enum {
|
|
i1d_status = 0x00, /* -:A Get status string */
|
|
i1d_rd_red = 0x01, /* -:W Read the red channel clk count (and trig ?) */
|
|
i1d_rd_green = 0x02, /* -:W Read the green channel clk count */
|
|
i1d_rd_blue = 0x03, /* -:W Read the blue channel clk count */
|
|
i1d_getmeas_p = 0x04, /* -:W Read the measure refresh period */
|
|
i1d_setintgt = 0x05, /* W:- Set the integration time */
|
|
i1d_getintgt = 0x06, /* -:W Get the integration time */
|
|
i1d_wrreg = 0x07, /* BB:- Write a register value */
|
|
i1d_rdreg = 0x08, /* B:B Read a register value */
|
|
i1d_getmeas_p2 = 0x09, /* -:W Read the measure refresh period (finer ?) */
|
|
i1d_m_red_p = 0x0a, /* B:W Measure the red period for given edge count */
|
|
i1d_m_green_p = 0x0b, /* B:W Measure the green period for given edge count */
|
|
i1d_m_blue_p = 0x0c, /* B:W Measure the blue period for given edge count */
|
|
i1d_m_rgb_p = 0x0d, /* BBB:W Measure the RGB period for given edge count */
|
|
i1d_unlock = 0x0e, /* BBBB:- Unlock the interface */
|
|
|
|
i1d_m_red_p2 = 0x10, /* S:W Measure the red period (16 bit) */
|
|
i1d_m_green_p2 = 0x11, /* S:W Measure the green period (16 bit) */
|
|
i1d_m_blue_p2 = 0x12, /* S:W Measure the blue period (16 bit) */
|
|
/* S = edge count */
|
|
|
|
i1d_m_red_2 = 0x13, /* B:W Measure the red channel (16 bit) */
|
|
/* B = sync mode, typically 1 */
|
|
|
|
i1d_setmedges2 = 0x14, /* SB:- Set number of edges used for measurment 16 bit */
|
|
/* B = channel */
|
|
i1d_getmedges2 = 0x15, /* B:S Get number of edges used for measurment 16 bit */
|
|
/* B = channel */
|
|
|
|
i1d_m_rgb_edge_2 = 0x16, /* -:W Measure RGB Edge (16 bit) */
|
|
|
|
/* Different meanings for different devices ? */
|
|
i1d_set_pll_p = 0x11, /* SS:- Set PLL period */
|
|
i1d_get_pll_p = 0x12, /* -:W Get PLL period */
|
|
|
|
i1d_m_rgb_edge_3 = 0x10, /* BBBB:W Measure RGB Edge & return red. */
|
|
/* BBBB = edge counts ??? */
|
|
i1d_g_green_3 = 0x11, /* -:W Get green data */
|
|
i1d_g_blue_3 = 0x12, /* -:W Get blue data */
|
|
|
|
i1d_wrxreg = 0x13, /* SB:- Write an extra register value */
|
|
i1d_rdxreg = 0x14, /* S:B Read an extra register value */
|
|
/* The address range overlapps i1d_rdreg */
|
|
/* Smile */
|
|
// i1d_xxxxxxx = 0x18, /* XXX:X Unknown */
|
|
i1d_rdexreg = 0x19 /* BS:BBBB Read an extended register value */
|
|
|
|
} i1DispCC;
|
|
|
|
/* Do a command/response exchange with the i1disp. */
|
|
/* Return the error code */
|
|
/* The i1 display uses a rather convoluted means of communication (historical?). */
|
|
/* Messages from the host are conveyed one byte per USB control message, */
|
|
/* with the byte conveyed in bRequest, with each such control message reading */
|
|
/* 8 bytes of data from the device. The last 8 bytes read in an overall */
|
|
/* message contains up to 5 response bytes, the last always being nul (?). */
|
|
/* Muti-byte quantities are transmitted in big-endian order. */
|
|
/* (Instructions 1,2,3,8 & 9 seem to be retried up to 5 times. We're */
|
|
/* not doing this, as it may just be historical) */
|
|
static inst_code
|
|
i1disp_command_1(
|
|
i1disp *p, /* i1display object */
|
|
i1DispCC cc, /* Command code */
|
|
unsigned char *in, int insize, /* Parameter to send */
|
|
unsigned char *out, int bsize, int *rsize, /* Parameter returned */
|
|
double to /* Timeout in seconds */
|
|
) {
|
|
int requesttype; /* 8 bit request type (USB bmRequestType) */
|
|
int request; /* 8 bit request code (USB bRequest) */
|
|
int value; /* 16 bit value (USB wValue, sent little endian) */
|
|
int index; /* 16 bit index (USB wIndex, sent little endian) */
|
|
int rwsize; /* 16 bit data size (USB wLength, send little endian) */
|
|
int rcc = 0; /* Return cc code from instruction */
|
|
int i, tsize;
|
|
unsigned char buf[8]; /* 8 bytes to read */
|
|
int se, ua = 0, rv = inst_ok;
|
|
|
|
tsize = insize + 2;
|
|
*rsize = 0;
|
|
|
|
a1logd(p->log, 4, "i1disp: Sending cmd %02x args '%s'\n",cc, icoms_tohex(in, insize));
|
|
|
|
/* For each byte to be sent */
|
|
for (i = 0; i < tsize; i++) {
|
|
unsigned int smsec;
|
|
|
|
/* Control message to read 8 bytes */
|
|
requesttype = IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_ENDPOINT;
|
|
if (i == 0) /* Count */
|
|
request = insize + 1;
|
|
else if (i == 1) /* Command */
|
|
request = (int)cc;
|
|
else /* Data */
|
|
request = (int)in[i-2];
|
|
value = i; /* Incrementing count */
|
|
index = (tsize - i - 1); /* Decrementing count */
|
|
rwsize = 8;
|
|
|
|
smsec = msec_time();
|
|
if ((se = p->icom->usb_control(p->icom, requesttype, request, value, index,
|
|
buf, rwsize, NULL, to)) != 0) {
|
|
a1logd(p->log, 1, "i1disp: Message send failed with ICOM err 0x%x\n",se);
|
|
p->last_com_err = se;
|
|
return i1disp_interp_code((inst *)p, I1DISP_COMS_FAIL);
|
|
}
|
|
|
|
/* We could check the return data. This seems to be what we sent, */
|
|
/* unless it's the last exchange in the message sequence. */
|
|
/* I don't currently know how or if the device signals an error. */
|
|
|
|
/* If this is the last exchange, copy return value out */
|
|
if (i == (tsize-1)) {
|
|
*rsize = buf[1];
|
|
if (*rsize > bsize)
|
|
*rsize = bsize;
|
|
if (*rsize > 5)
|
|
*rsize = 5;
|
|
memmove(out, buf + 3, *rsize);
|
|
|
|
/* buf[2] is usually the cc, except for i1d_unlock. */
|
|
/* If it is not the cc, this may indicate that the command */
|
|
/* should be retried up to a total of 5 times, before */
|
|
/* assuming it has succeeded. */
|
|
rcc = buf[2] & 0xff;
|
|
}
|
|
}
|
|
rv = i1disp_interp_code((inst *)p, icoms2i1disp_err(ua));
|
|
|
|
if (rv == inst_ok && rcc != cc)
|
|
rv = i1disp_interp_code((inst *)p, I1DISP_NOT_READY);
|
|
|
|
/* Instrument returns "LOCK" to any instruction if it is locked */
|
|
if (rv == inst_ok && *rsize == 5 && strncmp((char *)out,"LOCK",4) == 0) {
|
|
rv = i1disp_interp_code((inst *)p, I1DISP_LOCKED);
|
|
}
|
|
|
|
a1logd(p->log, 4, "i1disp: response '%s' ICOM err 0x%x\n",icoms_tohex(out, *rsize),ua);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* Do a command/response exchange with the i1disp, taking care of */
|
|
/* a LOCK error */
|
|
static inst_code
|
|
i1disp_command(
|
|
i1disp *p, /* i1display object */
|
|
i1DispCC cc, /* Command code */
|
|
unsigned char *in, int insize, /* Parameter to send */
|
|
unsigned char *out, int bsize, int *rsize, /* Parameter returned */
|
|
double to /* Timeout in seconds */
|
|
) {
|
|
inst_code rv;
|
|
|
|
if ((rv = i1disp_command_1(p, cc, in, insize, out, bsize, rsize, to)) == inst_ok)
|
|
return rv;
|
|
|
|
/* Unlock and try again */
|
|
if ((rv & inst_imask) == I1DISP_LOCKED) {
|
|
if ((rv = i1disp_check_unlock(p)) != inst_ok)
|
|
return rv;
|
|
rv = i1disp_command_1(p, cc, in, insize, out, bsize, rsize, to);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/* Take an int, and convert it into a byte buffer */
|
|
static void int2buf(unsigned char *buf, int inv) {
|
|
buf[0] = (inv >> 24) & 0xff;
|
|
buf[1] = (inv >> 16) & 0xff;
|
|
buf[2] = (inv >> 8) & 0xff;
|
|
buf[3] = (inv >> 0) & 0xff;
|
|
}
|
|
|
|
/* Take a short, and convert it into a byte buffer */
|
|
static void short2buf(unsigned char *buf, int inv) {
|
|
buf[0] = (inv >> 8) & 0xff;
|
|
buf[1] = (inv >> 0) & 0xff;
|
|
}
|
|
|
|
/* Take a word sized return buffer, and convert it to an int */
|
|
static int buf2int(unsigned char *buf) {
|
|
int val;
|
|
val = (signed char)buf[0];
|
|
val = ((val << 8) + (0xff & buf[1]));
|
|
val = ((val << 8) + (0xff & buf[2]));
|
|
val = ((val << 8) + (0xff & buf[3]));
|
|
return val;
|
|
}
|
|
|
|
/* Read a byte from a register */
|
|
static inst_code
|
|
i1disp_rdreg_byte(
|
|
i1disp *p, /* Object */
|
|
int *outp, /* Where to write value */
|
|
int addr /* Register Address, 0 - 127, 159 */
|
|
) {
|
|
unsigned char c, buf[16];
|
|
int rsize;
|
|
inst_code ev;
|
|
|
|
if (p->btype == 0) {
|
|
if (addr < 0 || addr > 127)
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_REG_ADDRESS);
|
|
} else {
|
|
if (addr < 0 || addr > 159)
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_REG_ADDRESS);
|
|
}
|
|
c = (unsigned char)addr;
|
|
|
|
/* Read a byte */
|
|
if ((ev = i1disp_command(p, i1d_rdreg, &c, 1,
|
|
buf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
|
|
if (rsize != 3)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
|
|
if (buf[0] != c)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_VAL);
|
|
|
|
*outp = (int)buf[1];
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Read a short from a register */
|
|
static inst_code
|
|
i1disp_rdreg_short(
|
|
i1disp *p, /* Object */
|
|
int *outp, /* Where to write value */
|
|
int addr /* Register Address, 0 - 126 */
|
|
) {
|
|
inst_code ev;
|
|
int v, val;
|
|
|
|
if ((ev = i1disp_rdreg_byte(p, &v, addr)) != inst_ok)
|
|
return ev;
|
|
val = v;
|
|
|
|
if ((ev = i1disp_rdreg_byte(p, &v, addr+1)) != inst_ok)
|
|
return ev;
|
|
val = ((val << 8) + (0xff & v));
|
|
|
|
*outp = val;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Read a word from a register */
|
|
static inst_code
|
|
i1disp_rdreg_word(
|
|
i1disp *p, /* Object */
|
|
int *outp, /* Where to write value */
|
|
int addr /* Register Address, 0 - 124 */
|
|
) {
|
|
inst_code ev;
|
|
int v, val;
|
|
|
|
if ((ev = i1disp_rdreg_byte(p, &v, addr)) != inst_ok)
|
|
return ev;
|
|
val = v;
|
|
|
|
if ((ev = i1disp_rdreg_byte(p, &v, addr+1)) != inst_ok)
|
|
return ev;
|
|
val = ((val << 8) + (0xff & v));
|
|
|
|
if ((ev = i1disp_rdreg_byte(p, &v, addr+2)) != inst_ok)
|
|
return ev;
|
|
val = ((val << 8) + (0xff & v));
|
|
|
|
if ((ev = i1disp_rdreg_byte(p, &v, addr+3)) != inst_ok)
|
|
return ev;
|
|
val = ((val << 8) + (0xff & v));
|
|
|
|
*outp = val;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
|
|
/* Read a float from a register */
|
|
/* Will return I1DISP_FLOAT_NOT_SET if the float value was 0xffffffff */
|
|
static inst_code
|
|
i1disp_rdreg_float(
|
|
i1disp *p, /* Object */
|
|
double *outp, /* Where to write value */
|
|
int addr /* Register Address, 0 - 124 */
|
|
) {
|
|
inst_code ev;
|
|
int val;
|
|
|
|
if ((ev = i1disp_rdreg_word(p, &val, addr)) != inst_ok)
|
|
return ev;
|
|
|
|
if (val == 0xffffffff) {
|
|
return I1DISP_FLOAT_NOT_SET;
|
|
}
|
|
|
|
*outp = IEEE754todouble((unsigned int)val);
|
|
return inst_ok;
|
|
}
|
|
|
|
|
|
/* Write a byte to a register */
|
|
static inst_code
|
|
i1disp_wrreg_byte(
|
|
i1disp *p, /* Object */
|
|
int inv, /* Input value */
|
|
int addr /* Register Address, 0 - 127 */
|
|
) {
|
|
int cval;
|
|
unsigned char ibuf[16], obuf[16];
|
|
int rsize;
|
|
inst_code ev;
|
|
|
|
inv &= 0xff;
|
|
|
|
/* Read it first, to see if it needs writing */
|
|
if ((ev = i1disp_rdreg_byte(p, &cval, addr) ) != inst_ok)
|
|
return ev;
|
|
|
|
if (cval == inv) /* No need to write */
|
|
return inst_ok;
|
|
|
|
ibuf[0] = (unsigned char)addr;
|
|
ibuf[1] = (unsigned char)inv;
|
|
|
|
/* Write a byte */
|
|
if ((ev = i1disp_command(p, i1d_wrreg, ibuf, 2,
|
|
obuf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
|
|
if (rsize != 2)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
|
|
if (obuf[0] != addr)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_VAL);
|
|
|
|
/* Check it got written properly */
|
|
if ((ev = i1disp_rdreg_byte(p, &cval, addr) ) != inst_ok)
|
|
return ev;
|
|
|
|
cval &= 0xff;
|
|
if (cval != inv) /* No need to write */
|
|
return i1disp_interp_code((inst *)p, I1DISP_EEPROM_WRITE_FAIL);
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Write a word to a register */
|
|
static inst_code
|
|
i1disp_wrreg_word(
|
|
i1disp *p, /* Object */
|
|
int inv, /* Where to write value */
|
|
int addr /* Register Address, 0 - 124 */
|
|
) {
|
|
inst_code ev;
|
|
int v;
|
|
|
|
v = (inv >> 24) & 0xff;
|
|
if ((ev = i1disp_wrreg_byte(p, v, addr) ) != inst_ok)
|
|
return ev;
|
|
|
|
v = (inv >> 16) & 0xff;
|
|
if ((ev = i1disp_wrreg_byte(p, v, addr+1) ) != inst_ok)
|
|
return ev;
|
|
|
|
v = (inv >> 8) & 0xff;
|
|
if ((ev = i1disp_wrreg_byte(p, v, addr+2) ) != inst_ok)
|
|
return ev;
|
|
|
|
v = (inv >> 0) & 0xff;
|
|
if ((ev = i1disp_wrreg_byte(p, v, addr+3) ) != inst_ok)
|
|
return ev;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Write a float to a register */
|
|
static inst_code
|
|
i1disp_wrreg_float(
|
|
i1disp *p, /* Object */
|
|
double inv, /* Value to write */
|
|
int addr /* Register Address, 0 - 124 */
|
|
) {
|
|
inst_code ev;
|
|
int val;
|
|
|
|
val = (int)doubletoIEEE754(inv);
|
|
|
|
if ((ev = i1disp_wrreg_word(p, val, addr)) != inst_ok)
|
|
return ev;
|
|
return inst_ok;
|
|
}
|
|
|
|
/* ColorMunki Smile: Read a byte from an extended register range */
|
|
static inst_code
|
|
i1disp_rdexreg_bytes(
|
|
i1disp *p, /* Object */
|
|
unsigned char *outp, /* Where to write values */
|
|
int addr, /* Register Address, 16 bit */
|
|
int len /* Number of bytes, 8 bits */
|
|
) {
|
|
unsigned char ibuf[16];
|
|
unsigned char obuf[16];
|
|
int ooff, rsize;
|
|
inst_code ev;
|
|
|
|
if (p->btype != 2) /* Only ColorMunki Smile ? */
|
|
return i1disp_interp_code((inst *)p, I1DISP_WRONG_DEVICE);
|
|
|
|
if (addr < 0 || addr > 0x0200)
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_REG_ADDRESS);
|
|
|
|
if (len < 0 || (addr + len) > 0x0200)
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_REG_ADDRESS);
|
|
|
|
for (ooff = 0; len > 0; ) {
|
|
int rlen = len;
|
|
if (rlen > 4)
|
|
rlen = 4;
|
|
|
|
/* Read up to 4 bytes at a time */
|
|
short2buf(ibuf+0, addr); /* Address to read from */
|
|
ibuf[2] = rlen; /* Number of bytes to read */
|
|
if ((ev = i1disp_command(p, i1d_rdexreg, ibuf, 3,
|
|
obuf, 16, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
|
|
if ((rsize < 4 && (rsize != (2 + rlen)))
|
|
|| (rsize >= 4 && (rsize != (1 + rlen))))
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
|
|
if (obuf[0] != rlen) /* Number of bytes returned */
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_VAL);
|
|
|
|
memcpy(outp + ooff, obuf + 1, rlen);
|
|
ooff += rlen;
|
|
addr += rlen;
|
|
len -= rlen;
|
|
}
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Read the integration time */
|
|
static inst_code
|
|
i1disp_rd_int_time(
|
|
i1disp *p, /* Object */
|
|
int *outp /* Where to write value */
|
|
) {
|
|
unsigned char buf[16];
|
|
int rsize;
|
|
inst_code ev;
|
|
|
|
if ((ev = i1disp_command(p, i1d_getintgt, NULL, 0,
|
|
buf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
|
|
*outp = buf2int(buf);
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Set the integration time */
|
|
static inst_code
|
|
i1disp_wr_int_time(
|
|
i1disp *p, /* Object */
|
|
int inv /* Value to write */
|
|
) {
|
|
unsigned char buf[16];
|
|
int rsize;
|
|
inst_code ev;
|
|
|
|
int2buf(buf, inv);
|
|
if ((ev = i1disp_command(p, i1d_setintgt, buf, 4,
|
|
buf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Read the refresh period */
|
|
static inst_code
|
|
i1disp_rd_meas_ref_period(
|
|
i1disp *p, /* Object */
|
|
int *outp /* Where to write value */
|
|
) {
|
|
unsigned char buf[16];
|
|
int rsize;
|
|
inst_code ev;
|
|
|
|
if ((ev = i1disp_command(p, i1d_getmeas_p, NULL, 0,
|
|
buf, 8, &rsize, 1.5)) != inst_ok)
|
|
return ev;
|
|
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
|
|
*outp = buf2int(buf);
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/* Take a raw RGB period measurement from the device for an i1d1. */
|
|
/* The time taken to count the given number of L2F clock edges (+ve & -ve) */
|
|
/* is measured in clk clk_freq counts. */
|
|
static inst_code
|
|
i1d1_period_measure(
|
|
i1disp *p, /* Object */
|
|
int edgec[3], /* Number of clock edges to count */
|
|
double rgb[3] /* Return the number of clk's */
|
|
) {
|
|
int i;
|
|
unsigned char ibuf[16];
|
|
unsigned char obuf[16];
|
|
int rsize;
|
|
inst_code ev;
|
|
|
|
/* Sanity check the number of edges */
|
|
for (i = 0; i < 3; i++) {
|
|
if (edgec[i] < 1 || edgec[i] > 255)
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_INT_THRESH);
|
|
ibuf[i] = (char)edgec[i];
|
|
}
|
|
|
|
/* Do the measurement, and return the Red value */
|
|
if ((ev = i1disp_command(p, i1d_m_rgb_p, ibuf, 3,
|
|
obuf, 8, &rsize, 60.0)) != inst_ok)
|
|
return ev;
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
rgb[0] = (double)buf2int(obuf);
|
|
|
|
/* Get the green value */
|
|
if ((ev = i1disp_command(p, i1d_rd_green, NULL, 0,
|
|
obuf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
rgb[1] = (double)buf2int(obuf);
|
|
|
|
/* Get the blue value */
|
|
if ((ev = i1disp_command(p, i1d_rd_blue, NULL, 0,
|
|
obuf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
rgb[2] = (double)buf2int(obuf);
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Take a cooked period measurement from the device for the i1d1 */
|
|
/* and return the frequency for each sensor. */
|
|
static inst_code
|
|
i1d1_take_measurement(
|
|
i1disp *p, /* Object */
|
|
int cal, /* nz if black is not to be subtracted */
|
|
double rgb[3] /* Return the rgb frequency values */
|
|
) {
|
|
int i;
|
|
int edgec[3]; /* Edge count 1..255 for each channel */
|
|
inst_code ev;
|
|
double edge_aim = p->clk_freq;
|
|
|
|
if (p->inited == 0)
|
|
return i1disp_interp_code((inst *)p, I1DISP_NOT_INITED);
|
|
|
|
if (p->btype != 0)
|
|
return i1disp_interp_code((inst *)p, I1DISP_WRONG_DEVICE);
|
|
|
|
/* Do an initial measurement with minimum edge count of 1 */
|
|
edgec[0] = edgec[1] = edgec[2] = 1;
|
|
|
|
if ((ev = i1d1_period_measure(p, edgec, rgb)) != inst_ok)
|
|
return ev;
|
|
|
|
a1logd(p->log, 3, "Initial RGB = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
|
|
|
|
/* Compute adjusted edge count, aiming */
|
|
/* for count values of clk_freq = 1 second (~1e6), */
|
|
/* or 2 seconds if an older instrument */
|
|
if (p->stype == i1d1_sencoreIV
|
|
|| p->stype == i1d1_sencoreIII)
|
|
edge_aim = 2.0 * p->clk_freq;;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
double ns;
|
|
if (edge_aim > ((255.0 - 0.5) * rgb[i]))
|
|
ns = 255.0;
|
|
else {
|
|
ns = floor(edge_aim/rgb[i]) + 0.5;
|
|
if (ns < 1.0)
|
|
ns = 1.0;
|
|
}
|
|
edgec[i] = (int)ns;
|
|
}
|
|
|
|
/* Only if we compute a different edge count, read again */
|
|
if (edgec[0] > 1 || edgec[1] > 1 || edgec[2] > 1) {
|
|
double rgb2[3]; /* 2nd RGB Readings */
|
|
|
|
if ((ev = i1d1_period_measure(p, edgec, rgb2)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Average readings if we repeated a measurement with the same edge count */
|
|
/* (Minor advantage, but may as well use it) */
|
|
for (i = 0; i < 3; i++) {
|
|
if (edgec[i] == 1)
|
|
rgb[i] = 0.5 * (rgb[i] + rgb2[i]);
|
|
else
|
|
rgb[i] = rgb2[i];
|
|
}
|
|
}
|
|
|
|
a1logd(p->log, 3, "scaled %d %d %d gives RGB = %f %f %f\n", edgec[0],edgec[1],edgec[2], rgb[0],rgb[1],rgb[2]);
|
|
|
|
/* Compute adjusted readings as a frequency. */
|
|
/* We need to divide the number of edges/2 by the period in seconds */
|
|
for (i = 0; i < 3; i++) {
|
|
rgb[i] = (p->rgbadj[i] * 0.5 * (double)edgec[i] * p->clk_freq)/rgb[i];
|
|
a1logd(p->log, 3, "%d sensor frequency = %f\n",i,rgb[i]);
|
|
|
|
/* If we're not calibrating the black */
|
|
if (cal == 0) {
|
|
rgb[i] -= p->reg103_F[i]; /* Subtract black level */
|
|
a1logd(p->log, 3, "%d after sub black = %f\n",i,rgb[i]);
|
|
|
|
if (rgb[i] < 0.0001)
|
|
rgb[i] = 0.0001;
|
|
a1logd(p->log, 3, "%d after limit min = %f\n",i,rgb[i]);
|
|
}
|
|
}
|
|
a1logd(p->log, 3, "Adjusted RGB = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* . . . . . . . . . . . . . . . . . . . . . . . . */
|
|
|
|
/* Take a fixed period frequency measurement from the device for an i1d2. */
|
|
/* This measures edge count over the set integration period. */
|
|
|
|
/* Take a raw measurement using a given integration time. */
|
|
/* The measureent is the count of (both) edges from the L2V */
|
|
/* over the integration time */
|
|
static inst_code
|
|
i1d2_freq_measure(
|
|
i1disp *p, /* Object */
|
|
double *inttime, /* Integration time in seconds. (Return clock rounded) */
|
|
double rgb[3] /* Return the RGB edge count values */
|
|
) {
|
|
unsigned char ibuf[16];
|
|
unsigned char obuf[16];
|
|
int intclks;
|
|
int rsize;
|
|
inst_code ev;
|
|
|
|
if (*inttime > 20.0) /* Hmm */
|
|
*inttime = 20.0;
|
|
|
|
intclks = (int)(*inttime * p->iclk_freq + 0.5);
|
|
*inttime = (double)intclks / p->iclk_freq;
|
|
if (intclks != p->int_clocks) {
|
|
if ((ev = i1disp_wr_int_time(p, intclks)) != inst_ok)
|
|
return ev;
|
|
if ((ev = i1disp_rd_int_time(p, &intclks) ) != inst_ok)
|
|
return ev;
|
|
p->int_clocks = intclks;
|
|
*inttime = (double)p->int_clocks/p->iclk_freq;
|
|
}
|
|
|
|
/* Do the measurement, and return the Red value */
|
|
ibuf[0] = 1; /* Sync mode 1 */
|
|
if ((ev = i1disp_command(p, i1d_m_red_2, ibuf, 1,
|
|
obuf, 8, &rsize, p->inttime + 1.0)) != inst_ok)
|
|
return ev;
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
rgb[0] = (double)buf2int(obuf);
|
|
|
|
/* Get the green value */
|
|
if ((ev = i1disp_command(p, i1d_rd_green, NULL, 0,
|
|
obuf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
rgb[1] = (double)buf2int(obuf);
|
|
|
|
/* Get the blue value */
|
|
if ((ev = i1disp_command(p, i1d_rd_blue, NULL, 0,
|
|
obuf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
rgb[2] = (double)buf2int(obuf);
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Take a raw measurement that returns the number of clocks */
|
|
/* between and initial edge and edgec[] subsequent edges of the L2F. */
|
|
/* Both edges are counted. */
|
|
static inst_code
|
|
i1d2_period_measure(
|
|
i1disp *p, /* Object */
|
|
int edgec[3], /* Measurement edge count for each channel */
|
|
double rgb[3] /* Return the RGB clock count values */
|
|
) {
|
|
int i;
|
|
unsigned char ibuf[16];
|
|
unsigned char obuf[16];
|
|
int rsize;
|
|
inst_code ev;
|
|
|
|
/* Set the edge count */
|
|
for (i = 0; i < 3; i++) {
|
|
short2buf(ibuf, edgec[i]); /* Edge count */
|
|
ibuf[2] = (unsigned char)i; /* Channel number */
|
|
if ((ev = i1disp_command(p, i1d_setmedges2, ibuf, 3,
|
|
obuf, 8, &rsize, 1.0)) != inst_ok)
|
|
return ev;
|
|
}
|
|
|
|
/* Do the measurement, and return the Red value */
|
|
if ((ev = i1disp_command(p, i1d_m_rgb_edge_2, ibuf, 0,
|
|
obuf, 8, &rsize, 120.0)) != inst_ok) {
|
|
return ev;
|
|
}
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
rgb[0] = (double)buf2int(obuf);
|
|
|
|
/* Get the green value */
|
|
if ((ev = i1disp_command(p, i1d_rd_green, NULL, 0,
|
|
obuf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
rgb[1] = (double)buf2int(obuf);
|
|
|
|
/* Get the blue value */
|
|
if ((ev = i1disp_command(p, i1d_rd_blue, NULL, 0,
|
|
obuf, 8, &rsize, 0.5)) != inst_ok)
|
|
return ev;
|
|
if (rsize != 5)
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_SIZE);
|
|
rgb[2] = (double)buf2int(obuf);
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
#define ME 1 /* One edge initially (should this be 2 ?) */
|
|
|
|
#ifndef NEVER
|
|
|
|
/* ### Quick and precise for low levels, but subject to long */
|
|
/* ### delays if the light level drops during measurement, and */
|
|
/* ### may be less accurate at low levels due to dark noise */
|
|
|
|
/* Take a cooked measurement from the device for the i1d2 */
|
|
static inst_code
|
|
i1d2_take_measurement(
|
|
i1disp *p, /* Object */
|
|
int refreshm, /* Measure in refresh mode flag */
|
|
double rgb[3] /* Return the rgb values */
|
|
) {
|
|
int i;
|
|
double rmeas[3]; /* Raw measurement */
|
|
int edgec[3] = {ME,ME,ME}; /* Measurement edge count for each channel */
|
|
int cdgec[3] = {ME,ME,ME}; /* CRT computed edge count for re-measure */
|
|
int mask = 0x0; /* Period measure mask */
|
|
inst_code ev;
|
|
|
|
if (p->inited == 0)
|
|
return i1disp_interp_code((inst *)p, I1DISP_NOT_INITED);
|
|
|
|
if (p->btype == 0)
|
|
return i1disp_interp_code((inst *)p, I1DISP_WRONG_DEVICE);
|
|
|
|
a1logd(p->log, 3, "i1d2_take_measurement called with refreshm = %d\n",refreshm);
|
|
|
|
/* Do refresh period measurement */
|
|
if (p->btype == 1 && refreshm && p->rrset == 0) {
|
|
if ((ev = i1disp_do_fcal_setit(p)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Quantize the sample time */
|
|
if (p->refperiod > 0.0) {
|
|
int n;
|
|
n = (int)ceil(p->dinttime/p->refperiod);
|
|
p->inttime = n * p->refperiod;
|
|
a1logd(p->log, 3, "i1disp: integration time quantize to %f secs\n",p->inttime);
|
|
} else {
|
|
p->inttime = p->dinttime;
|
|
a1logd(p->log, 3, "i1disp: integration time set to %f secs\n",p->inttime);
|
|
}
|
|
}
|
|
|
|
// Do a frequency measurement first, to reduces chances of a light change causing delays.
|
|
// This makes LCD same as CRT as far as quantization errors in high level readings.
|
|
// if (refreshm) {
|
|
/* Do an initial fixed integration time frequency measurement. */
|
|
a1logd(p->log, 3, "Doing fixed period frequency measurement over %f secs\n",p->inttime);
|
|
|
|
if ((ev = i1d2_freq_measure(p, &p->inttime, rmeas)) != inst_ok)
|
|
return ev;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
rgb[i] = p->rgbadj[i] * 0.5 * rmeas[i]/p->inttime;
|
|
|
|
a1logd(p->log, 3, "Got %f %f %f raw, %f %f %f Hz\n",
|
|
rmeas[0], rmeas[1], rmeas[2], rgb[0], rgb[1], rgb[2]);
|
|
|
|
/* Decide whether any channels need re-measuring, */
|
|
/* and computed cooked values. Threshold is a count of 75 */
|
|
for (i = 0; i < 3; i++) {
|
|
if (rmeas[i] <= 75.0) {
|
|
mask |= (1 << i); /* Yes */
|
|
if (rmeas[i] >= 10.0) { /* Compute target edges */
|
|
cdgec[i] = (int)(2.0 * rgb[i] * p->inttime + 0.5);
|
|
if (cdgec[i] > 2000)
|
|
cdgec[i] = 2000;
|
|
else if (cdgec[i] < ME)
|
|
cdgec[i] = ME;
|
|
}
|
|
}
|
|
}
|
|
// } else {
|
|
// mask = 0x7;
|
|
// }
|
|
a1logd(p->log, 3, "Re-measure mask = 0x%x\n",mask);
|
|
a1logd(p->log, 3, "cdgec = %d %d %d\n",cdgec[0],cdgec[1],cdgec[2]);
|
|
|
|
/* If any need re-measuring */
|
|
if (mask != 0) {
|
|
|
|
/* See if we need to compute a target edge count */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((mask & (1 << i)) && cdgec[i] == ME)
|
|
break;
|
|
}
|
|
|
|
/* Yes we do */
|
|
if (i < 3) {
|
|
|
|
a1logd(p->log, 3, "Doing 1st period pre-measurement mask 0x%x, edgec %d %d %d\n",
|
|
mask, edgec[0], edgec[1], edgec[2]);
|
|
|
|
/* Do an initial measurement of 1 edge to estimate the */
|
|
/* number of edges needed for the whole integration time. */
|
|
if ((ev = i1d2_period_measure(p, edgec, rmeas)) != inst_ok)
|
|
return ev;
|
|
|
|
a1logd(p->log, 3, "Got %f %f %f raw %f %f %f Hz\n",
|
|
rmeas[0], rmeas[1], rmeas[2],
|
|
(p->rgbadj[0] * 0.5 * (double)edgec[0] * p->clk_freq)/rmeas[0],
|
|
(p->rgbadj[1] * 0.5 * (double)edgec[1] * p->clk_freq)/rmeas[1],
|
|
(p->rgbadj[2] * 0.5 * (double)edgec[2] * p->clk_freq)/rmeas[2]);
|
|
|
|
/* Compute adjusted edge count for channels we're remeasuring, */
|
|
/* aiming for a values of int_clocks. */
|
|
for (i = 0; i < 3; i++) {
|
|
double ns;
|
|
if ((mask & (1 << i)) == 0)
|
|
continue;
|
|
if (p->int_clocks > ((2000.0 - 0.5) * rmeas[i]))
|
|
ns = 2000.0; /* Maximum edge count */
|
|
else {
|
|
ns = floor(p->inttime * edgec[i] * p->clk_freq/rmeas[i] + 0.5);
|
|
if (ns < ME) /* Minimum edge count */
|
|
ns = ME;
|
|
}
|
|
edgec[i] = (int)ns;
|
|
}
|
|
}
|
|
|
|
/* Use frequency computed edge count if available */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((mask & (1 << i)) == 0)
|
|
continue;
|
|
if (cdgec[i] != ME)
|
|
edgec[i] = cdgec[i];
|
|
}
|
|
|
|
/* If we compute a different edge count, read again */
|
|
if (edgec[0] > ME || edgec[1] > ME || edgec[2] > ME) {
|
|
double rmeas2[3]; /* 2nd RGB Readings */
|
|
|
|
a1logd(p->log, 3, "Doing period re-measurement mask 0x%x, edgec %d %d %d\n",
|
|
mask, edgec[0], edgec[1], edgec[2]);
|
|
|
|
if ((ev = i1d2_period_measure(p, edgec, rmeas2)) != inst_ok)
|
|
return ev;
|
|
|
|
a1logd(p->log, 3, "Got %f %f %f raw %f %f %f Hz\n",
|
|
rmeas[0], rmeas[1], rmeas[2],
|
|
(p->rgbadj[0] * 0.5 * (double)edgec[0] * p->clk_freq)/rmeas2[0],
|
|
(p->rgbadj[1] * 0.5 * (double)edgec[1] * p->clk_freq)/rmeas2[1],
|
|
(p->rgbadj[2] * 0.5 * (double)edgec[2] * p->clk_freq)/rmeas2[2]);
|
|
|
|
a1logd(p->log, 3, "Int period %f %f %f secs\n",
|
|
rmeas2[0]/p->clk_freq, rmeas2[1]/p->clk_freq, rmeas2[2]/p->clk_freq);
|
|
|
|
/* Average readings if we repeated a measurement with the same count */
|
|
/* (Minor advantage, but may as well use it) */
|
|
for (i = 0; i < 3; i++) {
|
|
if (edgec[i] == ME)
|
|
rmeas[i] = 0.5 * (rmeas[i] + rmeas2[i]);
|
|
else
|
|
rmeas[i] = rmeas2[i];
|
|
}
|
|
}
|
|
|
|
/* Compute adjusted readings, ovewritting initial cooked values */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((mask & (1 << i)) == 0)
|
|
continue;
|
|
rgb[i] = (p->rgbadj[i] * 0.5 * (double)edgec[i] * p->clk_freq)/rmeas[i];
|
|
a1logd(p->log, 3, "%d after scale = %f\n",i,rgb[i]);
|
|
|
|
rgb[i] -= p->reg103_F[i]; /* Subtract black level */
|
|
a1logd(p->log, 3, "%d after sub black = %f\n",i,rgb[i]);
|
|
|
|
if (rgb[i] < 0.0001)
|
|
rgb[i] = 0.0001;
|
|
a1logd(p->log, 3, "%d after limit min = %f\n",i,rgb[i]);
|
|
}
|
|
}
|
|
|
|
a1logd(p->log, 3, "Cooked RGB Hz = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
#else
|
|
/* Less precise (worse quatization errors), but more robust */
|
|
/* against excessive delays if the light level drops during measurement. */
|
|
/* Limits period measurement to an edge count < 35, but that can */
|
|
/* still take a long time in the dark. */
|
|
|
|
/* Take a cooked measurement from the device for the i1d2 */
|
|
static inst_code
|
|
i1d2_take_measurement(
|
|
i1disp *p, /* Object */
|
|
int refreshm, /* Measure in crt mode flag */
|
|
double rgb[3] /* Return the rgb values */
|
|
) {
|
|
int i;
|
|
double rmeas[3]; /* Raw measurement */
|
|
int edgec[3] = {ME,ME,ME}; /* Measurement edge count for each channel */
|
|
int cdgec[3] = {ME,ME,ME}; /* CRT computed edge count for re-measure */
|
|
int fmask = 0x0; /* Freq re-measure mask */
|
|
int mask = 0x0; /* Period measure mask */
|
|
inst_code ev;
|
|
|
|
if (p->inited == 0)
|
|
return i1disp_interp_code((inst *)p, I1DISP_NOT_INITED);
|
|
|
|
if (p->btype == 0)
|
|
return i1disp_interp_code((inst *)p, I1DISP_WRONG_DEVICE);
|
|
|
|
a1logd(p->log, 3, "i1d2_take_measurement called with refreshm = %d\n",refreshm);
|
|
|
|
/* Do refresh period measurement */
|
|
if (p->btype == 1 && refreshm && p->rrset == 0) {
|
|
if ((ev = i1disp_do_fcal_setit(p)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Quantize the sample time */
|
|
if (p->refperiod > 0.0) {
|
|
int n;
|
|
n = (int)ceil(p->dinttime/p->refperiod);
|
|
p->inttime = n * p->refperiod;
|
|
a1logd(p->log, 3, "i1disp: integration time quantize to %f secs\n",p->inttime);
|
|
} else {
|
|
p->inttime = p->dinttime;
|
|
a1logd(p->log, 3, "i1disp: ntegration time set to %f secs\n",p->inttime);
|
|
}
|
|
}
|
|
|
|
/* Do an initial fixed integration time frequency measurement. */
|
|
a1logd(p->log, 3, "Doing fixed period frequency measurement over %f secs\n",p->inttime)
|
|
|
|
if ((ev = i1d2_freq_measure(p, &p->inttime, rmeas)) != inst_ok)
|
|
return ev;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
rgb[i] = p->rgbadj[i] * 0.5 * rmeas[i]/p->inttime;
|
|
|
|
a1logd(p->log, 3, "Got %f %f %f raw, %f %f %f Hz\n",
|
|
rmeas[0], rmeas[1], rmeas[2], rgb[0], rgb[1], rgb[2]);
|
|
|
|
/* Decide whether any channels need re-measuring. */
|
|
/* Threshold is a count of 75, and switch to period */
|
|
/* measurement mode on count less than 37. */
|
|
fmask = 0x0;
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
if (rmeas[i] <= 75.0) {
|
|
fmask |= (1 << i); /* Yes, do another freq re-measure */
|
|
|
|
if (rmeas[i] <= 37.5) {
|
|
mask |= (1 << i); /* Do a period re-measure */
|
|
fmask = 0; /* Don't bother with freq re-measure */
|
|
}
|
|
if (rmeas[i] >= 10.0) { /* Compute target edges */
|
|
cdgec[i] = (int)(2.0 * rgb[i] * p->inttime + 0.5);
|
|
if (cdgec[i] > 2000)
|
|
cdgec[i] = 2000;
|
|
else if (cdgec[i] < ME)
|
|
cdgec[i] = ME;
|
|
}
|
|
}
|
|
}
|
|
a1logd(p->log, 3, "Freq mask = 0x%x, Period mask 0x%x\n",fmask, mask);
|
|
a1logd(p->log, 3, "cdgec = %d %d %d\n",cdgec[0],cdgec[1],cdgec[2]);
|
|
|
|
/* If there is a frequency re-measure */
|
|
/* ** This doesn't actually work. The quantization error */
|
|
/* for each read is 0.5, but averaging 2 reads it drops */
|
|
/* to 0.354, not the desired 0.25 that would have been */
|
|
/* acheived with double the integration time. ** */
|
|
if (fmask != 0) {
|
|
a1logd(p->log, 3, "Doing frequency re-measurement over %f secs\n",p->inttime);
|
|
if ((ev = i1d2_freq_measure(p, &p->inttime, rmeas)) != inst_ok)
|
|
return ev;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
rgb[i] += p->rgbadj[i] * 0.5 * rmeas[i]/p->inttime;
|
|
rgb[i] /= 2.0;
|
|
}
|
|
|
|
a1logd(p->log, 3, "Got %f %f %f raw, %f %f %f Avg. Hz\n",
|
|
rmeas[0], rmeas[1], rmeas[2], rgb[0], rgb[1], rgb[2]);
|
|
|
|
/* If there is a period re-measure */
|
|
} else if (mask != 0) {
|
|
|
|
/* See if we need to compute a target edge count */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((mask & (1 << i)) && cdgec[i] == ME)
|
|
break;
|
|
}
|
|
|
|
/* Yes we do */
|
|
if (i < 3) {
|
|
|
|
a1logd(p->log, 3, "Doing 1st period pre-measurement mask 0x%x, edgec %d %d %d\n",
|
|
mask, edgec[0], edgec[1], edgec[2]);
|
|
|
|
/* Do an initial measurement of 1 edge to estimate the */
|
|
/* number of edges needed for the whole integration time. */
|
|
if ((ev = i1d2_period_measure(p, edgec, rmeas)) != inst_ok)
|
|
return ev;
|
|
|
|
a1logd(p->log, 3, "Got %f %f %f raw %f %f %f Hz\n",
|
|
rmeas[0], rmeas[1], rmeas[2],
|
|
(p->rgbadj[0] * 0.5 * (double)edgec[0] * p->clk_freq)/rmeas[0],
|
|
(p->rgbadj[1] * 0.5 * (double)edgec[1] * p->clk_freq)/rmeas[1],
|
|
(p->rgbadj[2] * 0.5 * (double)edgec[2] * p->clk_freq)/rmeas[2]);
|
|
|
|
/* Compute adjusted edge count for channels we're remeasuring, */
|
|
/* aiming for a values of int_clocks. */
|
|
for (i = 0; i < 3; i++) {
|
|
double ns;
|
|
if ((mask & (1 << i)) == 0)
|
|
continue;
|
|
if (p->int_clocks > ((2000.0 - 0.5) * rmeas[i]))
|
|
ns = 2000.0; /* Maximum edge count */
|
|
else {
|
|
ns = floor(p->inttime * edgec[i] * p->clk_freq/rmeas[i] + 0.5);
|
|
if (ns < ME) /* Minimum edge count */
|
|
ns = ME;
|
|
}
|
|
edgec[i] = (int)ns;
|
|
|
|
/* Sanity check cdgec value, in case light level has changed */
|
|
if ((edgec[i] * 3) < (cdgec[i] * 2)) {
|
|
cdgec[i] = edgec[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Use frequency computed edge count if available */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((mask & (1 << i)) == 0)
|
|
continue;
|
|
if (cdgec[i] != ME)
|
|
edgec[i] = cdgec[i];
|
|
}
|
|
|
|
/* If we compute a different edge count, read again */
|
|
if (edgec[0] > ME || edgec[1] > ME || edgec[2] > ME) {
|
|
double rmeas2[3]; /* 2nd RGB Readings */
|
|
|
|
a1logd(p->log, 3, "Doing period re-measurement mask 0x%x, edgec %d %d %d\n",
|
|
mask, edgec[0], edgec[1], edgec[2]);
|
|
|
|
if ((ev = i1d2_period_measure(p, edgec, rmeas2)) != inst_ok)
|
|
return ev;
|
|
|
|
a1logd(p->log, 3, "Got %f %f %f raw %f %f %f Hz\n",
|
|
rmeas[0], rmeas[1], rmeas[2],
|
|
(p->rgbadj[0] * 0.5 * (double)edgec[0] * p->clk_freq)/rmeas2[0],
|
|
(p->rgbadj[1] * 0.5 * (double)edgec[1] * p->clk_freq)/rmeas2[1],
|
|
(p->rgbadj[2] * 0.5 * (double)edgec[2] * p->clk_freq)/rmeas2[2]);
|
|
|
|
a1logd(p->log, 3, "Int period %f %f %f secs\n",
|
|
rmeas2[0]/p->clk_freq, rmeas2[1]/p->clk_freq, rmeas2[2]/p->clk_freq);
|
|
|
|
/* Average readings if we repeated a measurement with the same count */
|
|
/* (Minor advantage, but may as well use it) */
|
|
for (i = 0; i < 3; i++) {
|
|
if (edgec[i] == ME)
|
|
rmeas[i] = 0.5 * (rmeas[i] + rmeas2[i]);
|
|
else
|
|
rmeas[i] = rmeas2[i];
|
|
}
|
|
}
|
|
|
|
/* Compute adjusted readings, ovewritting initial cooked values */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((mask & (1 << i)) == 0)
|
|
continue;
|
|
rgb[i] = (p->rgbadj[i] * 0.5 * (double)edgec[i] * p->clk_freq)/rmeas[i];
|
|
a1logd(p->log, 3, "%d after scale = %f\n",i,rgb[i]);
|
|
|
|
rgb[i] -= p->reg103_F[i]; /* Subtract black level */
|
|
a1logd(p->log, 3, "%d after sub black = %f\n",i,rgb[i]);
|
|
|
|
if (rgb[i] < 0.0001)
|
|
rgb[i] = 0.0001;
|
|
a1logd(p->log, 3, "%d after limit min = %f\n",i,rgb[i]);
|
|
}
|
|
}
|
|
|
|
a1logd(p->log, 3, "Cooked RGB Hz = %f %f %f\n",rgb[0],rgb[1],rgb[2]);
|
|
|
|
return inst_ok;
|
|
}
|
|
#endif
|
|
#undef ME
|
|
|
|
/* . . . . . . . . . . . . . . . . . . . . . . . . */
|
|
|
|
/* Take a XYZ measurement from the device */
|
|
static inst_code
|
|
i1disp_take_XYZ_measurement(
|
|
i1disp *p, /* Object */
|
|
double XYZ[3] /* Return the XYZ values */
|
|
) {
|
|
int i, j;
|
|
double rgb[3]; /* RGB Readings */
|
|
inst_code ev;
|
|
double *mat; /* Pointer to matrix */
|
|
|
|
if (p->btype == 0) { /* i1 disp 1 */
|
|
if ((ev = i1d1_take_measurement(p, 0, rgb)) != inst_ok)
|
|
return ev;
|
|
} else { /* i1 disp 2 or ColorMunki Smile */
|
|
|
|
if ((ev = i1d2_take_measurement(p, p->refrmode, rgb)) != inst_ok)
|
|
return ev;
|
|
}
|
|
|
|
/* Multiply by calibration matrix to arrive at XYZ */
|
|
if (IMODETST(p->mode, inst_mode_emis_ambient)) {
|
|
mat = p->amb; /* Ambient matrix */
|
|
} else {
|
|
if (p->icx)
|
|
mat = p->reg54_F; /* CRT/factory matrix/CCFL */
|
|
else
|
|
mat = p->reg4_F; /* LCD/user matrix/LED */
|
|
}
|
|
for (i = 0; i < 3; i++) {
|
|
XYZ[i] = 0.0;
|
|
for (j = 0; j < 3; j++) {
|
|
XYZ[i] += mat[i * 3 + j] * rgb[j];
|
|
}
|
|
|
|
|
|
/* Magic factors for other devices ?? */
|
|
if (p->stype == i1d1_sencoreIV)
|
|
XYZ[i] *= CALFACTOR; /* (Not sure about this factor!) */
|
|
else
|
|
XYZ[i] *= CALFACTOR; /* Times magic scale factor */
|
|
}
|
|
|
|
if (!IMODETST(p->mode, inst_mode_emis_ambient)) {
|
|
|
|
/* Apply the colorimeter correction matrix */
|
|
icmMulBy3x3(XYZ, p->ccmat, XYZ);
|
|
}
|
|
|
|
a1logd(p->log, 3, "returning XYZ = %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]);
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Do a black calibration (i1display 1) */
|
|
static inst_code
|
|
i1disp_do_black_cal(
|
|
i1disp *p /* Object */
|
|
) {
|
|
int i;
|
|
double rgb1[3], rgb2[3]; /* RGB Readings */
|
|
inst_code ev;
|
|
|
|
if (p->btype != 0)
|
|
return i1disp_interp_code((inst *)p, I1DISP_CANT_BLACK_CALIB);
|
|
|
|
/* Do a couple of readings without subtracting the black */
|
|
if ((ev = i1d1_take_measurement(p, 1, rgb1)) != inst_ok)
|
|
return ev;
|
|
if ((ev = i1d1_take_measurement(p, 1, rgb2)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Average the results */
|
|
for (i = 0; i < 3; i++) {
|
|
rgb1[i] = 0.5 * (rgb1[i] + rgb2[i]);
|
|
|
|
/* Since the readings are clamped to 0.0001, */
|
|
/* aim for a black level of 0.0001 */
|
|
rgb1[i] -= 0.0001;
|
|
}
|
|
|
|
a1logd(p->log, 3, "Black rgb = %f %f %f\n",rgb1[0],rgb1[1],rgb1[2]);
|
|
|
|
/* Save it to the EEPROM */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((ev = i1disp_wrreg_float(p, rgb1[i], 103 + 4 * i)) != inst_ok)
|
|
return ev;
|
|
p->reg103_F[i] = rgb1[i];
|
|
}
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Measure the refresh rate */
|
|
static inst_code
|
|
i1disp_read_refrate(
|
|
inst *pp,
|
|
double *ref_rate /* Return value, 0.0 if fails */
|
|
) {
|
|
i1disp *p = (i1disp *)pp;
|
|
int i;
|
|
inst_code ev;
|
|
double measp = 0.0;
|
|
|
|
a1logd(p->log, 3, "Frequency calibration called\n");
|
|
|
|
if (p->btype != 1)
|
|
return inst_unsupported;
|
|
|
|
if (ref_rate != NULL)
|
|
*ref_rate = 0.0;
|
|
|
|
/* Average a few refresh period readings */
|
|
for (i = 0; i < p->nmeasprds; i++) {
|
|
int mp;
|
|
|
|
/* Measures period in clocks */
|
|
if ((ev = i1disp_rd_meas_ref_period(p, &mp)) != inst_ok)
|
|
return ev;
|
|
if (mp == 0) {
|
|
measp = 0.0;
|
|
break; /* Too dark to measure */
|
|
}
|
|
measp += (double)mp;
|
|
}
|
|
|
|
/* Compute the measurement frequency */
|
|
if (measp != 0.0) {
|
|
double rrate = (p->clk_freq * (double)p->nmeasprds)/measp;
|
|
a1logd(p->log, 3, "Sample frequency measured = %f\n",rrate);
|
|
if (ref_rate != NULL)
|
|
*ref_rate = rrate;
|
|
return inst_ok;
|
|
} else {
|
|
a1logd(p->log, 3, "No discernible refresh frequency measured\n");
|
|
if (ref_rate != NULL)
|
|
*ref_rate = 0.0;
|
|
return inst_misread;
|
|
}
|
|
}
|
|
|
|
/* Do a refresh period cailbration */
|
|
static inst_code
|
|
i1disp_do_fcal_setit(
|
|
i1disp *p /* Object */
|
|
) {
|
|
int i;
|
|
inst_code ev;
|
|
|
|
a1logd(p->log, 3, "Frequency calibration called\n");
|
|
|
|
if (p->btype != 1)
|
|
return i1disp_interp_code((inst *)p, I1DISP_CANT_MEASP_CALIB);
|
|
|
|
if ((ev = i1disp_read_refrate((inst *)p, &p->refrate)) != inst_ok
|
|
&& ev != inst_misread)
|
|
return ev;
|
|
|
|
if (p->refrate != 0.0) {
|
|
p->refperiod = 1.0/p->refrate;
|
|
p->refrvalid = 1;
|
|
} else {
|
|
p->refrvalid = 0;
|
|
}
|
|
p->rrset = 1;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/* Check the device is responding, and unlock if necessary */
|
|
static inst_code
|
|
i1disp_check_unlock(
|
|
i1disp *p /* Object */
|
|
) {
|
|
unsigned char buf[16];
|
|
int rsize;
|
|
inst_code ev;
|
|
int i, vv;
|
|
double ver;
|
|
|
|
struct {
|
|
unsigned char code[4];
|
|
i1d2_stype stype;
|
|
} codes[] = {
|
|
{ { 'G','r','M','b' }, i1d2_norm }, /* "GrMb" i1 Display */
|
|
{ { 'L','i','t','e' }, i1d2_lite }, /* "Lite" i1 Display LT */
|
|
{ { 'M','u','n','k' }, i1d2_munki }, /* "Munk" ColorMunki Create */
|
|
{ { 'O','b','i','W' }, i1d2_hpdream }, /* "ObiW" HP DreamColor */
|
|
{ { 'O','b','i','w' }, i1d2_hpdream }, /* "Obiw" HP DreamColor */
|
|
{ { 'C','M','X','2' }, i1d1_calmanx2 }, /* "CMX2" Calman X2 */
|
|
{ { 0x24,0xb6,0xb5,0x13 }, i1d2_norm }, /* ColorMunki Smile */
|
|
{ { 'S','p','C','3' }, i1d2_norm }, /* SpectraCal C3 (Based on Smile) */
|
|
{ { 'R','G','B','c' }, i1d2_norm }, /* */
|
|
{ { 'C','E','C','5' }, i1d2_norm }, /* */
|
|
{ { 'C','M','C','5' }, i1d2_norm }, /* */
|
|
{ { 'C','M','G','5' }, i1d2_norm }, /* */
|
|
{ { 0x00,0x00,0x01,0x00 }, i1d2_norm }, /* */
|
|
{ { 0x09,0x0b,0x0c,0x0d }, i1d2_norm }, /* */
|
|
{ { 0x0e,0x0e,0x0e,0x0e }, i1d2_norm }, /* */
|
|
{ { 0x11,0x02,0xde,0xf0 }, i1d2_norm }, /* Barco Chroma 5 ? */
|
|
// { { 0xff,0xff,0xff,0xff }, i1d2_norm }, /* Chroma 5 isn't locked ? */
|
|
{ { ' ',' ',' ',' ' }, i1d2_unkn }
|
|
};
|
|
|
|
a1logd(p->log, 3, "i1disp: about to check response and unlock instrument if needed\n");
|
|
|
|
/* Get status */
|
|
if ((ev = i1disp_command_1(p, i1d_status, NULL, 0,
|
|
buf, 8, &rsize, 0.5)) != inst_ok && (ev & inst_imask) != I1DISP_LOCKED)
|
|
return ev; /* An error other than being locked */
|
|
|
|
/* Try and unlock it if it is locked */
|
|
if ((ev & inst_imask) == I1DISP_LOCKED) {
|
|
|
|
/* Try each code in turn */
|
|
for (i = 0; ;i++) {
|
|
if (codes[i].stype == i1d2_unkn) {
|
|
a1logd(p->log, 3, "Failed to find correct unlock code\n");
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNKNOWN_MODEL);
|
|
}
|
|
|
|
/* Try unlock code. Ignore I1DISP_NOT_READY status. */
|
|
if (((ev = i1disp_command_1(p, i1d_unlock, codes[i].code, 4,
|
|
buf, 8, &rsize, 0.5)) & inst_mask) != inst_ok
|
|
&& (ev & inst_imask) != I1DISP_LOCKED)
|
|
return ev; /* Some other sort of failure */
|
|
|
|
/* Get status */
|
|
if ((ev = i1disp_command_1(p, i1d_status, NULL, 0,
|
|
buf, 8, &rsize, 0.5)) != inst_ok
|
|
&& (ev & inst_imask) != I1DISP_LOCKED)
|
|
return ev; /* An error other than being locked */
|
|
|
|
if (ev == inst_ok) { /* Correct code */
|
|
p->stype = codes[i].stype;
|
|
a1logd(p->log, 3, "Unlocked with code '%c%c%c%c'\n",
|
|
codes[i].code[0], codes[i].code[1], codes[i].code[2], codes[i].code[3]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rsize != 5 || !isdigit(buf[0]) || buf[1] != '.'
|
|
|| !isdigit(buf[2]) || !isdigit(buf[3])) {
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_STATUS);
|
|
}
|
|
|
|
buf[4] = '\000';
|
|
ver = atof((char *)buf);
|
|
a1logd(p->log, 3, "Version string = %5.3f\n",ver);
|
|
|
|
/* Read register 0x79 for the model identifier */
|
|
if ((ev = i1disp_rdreg_byte(p, &vv, 121) ) != inst_ok) {
|
|
return ev;
|
|
}
|
|
vv &= 0xff;
|
|
|
|
a1logd(p->log, 3, "Version character = 0x%02x = '%c'\n",vv,vv);
|
|
|
|
/* Barco Chroma 5 with ver = 5.01 vv = '5' */
|
|
if (ver >= 4.0 && ver < 5.1 && vv == '5') {
|
|
p->btype = 0; /* Sequel Chroma 4 ?? */
|
|
p->stype = i1d1_chroma4; /* Treat like an Eye-One Display 1 */
|
|
|
|
/* Sequel Chroma 4 with vv == 0xff ???? */
|
|
/* Sencore ColorPro III with ver = 5.01 and vv = 0xff */
|
|
} else if (ver >= 4.0 && ver < 5.1 && vv == 0xff) {
|
|
p->btype = 0; /* Eye-One Display 1 */
|
|
p->stype = i1d1_sencoreIII;
|
|
|
|
/* Sencore ColorPro IV with ver = 5.01 and vv = 'L' */
|
|
} else if (ver >= 4.0 && ver < 5.1 && vv == 'L') {
|
|
p->btype = 0; /* Eye-One Display 1 */
|
|
p->stype = i1d1_sencoreIV; /* Treat like an Eye-One Display 1 */
|
|
|
|
/* Sencore ColorPro V with ver = 5.01 and vv = 'B' */
|
|
} else if (ver >= 4.0 && ver < 5.1 && vv == 'B') {
|
|
p->btype = 0; /* Eye-One Display 1 */
|
|
p->stype = i1d1_sencoreV; /* Treat like an Eye-One Display 1 */
|
|
|
|
} else if (ver >= 5.1 && ver <= 5.3 && vv == 'L') {
|
|
p->btype = 0; /* Eye-One Display 1 */
|
|
|
|
} else if (ver >= 6.0 && ver <= 6.29 && vv == 'L') {
|
|
p->btype = 1; /* Eye-One Display 2 */
|
|
|
|
} else if (ver >= 6.0 && ver <= 6.29
|
|
&& (vv = 0xff || vv == 'M')) { // Faulty Smile's have vv = 0xff
|
|
/* ColorMunki Create ? */
|
|
/* ColorMunki Smile */
|
|
if (p->btype == 0) /* Not sure if this is set by Create */
|
|
p->btype = 1;
|
|
|
|
} else {
|
|
/* Reject any version or model we don't know about */
|
|
a1logd(p->log, 1, "Version string = %5.3f\nID character = 0x%02x = '%c'\n",ver,vv,vv);
|
|
return i1disp_interp_code((inst *)p, I1DISP_UNKNOWN_VERS_ID);
|
|
}
|
|
|
|
a1logd(p->log, 2, "i1disp: instrument is responding, unlocked, and right type\n");
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Read all the relevant register values */
|
|
static inst_code
|
|
i1disp_read_all_regs(
|
|
i1disp *p /* Object */
|
|
) {
|
|
inst_code ev;
|
|
int i;
|
|
|
|
a1logd(p->log, 3, "i1disp: about to read all the registers\n");
|
|
|
|
/* Serial number */
|
|
if ((ev = i1disp_rdreg_word(p, &p->reg0_W, 0) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "serial number = %d\n",p->reg0_W);
|
|
sprintf(p->serno, "%u",p->reg0_W);
|
|
|
|
/* LCD/user calibration values */
|
|
for (i = 0; i < 9; i++) {
|
|
if ((ev = i1disp_rdreg_float(p, &p->reg4_F[i], 4 + 4 * i) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "LCD/user cal[%d] = %f\n",i,p->reg4_F[i]);
|
|
}
|
|
/* LCD/user calibration time */
|
|
if ((ev = i1disp_rdreg_word(p, &p->reg50_W, 50) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "LCD/user calibration time = 0x%x = %s\n",p->reg50_W, ctime_32(&p->reg50_W));
|
|
|
|
/* LCD/user calibration flag */
|
|
if ((ev = i1disp_rdreg_short(p, &p->reg126_S, 126) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "User cal flag = 0x%x\n",p->reg126_S);
|
|
|
|
|
|
/* CRT/factory calibration values */
|
|
for (i = 0; i < 9; i++) {
|
|
if ((ev = i1disp_rdreg_float(p, &p->reg54_F[i], 54 + 4 * i) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "CRT/factory cal[%d] = %f\n",i,p->reg54_F[i]);
|
|
}
|
|
/* CRT/factory calibration flag */
|
|
if ((ev = i1disp_rdreg_word(p, &p->reg90_W, 90) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "CRT/factory flag = 0x%x = %s\n",p->reg90_W, ctime_32(&p->reg90_W));
|
|
|
|
|
|
/* Integration clock period in nsec */
|
|
if ((ev = i1disp_rdreg_short(p, &p->reg40_S, 40) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "Reg40 = %d\n",p->reg40_S);
|
|
|
|
/* Calibration factor */
|
|
if ((ev = i1disp_rdreg_short(p, &p->reg42_S, 42) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "Reg42 = %d\n",p->reg42_S);
|
|
|
|
/* Calibration factors */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((ev = i1disp_rdreg_short(p, &p->reg44_S[i], 44 + 2 * i) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "reg44[%d] = %d\n",i,p->reg44_S[i]);
|
|
}
|
|
|
|
/* Measurement/master clock period */
|
|
if ((ev = i1disp_rdreg_float(p, &p->clk_prd, 94) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "Master clock Frequency = %e\n",1/p->clk_prd);
|
|
|
|
/* unknown */
|
|
if ((ev = i1disp_rdreg_word(p, &p->reg98_W, 98) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "reg98 = 0x%x = %s\n",p->reg98_W,ctime_32(&p->reg98_W));
|
|
|
|
/* unknown */
|
|
if ((ev = i1disp_rdreg_byte(p, &p->reg102_B, 102) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "reg102 = 0x%x\n",p->reg102_B);
|
|
|
|
/* Dark current calibration values */
|
|
/* Should we set to a default 0.0 if reg126_S < 0xd ?? */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((ev = i1disp_rdreg_float(p, &p->reg103_F[i], 103 + 4 * i)) != inst_ok) {
|
|
if ((ev & inst_imask) != I1DISP_FLOAT_NOT_SET)
|
|
return ev;
|
|
p->reg103_F[i] = 0.0;
|
|
}
|
|
a1logd(p->log, 3, "darkcal[%d] = %f\n",i,p->reg103_F[i]);
|
|
}
|
|
|
|
/* Unknown byte */
|
|
if ((ev = i1disp_rdreg_byte(p, &p->reg115_B, 115) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "Unknown 115 byte = 0x%x\n",p->reg115_B);
|
|
|
|
/* device ID byte */
|
|
if ((ev = i1disp_rdreg_byte(p, &p->reg121_B, 121) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "device type byte = 0x%x\n",p->reg121_B);
|
|
|
|
/* Unlock string */
|
|
for (i = 0; i < 4; i++) {
|
|
int vv;
|
|
if ((ev = i1disp_rdreg_byte(p, &vv, 122 + i) ) != inst_ok)
|
|
return ev;
|
|
p->reg122_B[i] = (char)vv;
|
|
}
|
|
p->reg122_B[i] = '\000';
|
|
a1logd(p->log, 3, "unlock string = '%s'\n",p->reg122_B);
|
|
|
|
p->serno[0] = '\000';
|
|
|
|
/* Read extra registers */
|
|
if (p->btype == 1) {
|
|
|
|
#ifdef NEVER /* Not used, so don't bother */
|
|
for (i = 0; i < 3; i++) {
|
|
if ((ev = i1disp_rdreg_float(p, &p->reg128_F[i], 128 + 4 * i) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "reg128_F[%d] = %f\n",i,p->reg128_F[i]);
|
|
}
|
|
#endif /* NEVER */
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if ((ev = i1disp_rdreg_float(p, &p->reg144_F[i], 144 + 4 * i) ) != inst_ok) {
|
|
if ((ev & inst_imask) != I1DISP_FLOAT_NOT_SET)
|
|
return ev;
|
|
p->reg144_F[i] = 1.0;
|
|
}
|
|
a1logd(p->log, 3, "Ambient scale factor [%d] = %f\n",i,p->reg144_F[i]);
|
|
}
|
|
|
|
/* Read the integration time */
|
|
if ((ev = i1disp_rd_int_time(p, &p->int_clocks) ) != inst_ok)
|
|
return ev;
|
|
a1logd(p->log, 3, "Integration time = %d\n",p->int_clocks);
|
|
|
|
/* ColorMunki Smile extra information */
|
|
/* (Colormunki Create too ????) */
|
|
} else if (p->btype == 2) {
|
|
int i, v;
|
|
|
|
/* Smile doesn't have ambient - reg144 seems to contain LCD cal type, */
|
|
/* ie. "CCFLWLED" */
|
|
|
|
/* Serial Number */
|
|
if ((ev = i1disp_rdexreg_bytes(p, (unsigned char *)p->serno, 0x104, 20)) != inst_ok)
|
|
return ev;
|
|
p->serno[19] = '\000';
|
|
}
|
|
a1logd(p->log, 2, "i1disp: all registers read OK\n");
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Compute factors that depend on the register values */
|
|
static inst_code
|
|
i1disp_compute_factors(
|
|
i1disp *p /* Object */
|
|
) {
|
|
int i;
|
|
|
|
/* Check that certain value are valid */
|
|
if (p->reg0_W == 0xffffffff)
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_SERIAL_NUMBER);
|
|
|
|
/* LCD calibration date valid ? */
|
|
if (p->reg50_W == 0xffffffff)
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_LCD_CALIBRATION);
|
|
|
|
/* The value stored in reg126_S ("user cal flag") seems hard to interpret. */
|
|
/* For the i1display 1&2, it has a value of 0x0d. */
|
|
/* Value 0x07 seems to be for a "user calibration" */
|
|
/* Values 3 & 6 seem to always "errors" as does a value */
|
|
/* < 7 in most circumstances. But the Heidelberg Viewmaker */
|
|
/* (from Sequel Imaging) and Lacie Blue Eye colorimeter seems to have a value of 2. */
|
|
/* The Barco sensor seems to have a value of 0x20 */
|
|
/* The ColorMunki Smile has a value of 0x21 */
|
|
if (p->reg126_S == 0xffffffff || (p->reg126_S < 7 && p->reg126_S != 2))
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_LCD_CALIBRATION);
|
|
|
|
/* Not quite sure about this, but we're assuming this */
|
|
/* is set to 2 or 0xd if reg4-36 hold the LCD calibration, */
|
|
/* and some other number if they are not set, or set */
|
|
/* to a custom user calibration. */
|
|
if (p->reg126_S != 0x02
|
|
&& p->reg126_S != 0x0d
|
|
&& p->reg126_S != 0x20
|
|
&& p->reg126_S != 0x21)
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_LCD_CALIBRATION);
|
|
|
|
if (p->reg90_W == 0xffffffff)
|
|
return i1disp_interp_code((inst *)p, I1DISP_BAD_CRT_CALIBRATION);
|
|
|
|
/* Compute ambient matrix for Lux */
|
|
for (i = 0; i < 9; i++)
|
|
p->amb[i] = 3.141592654 * p->reg144_F[i % 3] * 0.5 * (p->reg4_F[i] + p->reg54_F[i]);
|
|
|
|
/* Integration clock frequency */
|
|
p->iclk_freq = 1.0/(p->reg40_S * 1e-9);
|
|
|
|
/* Master/Measurement clock frequency */
|
|
p->clk_freq = 1.0/p->clk_prd;
|
|
|
|
/* RGB channel calibration factors */
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
/* Individual channel calibration factors, typically 1.0 */
|
|
p->rgbadj[i] = (double)p->reg44_S[i] * 100.0/(double)p->reg42_S;
|
|
a1logd(p->log, 3, "reg44+%dcalc2 = %f\n",i,p->rgbadj[i]);
|
|
}
|
|
|
|
/* Set some defaults */
|
|
p->nmeasprds = 5; /* Number of disp refresh period measurments to average */
|
|
/* in doing frequency calibration */
|
|
p->dinttime = 1.0; /* 1.0 second integration time default */
|
|
p->inttime = p->dinttime; /* Current integration time */
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
/* Establish communications with a I1DISP */
|
|
/* If it's a serial port, use the baud rate given, and timeout in to secs */
|
|
/* Return I1DISP_COMS_FAIL on failure to establish communications */
|
|
static inst_code
|
|
i1disp_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
|
|
i1disp *p = (i1disp *) pp;
|
|
unsigned char buf[16];
|
|
int rsize;
|
|
int se;
|
|
inst_code ev = inst_ok;
|
|
|
|
a1logd(p->log, 2, "i1disp: About to init coms\n");
|
|
|
|
if (p->icom->port_type(p->icom) != icomt_usb) {
|
|
a1logd(p->log, 1, "i1disp_init_coms: wrong communications type for device!\n");
|
|
return inst_coms_fail;
|
|
}
|
|
|
|
/* Set config, interface, write end point, read end point */
|
|
/* ("serial" end points aren't used - the i1display uses USB control messages) */
|
|
|
|
/* Set config, interface, write end point, read end point, read quanta */
|
|
if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, icomuf_none, 0, NULL)) != ICOM_OK) {
|
|
a1logd(p->log, 1, "i1disp_init_coms: set_usbe_port failed ICOM err 0x%x\n",se);
|
|
return i1disp_interp_code((inst *)p, icoms2i1disp_err(se));
|
|
}
|
|
|
|
/* Check instrument is responding */
|
|
if ((ev = i1disp_command_1(p, i1d_status, NULL, 0, buf, 8, &rsize, 0.5)) != inst_ok
|
|
&& (ev & inst_imask) != I1DISP_LOCKED) {
|
|
a1logd(p->log, 1, "i1disp_init_coms: failed with rv = 0x%x\n",ev);
|
|
return ev;
|
|
}
|
|
|
|
a1logd(p->log, 2, "i1disp: init coms OK\n");
|
|
|
|
p->gotcoms = 1;
|
|
return inst_ok;
|
|
}
|
|
|
|
static inst_code set_default_disp_type(i1disp *p);
|
|
|
|
/* Initialise the I1DISP */
|
|
/* return non-zero on an error, with dtp error code */
|
|
static inst_code
|
|
i1disp_init_inst(inst *pp) {
|
|
i1disp *p = (i1disp *)pp;
|
|
inst_code ev = inst_ok;
|
|
|
|
a1logd(p->log, 2, "i1disp_init_inst: called\n");
|
|
|
|
if (p->gotcoms == 0)
|
|
return i1disp_interp_code((inst *)p, I1DISP_NO_COMS); /* Must establish coms first */
|
|
|
|
/* Check instrument is responding, and right type */
|
|
if ((ev = i1disp_check_unlock(p)) != inst_ok)
|
|
return ev;
|
|
|
|
if (p->log->debug >= 3) {
|
|
|
|
/* Dump all the register space */
|
|
if (p->btype < 2) {
|
|
unsigned char buf[0x200];
|
|
int i, len;
|
|
|
|
len = 128;
|
|
if (p->btype != 0)
|
|
len = 160;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
int v;
|
|
if ((ev = i1disp_rdreg_byte(p, &v, i)) != inst_ok) {
|
|
return ev;
|
|
}
|
|
buf[i] = v;
|
|
}
|
|
adump_bytes(p->log, "dump:", buf, 0, len);
|
|
|
|
/* Dump ColorMunki Smile extended range */
|
|
/* Main difference is Ascii serial number + other minor unknown */
|
|
} else {
|
|
unsigned char buf[0x200];
|
|
|
|
|
|
if ((ev = i1disp_rdexreg_bytes(p, buf, 0, 0x200)) != inst_ok) {
|
|
return ev;
|
|
}
|
|
adump_bytes(p->log, "dump:", buf, 0, 0x200);
|
|
}
|
|
}
|
|
|
|
/* Read all the registers and store their contents */
|
|
if ((ev = i1disp_read_all_regs(p)) != inst_ok)
|
|
return ev;
|
|
|
|
if ((ev = i1disp_compute_factors(p)) != inst_ok)
|
|
return ev;
|
|
|
|
p->trig = inst_opt_trig_user;
|
|
|
|
/* Set a default calibration */
|
|
if ((ev = set_default_disp_type(p)) != inst_ok) {
|
|
return ev;
|
|
}
|
|
|
|
p->inited = 1;
|
|
a1logd(p->log, 2, "i1disp_init_inst: inited OK\n");
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
static char *i1disp_get_serial_no(inst *pp) {
|
|
i1disp *p = (i1disp *)pp;
|
|
|
|
if (!pp->gotcoms)
|
|
return "";
|
|
if (!pp->inited)
|
|
return "";
|
|
|
|
return p->serno;
|
|
}
|
|
|
|
/* Read a single sample */
|
|
/* Return the dtp error code */
|
|
static inst_code
|
|
i1disp_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 */
|
|
i1disp *p = (i1disp *)pp;
|
|
int user_trig = 0;
|
|
int rv = inst_protocol_error;
|
|
|
|
if (!p->gotcoms)
|
|
return inst_no_coms;
|
|
if (!p->inited)
|
|
return inst_no_init;
|
|
|
|
if (p->trig == inst_opt_trig_user) {
|
|
|
|
if (p->uicallback == NULL) {
|
|
a1logd(p->log, 1, "i1disp: 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 */
|
|
}
|
|
|
|
|
|
/* Read the XYZ value */
|
|
rv = i1disp_take_XYZ_measurement(p, val->XYZ);
|
|
|
|
if (rv != inst_ok)
|
|
return rv;
|
|
|
|
|
|
/* This may not change anything since instrument may clamp */
|
|
if (clamp)
|
|
icmClamp3(val->XYZ, val->XYZ);
|
|
val->loc[0] = '\000';
|
|
if (IMODETST(p->mode, inst_mode_emis_ambient))
|
|
val->mtype = inst_mrt_ambient;
|
|
else
|
|
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;
|
|
|
|
|
|
if (user_trig)
|
|
return inst_user_trig;
|
|
return rv;
|
|
}
|
|
|
|
static inst_code set_base_disp_type(i1disp *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 */
|
|
inst_code i1disp_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]
|
|
) {
|
|
i1disp *p = (i1disp *)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 i1disp_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
|
|
i1disp *p = (i1disp *)pp;
|
|
inst_cal_type n_cals = inst_calt_none;
|
|
inst_cal_type a_cals = inst_calt_none;
|
|
|
|
if (p->btype == 0) { /* Eye-One Display 1 */
|
|
a_cals |= inst_calt_emis_offset;
|
|
}
|
|
|
|
if (p->btype == 1 && p->refrmode != 0) {
|
|
if (p->rrset == 0)
|
|
n_cals |= inst_calt_ref_freq;
|
|
a_cals |= inst_calt_ref_freq;
|
|
}
|
|
|
|
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 us 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. */
|
|
inst_code i1disp_calibrate(
|
|
inst *pp,
|
|
inst_cal_type *calt, /* Calibration type to do/remaining */
|
|
inst_cal_cond *calc, /* Current condition/desired condition */
|
|
inst_calc_id_type *btype, /* Condition identifier type */
|
|
char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */
|
|
) {
|
|
i1disp *p = (i1disp *)pp;
|
|
inst_code ev;
|
|
inst_cal_type needed, available;
|
|
|
|
if (!p->gotcoms)
|
|
return inst_no_coms;
|
|
if (!p->inited)
|
|
return inst_no_init;
|
|
|
|
*btype = inst_calc_id_none;
|
|
id[0] = '\000';
|
|
|
|
if ((ev = i1disp_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,"i1disp_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;
|
|
}
|
|
|
|
/* Do the appropriate calibration */
|
|
if (p->btype == 0) { /* Eye-One Display 1 */
|
|
if (*calt & inst_calt_emis_offset) {
|
|
|
|
if ((*calc & inst_calc_cond_mask) != inst_calc_man_ref_dark) {
|
|
*calc = inst_calc_man_ref_dark;
|
|
return inst_cal_setup;
|
|
}
|
|
|
|
/* Do black offset calibration */
|
|
if ((ev = i1disp_do_black_cal(p)) != inst_ok)
|
|
return ev;
|
|
|
|
*calt &= ~inst_calt_emis_offset;
|
|
}
|
|
|
|
} else { /* Eye-One Display 2 */
|
|
if ((*calt & inst_calt_ref_freq) && p->refrmode != 0) {
|
|
|
|
if ((*calc & inst_calc_cond_mask) != inst_calc_emis_80pc) {
|
|
*calc = inst_calc_emis_80pc;
|
|
return inst_cal_setup;
|
|
}
|
|
|
|
/* Do CRT frequency calibration and set integration time */
|
|
if ((ev = i1disp_do_fcal_setit(p)) != inst_ok)
|
|
return ev;
|
|
|
|
/* Quantize the sample time */
|
|
if (p->refperiod > 0.0) {
|
|
int n;
|
|
n = (int)ceil(p->dinttime/p->refperiod);
|
|
p->inttime = n * p->refperiod;
|
|
a1logd(p->log, 3, "i1disp: integration time quantize to %f secs\n",p->inttime);
|
|
} else {
|
|
p->inttime = p->dinttime;
|
|
a1logd(p->log, 3, "i1disp: integration time set to %f secs\n",p->inttime);
|
|
}
|
|
|
|
*calt &= ~inst_calt_ref_freq;
|
|
}
|
|
}
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Return the last calibrated refresh rate in Hz. Returns: */
|
|
static inst_code i1disp_get_refr_rate(inst *pp,
|
|
double *ref_rate
|
|
) {
|
|
i1disp *p = (i1disp *)pp;
|
|
if (p->refrvalid) {
|
|
*ref_rate = p->refrate;
|
|
|
|
return inst_ok;
|
|
} else if (p->rrset) {
|
|
*ref_rate = 0.0;
|
|
return inst_misread;
|
|
}
|
|
return inst_needs_cal;
|
|
}
|
|
|
|
/* Set the calibrated refresh rate in Hz. */
|
|
/* Set refresh rate to 0.0 to mark it as invalid */
|
|
/* Rates outside the range 5.0 to 150.0 Hz will return an error */
|
|
static inst_code i1disp_set_refr_rate(inst *pp,
|
|
double ref_rate
|
|
) {
|
|
i1disp *p = (i1disp *)pp;
|
|
|
|
if (ref_rate != 0.0 && (ref_rate < 5.0 || ref_rate > 150.0))
|
|
return inst_bad_parameter;
|
|
|
|
p->refrate = ref_rate;
|
|
if (ref_rate == 0.0)
|
|
p->refrvalid = 0;
|
|
else {
|
|
p->refperiod = 1.0/p->refrate;
|
|
p->refrvalid = 1;
|
|
}
|
|
p->rrset = 1;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Error codes interpretation */
|
|
static char *
|
|
i1disp_interp_error(inst *pp, int ec) {
|
|
// i1disp *p = (i1disp *)pp;
|
|
ec &= inst_imask;
|
|
switch (ec) {
|
|
case I1DISP_INTERNAL_ERROR:
|
|
return "Internal software error";
|
|
case I1DISP_COMS_FAIL:
|
|
return "Communications failure";
|
|
case I1DISP_UNKNOWN_MODEL:
|
|
return "Not a i1 Display";
|
|
case I1DISP_DATA_PARSE_ERROR:
|
|
return "Data from i1 Display didn't parse as expected";
|
|
|
|
case I1DISP_OK:
|
|
return "No device error";
|
|
|
|
case I1DISP_FLOAT_NOT_SET:
|
|
return "Float value is not set in EEPROM";
|
|
case I1DISP_NOT_READY:
|
|
return "Command didn't return command code - not ready ?";
|
|
|
|
case I1DISP_BAD_SERIAL_NUMBER:
|
|
return "Serial number isn't set";
|
|
case I1DISP_BAD_LCD_CALIBRATION:
|
|
return "LCD calibration values aren't set";
|
|
case I1DISP_BAD_CRT_CALIBRATION:
|
|
return "CRT calibration values aren't set";
|
|
case I1DISP_EEPROM_WRITE_FAIL:
|
|
return "Write to EEPROM failed to verify";
|
|
|
|
case I1DISP_UNEXPECTED_RET_SIZE:
|
|
return "Message from instrument has unexpected size";
|
|
case I1DISP_UNEXPECTED_RET_VAL:
|
|
return "Message from instrument has unexpected value";
|
|
|
|
case I1DISP_BAD_STATUS:
|
|
return "Instrument status is unrecognised format";
|
|
case I1DISP_UNKNOWN_VERS_ID:
|
|
return "Instrument version number or ID byte not recognised";
|
|
|
|
/* Internal errors */
|
|
case I1DISP_BAD_REG_ADDRESS:
|
|
return "Out of range register address";
|
|
case I1DISP_BAD_INT_THRESH:
|
|
return "Out of range integration threshold";
|
|
case I1DISP_NO_COMS:
|
|
return "Communications hasn't been established";
|
|
case I1DISP_NOT_INITED:
|
|
return "Insrument hasn't been initialised";
|
|
case I1DISP_CANT_BLACK_CALIB:
|
|
return "Device doesn't support black calibration";
|
|
case I1DISP_CANT_MEASP_CALIB:
|
|
return "Device doesn't support measurment period calibration";
|
|
case I1DISP_WRONG_DEVICE:
|
|
return "Wrong type of device for called function";
|
|
default:
|
|
return "Unknown error code";
|
|
}
|
|
}
|
|
|
|
|
|
/* Convert a machine specific error code into an abstract dtp code */
|
|
static inst_code
|
|
i1disp_interp_code(inst *pp, int ec) {
|
|
// i1disp *p = (i1disp *)pp;
|
|
|
|
ec &= inst_imask;
|
|
switch (ec) {
|
|
|
|
case I1DISP_OK:
|
|
case I1DISP_FLOAT_NOT_SET: /* Internal indication */
|
|
case I1DISP_NOT_READY: /* Internal indication */
|
|
return inst_ok;
|
|
|
|
case I1DISP_INTERNAL_ERROR:
|
|
case I1DISP_BAD_REG_ADDRESS:
|
|
case I1DISP_BAD_INT_THRESH:
|
|
case I1DISP_NO_COMS:
|
|
case I1DISP_NOT_INITED:
|
|
case I1DISP_CANT_BLACK_CALIB:
|
|
case I1DISP_CANT_MEASP_CALIB:
|
|
case I1DISP_WRONG_DEVICE:
|
|
case I1DISP_LOCKED:
|
|
return inst_internal_error | ec;
|
|
|
|
case I1DISP_COMS_FAIL:
|
|
return inst_coms_fail | ec;
|
|
|
|
case I1DISP_UNKNOWN_MODEL:
|
|
case I1DISP_BAD_STATUS:
|
|
case I1DISP_UNKNOWN_VERS_ID:
|
|
return inst_unknown_model | ec;
|
|
|
|
case I1DISP_DATA_PARSE_ERROR:
|
|
case I1DISP_UNEXPECTED_RET_SIZE:
|
|
case I1DISP_UNEXPECTED_RET_VAL:
|
|
return inst_protocol_error | ec;
|
|
|
|
case I1DISP_BAD_SERIAL_NUMBER:
|
|
case I1DISP_BAD_LCD_CALIBRATION:
|
|
case I1DISP_BAD_CRT_CALIBRATION:
|
|
case I1DISP_EEPROM_WRITE_FAIL:
|
|
return inst_hardware_fail | ec;
|
|
|
|
/* return inst_misread | ec; */
|
|
/* return inst_needs_cal_2 | ec; */
|
|
}
|
|
return inst_other_error | ec;
|
|
}
|
|
|
|
/* Destroy ourselves */
|
|
static void
|
|
i1disp_del(inst *pp) {
|
|
i1disp *p = (i1disp *)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 capabilities */
|
|
static void i1disp_capabilities(inst *pp,
|
|
inst_mode *pcap1,
|
|
inst2_capability *pcap2,
|
|
inst3_capability *pcap3) {
|
|
i1disp *p = (i1disp *)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_disptype
|
|
| inst2_ccmx
|
|
;
|
|
|
|
/* i1D2 has refresh display & ambient capability */
|
|
/* but i1D1 & ColorMunki Smile don't */
|
|
if (p->btype == 1) {
|
|
cap1 |= inst_mode_emis_ambient
|
|
| inst_mode_emis_refresh_ovd
|
|
| inst_mode_emis_norefresh_ovd
|
|
;
|
|
|
|
cap2 |= inst2_get_refresh_rate
|
|
| inst2_set_refresh_rate
|
|
| 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 i1disp_check_mode(inst *pp, inst_mode m) {
|
|
i1disp *p = (i1disp *)pp;
|
|
inst_mode cap;
|
|
|
|
if (!p->gotcoms)
|
|
return inst_no_coms;
|
|
if (!p->inited)
|
|
return inst_no_init;
|
|
|
|
pp->capabilities(pp, &cap, NULL, NULL);
|
|
|
|
/* Simple filter for most modes */
|
|
if (m & ~cap)
|
|
return inst_unsupported;
|
|
|
|
/* Only display emission mode supported */
|
|
if (!IMODETST(m, inst_mode_emis_spot)
|
|
&& !(p->btype == 1 && IMODETST(m, inst_mode_emis_ambient))) {
|
|
return inst_unsupported;
|
|
}
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
/* Set device measurement mode */
|
|
static inst_code i1disp_set_mode(inst *pp, inst_mode m) {
|
|
i1disp *p = (i1disp *)pp;
|
|
inst_code ev;
|
|
|
|
if ((ev = i1disp_check_mode(pp, m)) != inst_ok)
|
|
return ev;
|
|
|
|
p->mode = m;
|
|
|
|
if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) /* Must test this first! */
|
|
p->refrmode = 0;
|
|
else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd))
|
|
p->refrmode = 1;
|
|
|
|
return inst_ok;
|
|
}
|
|
|
|
static inst_disptypesel i1disp_disptypesel[3] = {
|
|
{
|
|
inst_dtflags_default,
|
|
1,
|
|
"l",
|
|
"LCD display",
|
|
0,
|
|
disptech_lcd,
|
|
0
|
|
},
|
|
{
|
|
inst_dtflags_none, /* flags */
|
|
2, /* cbid */
|
|
"c", /* sel */
|
|
"CRT display", /* desc */
|
|
1, /* refr */
|
|
disptech_crt, /* disptype */
|
|
1 /* ix */
|
|
},
|
|
{
|
|
inst_dtflags_end,
|
|
0,
|
|
"",
|
|
"",
|
|
0,
|
|
disptech_none,
|
|
0
|
|
}
|
|
};
|
|
|
|
|
|
static inst_disptypesel smile_disptypesel[3] = {
|
|
{
|
|
inst_dtflags_default, /* flags */
|
|
1, /* cbid */
|
|
"fl", /* sel */
|
|
"LCD with CCFL backlight", /* desc */
|
|
0, /* refr */
|
|
disptech_lcd_ccfl, /* disptype */
|
|
1 /* ix */
|
|
},
|
|
{
|
|
inst_dtflags_none,
|
|
0,
|
|
"e",
|
|
"LCD with White LED backlight",
|
|
0,
|
|
disptech_lcd_wled,
|
|
0
|
|
},
|
|
{
|
|
inst_dtflags_end,
|
|
0,
|
|
"",
|
|
"",
|
|
0,
|
|
disptech_none,
|
|
0
|
|
}
|
|
};
|
|
|
|
static void set_base_disptype_list(i1disp *p) {
|
|
/* set the base display type list */
|
|
if (p->dtype == instSmile) {
|
|
p->_dtlist = smile_disptypesel;
|
|
} else {
|
|
p->_dtlist = i1disp_disptypesel;
|
|
}
|
|
}
|
|
|
|
/* Get mode and option details */
|
|
static inst_code i1disp_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 */
|
|
) {
|
|
i1disp *p = (i1disp *)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(i1disp *p, inst_disptypesel *dentry) {
|
|
int refrmode;
|
|
|
|
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 now */
|
|
|
|
} else { /* Native */
|
|
|
|
p->icx = dentry->ix;
|
|
p->dtech = dentry->dtech;
|
|
p->cbid = dentry->cbid;
|
|
p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */
|
|
icmSetUnity3x3(p->ccmat);
|
|
}
|
|
|
|
/* Implement any refresh mode change */
|
|
refrmode = dentry->refr;
|
|
|
|
if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */
|
|
refrmode = 0;
|
|
} else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) {
|
|
refrmode = 1;
|
|
}
|
|
|
|
if (p->refrmode != refrmode) {
|
|
p->rrset = 0; /* This is a hint we may have swapped displays */
|
|
p->refrvalid = 0;
|
|
}
|
|
p->refrmode = refrmode;
|
|
|
|
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 i1disp_set_disptype(inst *pp, int ix) {
|
|
i1disp *p = (i1disp *)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;
|
|
}
|
|
|
|
/* Get the disptech corresponding to the current */
|
|
/* Setup the default display type */
|
|
static inst_code set_default_disp_type(i1disp *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(i1disp *p, int cbid) {
|
|
inst_code ev;
|
|
int i;
|
|
|
|
if (cbid == 0) {
|
|
a1loge(p->log, 1, "i1disp 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,
|
|
i1disp_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 i1disp_get_disptechi(
|
|
inst *pp,
|
|
disptech *dtech,
|
|
int *refrmode,
|
|
int *cbid) {
|
|
i1disp *p = (i1disp *)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
|
|
*
|
|
* Since there is no interaction with the instrument,
|
|
* was assume that all of these can be done before initialisation.
|
|
*/
|
|
static inst_code
|
|
i1disp_get_set_opt(inst *pp, inst_opt_type m, ...) {
|
|
i1disp *p = (i1disp *)pp;
|
|
inst_code ev;
|
|
|
|
/* 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 i1disp *new_i1disp(icoms *icom, instType dtype) {
|
|
i1disp *p;
|
|
|
|
|
|
if ((p = (i1disp *)calloc(sizeof(i1disp),1)) == NULL) {
|
|
a1loge(icom->log, 1, "new_i1disp: malloc failed!\n");
|
|
return NULL;
|
|
}
|
|
|
|
p->log = new_a1log_d(icom->log);
|
|
|
|
p->init_coms = i1disp_init_coms;
|
|
p->init_inst = i1disp_init_inst;
|
|
p->get_serial_no = i1disp_get_serial_no;
|
|
p->capabilities = i1disp_capabilities;
|
|
p->check_mode = i1disp_check_mode;
|
|
p->set_mode = i1disp_set_mode;
|
|
p->get_disptechi = i1disp_get_disptechi;
|
|
p->get_disptypesel = i1disp_get_disptypesel;
|
|
p->set_disptype = i1disp_set_disptype;
|
|
p->get_set_opt = i1disp_get_set_opt;
|
|
p->read_sample = i1disp_read_sample;
|
|
p->read_refrate = i1disp_read_refrate;
|
|
p->get_n_a_cals = i1disp_get_n_a_cals;
|
|
p->calibrate = i1disp_calibrate;
|
|
p->col_cor_mat = i1disp_col_cor_mat;
|
|
p->get_refr_rate = i1disp_get_refr_rate;
|
|
p->set_refr_rate = i1disp_set_refr_rate;
|
|
p->interp_error = i1disp_interp_error;
|
|
p->del = i1disp_del;
|
|
|
|
p->icom = icom;
|
|
p->dtype = dtype;
|
|
|
|
if (p->dtype == instI1Disp2)
|
|
p->btype = 1; /* i1Display2 */
|
|
|
|
else if (p->btype == instSmile) {
|
|
p->btype = 2; /* Smile */
|
|
}
|
|
|
|
icmSetUnity3x3(p->ccmat); /* Set the colorimeter correction matrix to do nothing */
|
|
set_base_disptype_list(p);
|
|
p->dtech = disptech_unknown;
|
|
|
|
return p;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|