summaryrefslogtreecommitdiff
path: root/spectro/ss_imp.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/ss_imp.c')
-rw-r--r--spectro/ss_imp.c1943
1 files changed, 1943 insertions, 0 deletions
diff --git a/spectro/ss_imp.c b/spectro/ss_imp.c
new file mode 100644
index 0000000..b4c5d8a
--- /dev/null
+++ b/spectro/ss_imp.c
@@ -0,0 +1,1943 @@
+
+#ifndef SS_IMP_H
+
+/*
+ * Argyll Color Correction System
+ *
+ * Gretag Spectrolino and Spectroscan related
+ * defines and declarations - implementation.
+ *
+ * Author: Graeme W. Gill
+ * Date: 14/7/2005
+ *
+ * Copyright 2005 - 2013 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.
+ *
+ * This is an alternative driver to spm/gretag.
+ */
+
+/*
+ If you make use of the instrument driver code here, please note
+ that it is the author(s) of the code who take 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>
+#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 "ss.h"
+
+/* ------------------------------------------- */
+/* Serialisation for different types functions */
+
+/* QUERY: */
+
+/* These add characters to the current send buffer, */
+/* And set the status appropriately */
+
+/* 4 bits to hex character conversion table */
+static char b2h[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+/* Check that there is enough space to write size more characters */
+#define CHWSPACE(size) \
+ if (p->snerr == ss_et_NoError \
+ && (p->sbufe - p->sbuf) < (size)) { \
+ p->snerr = ss_et_SendBufferFull; \
+ } \
+ if (p->snerr != ss_et_NoError) \
+ return;
+
+/* Reset send buffer, and init with start character */
+void ss_init_send(ss *p) {
+ p->snerr = ss_et_NoError;
+ p->sbuf = p->_sbuf;
+ CHWSPACE(1);
+ p->sbuf[0] = ';';
+ p->sbuf += 1;
+}
+
+/* Reset send buffer, and add an Spectrolino Request enum */
+void ss_add_soreq(ss *p, int rq) {
+ ss_init_send(p);
+ CHWSPACE(2);
+ p->sbuf[0] = b2h[(rq >> 4) & 0xf];
+ p->sbuf[1] = b2h[(rq >> 0) & 0xf];
+ p->sbuf += 2;
+}
+
+/* Reset send buffer, and add an SpectroScan Request enum */
+void ss_add_ssreq(ss *p, int rq) {
+ ss_init_send(p);
+ CHWSPACE(4);
+ p->sbuf[0] = b2h[((int)ss_ReqPFX >> 4) & 0xf]; /* Prefix */
+ p->sbuf[1] = b2h[((int)ss_ReqPFX >> 0) & 0xf]; /* Prefix */
+ p->sbuf[2] = b2h[(rq >> 4) & 0xf];
+ p->sbuf[3] = b2h[(rq >> 0) & 0xf];
+ p->sbuf += 4;
+}
+
+/* Add an int/enum/char into one byte type */
+void ss_add_1(ss *p, int c) {
+ CHWSPACE(2);
+ p->sbuf[0] = b2h[(c >> 4) & 0xf];
+ p->sbuf[1] = b2h[(c >> 0) & 0xf];
+ p->sbuf += 2;
+}
+
+/* Add an int/enum into two byte type */
+void ss_add_2(ss *p, int s) {
+ CHWSPACE(4);
+ p->sbuf[0] = b2h[(s >> 4) & 0xf]; /* LSB */
+ p->sbuf[1] = b2h[(s >> 0) & 0xf];
+ p->sbuf[2] = b2h[(s >> 12) & 0xf]; /* MSB */
+ p->sbuf[3] = b2h[(s >> 8) & 0xf];
+ p->sbuf += 4;
+}
+
+/* Add an int/enum into four byte type */
+void ss_add_4(ss *p, int i) {
+ CHWSPACE(8);
+ p->sbuf[0] = b2h[(i >> 4) & 0xf]; /* LSB */
+ p->sbuf[1] = b2h[(i >> 0) & 0xf];
+ p->sbuf[2] = b2h[(i >> 12) & 0xf];
+ p->sbuf[3] = b2h[(i >> 8) & 0xf];
+ p->sbuf[4] = b2h[(i >> 20) & 0xf];
+ p->sbuf[5] = b2h[(i >> 16) & 0xf];
+ p->sbuf[6] = b2h[(i >> 28) & 0xf]; /* MSB */
+ p->sbuf[7] = b2h[(i >> 24) & 0xf];
+ p->sbuf += 8;
+}
+
+/* Add a double into four byte type */
+void ss_add_double(ss *p, double d) {
+ unsigned int id;
+
+ id = doubletoIEEE754(d);
+
+ ss_add_4(p, id);
+}
+
+/* Add an ASCII string into the send buffer. */
+/* The string will be padded with nul's up to len. */
+void ss_add_string(ss *p, char *t, int len) {
+ int i;
+
+ CHWSPACE(2 * len);
+ for (i = 0; i < len; i++) {
+ p->sbuf[2 * i + 0] = b2h[(t[i] >> 4) & 0xf];
+ p->sbuf[2 * i + 1] = b2h[(t[i] >> 0) & 0xf];
+ if (t[i] == '\000')
+ break;
+ }
+ for (; i < len; i++) {
+ p->sbuf[2 * i + 0] = '0';
+ p->sbuf[2 * i + 1] = '0';
+ }
+ p->sbuf += 2 * len;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* ANSWER: */
+
+/* Check that there is not already an error, */
+/* and that there is enough space to read size more characters. */
+/* Return nz if not. */
+static int chrspace(ss *p, int size) {
+
+ if (p->snerr != ss_et_NoError) /* Problem assembling request */
+ return 1;
+
+ if ((p->rbufe - p->rbuf) < (size)) {
+ p->snerr = ss_et_RecBufferEmpty;
+ return 1;
+ }
+ {
+ char *rr = p->rbuf, *re = p->rbuf + size;
+ while (rr < re && *rr != '\000')
+ rr++;
+ if (rr < re) {
+ p->snerr = ss_et_RecBufferEmpty;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Check that the read buffer is fully consumed. */
+static void chended(ss *p) {
+ if (p->snerr == ss_et_NoError /* Don't overrite any existing error */
+ && p->rbufe != p->rbuf) {
+ p->snerr = ss_et_BadAnsFormat;
+ }
+}
+
+/* Convert an ASCII Hex character to an integer. */
+static int h2b(ss *p, char c) {
+ if (c >= '0' && c <= '9')
+ return (c-(int)'0');
+ if (c >= 'A' && c <= 'F')
+ return (10 + c-(int)'A');
+ if (c >= 'a' && c <= 'f')
+ return (10 + c-(int)'a');
+ if (p->snerr == ss_et_NoError) /* Don't overrite any existing error */
+ p->snerr = ss_et_BadHexEncoding;
+ return 0;
+}
+
+/* Return the first enum from the recieve buffer without removing it. */
+int ss_peek_ans(ss *p) {
+ int rv;
+
+ if (chrspace(p, 2))
+ return 0;
+ rv = (h2b(p, p->rbuf[0]) << 4)
+ | (h2b(p, p->rbuf[1]) << 0);
+
+ return rv;
+}
+
+/* Remove a Spectrolino answer enum from the revieve buffer, */
+/* and check it is correct. */
+void ss_sub_soans(ss *p, int cv) {
+ int rv;
+
+ if (chrspace(p, 2))
+ return;
+ rv = (h2b(p, p->rbuf[0]) << 4)
+ | (h2b(p, p->rbuf[1]) << 0);
+ p->rbuf += 2;
+ if (rv != cv) {
+ if (p->snerr == ss_et_NoError) /* Don't overrite any existing error */
+ p->snerr = ss_et_BadAnsFormat;
+ return;
+ }
+ return;
+}
+
+/* Remove a SpectroScan Prefix and answer enum from the revieve buffer, */
+/* and check it is correct. */
+void ss_sub_ssans(ss *p, int cv) {
+ int rv;
+
+ if (chrspace(p, 4))
+ return;
+ if (p->rbuf[0] != 'D'
+ || p->rbuf[1] != '1') {
+ if (p->snerr == ss_et_NoError) /* Don't overrite any existing error */
+ p->snerr = ss_et_BadAnsFormat;
+ return;
+ }
+ rv = (h2b(p, p->rbuf[2]) << 4)
+ | (h2b(p, p->rbuf[3]) << 0);
+ p->rbuf += 4;
+ if (rv != cv) {
+ if (p->snerr == ss_et_NoError) /* Don't overrite any existing error */
+ p->snerr = ss_et_BadAnsFormat;
+ return;
+ }
+ return;
+}
+
+/* Remove an int/enum/char into one byte type */
+int ss_sub_1(ss *p) {
+ int rv;
+
+ if (chrspace(p, 2))
+ return 0;
+ rv = (h2b(p, p->rbuf[0]) << 4)
+ | (h2b(p, p->rbuf[1]) << 0);
+ p->rbuf += 2;
+
+ return rv;
+}
+
+/* Remove an int/enum into two byte type */
+int ss_sub_2(ss *p) {
+ int rv;
+
+ if (chrspace(p, 4))
+ return 0;
+ rv = (h2b(p, p->rbuf[0]) << 4)
+ | (h2b(p, p->rbuf[1]) << 0)
+ | (h2b(p, p->rbuf[2]) << 12)
+ | (h2b(p, p->rbuf[3]) << 8);
+ p->rbuf += 4;
+
+ return rv;
+}
+
+/* Remove an int/enum into four byte type */
+int ss_sub_4(ss *p) {
+ int rv;
+
+ if (chrspace(p, 8))
+ return 0;
+ rv = (h2b(p, p->rbuf[0]) << 4)
+ | (h2b(p, p->rbuf[1]) << 0)
+ | (h2b(p, p->rbuf[2]) << 12)
+ | (h2b(p, p->rbuf[3]) << 8)
+ | (h2b(p, p->rbuf[4]) << 20)
+ | (h2b(p, p->rbuf[5]) << 16)
+ | (h2b(p, p->rbuf[6]) << 28)
+ | (h2b(p, p->rbuf[7]) << 24);
+ p->rbuf += 8;
+
+ return rv;
+}
+
+/* Remove a double into four byte type */
+double ss_sub_double(ss *p) {
+ unsigned int ip;
+ double op;
+
+ ip = (unsigned int)ss_sub_4(p);
+
+ op = IEEE754todouble(ip);
+
+ return op;
+}
+
+/* Remove an ASCII string from the receive buffer. */
+/* The string will be terminated with a nul, so a buffer */
+/* of len+1 should be provided to return the string in. */
+void ss_sub_string(ss *p, char *t, int len) {
+ int i;
+
+ if (chrspace(p, 2 * len))
+ return;
+ for (i = 0; i < len; i++) {
+ t[i] = (char)((h2b(p, p->rbuf[2 * i + 0]) << 4)
+ | (h2b(p, p->rbuf[2 * i + 1]) << 0));
+ }
+ t[i] = '\000';
+ p->rbuf += 2 * len;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* ERROR CODES: */
+
+/* Convert an ss error into an inst_error */
+inst_code ss_inst_err(ss *p) {
+ ss_et ec = p->snerr;
+
+ switch(ec) {
+ case ss_et_NoError:
+ return inst_ok;
+
+ case ss_et_WhiteMeasOK:
+ case ss_et_ResetDone:
+ case ss_et_EmissionCalOK:
+ return inst_notify | ec;
+
+ case ss_et_WhiteMeasWarn:
+ return inst_warning | ec;
+
+ case ss_et_SendTimeout:
+ case ss_et_SerialFail:
+ return inst_coms_fail | ec;
+
+ case ss_et_StopButNoStart:
+ case ss_et_IllegalCharInRec:
+ case ss_et_IncorrectRecLen:
+ case ss_et_IllegalRecType:
+ case ss_et_NoTagField:
+ case ss_et_ConvError:
+ case ss_et_RecBufferEmpty:
+ case ss_et_BadAnsFormat:
+ case ss_et_BadHexEncoding:
+ case ss_et_RecBufferOverun:
+ case ss_et_OutOfRange:
+ case ss_et_NotAnSROrBoolean:
+ return inst_protocol_error | ec;
+
+ case ss_et_RemOverFlow:
+ case ss_et_MeasDisabled:
+ case ss_et_MeasurementError:
+ case ss_et_DorlOutOfRange:
+ case ss_et_ReflectanceOutOfRange:
+ case ss_et_Color1OutOfRange:
+ case ss_et_Color2OutOfRange:
+ case ss_et_Color3OutOfRange:
+ return inst_misread | ec;
+
+ case ss_et_NoValidCommand:
+ case ss_et_NoUserAccess:
+ return inst_unsupported | ec;
+
+ case ss_et_NotReady:
+ case ss_et_NoAccess:
+ return inst_other_error | ec;
+ break;
+
+ case ss_et_SendBufferFull:
+ return inst_internal_error | ec;
+
+ case ss_et_NoValidMeas:
+ case ss_et_OnlyEmission:
+ case ss_et_DensCalError:
+ case ss_et_NoTransmTable:
+ case ss_et_InvalidForEmission:
+ case ss_et_NotInTransmMode:
+ case ss_et_NotInReflectMode:
+ case ss_et_NoValidDStd:
+ case ss_et_NoValidWhite:
+ case ss_et_NoValidIllum:
+ case ss_et_NoValidObserver:
+ case ss_et_NoValidMaxLambda:
+ case ss_et_NoValidSpect:
+ case ss_et_NoValidColSysOrIndex:
+ case ss_et_NoValidChar:
+ case ss_et_NoValidValOrRef:
+ case ss_et_DeviceIsOffline:
+ case ss_et_NoDeviceFound:
+ return inst_wrong_setup | ec;
+
+ case ss_et_BackupError:
+ case ss_et_ProgramROMError:
+ case ss_et_CheckSumWrong:
+ case ss_et_MemoryError:
+ case ss_et_FullMemory:
+ case ss_et_EPROMFailure:
+ case ss_et_MemoryFailure:
+ case ss_et_PowerFailure:
+ case ss_et_LampFailure:
+ case ss_et_HardwareFailure:
+ case ss_et_DriveError:
+ case ss_et_FilterOutOfPos:
+ case ss_et_ProgrammingError:
+ return inst_hardware_fail | ec;
+
+ }
+ return inst_other_error | ec;
+}
+
+/* Incorporate a error into the snerr value */
+void ss_incorp_err(ss *p, ss_et se) {
+ if (p->snerr != ss_et_NoError) /* Don't overrite any existing error */
+ return;
+ if (se == ss_set_NoError)
+ return;
+
+ p->snerr = se;
+}
+
+/* Incororate Remote Error Set values into snerr value */
+/* Since ss_res is a bit mask, we just prioritize the errors. */
+void ss_incorp_remerrset(ss *p, ss_res es) {
+ int i, ii;
+ if (p->snerr != ss_et_NoError) /* Don't overrite any existing error */
+ return;
+ if (es == ss_res_NoError)
+ return;
+
+ for (i = ss_et_NoValidDStd, ii = 0x0001; ii < 0x10000; i++, ii <<= 1) {
+ if ((ii & es) != 0) {
+ break;
+ }
+ }
+ p->snerr = i;
+}
+
+/* Incorporate a scan error into the snerr value */
+void ss_incorp_scanerr(ss *p, ss_set se) {
+ if (p->snerr != ss_et_NoError) /* Don't overrite any existing error */
+ return;
+ if (se == ss_set_NoError)
+ return;
+
+ p->snerr = se + ss_et_DeviceIsOffline - 1;
+}
+
+/* Incorporate a device communication error into the snerr value */
+void ss_incorp_comerr(ss *p, ss_cet se) {
+ if (p->snerr != ss_et_NoError) /* Don't overrite any existing error */
+ return;
+ if (se == ss_cet_NoError)
+ return;
+
+ p->snerr = se + ss_et_StopButNoStart - 1;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* EXECUTION: */
+
+/* Interpret an icoms error into a SS error */
+int icoms2ss_err(int se) {
+ if (se != ICOM_OK)
+ return ss_et_SerialFail;
+ return ss_et_NoError;
+}
+
+/* Terminate the send buffer, and then do a */
+/* send/receieve to the device. */
+/* Leave any error in p->snerr */
+void ss_command(ss *p, double tmo) {
+ int se;
+
+ CHWSPACE(3);
+ p->sbuf[0] = '\r';
+ p->sbuf[1] = '\n';
+ p->sbuf[2] = '\00'; /* write_read terminates on nul */
+
+ p->rbuf = p->_rbuf; /* Reset read pointer */
+ if ((se = p->icom->write_read(p->icom, p->_sbuf, p->_rbuf, SS_MAX_RD_SIZE, '\n', 1, tmo)) != 0) {
+ p->snerr = icoms2ss_err(se);
+ return;
+ }
+
+ /* Figure out receive size, and clean up termination. */
+ p->rbufe = p->_rbuf + strlen(p->_rbuf);
+ if ((p->rbufe - p->rbuf) >= 1 && p->rbufe[-1] == '\n') {
+ --p->rbufe;
+ *p->rbufe = '\000';
+ }
+ if ((p->rbufe - p->rbuf) >= 1 && p->rbufe[-1] == '\r') {
+ --p->rbufe;
+ *p->rbufe = '\000';
+ }
+
+ /* Check receive format and look for a COM error */
+ if ((p->rbufe - p->rbuf) < 1 || p->rbuf[0] != ':') {
+ p->snerr = ss_et_BadAnsFormat;
+ return;
+ }
+ p->rbuf++;
+
+ /* See if this is a Spectroscan COM error */
+ if ((p->rbufe - p->rbuf) >= 2
+ && p->rbuf[0] == 'D'
+ && p->rbuf[1] == '1') {
+
+ if ((p->rbufe - p->rbuf) >= 4
+ && p->rbuf[0] == 'A'
+ && p->rbuf[1] == '0') { /* COMM Error */
+ p->rbuf += 4;
+ ss_incorp_comerr(p, (ss_cet)ss_sub_1(p));
+ return;
+ }
+ }
+
+ /* See if it is a Spectrolino COMM error */
+ if ((p->rbufe - p->rbuf) >= 2
+ && p->rbuf[0] == '2'
+ && p->rbuf[1] == '6') { /* COMM Error */
+ p->rbuf += 2;
+ ss_incorp_comerr(p, (ss_cet)ss_sub_1(p));
+ return;
+ }
+ return;
+}
+
+/* =================================================================== */
+/* Complete Spectrolino instructions. */
+/* [This is less elegant than Neil Okamoto's */
+/* table approach, but is more flexible, */
+/* and it does the job.] */
+
+/* - - - - - - - - - - - - - - - - - - - - */
+/* Device Initialisation and configuration */
+
+/* Reset instrument */
+inst_code so_do_ResetStatusDownload(
+ss *p,
+ss_smt sm /* Init all or all except communications */
+) {
+ ss_add_soreq(p, ss_ResetStatusDownload);
+ ss_add_1(p, 0x01);
+ ss_add_1(p, 0x04);
+ ss_add_1(p, sm);
+ ss_command(p, IT_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Load various parameters, such as: */
+/* comm flow control, baud rate, speaker, */
+/* reflective/tranmission/emmission mode, */
+/* custom filter on/off */
+inst_code so_do_MeasControlDownload(
+ss *p,
+ss_ctt ct /* Control */
+) {
+ ss_add_soreq(p, ss_MeasControlDownload);
+ ss_add_1(p, ct);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+
+ return ss_inst_err(p);
+}
+
+/* Query various current parameters, such as: */
+/* comm flow control, baud rate, speaker, */
+/* reflective/tranmission/emmission mode, */
+/* custom filter on/off. */
+inst_code so_do_MeasControlRequest(
+ss *p,
+ss_ctt ct, /* Control to query */
+ss_ctt *rct /* Return current state */
+) {
+ ss_add_soreq(p, ss_MeasControlRequest);
+ ss_add_1(p, ct);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_MeasControlAnswer);
+ ss_sub_1(p); /* Should be the same as ct */
+ *rct = ss_sub_1(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Queries specific device data */
+inst_code so_do_DeviceDataRequest(
+ss *p,
+char dn[19], /* Return the device name */
+ss_dnot *dno, /* Return device number */
+char pn[9], /* Return the part number */
+unsigned int *sn, /* Return serial number */
+char sv[13] /* Return software version */
+) {
+ char rsv[17]; /* Space for resered field */
+ ss_add_soreq(p, ss_DeviceDataRequest);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DeviceDataAnswer);
+ ss_sub_string(p, dn, 18);
+ *dno = ss_sub_1(p);
+ ss_sub_string(p, pn, 8);
+ *sn = (unsigned int)ss_sub_4(p);
+ ss_sub_string(p, sv, 12);
+ ss_sub_string(p, rsv, 16);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query special device data */
+inst_code so_do_TargetIdRequest(
+ss *p,
+char dn[19], /* Return Device Name */
+int *sn, /* Return Serial Number (1-65535) */
+int *sr, /* Return Software Release */
+int *yp, /* Return Year of production (e.g. 1996) */
+int *mp, /* Return Month of production (1-12) */
+int *dp, /* Return Day of production (1-31) */
+int *hp, /* Return Hour of production (0-23) */
+int *np, /* Return Minuite of production (0-59) */
+ss_ttt *tt, /* Return Target Tech Type (SPM/Spectrolino etc.) */
+int *fswl, /* Return First spectral wavelength (nm) */
+int *nosw, /* Return Number of spectral wavelengths */
+int *dpsw /* Return Distance between spectral wavelengths (nm) */
+) {
+ ss_add_soreq(p, ss_TargetIdRequest);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_TargetIdAnswer);
+ ss_sub_string(p, dn, 18);
+ *sn = ss_sub_2(p);
+ *sr = ss_sub_2(p);
+ *yp = ss_sub_2(p);
+ *mp = ss_sub_2(p);
+ *dp = ss_sub_2(p);
+ *hp = ss_sub_2(p);
+ *np = ss_sub_2(p);
+ *tt = ss_sub_1(p);
+ *fswl = ss_sub_2(p);
+ *nosw = ss_sub_2(p);
+ *dpsw = ss_sub_2(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* - - - - - - - - - - - - - */
+/* Measurement configuration */
+
+/* Query the standard or user definable densitometric tables */
+inst_code so_do_DensTabRequest(
+ss *p,
+ss_dst ds, /* Density standard (ANSI/DIN/User etc.) */
+ss_dst *rds, /* Return Density standard (ANSI/DIN/User etc.) */
+double sp[4][36] /* Return 4 * 36 spectral weighting values */
+) {
+ int n,i;
+ ss_add_soreq(p, ss_DensTabRequest);
+ ss_add_1(p, 0x00);
+ ss_add_1(p, ds);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DensTabAnswer);
+ ss_sub_soans(p, 0x00);
+ *rds = ss_sub_1(p);
+ for (n = 0; n < 4; n++)
+ for (i = 0; i < 36; i++)
+ sp[n][i] = ss_sub_double(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Download user definable densitometric tables */
+inst_code so_do_DensTabDownload(
+ss *p,
+double sp[4][36] /* 4 * 36 spectral weighting values */
+) {
+ int i, n;
+ ss_add_soreq(p, ss_DensTabDownload);
+ ss_add_1(p, 0x08);
+ for (n = 0; n < 4; n++)
+ for (i = 0; i < 36; i++)
+ ss_add_double(p, sp[n][i]);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Set slope values for densitometry */
+inst_code so_do_SlopeDownload(
+ss *p,
+double dv[4] /* Db Dc Dm Dy density values */
+) {
+ int i;
+ ss_add_soreq(p, ss_SlopeDownload);
+ for (i = 0; i < 4; i++)
+ ss_add_double(p, dv[i]);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query slope values of densitometry */
+inst_code so_do_SlopeRequest(
+ss *p,
+double dv[4] /* Return Db Dc Dm Dy density values */
+) {
+ int i;
+ ss_add_soreq(p, ss_SlopeRequest);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_SlopeAnswer);
+ for (i = 0; i < 4; i++)
+ dv[i] = ss_sub_double(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Set the colorimetric parameters */
+inst_code so_do_ParameterDownload(
+ss *p,
+ss_dst ds, /* Density standard (ANSI/DIN etc.) */
+ss_wbt wb, /* White base (Paper/Absolute) */
+ss_ilt it, /* Illuminant type (A/C/D65 etc.) */
+ss_ot ot /* Observer type (2deg/10deg) */
+) {
+ ss_add_soreq(p, ss_ParameterDownload);
+ ss_add_1(p, ds);
+ ss_add_1(p, wb);
+ ss_add_1(p, it);
+ ss_add_1(p, ot);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query colorimetric parameters */
+inst_code so_do_ParameterRequest(
+ss *p,
+ss_dst *ds, /* Return Density Standard */
+ss_wbt *wb, /* Return White Base */
+ss_ilt *it, /* Return Illuminant type (A/C/D65/User etc.) */
+ss_ot *ot, /* Return Observer type (2deg/10deg) */
+ss_aft *af /* Return Filter being used (None/Pol/D65/UV/custom */
+) {
+ ss_add_soreq(p, ss_ParameterRequest);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_ParameterAnswer);
+ *ds = ss_sub_1(p);
+ *wb = ss_sub_1(p);
+ *it = ss_sub_1(p);
+ *ot = ss_sub_1(p);
+ *af = ss_sub_1(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the standard or user defined illuminant tables (Colorimetry) */
+inst_code so_do_IllumTabRequest(
+ss *p,
+ss_ilt it, /* Illuminant type (A/C/D65/User etc.) */
+ss_ilt *rit, /* Return Illuminant type (A/C/D65/User etc.) */
+double sp[36] /* Return 36 spectral values */
+) {
+ int i;
+ ss_add_soreq(p, ss_IllumTabRequest);
+ ss_add_1(p, 0x00);
+ ss_add_1(p, it);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_IllumTabAnswer);
+ ss_sub_soans(p, 0x00);
+ *rit = ss_sub_1(p);
+ for (i = 0; i < 36; i++)
+ sp[i] = ss_sub_double(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Download user definable illuminant tables (Colorimetry) */
+inst_code so_do_IllumTabDownload(
+ss *p,
+double sp[36] /* 36 spectral values to set */
+) {
+ int i;
+ ss_add_soreq(p, ss_IllumTabDownload);
+ ss_add_1(p, 0x08);
+ for (i = 0; i < 36; i++)
+ ss_add_double(p, sp[i]);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query for the color temperature of daylight illuminant Dxx */
+inst_code so_do_GetValNr(
+ss *p,
+int *ct /* Return color temperature in deg K/100 */
+) {
+ ss_add_soreq(p, ss_GetValNr);
+ ss_add_1(p, 0x60);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_ValNrAnswer);
+ ss_sub_soans(p, 0x60);
+ *ct = ss_sub_1(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Download user definable illuminant tables (Colorimetry) */
+inst_code so_do_SetValNr(
+ss *p,
+int ct /* Color temperature to set for illuminant Dxx in deg K/100 */
+) {
+ ss_add_soreq(p, ss_IllumTabDownload);
+ ss_add_1(p, 0x60);
+ ss_add_2(p, ct);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Queries the spectra of the white reference for the desired filter */
+inst_code so_do_WhiteReferenceRequest(
+ss *p,
+ss_aft af, /* Filter being queried (None/Pol/D65/UV/custom */
+ss_aft *raf, /* Return filter being queried (None/Pol/D65/UV/custom */
+double sp[36], /* Return 36 spectral values */
+ss_owrt *owr, /* Return original white reference */
+char dtn[19] /* Return name of data table */
+) {
+ int i;
+ ss_add_soreq(p, ss_WhiteReferenceRequest);
+ ss_add_1(p, af);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_WhiteReferenceAnswer);
+ *raf = ss_sub_1(p);
+ for (i = 0; i < 36; i++)
+ sp[i] = ss_sub_double(p);
+ *owr = ss_sub_1(p);
+ ss_sub_string(p, dtn, 18);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Load spectra of a user defined white reference for the desired filter. */
+/* A name can be given to the white reference. */
+inst_code so_do_WhiteReferenceDownld(
+ss *p,
+ss_aft af, /* Filter being set (None/Pol/D65/UV/custom */
+double sp[36], /* 36 spectral values being set */
+char dtn[19] /* Name for data table */
+) {
+ int i;
+ ss_add_soreq(p, ss_WhiteReferenceDownld);
+ ss_add_1(p, af);
+ for (i = 0; i < 36; i++)
+ ss_add_double(p, sp[i]);
+ ss_add_string(p, dtn, 18);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the reference value for the relative photometric (emission) reference value */
+inst_code so_do_FloatRequest(
+ss *p,
+ss_comft comf, /* Choose common float type (PhotometricYRef) */
+ss_comft *rcomf, /* Return common float type (PhotometricYRef) */
+double *comfv /* Return the reference value */
+) {
+ ss_add_soreq(p, ss_FloatRequest);
+ ss_add_1(p, comf);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_FloatAnswer);
+ *rcomf = ss_sub_1(p);
+ *comfv = ss_sub_double(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Set the reference value for the relative photometric (emission) reference value */
+inst_code so_do_FloatDownload(
+ss *p,
+ss_comft comf, /* Choose common float type (PhotometricYRef) */
+double comfv /* The reference value */
+) {
+ ss_add_soreq(p, ss_FloatDownload);
+ ss_add_1(p, comf);
+ ss_add_double(p, comfv);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* - - - - - - */
+/* Calibration */
+
+/* Reset the spectra of the respective white reference to the original data */
+inst_code so_do_ExecWhiteRefToOrigDat(
+ss *p
+) {
+ ss_add_soreq(p, ss_ExecWhiteRefToOrigDat);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError); /* Instrument behavour doesn't match doco. */
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+
+/* Perform a Reference Measurement */
+inst_code so_do_ExecRefMeasurement(
+ss *p,
+ss_mmt mm /* Measurement Mode (Meas/Cal etc.) */
+) {
+#ifdef EMSST
+ if (p->tmode != 0) {
+ ss_rvt rv;
+ double sp[36];
+ ss_nmt nm;
+ p->tmode = 0;
+ ss_do_MoveAndMeasure(p, 155.0, 230.0, sp, &rv);
+ so_do_NewMeasureRequest(p, &nm);
+ ss_do_MoveAbsolut(p, p->sbr, p->sbx, p->sby);
+ p->tmode = 1;
+ return (inst_notify | ss_et_WhiteMeasOK);
+ }
+#endif
+ ss_add_soreq(p, ss_ExecRefMeasurement);
+ ss_add_1(p, 0x09);
+ ss_add_1(p, mm);
+ ss_command(p, 2.0 * DF_TMO);
+ ss_sub_soans(p, ss_ExecError);
+ ss_incorp_err(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Perform a White Measurement - not recommended */
+/* (ExecRefMeasuremen is preferred instead) */
+inst_code so_do_ExecWhiteMeasurement(
+ss *p
+) {
+ ss_add_soreq(p, ss_ExecWhiteMeasurement);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_ExecError);
+ ss_incorp_err(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* - - - - - - - - - - - - */
+/* Performing measurements */
+
+
+/* Perform a Measurement */
+inst_code so_do_ExecMeasurement(
+ss *p
+) {
+#ifdef EMSST
+ if (p->tmode != 0) {
+ inst_code rc;
+ p->tmode = 0;
+ ss_do_MoveAbsolut(p, ss_rt_SensorRef, 155.0, 230.0);
+ ss_do_MoveDown(p);
+ rc = so_do_ExecMeasurement(p);
+ ss_do_MoveUp(p);
+ ss_do_MoveAbsolut(p, p->sbr, p->sbx, p->sby);
+ p->tmode = 1;
+ return rc;
+ }
+#endif
+ ss_add_soreq(p, ss_ExecMeasurement);
+ ss_command(p, 2.0 * DF_TMO);
+ ss_sub_soans(p, ss_ExecError);
+ ss_incorp_err(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Define automatic output after each measurement */
+/* [Note that dealing with the resulting measurement replies */
+/* isn't directly supported, currently.] */
+inst_code so_do_SetMeasurementOutput(
+ss *p,
+ss_ost os, /* Type of output to request */
+ss_os o /* bitmask of output */
+) {
+ ss_add_soreq(p, ss_SetMeasurementOutput);
+ ss_add_1(p, os);
+ ss_add_1(p, o.i);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+
+ return ss_inst_err(p);
+}
+
+/* - - - - - - - - */
+/* Getting results */
+
+/* so_do_Printout isn't currently defined. */
+/* It needs to cope with the expected number of values that */
+/* will be returned. This could probably be figured out from */
+/* the string itself ? */
+
+/* Query Density measurement results and associated parameters */
+inst_code so_do_DensityParameterRequest(
+ss *p,
+ss_cst *rct, /* Return Color Type (Lab/XYZ etc.) */
+double dv[4], /* Return Db Dc Dm Dy density values */
+ss_sdft *sdf, /* Return Standard Density Filter (Db/Dc/Dm/Dy) */
+ss_rvt *rv, /* Return Reference Valid Flag */
+ss_aft *af, /* Return filter being used (None/Pol/D65/UV/custom */
+ss_wbt *wb, /* Return white base (Paper/Absolute) */
+ss_dst *ds, /* Return Density standard (ANSI/DIN/User etc.) */
+ss_ilt *rit, /* Return Illuminant type (A/C/D65/User etc.) */
+ss_ot *ot /* Return Observer type (2deg/10deg) */
+) {
+ int i;
+ ss_add_soreq(p, ss_DensityParameterRequest);
+ ss_add_1(p, 0x09);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DensityParameterAnswer);
+ ss_sub_soans(p, 0x09);
+ for (i = 0; i < 4; i++)
+ dv[i] = ss_sub_double(p);
+ *sdf = ss_sub_1(p);
+ *rv = ss_sub_1(p);
+ *af = ss_sub_1(p);
+ *wb = ss_sub_1(p);
+ ss_sub_soans(p, 0x02);
+ *ds = ss_sub_1(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query Densitometric measurement values - not recommended */
+/* (DensityParameterRequest is preferred instead) */
+inst_code so_do_DensityRequest(
+ss *p,
+double dv[4], /* Return Db Dc Dm Dy density values */
+ss_sdft *sdf, /* Return Standard Density Filter (Db/Dc/Dm/Dy) */
+ss_rvt *rv /* Return Reference Valid */
+) {
+ int i;
+ ss_add_soreq(p, ss_DensityRequest);
+ ss_add_1(p, 0x09);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DensityAnswer);
+ ss_sub_soans(p, 0x09);
+ for (i = 0; i < 4; i++)
+ dv[i] = ss_sub_double(p);
+ *sdf = ss_sub_1(p);
+ *rv = ss_sub_1(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query maximum density reading */
+inst_code so_do_DmaxRequest(
+ss *p,
+double *Dmax, /* Return Value of Maximum Density */
+int *lambda, /* Return wavelength where maximum density was found */
+ss_dmot *dmo, /* Return Dmax OK flag. */
+ss_rvt *rv /* Return Reference Valid Flag */
+) {
+ ss_add_soreq(p, ss_DmaxRequest);
+ ss_add_1(p, 0x09);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DmaxAnswer);
+ ss_sub_soans(p, 0x09);
+ *Dmax = ss_sub_double(p);
+ *lambda = ss_sub_2(p);
+ *dmo = ss_sub_1(p);
+ *rv = ss_sub_1(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query Color measurement results and associated parameters */
+inst_code so_do_CParameterRequest(
+ss *p,
+ss_cst ct, /* Choose Color Type (Lab/XYZ etc.) */
+ss_cst *rct, /* Return Color Type (Lab/XYZ etc.) */
+double cv[3], /* Return 3 color values */
+ss_rvt *rv, /* Return Reference Valid Flag */
+ss_aft *af, /* Return filter being used (None/Pol/D65/UV/custom) */
+ss_wbt *wb, /* Return white base (Paper/Absolute) */
+ss_ilt *it, /* Return Illuminant type (A/C/D65/User etc.) */
+ss_ot *ot /* Return Observer type (2deg/10deg) */
+) {
+ int i;
+ ss_add_soreq(p, ss_CParameterRequest);
+ ss_add_1(p, 0x09);
+ ss_add_1(p,ct);
+ ss_command(p, DF_TMO);
+
+ ss_sub_soans(p, ss_CParameterAnswer);
+ ss_sub_soans(p, 0x09);
+ *rct = ss_sub_1(p);
+ for (i = 0; i < 3; i++)
+ cv[i] = ss_sub_double(p);
+ *rv = ss_sub_1(p);
+ *af = ss_sub_1(p);
+ *wb = ss_sub_1(p);
+ ss_sub_soans(p, 0x02);
+ *it = ss_sub_1(p);
+ *ot = ss_sub_1(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query Colorimetric measurement results - not recommended */
+/* (CParameterRequest is prefered instead) */
+inst_code so_do_CRequest(
+ss *p,
+ss_cst *ct, /* Return Color Type (Lab/XYZ etc.) */
+double *cv[3], /* Return 3 color values */
+ss_rvt *rv /* Return Reference Valid Flag */
+) {
+ int i;
+ ss_add_soreq(p, ss_CRequest);
+ ss_add_1(p, 0x09);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_CAnswer);
+ ss_sub_soans(p, 0x09);
+ *ct = ss_sub_1(p);
+ for (i = 0; i < 3; i++)
+ *cv[i] = ss_sub_double(p);
+ *rv = ss_sub_1(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query Spectral measurement results and associated parameters */
+inst_code so_do_SpecParameterRequest(
+ss *p,
+ss_st st, /* Choose Spectrum Type (Reflectance/Density) */
+ss_st *rst, /* Return Spectrum Type (Reflectance/Density) */
+double sp[36], /* Return 36 spectral values */
+ss_rvt *rv, /* Return Reference Valid Flag */
+ss_aft *af, /* Return filter being used (None/Pol/D65/UV/custom */
+ss_wbt *wb /* Return white base (Paper/Absolute) */
+) {
+ int i;
+ ss_add_soreq(p, ss_SpecParameterRequest);
+ ss_add_1(p, 0x09);
+ ss_add_1(p,st);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_SpecParameterAnswer);
+ ss_sub_soans(p, 0x09);
+ *rst = ss_sub_1(p);
+ for (i = 0; i < 36; i++)
+ sp[i] = ss_sub_double(p);
+ *rv = ss_sub_1(p);
+ *af = ss_sub_1(p);
+ *wb = ss_sub_1(p);
+ ss_sub_soans(p, 0x02);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query Spectral measurement results - not recommended */
+/* (SpecParameterRequest is preferred instead) */
+inst_code so_do_SpectrumRequest(
+ss *p,
+ss_st *st, /* Return Spectrum Type (Reflectance/Density) */
+double sp[36], /* Return 36 spectral values */
+ss_rvt *rv /* Return Reference Valid Flag */
+) {
+ int i;
+ ss_add_soreq(p, ss_SpectrumRequest);
+ ss_add_1(p, 0x09);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_SpectrumAnswer);
+ ss_sub_soans(p, 0x09);
+ *st = ss_sub_1(p);
+ for (i = 0; i < 36; i++)
+ sp[i] = ss_sub_double(p);
+ *rv = ss_sub_1(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* - - - - - - */
+/* Miscelanious */
+
+/* Query whether a new measurement was performed since the last access */
+inst_code so_do_NewMeasureRequest(
+ss *p,
+ss_nmt *nm /* Return New Measurement (None/Meas/White etc.) */
+) {
+ ss_add_soreq(p, ss_NewMeasureRequest);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_NewMeasureAnswer);
+ if (nm != NULL)
+ *nm = ss_sub_1(p);
+ ss_sub_soans(p, 0x09);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query whether a key was pressed since the last access */
+inst_code so_do_NewKeyRequest(
+ss *p,
+ss_nkt *nk, /* Return whether a new key was pressed */
+ss_ks *k /* Return the key that was pressed (none/meas) */
+) {
+ ss_add_soreq(p, ss_NewKeyRequest);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_NewKeyAnswer);
+ *nk = ss_sub_1(p);
+ *k = ss_sub_2(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query for the general error status */
+inst_code so_do_ActErrorRequest(
+ss *p
+) {
+ ss_add_soreq(p, ss_ActErrorRequest);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_ActErrorAnswer);
+ ss_incorp_err(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Set Target On/Off status (locks key function of device?) */
+inst_code so_do_TargetOnOffStDownload(
+ss *p,
+ss_toost oo /* Activated/Deactivated */
+) {
+ ss_add_soreq(p, ss_TargetOnOffStDownload);
+ ss_add_1(p, 0x00);
+ ss_add_1(p, oo);
+ ss_add_1(p, 0x00);
+ ss_command(p, DF_TMO);
+ ss_sub_soans(p, ss_DownloadError);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* =========================================== */
+/* SpectroScan/T specific commands and queries */
+
+/* - - - - - - - - - - - - - - - - - - - - */
+/* Device Initialisation and configuration */
+
+/* Initialise the device. Scans the Spectrolino */
+/* (Doesn't work when device is offline ) */
+inst_code ss_do_ScanInitializeDevice(ss *p) {
+ ss_add_ssreq(p, ss_InitializeDevice);
+ ss_command(p, IT_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Establish communications between the SpectroScan and Spectrolino */
+/* at the highest possible baud rate. */
+/* (Doesn't work when device is offline ) */
+inst_code ss_do_ScanSpectrolino(ss *p) {
+ ss_add_ssreq(p, ss_ScanSpectrolino);
+ ss_command(p, IT_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Establish the zero position of the motors and set the position to 0,0 */
+/* (Doesn't work when device is offline ) */
+inst_code ss_do_InitMotorPosition(ss *p) {
+ ss_add_ssreq(p, ss_InitMotorPosition);
+ ss_command(p, MV_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Change the SpectroScan baud rate */
+inst_code ss_do_ChangeBaudRate(
+ss *p,
+ss_bt br /* Baud rate (110 - 57600) */
+) {
+ ss_add_ssreq(p, ss_ChangeBaudRate);
+ ss_add_1(p, br);
+ ss_command(p, SH_TMO); /* Don't really expect an answer */
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p); /* Will probably bomb because comms will break down */
+}
+
+/* Change the SpectroScan handshaking mode. */
+inst_code ss_do_ChangeHandshake(
+ss *p,
+ss_hst hs /* Handshake type (None/XonXoff/HW) */
+) {
+ ss_add_ssreq(p, ss_ChangeHandshake);
+ ss_add_1(p, hs);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the type of XY table */
+inst_code ss_do_OutputType(
+ss *p,
+char dt[19] /* Return Device Type ("SpectroScan", "SpectroScan " or "SpectroScanT") */
+) {
+ ss_add_ssreq(p, ss_OutputType);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_TypeAnswer);
+ ss_sub_string(p, dt, 18);
+ chended(p);
+#ifdef EMSST
+ if (strcmp(dt,"SpectroScan ") == 0
+ || strcmp(dt,"SpectroScan") == 0)
+ sprintf(dt,"SpectroScanT");
+#endif
+ return ss_inst_err(p);
+}
+
+/* Query the serial number of the XY table */
+inst_code ss_do_OutputSerialNumber(
+ss *p,
+unsigned int *sn /* Return serial number */
+) {
+ ss_add_ssreq(p, ss_OutputSerialNumber);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_SerialNumberAnswer);
+ *sn = (unsigned int)ss_sub_4(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the part number of the XY table */
+inst_code ss_do_OutputArticleNumber(
+ss *p,
+char pn[9] /* Return Part Number */
+) {
+ ss_add_ssreq(p, ss_OutputArticleNumber);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ArticleNumberAnswer);
+ ss_sub_string(p, pn, 8);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the production date of the XY table */
+inst_code ss_do_OutputProductionDate(
+ss *p,
+int *yp, /* Return Year of production (e.g. 1996) */
+int *mp, /* Return Month of production (1-12) */
+int *dp /* Return Day of production (1-31) */
+) {
+ ss_add_ssreq(p, ss_OutputProductionDate);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ProductionDateAnswer);
+ *dp = ss_sub_2(p);
+ *mp = ss_sub_2(p);
+ *yp = ss_sub_2(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the Software Version of the XY table */
+inst_code ss_do_OutputSoftwareVersion(
+ss *p,
+char sv[13] /* Return Software Version */
+) {
+ ss_add_ssreq(p, ss_OutputSoftwareVersion);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_SoftwareVersionAnswer);
+ ss_sub_string(p, sv, 12);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* - - - - - - - - - - - - - */
+/* Measurement configuration */
+
+/* Set the SpectroScanT to reflectance or transmission. */
+/* The Spectrolino is also automatically set to the corresponding mode. */
+/* (Doesn't work when device is offline ) */
+inst_code ss_do_SetTableMode(
+ss *p,
+ss_tmt tm /* Table mode (Reflectance/Transmission) */
+) {
+#ifdef EMSST
+ if (tm == ss_tmt_Transmission) {
+ if (p->tmode == 0) {
+ ss_do_MoveAbsolut(p, p->sbr, p->sbx, p->sby); /* ?? */
+ }
+ p->tmode = 1;
+ } else {
+ if (p->tmode != 0) {
+ p->tmode = 0;
+ ss_do_MoveHome(p);
+ }
+ p->tmode = 0;
+ }
+ return inst_ok;
+#endif
+ ss_add_ssreq(p, ss_SetTableMode);
+ ss_add_1(p, tm);
+ ss_command(p, IT_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* - - - - - - - - - - - - - */
+/* Table operation */
+
+/* Set the SpectrScan to online. All moving keys are disabled. */
+/* (Only valid when device is in reflectance mode.) */
+inst_code ss_do_SetDeviceOnline(ss *p) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ *((char *)0) = 55;
+// return inst_unsupported;
+#endif
+ ss_add_ssreq(p, ss_SetDeviceOnline);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Set the SpectrScan to offline. All moving keys are enabled. */
+/* (Only valid when device is in reflectance mode.) */
+/* (All remote commands to move the device will be ignored.) */
+inst_code ss_do_SetDeviceOffline(ss *p) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ *((char *)0) = 55;
+#endif
+ ss_add_ssreq(p, ss_SetDeviceOffline);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Enable electrostatic paper hold. */
+/* (Not valid when device is offline) */
+inst_code ss_do_HoldPaper(ss *p) {
+ ss_add_ssreq(p, ss_HoldPaper);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Disable electrostatic paper hold. */
+/* (Not valid when device is offline) */
+inst_code ss_do_ReleasePaper(ss *p) {
+ ss_add_ssreq(p, ss_ReleasePaper);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* - - - - - - */
+/* Positioning */
+
+/* Move either the sight or sensor to an absolute position. */
+/* (Doesn't work when device is offline or transmissioin mode.) */
+inst_code ss_do_MoveAbsolut(
+ss *p,
+ss_rt r, /* Reference (Sensor/Sight) */
+double x, /* X coord in mm, 0-310.0, accurate to 0.1mm */
+double y /* Y coord in mm, 0-230.0, accurate to 0.1mm */
+) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ *((char *)0) = 55;
+#endif
+ ss_add_ssreq(p, ss_MoveAbsolut);
+ ss_add_1(p, r);
+ ss_add_2(p, (int)(x * 10 + 0.5));
+ ss_add_2(p, (int)(y * 10 + 0.5));
+ ss_command(p, MV_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Move relative to current position. */
+/* (Doesn't work when device is offline or transmissioin mode.) */
+inst_code ss_do_MoveRelative(
+ss *p,
+double x, /* X distance in mm, 0-310.0, accurate to 0.1mm */
+double y /* Y distance in mm, 0-230.0, accurate to 0.1mm */
+) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ *((char *)0) = 55;
+#endif
+ ss_add_ssreq(p, ss_MoveRelative);
+ ss_add_2(p, (int)(x * 10 + 0.5));
+ ss_add_2(p, (int)(y * 10 + 0.5));
+ ss_command(p, MV_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Move to the home position (== 0,0). */
+/* (Doesn't work when device is offline or transmissioin mode.) */
+inst_code ss_do_MoveHome(
+ss *p
+) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ *((char *)0) = 55;
+#endif
+ ss_add_ssreq(p, ss_MoveHome);
+ ss_command(p, MV_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Move to the sensor up. */
+/* (Doesn't work when device is offline or transmissioin mode.) */
+inst_code ss_do_MoveUp(
+ss *p
+) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ *((char *)0) = 55;
+#endif
+ ss_add_ssreq(p, ss_MoveUp);
+ ss_command(p, MV_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Move to the sensor down. */
+/* (Doesn't work when device is offline or transmission mode.) */
+inst_code ss_do_MoveDown(
+ss *p
+) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ *((char *)0) = 55;
+#endif
+ ss_add_ssreq(p, ss_MoveDown);
+ ss_command(p, MV_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the current absolute position of the sensor or sight. */
+/* (Doesn't work when device is offline or transmissioin mode.) */
+inst_code ss_do_OutputActualPosition(
+ss *p,
+ss_rt r, /* Reference (Sensor/Sight) */
+ss_rt *rr, /* Return reference (Sensor/Sight) */
+double *x, /* Return the X coord in mm, 0-310.0, accurate to 0.1mm */
+double *y, /* Return the Y coord in mm, 0-230.0, accurate to 0.1mm */
+ss_zkt *zk /* Return the Z coordinate (Up/Down) */
+) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ *((char *)0) = 55;
+#endif
+ ss_add_ssreq(p, ss_OutputActualPosition);
+ ss_add_1(p, r);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_PositionAnswer);
+ *rr = ss_sub_1(p);
+ ss_sub_soans(p, 0x00);
+ ss_sub_soans(p, 0x00);
+ *x = ss_sub_2(p)/10.0;
+ *y = ss_sub_2(p)/10.0;
+ *zk = ss_sub_1(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Move to the white reference position */
+/* (Doesn't work when device is offline or transmissioin mode.) */
+inst_code ss_do_MoveToWhiteRefPos(
+ss *p,
+ss_wrpt wrp /* White Reference Position (Tile1/Tile2) */
+) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ *((char *)0) = 55;
+#endif
+ ss_add_ssreq(p, ss_MoveToWhiteRefPos);
+ ss_add_1(p, wrp);
+ ss_command(p, MV_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* - - - - - - - - - - - - */
+/* Performing measurements */
+
+/* Move the sensor to an absolute position, move the */
+/* sensor down, execute a measurement, move the head up, */
+/* and return spectral measuring results. */
+inst_code ss_do_MoveAndMeasure(
+ss *p,
+double x, /* X coord in mm, 0-310.0, accurate to 0.1mm */
+double y, /* Y coord in mm, 0-230.0, accurate to 0.1mm */
+double sp[36], /* Return 36 spectral values */
+ss_rvt *rv /* Return Reference Valid Flag */
+) {
+#ifdef EMSST
+ /* Not sure if this is valid on the SpectroScanT in trans mode ? */
+ if (p->tmode != 0) {
+ inst_code rc;
+ p->tmode = 0;
+ rc = ss_do_MoveAndMeasure(p, 155.0, 230.0, sp, rv);
+ ss_do_MoveAbsolut(p, p->sbr, p->sbx, p->sby);
+ p->tmode = 1;
+ return rc;
+ }
+#endif
+ ss_add_ssreq(p, ss_MoveAndMeasure);
+ ss_add_2(p, (int)(x * 10 + 0.5));
+ ss_add_2(p, (int)(y * 10 + 0.5));
+ ss_command(p, MV_TMO);
+ if (ss_peek_ans(p) == ss_SpectrumAnswer) {
+ int i;
+ ss_sub_soans(p, ss_SpectrumAnswer);
+ ss_sub_soans(p, 0x09);
+ ss_sub_soans(p, 0x00);
+ for (i = 0; i < 36; i++)
+ sp[i] = ss_sub_double(p);
+ *rv = ss_sub_1(p);
+ ss_incorp_remerrset(p, ss_sub_2(p));
+ } else {
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ }
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* - - - - - - */
+/* Miscelanious */
+
+/* Set the SpectroScanT transmission light level during standby. */
+/* (Only valid on SpectroScanT in transmission mode) */
+inst_code ss_do_SetLightLevel(
+ss *p,
+ss_llt ll /* Transmission light level (Off/Surround/Low) */
+) {
+#ifdef EMSST
+ if (p->tmode != 0)
+ return inst_ok;
+ else
+ *((char *)0) = 55;
+#endif
+ ss_add_ssreq(p, ss_SetLightLevel);
+ ss_add_1(p, ll);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Set tranmission standby position. */
+/* (Only valid on SpectroScanT in transmission mode) */
+inst_code ss_do_SetTransmStandbyPos(
+ss *p,
+ss_rt r, /* Reference (Sensor/Sight) */
+double x, /* X coord in mm, 0-310.0, accurate to 0.1mm */
+double y /* Y coord in mm, 0-230.0, accurate to 0.1mm */
+) {
+#ifdef EMSST
+ if (p->tmode != 0) {
+ p->sbr = r;
+ p->sbx = x;
+ p->sby = y;
+ return inst_ok;
+ } else {
+ *((char *)0) = 55;
+ }
+#endif
+ ss_add_ssreq(p, ss_SetTransmStandbyPos);
+ ss_add_1(p, r);
+ ss_add_2(p, (int)(x * 10 + 0.5));
+ ss_add_2(p, (int)(y * 10 + 0.5));
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Set digitizing mode. Clears digitizing buffer, */
+/* and puts the device offline. The user can move */
+/* and enter positions. */
+inst_code ss_do_SetDigitizingMode(ss *p) {
+ ss_add_ssreq(p, ss_SetDigitizingMode);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Get last digitized position from memory. */
+inst_code ss_do_OutputDigitizingValues(
+ss *p,
+ss_rt r, /* Reference (Sensor/Sight) */
+ss_rt *rr, /* Return reference (Sensor/Sight) */
+int *nrp,/* Return the number of remaining positions in memory. */
+double *x, /* Return the X coord in mm, 0-310.0, accurate to 0.1mm */
+double *y, /* Return the Y coord in mm, 0-230.0, accurate to 0.1mm */
+ss_zkt *zk /* Return the Z coordinate (Up/Down) */
+) {
+ ss_add_ssreq(p, ss_OutputDigitizingValues);
+ ss_add_1(p, r);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_PositionAnswer);
+ *rr = ss_sub_1(p);
+ *nrp = ss_sub_2(p); /* Should be unsigned ?? */
+ *x = ss_sub_2(p)/10.0;
+ *y = ss_sub_2(p)/10.0;
+ *zk = ss_sub_1(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Turn on key aknowledge mode. Causes a KeyAnswer message */
+/* to be generated whenever a key is pressed. */
+/* (KetAnswer isn't well supported here ?) */
+inst_code ss_do_SetKeyAcknowlge(ss *p) {
+ ss_add_ssreq(p, ss_SetKeyAcknowlge);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Turn off key aknowledge mode. */
+inst_code ss_do_ResetKeyAcknowlge(ss *p) {
+ ss_add_ssreq(p, ss_ResetKeyAcknowlge);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the keys that are currently pressed */
+inst_code ss_do_OutputActualKey(
+ss *p,
+ss_sks *sk, /* Return Scan Key Set (Key bitmask) */
+ss_ptt *pt /* Return press time (Short/Long) */
+) {
+ ss_add_ssreq(p, ss_OutputActualKey);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_KeyAnswer);
+ *sk = ss_sub_1(p);
+ *pt = ss_sub_1(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the keys that were last pressed */
+inst_code ss_do_OutputLastKey(
+ss *p,
+ss_sks *sk, /* Return Scan Key bitmask (Keys) */
+ss_ptt *pt /* Return press time (Short/Long) */
+) {
+ ss_add_ssreq(p, ss_OutputLastKey);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_KeyAnswer);
+ *sk = ss_sub_1(p);
+ *pt = ss_sub_1(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the status register */
+inst_code ss_do_OutputStatus(
+ss *p,
+ss_sts *st /* Return status bitmask (Enter key/Online/Digitize/KeyAck/Paper) */
+) {
+ ss_add_ssreq(p, ss_OutputStatus);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_StatusAnswer);
+ *st = ss_sub_1(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Clear the status register */
+inst_code ss_do_ClearStatus(
+ss *p,
+ss_sts st /* Status to reset (Enter key/Online/Digitize/KeyAck/Paper) */
+) {
+ ss_add_ssreq(p, ss_ClearStatus);
+ ss_add_1(p, st);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Set the special status register */
+/* (Set to all 0 on reset) */
+inst_code ss_do_SetSpecialStatus(
+ss *p,
+ss_sss sss /* Status bits to set (HeadDwnOnMv/TableInTransMode/AllLightsOn) */
+) {
+ ss_add_ssreq(p, ss_SetSpecialStatus);
+ ss_add_1(p, sss);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Clear the special status register */
+inst_code ss_do_ClearSpecialStatus(
+ss *p,
+ss_sss sss /* Status bits to clear (HeadDwnOnMv/TableInTransMode/AllLightsOn) */
+) {
+ ss_add_ssreq(p, ss_ClearSpecialStatus);
+ ss_add_1(p, sss);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_ErrorAnswer);
+ ss_incorp_scanerr(p, ss_sub_1(p));
+ chended(p);
+ return ss_inst_err(p);
+}
+
+/* Query the special status register */
+inst_code ss_do_OutputSpecialStatus(
+ss *p,
+ss_sss *sss /* Return Special Status bits */
+) {
+ ss_add_ssreq(p, ss_OutputSpecialStatus);
+ ss_command(p, DF_TMO);
+ ss_sub_ssans(p, ss_StatusAnswer);
+ *sss = ss_sub_1(p);
+ chended(p);
+ return ss_inst_err(p);
+}
+
+
+#define SS_IMP_H
+#endif /* SS_IMP_H */
+
+