Richard Hughes aaa2caf
commit c75380c239b950eff540d9c8d4ede12c67d2d886
Richard Hughes aaa2caf
Author: Richard Hughes <richard@hughsie.com>
Richard Hughes aaa2caf
Date:   Wed Nov 30 15:28:57 2011 +0000
Richard Hughes aaa2caf
Richard Hughes aaa2caf
    Add an experimental ColorHug sensor driver
Richard Hughes aaa2caf
Richard Hughes aaa2caf
diff --git a/libusb/55-Argyll.rules b/libusb/55-Argyll.rules
Richard Hughes aaa2caf
index a3b3cd4..a047d26 100644
Richard Hughes aaa2caf
--- a/libusb/55-Argyll.rules
Richard Hughes aaa2caf
+++ b/libusb/55-Argyll.rules
Richard Hughes aaa2caf
@@ -46,6 +46,9 @@ ATTRS{idVendor}=="085c", ATTRS{idProduct}=="0300", ENV{COLOR_MEASUREMENT_DEVICE}
Richard Hughes aaa2caf
 # Huey
Richard Hughes aaa2caf
 ATTRS{idVendor}=="0971", ATTRS{idProduct}=="2005", ENV{COLOR_MEASUREMENT_DEVICE}="1"
Richard Hughes aaa2caf
 
Richard Hughes aaa2caf
+# ColorHug
Richard Hughes aaa2caf
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="f8dA", ENV{COLOR_MEASUREMENT_DEVICE}="1"
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
 # Let udev-acl and ConsoleKit manage these devices, if applicable
Richard Hughes aaa2caf
 TEST=="/lib/udev/udev-acl", TEST=="/var/run/ConsoleKit/database", ENV{COLOR_MEASUREMENT_DEVICE}=="*?", ENV{ACL_MANAGE}="1"
Richard Hughes aaa2caf
 
