From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- spectro/colorhug.c | 1093 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1093 insertions(+) create mode 100644 spectro/colorhug.c (limited to 'spectro/colorhug.c') diff --git a/spectro/colorhug.c b/spectro/colorhug.c new file mode 100644 index 0000000..dad7012 --- /dev/null +++ b/spectro/colorhug.c @@ -0,0 +1,1093 @@ + + +/* + * Argyll Color Correction System + * + * Hughski ColorHug related functions + * + * Author: Richard Hughes + * Date: 30/11/2011 + * + * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2011, Richard Hughes + * All rights reserved. + * + * (Based on huey.c) + * + * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- + * see the License2.txt file for licencing details. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef SALONEINSTLIB +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#else /* SALONEINSTLIB */ +#include "sa_config.h" +#include "numsup.h" +#endif /* SALONEINSTLIB */ +#include "xspect.h" +#include "insttypes.h" +#include "conv.h" +#include "icoms.h" +#include "colorhug.h" + +static inst_code colorhug_interp_code(inst *pp, int ec); + +/* Interpret an icoms error into a ColorHug error */ +static int icoms2colorhug_err(int se) { + if (se != ICOM_OK) + return COLORHUG_COMS_FAIL; + return COLORHUG_OK; +} + +/* ColorHug commands that we care about */ +typedef enum { + ch_set_mult = 0x04, /* Set multiplier value */ + ch_set_integral = 0x06, /* Set integral time */ + ch_get_firmware_version = 0x07, /* Get the Firmware version number */ + ch_get_serial = 0x0b, /* Gets the serial number */ + ch_set_leds = 0x0e, /* Sets the LEDs */ + ch_take_reading = 0x22, /* Takes a raw reading minus dark offset */ + ch_take_reading_xyz = 0x23, /* Takes an XYZ reading using the current matrix */ + ch_get_post_scale = 0x2a /* Get the post scaling factor */ +} ColorHugCmd; + +/* Diagnostic - return a description given the instruction code */ +static char *inst_desc(int cc) { + static char buf[40]; + switch(cc) { + case 0x04: + return "SetMultiplier"; + case 0x06: + return "SetIntegral"; + case 0x07: + return "GetFirmwareVersion"; + case 0x0b: + return "GetSerial"; + case 0x0e: + return "SetLeds"; + case 0x22: + return "TakeReading"; + case 0x23: + return "TakeReadingXYZ"; + case 0x2a: + return "GetPostScale"; + } + sprintf(buf,"Unknown %02x",cc); + return buf; +} + +/* Error codes interpretation */ +static char * +colorhug_interp_error(inst *pp, int ec) { + ec &= inst_imask; + switch (ec) { + case COLORHUG_INTERNAL_ERROR: + return "Internal software error"; + case COLORHUG_COMS_FAIL: + return "Communications failure"; + case COLORHUG_UNKNOWN_MODEL: + return "Not a known ColorHug Model"; + + case COLORHUG_OK: + return "OK"; + case COLORHUG_UNKNOWN_CMD: + return "Unknown connamd"; + case COLORHUG_WRONG_UNLOCK_CODE: + return "Wrong unlock code"; + case COLORHUG_NOT_IMPLEMENTED: + return "Not implemented"; + case COLORHUG_UNDERFLOW_SENSOR: + return "Sensor underflow"; + case COLORHUG_NO_SERIAL: + return "No serial"; + case COLORHUG_WATCHDOG: + return "Watchdog"; + case COLORHUG_INVALID_ADDRESS: + return "Invalid address"; + case COLORHUG_INVALID_LENGTH: + return "Invalid length"; + case COLORHUG_INVALID_CHECKSUM: + return "Invlid checksum"; + case COLORHUG_INVALID_VALUE: + return "Invalid value"; + case COLORHUG_UNKNOWN_CMD_FOR_BOOTLOADER: + return "Unknown command for bootloader"; + case COLORHUG_NO_CALIBRATION: + return "No calibration"; + case COLORHUG_OVERFLOW_MULTIPLY: + return "Multiply overflow"; + case COLORHUG_OVERFLOW_ADDITION: + return "Addition overflow"; + case COLORHUG_OVERFLOW_SENSOR: + return "Sensor overflow"; + case COLORHUG_OVERFLOW_STACK: + return "Stack overflow"; + case COLORHUG_DEVICE_DEACTIVATED: + return "Device deactivated"; + case COLORHUG_INCOMPLETE_REQUEST: + return "Incomplete request"; + + /* Internal errors */ + case COLORHUG_NO_COMS: + return "Communications hasn't been established"; + case COLORHUG_NOT_INITED: + return "Insrument hasn't been initialised"; + default: + return "Unknown error code"; + } +} + +/* Do a command/response exchange with the colorhug */ +static inst_code +colorhug_command(colorhug *p, + ColorHugCmd cmd, + unsigned char *in, unsigned int in_size, + unsigned char *out, unsigned int out_size, + double timeout) +{ + int i; + unsigned char buf[64]; + int xwbytes, wbytes; + int xrbytes, rbytes; + int se, ua = 0, rv = inst_ok; + int ishid = p->icom->port_type(p->icom) == icomt_hid; + + a1logd(p->log,5,"colorhg_command: sending cmd '%s' args '%s'\n", + inst_desc(cmd), icoms_tohex(in, in_size)); + + /* Send the command with any specified data */ + memset(buf, 0, 64); + buf[0] = cmd; + if (in != NULL) + memcpy(buf + 1, in, in_size); + if (ishid) { + xwbytes = 64; + se = p->icom->hid_write(p->icom, buf, xwbytes, &wbytes, timeout); + } else { +// xwbytes = in_size + 1; /* cmd + arguments */ + xwbytes = 64; + se = p->icom->usb_write(p->icom, NULL, 0x01, buf, xwbytes, &wbytes, timeout); + } + if (se != 0) { + a1logd(p->log,1,"colorhug_command: command send failed with ICOM err 0x%x\n",se); + return colorhug_interp_code((inst *)p, COLORHUG_COMS_FAIL); + } + rv = colorhug_interp_code((inst *)p, icoms2colorhug_err(ua)); + if (rv == inst_ok && wbytes != xwbytes) + rv = colorhug_interp_code((inst *)p, COLORHUG_BAD_WR_LENGTH); + a1logd(p->log,6,"colorhug_command: got inst code \n",rv); + + if (rv != inst_ok) { + /* Flush any response if write failed */ + if (ishid) + p->icom->hid_read(p->icom, buf, 64, &rbytes, timeout); + else + p->icom->usb_read(p->icom, NULL, 0x81, buf, out_size + 2, &rbytes, timeout); + return rv; + } + + /* Now fetch the response */ + a1logd(p->log,6,"colorhug_command: Reading response\n"); + + if (ishid) { + xrbytes = 64; + se = p->icom->hid_read(p->icom, buf, xrbytes, &rbytes, timeout); + } else { +// xrbytes = out_size + 2; + xrbytes = 64; + se = p->icom->usb_read(p->icom, NULL, 0x81, buf, xrbytes, &rbytes, timeout); + } + + if (rbytes >= 2) { + a1logd(p->log,6,"colorhug_command: recieved cmd '%s' error '%s' args '%s'\n", + inst_desc(buf[1]), + colorhug_interp_error((inst *) p, buf[0]), + icoms_tohex(buf, rbytes - 2)); + } + + if (se != 0) { + + /* deal with command error */ +// if (rbytes == 2 && buf[0] != COLORHUG_OK) { + if (buf[0] != COLORHUG_OK) { + a1logd(p->log,1,"colorhug_command: Got Colorhug !OK\n"); + rv = colorhug_interp_code((inst *)p, buf[0]); + return rv; + } + + /* deal with underrun or overrun */ + if (rbytes != xrbytes) { + a1logd(p->log,1,"colorhug_command: got underrun or overrun\n"); + rv = colorhug_interp_code((inst *)p, COLORHUG_BAD_RD_LENGTH); + return rv; + } + + /* there's another reason it failed */ + a1logd(p->log,1,"colorhug_command: read failed with ICOM err 0x%x\n",se); + return colorhug_interp_code((inst *)p, COLORHUG_COMS_FAIL); + } + rv = colorhug_interp_code((inst *)p, icoms2colorhug_err(ua)); + + /* check the command was the same */ + if (rv == inst_ok && buf[1] != cmd) { + a1logd(p->log,1,"colorhug_command: command wasn't echo'd\n"); + rv = colorhug_interp_code((inst *)p, COLORHUG_BAD_RET_CMD); + return rv; + } + if (rv == inst_ok && out != NULL) + memcpy(out, buf + 2, out_size); + + a1logd(p->log,5,"colorhg_command: returning '%s' ICOM err 0x%x\n", + icoms_tohex(buf + 2, out_size),ua); + return rv; +} + +/* --------------------------------------------- */ +/* Little endian wire format conversion routines */ + +/* Take an int, and convert it into a byte buffer */ +static void int2buf_le(unsigned char *buf, int inv) { + buf[0] = (inv >> 0) & 0xff; + buf[1] = (inv >> 8) & 0xff; + buf[2] = (inv >> 16) & 0xff; + buf[3] = (inv >> 24) & 0xff; +} + +/* Take an unsigned int, and convert it into a byte buffer */ +static void uint2buf_le(unsigned char *buf, unsigned int inv) { + buf[0] = (inv >> 0) & 0xff; + buf[1] = (inv >> 8) & 0xff; + buf[2] = (inv >> 16) & 0xff; + buf[3] = (inv >> 24) & 0xff; +} + +/* Take a short, and convert it into a byte buffer */ +static void short2buf_le(unsigned char *buf, int inv) { + buf[0] = (inv >> 0) & 0xff; + buf[1] = (inv >> 8) & 0xff; +} + +/* Take an unsigned short, and convert it into a byte buffer */ +static void ushort2buf_le(unsigned char *buf, unsigned int inv) { + buf[0] = (inv >> 0) & 0xff; + buf[1] = (inv >> 8) & 0xff; +} + +/* Take a word sized buffer, and convert it to an int */ +static int buf2int_le(unsigned char *buf) { + int val; + val = buf[3]; + val = ((val << 8) + buf[2]); + val = ((val << 8) + buf[1]); + val = ((val << 8) + buf[0]); + return val; +} + +/* Take a word sized buffer, and convert it to an unsigned int */ +static unsigned int buf2uint_le(unsigned char *buf) { + unsigned int val; + val = buf[3]; + val = ((val << 8) + buf[2]); + val = ((val << 8) + buf[1]); + val = ((val << 8) + buf[0]); + return val; +} + +/* Take a short sized buffer, and convert it to an int */ +static int buf2short_le(unsigned char *buf) { + int val; + val = buf[1]; + val = ((val << 8) + buf[0]); + return val; +} + +/* Take an unsigned short sized buffer, and convert it to an int */ +static unsigned int buf2ushort_le(unsigned char *buf) { + unsigned int val; + val = buf[1]; + val = ((val << 8) + buf[0]); + return val; +} + +/* --------------------------------------------- */ + + +/* Converts 4 bytes of packed float into a double */ +static double buf2pfdouble(unsigned char *buf) +{ + return (double) buf2int_le(buf) / (double) 0x10000; +} + +/* Set the device LED state */ +static inst_code +colorhug_set_LEDs(colorhug *p, int mask) +{ + int i; + unsigned char ibuf[4]; + inst_code ev; + + mask &= 0x3; + p->led_state = mask; + + ibuf[0] = mask; + ibuf[1] = 0; /* repeat */ + ibuf[2] = 0; /* on */ + ibuf[3] = 0; /* off */ + + /* Do command */ + ev = colorhug_command(p, ch_set_leds, + ibuf, sizeof (ibuf), /* input */ + NULL, 0, /* output */ + 2.0); + return ev; +} + +/* Take a measurement from the device */ +/* There are 64 calibration matricies, index 0..63 */ +/* 0 is the factory calibration, while 1..63 are */ +/* applied on top of the factory calibration as corrections. */ +/* Index 64..70 are mapped via the mapping table */ +/* to an index between 0 and 63, and notionaly correspond */ +/* as follows: */ +/* LCD = 0 */ +/* CRT = 1 */ +/* Projector = 2 */ +/* LED = 3 */ +/* Custom1 = 4 */ +/* Custom2 = 5 */ +static inst_code +colorhug_take_measurement(colorhug *p, double XYZ[3]) +{ + inst_code ev; + int i; + ORD8 ibuf[2]; + + if (!p->inited) + return colorhug_interp_code((inst *)p, COLORHUG_NOT_INITED); + + if (p->icx == 11) { /* Raw */ + unsigned char obuf[3 * 4]; + + /* Do the measurement, and return the values */ + ev = colorhug_command(p, ch_take_reading, + NULL, 0, + obuf, 3 * 4, + 30.0); + if (ev != inst_ok) + return ev; + + /* Convert to doubles */ + for (i = 0; i < 3; i++) + XYZ[i] = p->postscale * buf2pfdouble(obuf + i * 4); + } else { + int icx = 64 + p->icx; + unsigned char obuf[3 * 4]; + + if (p->icx == 10) /* Factory */ + icx = 0; + + /* Choose the calibration matrix */ + short2buf_le(ibuf + 0, icx); + + /* Do the measurement, and return the values */ + ev = colorhug_command(p, ch_take_reading_xyz, + ibuf, sizeof (ibuf), + obuf, 3 * 4, + 30.0); + if (ev != inst_ok) + return ev; + + /* Convert to doubles */ + for (i = 0; i < 3; i++) + XYZ[i] = buf2pfdouble(obuf + i * 4); + } + + /* Apply the colorimeter correction matrix */ + icmMulBy3x3(XYZ, p->ccmat, XYZ); + + a1logd(p->log,3,"colorhug_take_measurement: XYZ = %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]); + + return inst_ok; +} + +/* Establish communications with a ColorHug */ +static inst_code +colorhug_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { + int se; + colorhug *p = (colorhug *) pp; + + a1logd(p->log, 2, "colorhug_init_coms: About to init coms\n"); + + /* Open as an HID if available */ + if (p->icom->port_type(p->icom) == icomt_hid) { + + a1logd(p->log, 3, "colorhug_init_coms: About to init HID\n"); + + /* Set config, interface */ + if ((se = p->icom->set_hid_port(p->icom, icomuf_none, 0, NULL)) != ICOM_OK) { + a1logd(p->log, 1, "colorhug_init_coms: set_hid_port failed ICOM err 0x%x\n",se); + return colorhug_interp_code((inst *)p, icoms2colorhug_err(se)); + } + + } else if (p->icom->port_type(p->icom) == icomt_usb) { + + a1logd(p->log, 3, "colorhug_init_coms: About to init USB\n"); + + /* Set config, interface, write end point, read end point */ + if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, icomuf_detach, 0, NULL)) + != ICOM_OK) { + a1logd(p->log, 1, "colorhug_init_coms: set_usb_port failed ICOM err 0x%x\n",se); + return colorhug_interp_code((inst *)p, icoms2colorhug_err(se)); + } + + } else { + a1logd(p->log, 1, "colorhug_init_coms: wrong communications type for device!\n"); + return colorhug_interp_code((inst *)p, COLORHUG_UNKNOWN_MODEL); + } + + a1logd(p->log, 2, "colorhug_init_coms: inited coms OK\n"); + + p->gotcoms = 1; + return inst_ok; +} + +/* Get the firmware version */ +static inst_code +colorhug_get_firmwareversion (colorhug *p) +{ + inst_code ev; + unsigned char obuf[6]; + + /* Hmm. The post scale is in the 2nd short returned */ + ev = colorhug_command(p, ch_get_firmware_version, + NULL, 0, + obuf, 6, + 2.0); + if (ev != inst_ok) + return ev; + + p->maj = buf2short_le(obuf + 0); + p->min = buf2short_le(obuf + 2); + p->uro = buf2short_le(obuf + 4); + + a1logd(p->log,2,"colorhug: Firware version = %d.%d.%d\n",p->maj,p->min,p->uro); + + return ev; +} + +/* Set the device multiplier */ +static inst_code +colorhug_set_multiplier (colorhug *p, int multiplier) +{ + inst_code ev; + unsigned char ibuf[1]; + + /* Set the desired multiplier */ + ibuf[0] = multiplier; + ev = colorhug_command(p, ch_set_mult, + ibuf, sizeof (ibuf), + NULL, 0, + 2.0); + return ev; +} + +/* Set the device integral time */ +static inst_code +colorhug_set_integral (colorhug *p, int integral) +{ + inst_code ev; + unsigned char ibuf[2]; + + /* Set the desired integral time */ + short2buf_le(ibuf + 0, integral); + ev = colorhug_command(p, ch_set_integral, + ibuf, sizeof (ibuf), + NULL, 0, + 2.0); + return ev; +} + +/* Get the post scale factor */ +static inst_code +colorhug_get_postscale (colorhug *p, double *postscale) +{ + inst_code ev; + unsigned char obuf[4]; + + /* Hmm. The post scale is in the 2nd short returned */ + ev = colorhug_command(p, ch_get_post_scale, + NULL, 0, + obuf, 4, + 2.0); + *postscale = buf2pfdouble(obuf); + return ev; +} + +static inst_code set_default_disp_type(colorhug *p); + +/* Initialise the ColorHug */ +static inst_code +colorhug_init_inst(inst *pp) +{ + colorhug *p = (colorhug *)pp; + inst_code ev; + int i; + + a1logd(p->log, 2, "colorhug_init_coms: About to init coms\n"); + + /* Must establish coms first */ + if (p->gotcoms == 0) + return colorhug_interp_code((inst *)p, COLORHUG_NO_COMS); + + /* Get the firmware version */ + ev = colorhug_get_firmwareversion(p); + if (ev != inst_ok) + return ev; + + /* Turn the LEDs off */ + ev = colorhug_set_LEDs(p, 0x0); + if (ev != inst_ok) + return ev; + + /* Turn the sensor on */ + ev = colorhug_set_multiplier(p, 0x03); + if (ev != inst_ok) + return ev; + + /* Set the integral time to maximum precision */ + ev = colorhug_set_integral(p, 0xffff); + if (ev != inst_ok) + return ev; + + if (p->maj <= 1 && p->min <= 1 && p->uro <= 4) { + + /* Get the post scale factor */ + ev = colorhug_get_postscale(p, &p->postscale); + if (ev != inst_ok) + return ev; + + + /* In firmware >= 1.1.5, the postscale is done in the firmware */ + } else { + p->postscale = 1.0; + } + + p->trig = inst_opt_trig_user; + + /* Setup the default display type */ + if ((ev = set_default_disp_type(p)) != inst_ok) { + return ev; + } + + p->inited = 1; + a1logd(p->log, 2, "colorhug_init: inited coms OK\n"); + + /* Flash the LEDs */ + ev = colorhug_set_LEDs(p, 0x1); + if (ev != inst_ok) + return ev; + msec_sleep(50); + ev = colorhug_set_LEDs(p, 0x2); + if (ev != inst_ok) + return ev; + msec_sleep(50); + ev = colorhug_set_LEDs(p, 0x1); + if (ev != inst_ok) + return ev; + msec_sleep(50); + ev = colorhug_set_LEDs(p, 0x0); + if (ev != inst_ok) + return ev; + + return inst_ok; +} + +/* Read a single sample */ +static inst_code +colorhug_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 */ + colorhug *p = (colorhug *)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, "colorhug: 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 */ + if ((rv = colorhug_take_measurement(p, val->XYZ)) != inst_ok) { + return rv; + } + /* This may not change anything since instrument may clamp */ + if (clamp) + icmClamp3(val->XYZ, val->XYZ); + + val->mtype = inst_mrt_emission; + 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; +} + +/* Insert a colorimetric correction matrix */ +inst_code colorhug_col_cor_mat( +inst *pp, +double mtx[3][3] +) { + colorhug *p = (colorhug *)pp; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + + if (mtx == NULL) { + icmSetUnity3x3(p->ccmat); + } else { + if (p->cbid == 0) { + a1loge(p->log, 1, "colorhug: can't set col_cor_mat over non base display type\n"); + inst_wrong_setup; + } + icmCpy3x3(p->ccmat, mtx); + } + + return inst_ok; +} + +/* Convert a machine specific error code into an abstract dtp code */ +static inst_code +colorhug_interp_code(inst *pp, int ec) { + ec &= inst_imask; + switch (ec) { + + case COLORHUG_OK: + return inst_ok; + + case COLORHUG_INTERNAL_ERROR: + case COLORHUG_NO_COMS: + case COLORHUG_NOT_INITED: + return inst_internal_error | ec; + + case COLORHUG_COMS_FAIL: + return inst_coms_fail | ec; + + case COLORHUG_UNKNOWN_MODEL: + return inst_unknown_model | ec; + + case COLORHUG_UNKNOWN_CMD: + case COLORHUG_WRONG_UNLOCK_CODE: + case COLORHUG_NOT_IMPLEMENTED: + case COLORHUG_UNDERFLOW_SENSOR: + case COLORHUG_NO_SERIAL: + case COLORHUG_WATCHDOG: + case COLORHUG_INVALID_ADDRESS: + case COLORHUG_INVALID_LENGTH: + case COLORHUG_INVALID_CHECKSUM: + case COLORHUG_INVALID_VALUE: + case COLORHUG_UNKNOWN_CMD_FOR_BOOTLOADER: + case COLORHUG_NO_CALIBRATION: + case COLORHUG_OVERFLOW_MULTIPLY: + case COLORHUG_OVERFLOW_ADDITION: + case COLORHUG_OVERFLOW_SENSOR: + case COLORHUG_OVERFLOW_STACK: + case COLORHUG_DEVICE_DEACTIVATED: + case COLORHUG_INCOMPLETE_REQUEST: + case COLORHUG_BAD_WR_LENGTH: + case COLORHUG_BAD_RD_LENGTH: + case COLORHUG_BAD_RET_CMD: + case COLORHUG_BAD_RET_STAT: + return inst_protocol_error | ec; + } + return inst_other_error | ec; +} + +/* Destroy ourselves */ +static void +colorhug_del(inst *pp) { + colorhug *p = (colorhug *)pp; + if (p != NULL) { + if (p->icom != NULL) + p->icom->del(p->icom); + inst_del_disptype_list(p->dtlist, p->ndtlist); + free(p); + } +} + +/* Return the instrument mode capabilities */ +void colorhug_capabilities(inst *pp, +inst_mode *pcap1, +inst2_capability *pcap2, +inst3_capability *pcap3) { + colorhug *p = (colorhug *)pp; + inst_mode cap = 0; + inst2_capability cap2 = 0; + + cap |= inst_mode_emis_spot + | inst_mode_colorimeter + ; + + cap2 |= inst2_prog_trig + | inst2_user_trig + | inst2_has_leds + | inst2_disptype + | inst2_ccmx + ; + + if (pcap1 != NULL) + *pcap1 = cap; + if (pcap2 != NULL) + *pcap2 = cap2; + if (pcap3 != NULL) + *pcap3 = inst3_none; +} + +/* Check device measurement mode */ +inst_code colorhug_check_mode(inst *pp, inst_mode m) { + colorhug *p = (colorhug *)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 test */ + if (m & ~cap) + return inst_unsupported; + + /* only display emission mode and ambient supported */ + if (!IMODETST(m, inst_mode_emis_spot) + && !IMODETST(m, inst_mode_emis_ambient)) { + return inst_unsupported; + } + + return inst_ok; +} + +/* Set device measurement mode */ +inst_code colorhug_set_mode(inst *pp, inst_mode m) { + colorhug *p = (colorhug *)pp; + inst_code ev; + + if ((ev = colorhug_check_mode(pp, m)) != inst_ok) + return ev; + + p->mode = m; + + return inst_ok; +} + +/* The HW handles up to 6, + 2 special */ +static inst_disptypesel colorhug_disptypesel[7] = { + { + inst_dtflags_default, /* flags */ + 0, /* cbix */ + "l", /* sel */ + "LCD, CCFL Backlight", /* desc */ + 0, /* refr */ + 0 /* ix */ + }, + { + inst_dtflags_none, + 0, + "c", + "CRT display", + 0, + 1 + }, + { + inst_dtflags_none, + 0, + "p", + "Projector", + 0, + 2 + }, + { + inst_dtflags_none, + 0, + "e", + "LCD, White LED Backlight", + 0, + 3 + }, + { + inst_dtflags_none, + 1, + "F", + "Factory matrix (For Calibration)", + 0, + 10 + }, + { + inst_dtflags_none, + 2, + "R", + "Raw Reading (For Factory matrix Calibration)", + 0, + 11 + }, + { + inst_dtflags_end, + 0, + "", + "", + 0, + 0 + } +}; + +/* Get mode and option details */ +static inst_code colorhug_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 */ +) { + colorhug *p = (colorhug *)pp; + inst_code rv = inst_ok; + + /* Create/Re-create a current list of abailable display types */ + if (p->dtlist == NULL || recreate) { + if ((rv = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + colorhug_disptypesel, 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(colorhug *p, inst_disptypesel *dentry) { + int ix; + + /* The HW handles up to 6 calibrations */ + ix = dentry->ix; + if (ix != 10 && ix != 11 && (ix < 0 || ix > 3)) + return inst_unsupported; + + p->icx = ix; + p->refrmode = dentry->refr; + p->cbid = dentry->cbid; + if (dentry->flags & inst_dtflags_ccmx) { + icmCpy3x3(p->ccmat, dentry->mat); + } else { + icmSetUnity3x3(p->ccmat); + } + + return inst_ok; +} + +/* Setup the default display type */ +static inst_code set_default_disp_type(colorhug *p) { + inst_code ev; + int i; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, + colorhug_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_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; +} + +/* Set the display type */ +static inst_code colorhug_set_disptype(inst *pp, int ix) { + colorhug *p = (colorhug *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + colorhug_disptypesel, 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; +} + +/* + * 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 +colorhug_get_set_opt(inst *pp, inst_opt_type m, ...) +{ + colorhug *p = (colorhug *)pp; + 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; + } + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + /* Get the display type information */ + if (m == inst_opt_get_dtinfo) { + va_list args; + int *refrmode, *cbid; + + va_start(args, m); + refrmode = va_arg(args, int *); + cbid = va_arg(args, int *); + va_end(args); + + if (refrmode != NULL) + *refrmode = p->refrmode; + if (cbid != NULL) + *cbid = p->cbid; + + return inst_ok; + } + + /* Operate the LEDs */ + if (m == inst_opt_get_gen_ledmask) { + va_list args; + int *mask = NULL; + + va_start(args, m); + mask = va_arg(args, int *); + va_end(args); + *mask = 0x3; /* Two general LEDs */ + return inst_ok; + } else if (m == inst_opt_get_led_state) { + va_list args; + int *mask = NULL; + + va_start(args, m); + mask = va_arg(args, int *); + va_end(args); + *mask = p->led_state; + return inst_ok; + } else if (m == inst_opt_set_led_state) { + va_list args; + int mask = 0; + + va_start(args, m); + mask = va_arg(args, int); + va_end(args); + return colorhug_set_LEDs(p, mask); + } + + return inst_unsupported; +} + +/* Constructor */ +extern colorhug *new_colorhug(icoms *icom, instType itype) { + colorhug *p; + int i; + + if ((p = (colorhug *)calloc(sizeof(colorhug),1)) == NULL) { + a1loge(icom->log, 1, "new_colorhug: malloc failed!\n"); + return NULL; + } + + p->log = new_a1log_d(icom->log); + + p->init_coms = colorhug_init_coms; + p->init_inst = colorhug_init_inst; + p->capabilities = colorhug_capabilities; + p->check_mode = colorhug_check_mode; + p->set_mode = colorhug_set_mode; + p->get_disptypesel = colorhug_get_disptypesel; + p->set_disptype = colorhug_set_disptype; + p->get_set_opt = colorhug_get_set_opt; + p->read_sample = colorhug_read_sample; + p->col_cor_mat = colorhug_col_cor_mat; + p->interp_error = colorhug_interp_error; + p->del = colorhug_del; + + p->icom = icom; + p->itype = icom->itype; + + icmSetUnity3x3(p->ccmat); + + return p; +} + -- cgit v1.2.3