Richard Hughes aaa2caf
diff --git a/spectro/colorhug.c b/spectro/colorhug.c
Richard Hughes aaa2caf
new file mode 100644
Richard Hughes aaa2caf
index 0000000..90f1b7e
Richard Hughes aaa2caf
--- /dev/null
Richard Hughes aaa2caf
+++ b/spectro/colorhug.c
Richard Hughes aaa2caf
@@ -0,0 +1,752 @@
Richard Hughes aaa2caf
+/*
Richard Hughes aaa2caf
+ * Argyll Color Correction System
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * Hughski ColorHug related functions
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * Author: Richard Hughes
Richard Hughes aaa2caf
+ * Date:   30/11/2011
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * Copyright 2006 - 2007, Graeme W. Gill
Richard Hughes aaa2caf
+ * Copyright 2011, Richard Hughes
Richard Hughes aaa2caf
+ * All rights reserved.
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * (Based on huey.c)
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
Richard Hughes aaa2caf
+ * see the License2.txt file for licencing details.
Richard Hughes aaa2caf
+ */
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+#include <stdio.h>
Richard Hughes aaa2caf
+#include <stdlib.h>
Richard Hughes aaa2caf
+#include <ctype.h>
Richard Hughes aaa2caf
+#include <string.h>
Richard Hughes aaa2caf
+#include <time.h>
Richard Hughes aaa2caf
+#include <stdarg.h>
Richard Hughes aaa2caf
+#include <math.h>
Richard Hughes aaa2caf
+#ifndef SALONEINSTLIB
Richard Hughes aaa2caf
+#include "copyright.h"
Richard Hughes aaa2caf
+#include "aconfig.h"
Richard Hughes aaa2caf
+#include "numlib.h"
Richard Hughes aaa2caf
+#else /* SALONEINSTLIB */
Richard Hughes aaa2caf
+#include "sa_config.h"
Richard Hughes aaa2caf
+#include "numsup.h"
Richard Hughes aaa2caf
+#endif /* SALONEINSTLIB */
Richard Hughes aaa2caf
+#include "xspect.h"
Richard Hughes aaa2caf
+#include "insttypes.h"
Richard Hughes aaa2caf
+#include "icoms.h"
Richard Hughes aaa2caf
+#include "conv.h"
Richard Hughes aaa2caf
+#include "colorhug.h"
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+static inst_code colorhug_interp_code(inst *pp, int ec);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Interpret an icoms error into a ColorHug error */
Richard Hughes aaa2caf
+static int icoms2colorhug_err(int se) {
Richard Hughes aaa2caf
+	if (se & ICOM_USERM) {
Richard Hughes aaa2caf
+		se &= ICOM_USERM;
Richard Hughes aaa2caf
+		if (se == ICOM_USER)
Richard Hughes aaa2caf
+			return COLORHUG_USER_ABORT;
Richard Hughes aaa2caf
+		if (se == ICOM_TERM)
Richard Hughes aaa2caf
+			return COLORHUG_USER_TERM;
Richard Hughes aaa2caf
+		if (se == ICOM_TRIG)
Richard Hughes aaa2caf
+			return COLORHUG_USER_TRIG;
Richard Hughes aaa2caf
+		if (se == ICOM_CMND)
Richard Hughes aaa2caf
+			return COLORHUG_USER_CMND;
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	if (se != ICOM_OK)
Richard Hughes aaa2caf
+		return COLORHUG_COMS_FAIL;
Richard Hughes aaa2caf
+	return COLORHUG_OK;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* ColorHug commands that we care about */
Richard Hughes aaa2caf
+typedef enum {
Richard Hughes aaa2caf
+	ch_set_mult		= 0x04,		/* Set multiplier value */
Richard Hughes aaa2caf
+	ch_set_integral	= 0x06,		/* Set integral time */
Richard Hughes aaa2caf
+	ch_get_serial	= 0x0b,		/* Gets the serial number */
Richard Hughes aaa2caf
+	ch_set_leds		= 0x0e,		/* Sets the LEDs */
Richard Hughes aaa2caf
+	ch_take_reading	= 0x23		/* Takes an XYZ reading */
Richard Hughes aaa2caf
+} ColorHugCmd;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Diagnostic - return a description given the instruction code */
Richard Hughes aaa2caf
+static char *inst_desc(int cc) {
Richard Hughes aaa2caf
+	static char buf[40];
Richard Hughes aaa2caf
+	switch(cc) {
Richard Hughes aaa2caf
+	case 0x04:
Richard Hughes aaa2caf
+		return "SetMultiplier";
Richard Hughes aaa2caf
+	case 0x06:
Richard Hughes aaa2caf
+		return "SetIntegral";
Richard Hughes aaa2caf
+	case 0x0b:
Richard Hughes aaa2caf
+		return "GetSerial";
Richard Hughes aaa2caf
+	case 0x0e:
Richard Hughes aaa2caf
+		return "SetLeds";
Richard Hughes aaa2caf
+	case 0x23:
Richard Hughes aaa2caf
+		return "TakeReadingXYZ";
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	sprintf(buf,"Unknown %02x",cc);
Richard Hughes aaa2caf
+	return buf;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Error codes interpretation */
Richard Hughes aaa2caf
+static char *
Richard Hughes aaa2caf
+colorhug_interp_error(inst *pp, int ec) {
Richard Hughes aaa2caf
+	ec &= inst_imask;
Richard Hughes aaa2caf
+	switch (ec) {
Richard Hughes aaa2caf
+		case COLORHUG_INTERNAL_ERROR:
Richard Hughes aaa2caf
+			return "Internal software error";
Richard Hughes aaa2caf
+		case COLORHUG_COMS_FAIL:
Richard Hughes aaa2caf
+			return "Communications failure";
Richard Hughes aaa2caf
+		case COLORHUG_UNKNOWN_MODEL:
Richard Hughes aaa2caf
+			return "Not a known ColorHug Model";
Richard Hughes aaa2caf
+		case COLORHUG_USER_ABORT:
Richard Hughes aaa2caf
+			return "User hit Abort key";
Richard Hughes aaa2caf
+		case COLORHUG_USER_TERM:
Richard Hughes aaa2caf
+			return "User hit Terminate key";
Richard Hughes aaa2caf
+		case COLORHUG_USER_TRIG:
Richard Hughes aaa2caf
+			return "User hit Trigger key";
Richard Hughes aaa2caf
+		case COLORHUG_USER_CMND:
Richard Hughes aaa2caf
+			return "User hit a Command key";
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		case COLORHUG_OK:
Richard Hughes aaa2caf
+			return "OK";
Richard Hughes aaa2caf
+		case COLORHUG_UNKNOWN_CMD:
Richard Hughes aaa2caf
+			return "Unknown connamd";
Richard Hughes aaa2caf
+		case COLORHUG_WRONG_UNLOCK_CODE:
Richard Hughes aaa2caf
+			return "Wrong unlock code";
Richard Hughes aaa2caf
+		case COLORHUG_NOT_IMPLEMENTED:
Richard Hughes aaa2caf
+			return "Not implemented";
Richard Hughes aaa2caf
+		case COLORHUG_UNDERFLOW_SENSOR:
Richard Hughes aaa2caf
+			return "Sensor underflow";
Richard Hughes aaa2caf
+		case COLORHUG_NO_SERIAL:
Richard Hughes aaa2caf
+			return "No serial";
Richard Hughes aaa2caf
+		case COLORHUG_WATCHDOG:
Richard Hughes aaa2caf
+			return "Watchdog";
Richard Hughes aaa2caf
+		case COLORHUG_INVALID_ADDRESS:
Richard Hughes aaa2caf
+			return "Invalid address";
Richard Hughes aaa2caf
+		case COLORHUG_INVALID_LENGTH:
Richard Hughes aaa2caf
+			return "Invalid length";
Richard Hughes aaa2caf
+		case COLORHUG_INVALID_CHECKSUM:
Richard Hughes aaa2caf
+			return "Invlid checksum";
Richard Hughes aaa2caf
+		case COLORHUG_INVALID_VALUE:
Richard Hughes aaa2caf
+			return "Invalid value";
Richard Hughes aaa2caf
+		case COLORHUG_UNKNOWN_CMD_FOR_BOOTLOADER:
Richard Hughes aaa2caf
+			return "Unknown command for bootloader";
Richard Hughes aaa2caf
+		case COLORHUG_NO_CALIBRATION:
Richard Hughes aaa2caf
+			return "No calibration";
Richard Hughes aaa2caf
+		case COLORHUG_OVERFLOW_MULTIPLY:
Richard Hughes aaa2caf
+			return "Multiply overflow";
Richard Hughes aaa2caf
+		case COLORHUG_OVERFLOW_ADDITION:
Richard Hughes aaa2caf
+			return "Addition overflow";
Richard Hughes aaa2caf
+		case COLORHUG_OVERFLOW_SENSOR:
Richard Hughes aaa2caf
+			return "Sensor overflow";
Richard Hughes aaa2caf
+		case COLORHUG_OVERFLOW_STACK:
Richard Hughes aaa2caf
+			return "Stack overflow";
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		/* Internal errors */
Richard Hughes aaa2caf
+		case COLORHUG_NO_COMS:
Richard Hughes aaa2caf
+			return "Communications hasn't been established";
Richard Hughes aaa2caf
+		case COLORHUG_NOT_INITED:
Richard Hughes aaa2caf
+			return "Insrument hasn't been initialised";
Richard Hughes aaa2caf
+		default:
Richard Hughes aaa2caf
+			return "Unknown error code";
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Do a command/response exchange with the colorhug */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_command(colorhug *p,
Richard Hughes aaa2caf
+				 ColorHugCmd cmd,
Richard Hughes aaa2caf
+				 unsigned char *in, uint in_size,
Richard Hughes aaa2caf
+				 unsigned char *out, uint out_size,
Richard Hughes aaa2caf
+				 double timeout)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	int i;
Richard Hughes aaa2caf
+	unsigned char buf[64];
Richard Hughes aaa2caf
+	int wbytes;
Richard Hughes aaa2caf
+	int rbytes;
Richard Hughes aaa2caf
+	int se, ua = 0, rv = inst_ok;
Richard Hughes aaa2caf
+	int isdeb = 0;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Turn off low level debug messages, and sumarise them here */
Richard Hughes aaa2caf
+	isdeb = p->icom->debug;
Richard Hughes aaa2caf
+	if (isdeb <= 2)
Richard Hughes aaa2caf
+		p->icom->debug = 0;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (isdeb) {
Richard Hughes aaa2caf
+		fprintf(stderr,"colorhug: Sending cmd '%s' args '%s'\n",
Richard Hughes aaa2caf
+				inst_desc(cmd), icoms_tohex(in, in_size));
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Send the command with any specified data */
Richard Hughes aaa2caf
+	buf[0] = cmd;
Richard Hughes aaa2caf
+	if (in != NULL)
Richard Hughes aaa2caf
+		memcpy(buf + 1, in, in_size);
Richard Hughes aaa2caf
+	if (p->icom->is_hid) {
Richard Hughes aaa2caf
+		se = p->icom->hid_write(p->icom, buf, in_size + 1, &wbytes, timeout);
Richard Hughes aaa2caf
+	} else {
Richard Hughes aaa2caf
+		se = p->icom->usb_write(p->icom, 0x01, buf, in_size + 1, &wbytes, timeout);
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	if (se != 0) {
Richard Hughes aaa2caf
+		if (se & ICOM_USERM) {
Richard Hughes aaa2caf
+			ua = (se & ICOM_USERM);
Richard Hughes aaa2caf
+		}
Richard Hughes aaa2caf
+		if (se & ~ICOM_USERM) {
Richard Hughes aaa2caf
+			if (isdeb)
Richard Hughes aaa2caf
+				fprintf(stderr,"colorhug: Command send failed with ICOM err 0x%x\n", se);
Richard Hughes aaa2caf
+			p->icom->debug = isdeb;
Richard Hughes aaa2caf
+			return colorhug_interp_code((inst *)p, COLORHUG_COMS_FAIL);
Richard Hughes aaa2caf
+		}
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	rv = colorhug_interp_code((inst *)p, icoms2colorhug_err(ua));
Richard Hughes aaa2caf
+	if (isdeb)
Richard Hughes aaa2caf
+		fprintf(stderr,"colorhug: ICOM err 0x%x\n",ua);
Richard Hughes aaa2caf
+	if (wbytes != in_size + 1)
Richard Hughes aaa2caf
+		rv = colorhug_interp_code((inst *)p, COLORHUG_BAD_WR_LENGTH);
Richard Hughes aaa2caf
+	if (rv != inst_ok) {
Richard Hughes aaa2caf
+		p->icom->debug = isdeb;
Richard Hughes aaa2caf
+		return rv;
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Now fetch the response */
Richard Hughes aaa2caf
+	if (isdeb)
Richard Hughes aaa2caf
+		fprintf(stderr,"colorhug: Reading response\n");
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (p->icom->is_hid) {
Richard Hughes aaa2caf
+		se = p->icom->hid_read(p->icom, buf, out_size + 2, &rbytes, timeout);
Richard Hughes aaa2caf
+	} else {
Richard Hughes aaa2caf
+		se = p->icom->usb_read(p->icom, 0x81, buf, out_size + 2, &rbytes, timeout);
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (isdeb && rbytes >= 2) {
Richard Hughes aaa2caf
+		fprintf(stderr,"Recieved cmd '%s' error '%s' args '%s'\n",
Richard Hughes aaa2caf
+				inst_desc(buf[1]),
Richard Hughes aaa2caf
+				colorhug_interp_error((inst *) p, buf[0]),
Richard Hughes aaa2caf
+				icoms_tohex(buf, rbytes - 2));
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (se != 0) {
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		/* deal with command error */
Richard Hughes aaa2caf
+		if (rbytes == 2 && buf[0] != COLORHUG_OK) {
Richard Hughes aaa2caf
+			rv = colorhug_interp_code((inst *)p, buf[0]);
Richard Hughes aaa2caf
+			return rv;
Richard Hughes aaa2caf
+		}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		/* deal with underrun or overrun */
Richard Hughes aaa2caf
+		if (rbytes != out_size + 2) {
Richard Hughes aaa2caf
+			rv = colorhug_interp_code((inst *)p, COLORHUG_BAD_RD_LENGTH);
Richard Hughes aaa2caf
+			return rv;
Richard Hughes aaa2caf
+		}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		/* there's another reason it failed */
Richard Hughes aaa2caf
+		if (se & ICOM_USERM) {
Richard Hughes aaa2caf
+			ua = (se & ICOM_USERM);
Richard Hughes aaa2caf
+		}
Richard Hughes aaa2caf
+		if (se & ~ICOM_USERM) {
Richard Hughes aaa2caf
+			if (isdeb)
Richard Hughes aaa2caf
+				fprintf(stderr,"colorhug: Response read failed with ICOM err 0x%x\n",se);
Richard Hughes aaa2caf
+			p->icom->debug = isdeb;
Richard Hughes aaa2caf
+			return colorhug_interp_code((inst *)p, COLORHUG_COMS_FAIL);
Richard Hughes aaa2caf
+		}
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	rv = colorhug_interp_code((inst *)p, icoms2colorhug_err(ua));
Richard Hughes aaa2caf
+	if (rv != inst_ok)
Richard Hughes aaa2caf
+		return rv;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* check the command was the same */
Richard Hughes aaa2caf
+	if (buf[1] != cmd) {
Richard Hughes aaa2caf
+		rv = colorhug_interp_code((inst *)p, COLORHUG_BAD_RET_CMD);
Richard Hughes aaa2caf
+		return rv;
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	if (out != NULL) {
Richard Hughes aaa2caf
+		memcpy(out, buf + 2, out_size);
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	if (isdeb) {
Richard Hughes aaa2caf
+		fprintf(stderr,"colorhug: '%s' ICOM err 0x%x\n",
Richard Hughes aaa2caf
+				icoms_tohex(buf + 2, out_size),ua);
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	p->icom->debug = isdeb;
Richard Hughes aaa2caf
+	return rv;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Take a short, and convert it into a byte buffer */
Richard Hughes aaa2caf
+static void short2buf(unsigned char *buf, int inv)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	buf[0] = (inv >> 0) & 0xff;
Richard Hughes aaa2caf
+	buf[1] = (inv >> 8) & 0xff;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Converts a packed float to a double */
Richard Hughes aaa2caf
+static double packed_float_to_double (uint32_t pf)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	return (double) pf / (double) 0x10000;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Set the device LED state */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_set_LEDs(colorhug *p, int mask)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	int i;
Richard Hughes aaa2caf
+	unsigned char ibuf[4];
Richard Hughes aaa2caf
+	inst_code ev;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	mask &= 0x3;
Richard Hughes aaa2caf
+	p->led_state = mask;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	ibuf[0] = mask;
Richard Hughes aaa2caf
+	ibuf[1] = 0; /* repeat */
Richard Hughes aaa2caf
+	ibuf[2] = 0; /* on */
Richard Hughes aaa2caf
+	ibuf[3] = 0; /* off */
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Do command */
Richard Hughes aaa2caf
+	ev = colorhug_command(p, ch_set_leds,
Richard Hughes aaa2caf
+						  ibuf, sizeof (ibuf), /* input */
Richard Hughes aaa2caf
+						  NULL, 0, /* output */
Richard Hughes aaa2caf
+						  1.0);
Richard Hughes aaa2caf
+	return ev;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Take a XYZ measurement from the device */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_take_XYZ_measurement(colorhug *p, double XYZ[3])
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	inst_code ev;
Richard Hughes aaa2caf
+	int i;
Richard Hughes aaa2caf
+	uint8_t ibuf[2];
Richard Hughes aaa2caf
+	uint32_t obuf[3];
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (!p->inited)
Richard Hughes aaa2caf
+		return colorhug_interp_code((inst *)p, COLORHUG_NOT_INITED);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Choose the calibration matrix */
Richard Hughes aaa2caf
+	short2buf(ibuf + 0, p->crt ? 65 : 64);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Do the measurement, and return the values */
Richard Hughes aaa2caf
+	ev = colorhug_command(p, ch_take_reading,
Richard Hughes aaa2caf
+						  ibuf, sizeof (ibuf),
Richard Hughes aaa2caf
+						  (unsigned char *) obuf, sizeof (obuf),
Richard Hughes aaa2caf
+						  1.0);
Richard Hughes aaa2caf
+	if (ev != inst_ok)
Richard Hughes aaa2caf
+		return ev;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Convert to doubles */
Richard Hughes aaa2caf
+	for (i = 0; i < 3; i++)
Richard Hughes aaa2caf
+		XYZ[i] = packed_float_to_double (obuf[i]);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Apply the colorimeter correction matrix */
Richard Hughes aaa2caf
+	icmMulBy3x3(XYZ, p->ccmat, XYZ);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (p->debug) {
Richard Hughes aaa2caf
+		fprintf(stderr,"colorhug: returning XYZ = %f %f %f\n",
Richard Hughes aaa2caf
+				XYZ[0],XYZ[1],XYZ[2]);
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	return inst_ok;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Establish communications with a ColorHug */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_init_coms(inst *pp, int port, baud_rate br, flow_control fc, double tout) {
Richard Hughes aaa2caf
+	colorhug *p = (colorhug *) pp;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (p->debug) {
Richard Hughes aaa2caf
+		p->icom->debug = p->debug;	/* Turn on debugging */
Richard Hughes aaa2caf
+		fprintf(stderr,"colorhug: About to init coms\n");
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Open as an HID if available */
Richard Hughes aaa2caf
+	if (p->icom->is_hid_portno(p->icom, port) != instUnknown) {
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		if (p->debug)
Richard Hughes aaa2caf
+			fprintf(stderr,"colorhug: About to init HID\n");
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		/* Set config, interface */
Richard Hughes aaa2caf
+		p->icom->set_hid_port(p->icom, port, icomuf_none, 0, NULL);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	} else if (p->icom->is_usb_portno(p->icom, port) != instUnknown) {
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		if (p->debug)
Richard Hughes aaa2caf
+			fprintf(stderr,"colorhug: About to init USB\n");
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		/* Set config, interface, write end point, read end point */
Richard Hughes aaa2caf
+		p->icom->set_usb_port(p->icom, port, 1, 0x00, 0x00, icomuf_detach, 0, NULL);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	} else {
Richard Hughes aaa2caf
+		if (p->debug)
Richard Hughes aaa2caf
+			fprintf(stderr,"colorhug: init_coms called to wrong device!\n");
Richard Hughes aaa2caf
+		return colorhug_interp_code((inst *)p, COLORHUG_UNKNOWN_MODEL);
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (p->debug)
Richard Hughes aaa2caf
+		fprintf(stderr,"colorhug: init coms has suceeded\n");
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	p->gotcoms = 1;
Richard Hughes aaa2caf
+	return inst_ok;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Set the device multiplier */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_set_multiplier (colorhug *p, int multiplier)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	inst_code ev;
Richard Hughes aaa2caf
+	unsigned char ibuf[1];
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Set the desired multiplier */
Richard Hughes aaa2caf
+	ibuf[0] = multiplier;
Richard Hughes aaa2caf
+	ev = colorhug_command(p, ch_set_mult,
Richard Hughes aaa2caf
+						  ibuf, sizeof (ibuf),
Richard Hughes aaa2caf
+						  NULL, 0,
Richard Hughes aaa2caf
+						  1.0);
Richard Hughes aaa2caf
+	return ev;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Set the device integral time */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_set_integral (colorhug *p, int integral)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	inst_code ev;
Richard Hughes aaa2caf
+	unsigned char ibuf[2];
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Set the desired integral time */
Richard Hughes aaa2caf
+	short2buf(ibuf + 0, integral);
Richard Hughes aaa2caf
+	ev = colorhug_command(p, ch_set_integral,
Richard Hughes aaa2caf
+						  ibuf, sizeof (ibuf),
Richard Hughes aaa2caf
+						  NULL, 0,
Richard Hughes aaa2caf
+						  1.0);
Richard Hughes aaa2caf
+	return ev;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Initialise the ColorHug */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_init_inst(inst *pp)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	colorhug *p = (colorhug *)pp;
Richard Hughes aaa2caf
+	inst_code ev;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (p->debug)
Richard Hughes aaa2caf
+		fprintf(stderr,"colorhug: About to init instrument\n");
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Must establish coms first */
Richard Hughes aaa2caf
+	if (p->gotcoms == 0)
Richard Hughes aaa2caf
+		return colorhug_interp_code((inst *)p, COLORHUG_NO_COMS);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Turn the LEDs off */
Richard Hughes aaa2caf
+	ev = colorhug_set_LEDs(p, 0x0);
Richard Hughes aaa2caf
+	if (ev != inst_ok)
Richard Hughes aaa2caf
+		return ev;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Turn the sensor on */
Richard Hughes aaa2caf
+	ev = colorhug_set_multiplier(p, 0x03);
Richard Hughes aaa2caf
+	if (ev != inst_ok)
Richard Hughes aaa2caf
+		return ev;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Set the integral time to maximum precision */
Richard Hughes aaa2caf
+	ev = colorhug_set_integral(p, 0xffff);
Richard Hughes aaa2caf
+	if (ev != inst_ok)
Richard Hughes aaa2caf
+		return ev;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	p->itype = instColorHug;
Richard Hughes aaa2caf
+	p->trig = inst_opt_trig_keyb;
Richard Hughes aaa2caf
+	p->inited = 1;
Richard Hughes aaa2caf
+	if (p->debug)
Richard Hughes aaa2caf
+		fprintf(stderr,"colorhug: instrument inited OK\n");
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Flash the LEDs */
Richard Hughes aaa2caf
+	ev = colorhug_set_LEDs(p, 0x1);
Richard Hughes aaa2caf
+	if (ev != inst_ok)
Richard Hughes aaa2caf
+		return ev;
Richard Hughes aaa2caf
+	msec_sleep(50);
Richard Hughes aaa2caf
+	ev = colorhug_set_LEDs(p, 0x2);
Richard Hughes aaa2caf
+	if (ev != inst_ok)
Richard Hughes aaa2caf
+		return ev;
Richard Hughes aaa2caf
+	msec_sleep(50);
Richard Hughes aaa2caf
+	ev = colorhug_set_LEDs(p, 0x1);
Richard Hughes aaa2caf
+	if (ev != inst_ok)
Richard Hughes aaa2caf
+		return ev;
Richard Hughes aaa2caf
+	msec_sleep(50);
Richard Hughes aaa2caf
+	ev = colorhug_set_LEDs(p, 0x0);
Richard Hughes aaa2caf
+	if (ev != inst_ok)
Richard Hughes aaa2caf
+		return ev;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	return ev;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Read a single sample */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_read_sample(
Richard Hughes aaa2caf
+inst *pp,
Richard Hughes aaa2caf
+char *name,			/* Strip name (7 chars) */
Richard Hughes aaa2caf
+ipatch *val) {		/* Pointer to instrument patch value */
Richard Hughes aaa2caf
+	colorhug *p = (colorhug *)pp;
Richard Hughes aaa2caf
+	int user_trig = 0;
Richard Hughes aaa2caf
+	int rv = inst_protocol_error;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (p->trig == inst_opt_trig_keyb) {
Richard Hughes aaa2caf
+		int se;
Richard Hughes aaa2caf
+		if ((se = icoms_poll_user(p->icom, 1)) != ICOM_TRIG) {
Richard Hughes aaa2caf
+			/* Abort, term or command */
Richard Hughes aaa2caf
+			return colorhug_interp_code((inst *)p, icoms2colorhug_err(se));
Richard Hughes aaa2caf
+		}
Richard Hughes aaa2caf
+		user_trig = 1;
Richard Hughes aaa2caf
+		if (p->trig_return)
Richard Hughes aaa2caf
+			printf("\n");
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Read the XYZ value */
Richard Hughes aaa2caf
+	if ((rv = colorhug_take_XYZ_measurement(p, val->aXYZ)) != inst_ok) {
Richard Hughes aaa2caf
+		return rv;
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	val->XYZ_v = 0;
Richard Hughes aaa2caf
+	val->aXYZ_v = 1;		/* These are absolute XYZ readings ? */
Richard Hughes aaa2caf
+	val->Lab_v = 0;
Richard Hughes aaa2caf
+	val->sp.spec_n = 0;
Richard Hughes aaa2caf
+	val->duration = 0.0;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (user_trig)
Richard Hughes aaa2caf
+		return inst_user_trig;
Richard Hughes aaa2caf
+	return rv;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Insert a colorimetric correction matrix */
Richard Hughes aaa2caf
+inst_code colorhug_col_cor_mat(
Richard Hughes aaa2caf
+inst *pp,
Richard Hughes aaa2caf
+double mtx[3][3]
Richard Hughes aaa2caf
+) {
Richard Hughes aaa2caf
+	colorhug *p = (colorhug *)pp;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (mtx == NULL)
Richard Hughes aaa2caf
+		icmSetUnity3x3(p->ccmat);
Richard Hughes aaa2caf
+	else
Richard Hughes aaa2caf
+		icmCpy3x3(p->ccmat, mtx);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	return inst_ok;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Determine if a calibration is needed */
Richard Hughes aaa2caf
+inst_cal_type colorhug_needs_calibration(inst *pp) {
Richard Hughes aaa2caf
+	return inst_ok;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Request an instrument calibration */
Richard Hughes aaa2caf
+inst_code colorhug_calibrate(
Richard Hughes aaa2caf
+inst *pp,
Richard Hughes aaa2caf
+inst_cal_type calt,		/* Calibration type. inst_calt_all for all neeeded */
Richard Hughes aaa2caf
+inst_cal_cond *calc,	/* Current condition/desired condition */
Richard Hughes aaa2caf
+char id[CALIDLEN]		/* Condition identifier (ie. white reference ID) */
Richard Hughes aaa2caf
+) {
Richard Hughes aaa2caf
+	id[0] = '\000';
Richard Hughes aaa2caf
+	return inst_unsupported;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Convert a machine specific error code into an abstract dtp code */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_interp_code(inst *pp, int ec) {
Richard Hughes aaa2caf
+	ec &= inst_imask;
Richard Hughes aaa2caf
+	switch (ec) {
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		case COLORHUG_OK:
Richard Hughes aaa2caf
+			return inst_ok;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		case COLORHUG_INTERNAL_ERROR:
Richard Hughes aaa2caf
+		case COLORHUG_NO_COMS:
Richard Hughes aaa2caf
+		case COLORHUG_NOT_INITED:
Richard Hughes aaa2caf
+			return inst_internal_error | ec;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		case COLORHUG_COMS_FAIL:
Richard Hughes aaa2caf
+			return inst_coms_fail | ec;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		case COLORHUG_UNKNOWN_MODEL:
Richard Hughes aaa2caf
+			return inst_unknown_model | ec;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		case COLORHUG_UNKNOWN_CMD:
Richard Hughes aaa2caf
+		case COLORHUG_WRONG_UNLOCK_CODE:
Richard Hughes aaa2caf
+		case COLORHUG_NOT_IMPLEMENTED:
Richard Hughes aaa2caf
+		case COLORHUG_UNDERFLOW_SENSOR:
Richard Hughes aaa2caf
+		case COLORHUG_NO_SERIAL:
Richard Hughes aaa2caf
+		case COLORHUG_WATCHDOG:
Richard Hughes aaa2caf
+		case COLORHUG_INVALID_ADDRESS:
Richard Hughes aaa2caf
+		case COLORHUG_INVALID_LENGTH:
Richard Hughes aaa2caf
+		case COLORHUG_INVALID_CHECKSUM:
Richard Hughes aaa2caf
+		case COLORHUG_INVALID_VALUE:
Richard Hughes aaa2caf
+		case COLORHUG_UNKNOWN_CMD_FOR_BOOTLOADER:
Richard Hughes aaa2caf
+		case COLORHUG_NO_CALIBRATION:
Richard Hughes aaa2caf
+		case COLORHUG_OVERFLOW_MULTIPLY:
Richard Hughes aaa2caf
+		case COLORHUG_OVERFLOW_ADDITION:
Richard Hughes aaa2caf
+		case COLORHUG_OVERFLOW_SENSOR:
Richard Hughes aaa2caf
+		case COLORHUG_OVERFLOW_STACK:
Richard Hughes aaa2caf
+		case COLORHUG_BAD_WR_LENGTH:
Richard Hughes aaa2caf
+		case COLORHUG_BAD_RD_LENGTH:
Richard Hughes aaa2caf
+		case COLORHUG_BAD_RET_CMD:
Richard Hughes aaa2caf
+		case COLORHUG_BAD_RET_STAT:
Richard Hughes aaa2caf
+			return inst_protocol_error | ec;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		case COLORHUG_USER_ABORT:
Richard Hughes aaa2caf
+			return inst_user_abort | ec;
Richard Hughes aaa2caf
+		case COLORHUG_USER_TERM:
Richard Hughes aaa2caf
+			return inst_user_term | ec;
Richard Hughes aaa2caf
+		case COLORHUG_USER_TRIG:
Richard Hughes aaa2caf
+			return inst_user_trig | ec;
Richard Hughes aaa2caf
+		case COLORHUG_USER_CMND:
Richard Hughes aaa2caf
+			return inst_user_cmnd | ec;
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	return inst_other_error | ec;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Destroy ourselves */
Richard Hughes aaa2caf
+static void
Richard Hughes aaa2caf
+colorhug_del(inst *pp) {
Richard Hughes aaa2caf
+	colorhug *p = (colorhug *)pp;
Richard Hughes aaa2caf
+	if (p->icom != NULL)
Richard Hughes aaa2caf
+		p->icom->del(p->icom);
Richard Hughes aaa2caf
+	free(p);
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Return the instrument capabilities */
Richard Hughes aaa2caf
+inst_capability colorhug_capabilities(inst *pp) {
Richard Hughes aaa2caf
+	colorhug *p = (colorhug *)pp;
Richard Hughes aaa2caf
+	inst_capability rv;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	rv = inst_emis_spot
Richard Hughes aaa2caf
+	   | inst_emis_disp
Richard Hughes aaa2caf
+	   | inst_emis_disp_lcd
Richard Hughes aaa2caf
+	   | inst_colorimeter
Richard Hughes aaa2caf
+	   | inst_ccmx
Richard Hughes aaa2caf
+	;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	return rv;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Return the instrument capabilities 2 */
Richard Hughes aaa2caf
+inst2_capability colorhug_capabilities2(inst *pp) {
Richard Hughes aaa2caf
+	colorhug *p = (colorhug *)pp;
Richard Hughes aaa2caf
+	inst2_capability rv = 0;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	rv |= inst2_prog_trig;
Richard Hughes aaa2caf
+	rv |= inst2_keyb_trig;
Richard Hughes aaa2caf
+	rv |= inst2_has_leds;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	return rv;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Set device measurement mode */
Richard Hughes aaa2caf
+inst_code colorhug_set_mode(inst *pp, inst_mode m)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	colorhug *p = (colorhug *)pp;
Richard Hughes aaa2caf
+	inst_mode mm;		/* Measurement mode */
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* The measurement mode portion of the mode */
Richard Hughes aaa2caf
+	mm = m & inst_mode_measurement_mask;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* only display emission mode and ambient supported */
Richard Hughes aaa2caf
+	if (mm != inst_mode_emis_spot
Richard Hughes aaa2caf
+	 && mm != inst_mode_emis_disp
Richard Hughes aaa2caf
+	 && mm != inst_mode_emis_ambient) {
Richard Hughes aaa2caf
+		return inst_unsupported;
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Spectral mode is not supported */
Richard Hughes aaa2caf
+	if (m & inst_mode_spectral)
Richard Hughes aaa2caf
+		return inst_unsupported;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	p->mode = m;
Richard Hughes aaa2caf
+	return inst_ok;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Set or reset an optional mode */
Richard Hughes aaa2caf
+static inst_code
Richard Hughes aaa2caf
+colorhug_set_opt_mode(inst *pp, inst_opt_mode m, ...)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	colorhug *p = (colorhug *)pp;
Richard Hughes aaa2caf
+	inst_code ev = inst_ok;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Select CRT/LCD */
Richard Hughes aaa2caf
+	if (m == inst_opt_disp_crt) {
Richard Hughes aaa2caf
+		if (p->crt == 0)
Richard Hughes aaa2caf
+		p->crt = 1;
Richard Hughes aaa2caf
+		return inst_ok;
Richard Hughes aaa2caf
+	} else if (m == inst_opt_disp_lcd) {
Richard Hughes aaa2caf
+		if (p->crt != 0)
Richard Hughes aaa2caf
+		p->crt = 0;
Richard Hughes aaa2caf
+		return inst_ok;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	/* Record the trigger mode */
Richard Hughes aaa2caf
+	if (m == inst_opt_trig_prog
Richard Hughes aaa2caf
+	 || m == inst_opt_trig_keyb) {
Richard Hughes aaa2caf
+		p->trig = m;
Richard Hughes aaa2caf
+		return inst_ok;
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+	if (m == inst_opt_trig_return) {
Richard Hughes aaa2caf
+		p->trig_return = 1;
Richard Hughes aaa2caf
+		return inst_ok;
Richard Hughes aaa2caf
+	} else if (m == inst_opt_trig_no_return) {
Richard Hughes aaa2caf
+		p->trig_return = 0;
Richard Hughes aaa2caf
+		return inst_ok;
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Operate the LEDs */
Richard Hughes aaa2caf
+	if (m == inst_opt_get_gen_ledmask) {
Richard Hughes aaa2caf
+		va_list args;
Richard Hughes aaa2caf
+		int *mask = NULL;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		va_start(args, m);
Richard Hughes aaa2caf
+		mask = va_arg(args, int *);
Richard Hughes aaa2caf
+		va_end(args);
Richard Hughes aaa2caf
+		*mask = 0x3;			/* Two general LEDs */
Richard Hughes aaa2caf
+		return inst_ok;
Richard Hughes aaa2caf
+	} else if (m == inst_opt_get_led_state) {
Richard Hughes aaa2caf
+		va_list args;
Richard Hughes aaa2caf
+		int *mask = NULL;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		va_start(args, m);
Richard Hughes aaa2caf
+		mask = va_arg(args, int *);
Richard Hughes aaa2caf
+		va_end(args);
Richard Hughes aaa2caf
+		*mask = p->led_state;
Richard Hughes aaa2caf
+		return inst_ok;
Richard Hughes aaa2caf
+	} else if (m == inst_opt_set_led_state) {
Richard Hughes aaa2caf
+		va_list args;
Richard Hughes aaa2caf
+		int mask = 0;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+		va_start(args, m);
Richard Hughes aaa2caf
+		mask = va_arg(args, int);
Richard Hughes aaa2caf
+		va_end(args);
Richard Hughes aaa2caf
+		return colorhug_set_LEDs(p, mask);
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	return inst_unsupported;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Constructor */
Richard Hughes aaa2caf
+extern colorhug *new_colorhug(icoms *icom, int debug, int verb)
Richard Hughes aaa2caf
+{
Richard Hughes aaa2caf
+	colorhug *p;
Richard Hughes aaa2caf
+	if ((p = (colorhug *)calloc(sizeof(colorhug),1)) == NULL)
Richard Hughes aaa2caf
+		error("colorhug: malloc failed!");
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	if (icom == NULL)
Richard Hughes aaa2caf
+		p->icom = new_icoms();
Richard Hughes aaa2caf
+	else
Richard Hughes aaa2caf
+		p->icom = icom;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	p->debug = debug;
Richard Hughes aaa2caf
+	p->verb = verb;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Set the colorimeter correction matrix to do nothing */
Richard Hughes aaa2caf
+	icmSetUnity3x3(p->ccmat);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	p->init_coms         = colorhug_init_coms;
Richard Hughes aaa2caf
+	p->init_inst         = colorhug_init_inst;
Richard Hughes aaa2caf
+	p->capabilities      = colorhug_capabilities;
Richard Hughes aaa2caf
+	p->capabilities2     = colorhug_capabilities2;
Richard Hughes aaa2caf
+	p->set_mode          = colorhug_set_mode;
Richard Hughes aaa2caf
+	p->set_opt_mode      = colorhug_set_opt_mode;
Richard Hughes aaa2caf
+	p->read_sample       = colorhug_read_sample;
Richard Hughes aaa2caf
+	p->needs_calibration = colorhug_needs_calibration;
Richard Hughes aaa2caf
+	p->col_cor_mat       = colorhug_col_cor_mat;
Richard Hughes aaa2caf
+	p->calibrate         = colorhug_calibrate;
Richard Hughes aaa2caf
+	p->interp_error      = colorhug_interp_error;
Richard Hughes aaa2caf
+	p->del               = colorhug_del;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	/* Until initalisation */
Richard Hughes aaa2caf
+	p->itype = instUnknown;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	return p;
Richard Hughes aaa2caf
+}
Richard Hughes aaa2caf
diff --git a/spectro/colorhug.h b/spectro/colorhug.h
Richard Hughes aaa2caf
new file mode 100644
Richard Hughes aaa2caf
index 0000000..6c0a485
Richard Hughes aaa2caf
--- /dev/null
Richard Hughes aaa2caf
+++ b/spectro/colorhug.h
Richard Hughes aaa2caf
@@ -0,0 +1,85 @@
Richard Hughes aaa2caf
+/*
Richard Hughes aaa2caf
+ * Argyll Color Correction System
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * Hughski ColorHug related defines
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * Author: Richard Hughes
Richard Hughes aaa2caf
+ * Date:   30/11/2011
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * Copyright 2006 - 2007, Graeme W. Gill
Richard Hughes aaa2caf
+ * Copyright 2011, Richard Hughes
Richard Hughes aaa2caf
+ * All rights reserved.
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * (Based on huey.h)
Richard Hughes aaa2caf
+ *
Richard Hughes aaa2caf
+ * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
Richard Hughes aaa2caf
+ * see the License2.txt file for licencing details.
Richard Hughes aaa2caf
+ */
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+#ifndef COLORHUG_H
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+#include "inst.h"
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Note: update colorhug_interp_error() and colorhug_interp_code() in colorhug.c */
Richard Hughes aaa2caf
+/* if anything of these #defines are added or subtracted */
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Fake Error codes */
Richard Hughes aaa2caf
+#define COLORHUG_INTERNAL_ERROR			0x61		/* Internal software error */
Richard Hughes aaa2caf
+#define COLORHUG_COMS_FAIL				0x62		/* Communication failure */
Richard Hughes aaa2caf
+#define COLORHUG_UNKNOWN_MODEL			0x63		/* Not an colorhug */
Richard Hughes aaa2caf
+#define COLORHUG_DATA_PARSE_ERROR 		0x64		/* Read data parsing error */
Richard Hughes aaa2caf
+#define COLORHUG_USER_ABORT				0x65		/* User hit abort */
Richard Hughes aaa2caf
+#define COLORHUG_USER_TERM				0x66		/* User hit terminate */
Richard Hughes aaa2caf
+#define COLORHUG_USER_TRIG				0x67		/* User hit trigger */
Richard Hughes aaa2caf
+#define COLORHUG_USER_CMND				0x68		/* User hit command */
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Real error codes */
Richard Hughes aaa2caf
+#define COLORHUG_OK  					0x00
Richard Hughes aaa2caf
+#define COLORHUG_UNKNOWN_CMD			0x01
Richard Hughes aaa2caf
+#define COLORHUG_WRONG_UNLOCK_CODE		0x02
Richard Hughes aaa2caf
+#define COLORHUG_NOT_IMPLEMENTED		0x03
Richard Hughes aaa2caf
+#define COLORHUG_UNDERFLOW_SENSOR		0x04
Richard Hughes aaa2caf
+#define COLORHUG_NO_SERIAL				0x05
Richard Hughes aaa2caf
+#define COLORHUG_WATCHDOG				0x06
Richard Hughes aaa2caf
+#define COLORHUG_INVALID_ADDRESS		0x07
Richard Hughes aaa2caf
+#define COLORHUG_INVALID_LENGTH			0x08
Richard Hughes aaa2caf
+#define COLORHUG_INVALID_CHECKSUM		0x09
Richard Hughes aaa2caf
+#define COLORHUG_INVALID_VALUE			0x0a
Richard Hughes aaa2caf
+#define COLORHUG_UNKNOWN_CMD_FOR_BOOTLOADER		0x0b
Richard Hughes aaa2caf
+#define COLORHUG_NO_CALIBRATION			0x0c
Richard Hughes aaa2caf
+#define COLORHUG_OVERFLOW_MULTIPLY		0x0d
Richard Hughes aaa2caf
+#define COLORHUG_OVERFLOW_ADDITION		0x0e
Richard Hughes aaa2caf
+#define COLORHUG_OVERFLOW_SENSOR		0x0f
Richard Hughes aaa2caf
+#define COLORHUG_OVERFLOW_STACK			0x10
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Internal errors */
Richard Hughes aaa2caf
+#define COLORHUG_NO_COMS  				0x22
Richard Hughes aaa2caf
+#define COLORHUG_NOT_INITED 			0x23
Richard Hughes aaa2caf
+#define COLORHUG_BAD_WR_LENGTH			0x25
Richard Hughes aaa2caf
+#define COLORHUG_BAD_RD_LENGTH			0x26
Richard Hughes aaa2caf
+#define COLORHUG_BAD_RET_CMD			0x27
Richard Hughes aaa2caf
+#define COLORHUG_BAD_RET_STAT			0x28
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* COLORHUG communication object */
Richard Hughes aaa2caf
+struct _colorhug {
Richard Hughes aaa2caf
+	INST_OBJ_BASE
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	inst_mode mode;				/* Currently selected mode */
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	inst_opt_mode trig;			/* Reading trigger mode */
Richard Hughes aaa2caf
+	int trig_return;			/* Emit "\n" after trigger */
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+	int ser_no;					/* Serial number */
Richard Hughes aaa2caf
+	int crt;					/* NZ if set to CRT */
Richard Hughes aaa2caf
+	double ccmat[3][3];			/* Colorimeter correction matrix */
Richard Hughes aaa2caf
+	int	 led_state;				/* Current LED state */
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+}; typedef struct _colorhug colorhug;
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+/* Constructor */
Richard Hughes aaa2caf
+extern colorhug *new_colorhug(icoms *icom, int debug, int verb);
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
+#define COLORHUG_H
Richard Hughes aaa2caf
+#endif /* COLORHUG_H */
Richard Hughes aaa2caf
diff --git a/spectro/inst.c b/spectro/inst.c
Richard Hughes aaa2caf
index b73ff6c..cbcda9e 100644
Richard Hughes aaa2caf
--- a/spectro/inst.c
Richard Hughes aaa2caf
+++ b/spectro/inst.c
Richard Hughes aaa2caf
@@ -476,6 +476,8 @@ int verb			/* Verbosity flag */
Richard Hughes aaa2caf
 		p = (inst *)new_spyd2(icom, debug, verb);
Richard Hughes aaa2caf
 	else if (itype == instHuey)
Richard Hughes aaa2caf
 		p = (inst *)new_huey(icom, debug, verb);
Richard Hughes aaa2caf
+	else if (itype == instColorHug)
Richard Hughes aaa2caf
+		p = (inst *)new_colorhug(icom, debug, verb);
Richard Hughes aaa2caf
 	else {
Richard Hughes aaa2caf
 		return NULL;
Richard Hughes aaa2caf
 	}
Richard Hughes aaa2caf
diff --git a/spectro/insttypes.c b/spectro/insttypes.c
Richard Hughes aaa2caf
index a11f299..ed0170e 100644
Richard Hughes aaa2caf
--- a/spectro/insttypes.c
Richard Hughes aaa2caf
+++ b/spectro/insttypes.c
Richard Hughes aaa2caf
@@ -73,6 +73,8 @@ char *inst_name(instType itype) {
Richard Hughes aaa2caf
 			return "Datacolor Spyder3";
Richard Hughes aaa2caf
 		case instHuey:
Richard Hughes aaa2caf
 			return "GretagMacbeth Huey";
Richard Hughes aaa2caf
+		case instColorHug:
Richard Hughes aaa2caf
+			return "Hughski ColorHug";
Richard Hughes aaa2caf
 		default:
Richard Hughes aaa2caf
 			break;
Richard Hughes aaa2caf
 	}
Richard Hughes aaa2caf
@@ -124,6 +126,8 @@ instType inst_enum(char *name) {
Richard Hughes aaa2caf
 		return instSpyder3;
Richard Hughes aaa2caf
 	else if (strcmp(name, "GretagMacbeth Huey") == 0)
Richard Hughes aaa2caf
 		return instHuey;
Richard Hughes aaa2caf
+	else if (strcmp(name, "Hughski ColorHug") == 0)
Richard Hughes aaa2caf
+		return instColorHug;
Richard Hughes aaa2caf
 
Richard Hughes aaa2caf
 	return instUnknown;
Richard Hughes aaa2caf
 }
Richard Hughes aaa2caf
@@ -183,6 +187,11 @@ unsigned short idProduct) {
Richard Hughes aaa2caf
 			return instSpyder3;
Richard Hughes aaa2caf
 	}
Richard Hughes aaa2caf
 
Richard Hughes aaa2caf
+	if (idVendor  == 0x04d8) {		/* Microchip */
Richard Hughes aaa2caf
+		if (idProduct == 0xf8da)	/* Hughski ColorHug */
Richard Hughes aaa2caf
+			return instColorHug;
Richard Hughes aaa2caf
+	}
Richard Hughes aaa2caf
+
Richard Hughes aaa2caf
 	/* Add other instruments here */
Richard Hughes aaa2caf
 
Richard Hughes aaa2caf
 	return instUnknown;
Richard Hughes aaa2caf
@@ -260,6 +269,8 @@ int inst_illuminant(xspect *sp, instType itype) {
Richard Hughes aaa2caf
 		case instHuey:
Richard Hughes aaa2caf
 			return 1;										/* Not applicable */
Richard Hughes aaa2caf
 
Richard Hughes aaa2caf
+		case instColorHug:
Richard Hughes aaa2caf
+			return 1;										/* Not applicable */
Richard Hughes aaa2caf
 
Richard Hughes aaa2caf
 		default:
Richard Hughes aaa2caf
 			break;
Richard Hughes aaa2caf
diff --git a/spectro/insttypes.h b/spectro/insttypes.h
Richard Hughes aaa2caf
index 6155cf0..15c6545 100644
Richard Hughes aaa2caf
--- a/spectro/insttypes.h
Richard Hughes aaa2caf
+++ b/spectro/insttypes.h
Richard Hughes aaa2caf
@@ -44,6 +44,7 @@ typedef enum {
Richard Hughes aaa2caf
 	instSpyder2,				/* Datacolor/ColorVision Spyder2 */
Richard Hughes aaa2caf
 	instSpyder3,				/* Datacolor Spyder3 */
Richard Hughes aaa2caf
 	instHuey,					/* GretagMacbeth Huey */
Richard Hughes aaa2caf
+	instColorHug,				/* Hughski ColorHug */
Richard Hughes aaa2caf
 
Richard Hughes aaa2caf
 } instType;
Richard Hughes aaa2caf
 
Richard Hughes aaa2caf
diff --git a/lib/Makefile.am b/lib/Makefile.am
Richard Hughes aaa2caf
index ad5fcdc..d393228 100644
Richard Hughes aaa2caf
--- a/lib/Makefile.am
Richard Hughes aaa2caf
+++ b/lib/Makefile.am
Richard Hughes aaa2caf
@@ -42,6 +42,7 @@ libargyll_a_SOURCES += ../spectro/inst.h ../spectro/inst.c ../spectro/insttypes.
Richard Hughes aaa2caf
 	../spectro/i1disp.c ../spectro/i1disp.h ../spectro/i1pro.c ../spectro/i1pro.h ../spectro/i1pro_imp.c ../spectro/i1pro_imp.h	\
Richard Hughes aaa2caf
 	../spectro/munki.c ../spectro/munki_imp.c ../spectro/ss.c ../spectro/ss.h ../spectro/ss_imp.c ../spectro/ss_imp.h ../spectro/hcfr.c ../spectro/hcfr.h	\
Richard Hughes aaa2caf
 	../spectro/spyd2.c ../spectro/spyd2.h ../spectro/spyd2setup.h ../spectro/spyd2PLD.h ../spectro/huey.c ../spectro/huey.h ../spectro/unixio.c	\
Richard Hughes aaa2caf
+	../spectro/colorhug.c ../spectro/colorhug.c	\
Richard Hughes aaa2caf
 	../spectro/usbio.c ../spectro/hidio.c ../spectro/pollem.c ../spectro/pollem.h ../spectro/icoms.h ../spectro/conv.h ../spectro/usbio.h	\
Richard Hughes aaa2caf
 	../spectro/hidio.h ../spectro/munki.h ../spectro/munki_imp.h
Richard Hughes aaa2caf