summaryrefslogtreecommitdiff
path: root/spectro/specbos.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/specbos.c')
-rw-r--r--spectro/specbos.c345
1 files changed, 235 insertions, 110 deletions
diff --git a/spectro/specbos.c b/spectro/specbos.c
index f81f2d7..0d5bc5b 100644
--- a/spectro/specbos.c
+++ b/spectro/specbos.c
@@ -63,9 +63,6 @@
#include "icoms.h"
#include "specbos.h"
-/* Default flow control */
-#define DEFFC fc_none
-
static inst_code specbos_interp_code(inst *pp, int ec);
#define MAX_MES_SIZE 500 /* Maximum normal message reply size */
@@ -96,6 +93,7 @@ int ctype, /* 0 = normal, 1 = *init, 2 = refr reading */
int nd /* nz to disable debug messages */
) {
int se;
+ int bread = 0;
char *cp, *tc = "", *dp;
if (ctype == 0)
@@ -105,23 +103,34 @@ int nd /* nz to disable debug messages */
else if (ctype == 2)
tc = "\r\025"; /* Return or Nak */
- if ((se = p->icom->write_read(p->icom, in, out, bsize, tc, ntc, to)) != 0) {
+ se = p->icom->write_read(p->icom, in, 0, out, bsize, &bread, tc, ntc, to);
+
+ /* Because we are sometimes waiting for 3 x \r characters to terminate the read, */
+ /* we will instead time out on getting a single NAK (\025), so convert timout */
+ /* with bytes to non-timeout, so that we can process the error. */
+ if (se == ICOM_TO && bread > 0)
+ se = ICOM_OK;
+
+ if (se != 0) {
if (!nd) a1logd(p->log, 1, "specbos_fcommand: serial i/o failure on write_read '%s' 0x%x\n",icoms_fix(in),se);
return icoms2specbos_err(se);
}
/* See if there was an error, and remove any enquire codes */
- for (dp = cp = out; *cp != '\000' && (out - dp) < bsize; cp++) {
- if (*cp == '\025') { /* Got a Nak */
+ for (dp = cp = out; *cp != '\000' && (dp - out) < bsize; cp++) {
+ if (*cp == '\025') { /* Got a NAK */
char buf[100];
- if ((se = p->icom->write_read(p->icom, "*stat:err?\r", buf, 100, "\r", 1, 1.0)) != 0) {
+ if ((se = p->icom->write_read(p->icom, "*stat:err?\r", 0, buf, 100, NULL, "\r", 1, 1.0)) != 0) {
if (!nd) a1logd(p->log, 1, "specbos_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in));
- if (sscanf(buf, "Error Code: %d ",&se) != 1) {
- if (!nd) a1logd(p->log, 1, "specbos_fcommand: failed to parse error code '%s'\n",icoms_fix(buf));
- return icoms2specbos_err(se);
- }
+ return icoms2specbos_err(se);;
}
+ if (sscanf(buf, "Error Code: %d ",&se) != 1) {
+ if (!nd) a1logd(p->log, 1, "specbos_fcommand: failed to parse error code '%s'\n",icoms_fix(buf));
+ return SPECBOS_DATA_PARSE_ERROR;
+ }
+
+ if (!nd) a1logd(p->log, 1, "Got specbos error code %d\n",se);
break;
}
if (*cp == '\005') /* Got an Enquire */
@@ -131,10 +140,9 @@ int nd /* nz to disable debug messages */
}
out[bsize-1] = '\000';
- if (!nd) a1logd(p->log, 4, "specbos_fcommand: command '%s' returned '%s'\n",
- icoms_fix(in), icoms_fix(out));
-
- return SPECBOS_OK;
+ if (!nd) a1logd(p->log, 4, "specbos_fcommand: command '%s' returned '%s' bytes %d, err 0x%x\n",
+ icoms_fix(in), icoms_fix(out),strlen(out), se);
+ return se;
}
/* Do a normal command/response echange with the specbos. */
@@ -164,7 +172,7 @@ double to /* Timeout in seconds */
int rv, se;
char *cp, *tc = "\r\006\025"; /* Return, Ack or Nak */
- if ((se = p->icom->read(p->icom, out, bsize, tc, 1, to)) != 0) {
+ if ((se = p->icom->read(p->icom, out, bsize, NULL, tc, 1, to)) != 0) {
a1logd(p->log, 1, "specbos_readresp: serial i/o failure\n");
return icoms2specbos_err(se);
}
@@ -172,7 +180,6 @@ double to /* Timeout in seconds */
}
/* Establish communications with a specbos */
-/* Ignore the serial parameters - leave serial in default state. */
/* Return SPECBOS_COMS_FAIL on failure to establish communications */
static inst_code
specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
@@ -217,7 +224,7 @@ specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
/* Check instrument is responding */
- if (((ev = specbos_command(p, "*idn?\r", buf, MAX_MES_SIZE, 0.2)) & inst_mask)
+ if (((ev = specbos_command(p, "*idn?\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask)
!= inst_coms_fail) {
break; /* We've got coms or user abort */
}
@@ -282,8 +289,6 @@ specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
return inst_ok;
}
-static inst_code set_default_disp_type(specbos *p);
-
/*
Notes on commands
@@ -357,28 +362,33 @@ static inst_code set_default_disp_type(specbos *p);
*/
static inst_code specbos_get_diffpos(specbos *p, int *pos, int nd);
+static inst_code specbos_get_target_laser(specbos *p, int *laser, int nd);
-/* Diffuser position thread. */
+/* Diffuser position and laser state thread. */
/* Poll the instrument at 500msec intervals */
int specbos_diff_thread(void *pp) {
int nfailed = 0;
specbos *p = (specbos *)pp;
- inst_code rv = inst_ok;
+ inst_code rv1 = inst_ok;
+ inst_code rv2 = inst_ok;
a1logd(p->log,3,"Diffuser thread started\n");
- for (nfailed = 0; nfailed < 5;) {
+// for (nfailed = 0; nfailed < 5;)
+ /* Try indefinitely, in case instrument is put to sleep */
+ for (;;) {
int pos;
amutex_lock(p->lock);
- rv = specbos_get_diffpos(p, &pos, 1);
+ rv1 = specbos_get_diffpos(p, &pos, 1);
+ rv2 = specbos_get_target_laser(p, &p->laser, 1);
amutex_unlock(p->lock);
if (p->th_term) {
p->th_termed = 1;
break;
}
- if (rv != inst_ok) {
+ if (rv1 != inst_ok || rv2 != inst_ok) {
nfailed++;
- a1logd(p->log,3,"Diffuser thread failed with 0x%x\n",rv);
+ a1logd(p->log,3,"Diffuser thread failed with 0x%x 0x%x\n",rv1,rv2);
continue;
}
if (pos != p->dpos) {
@@ -390,7 +400,7 @@ int specbos_diff_thread(void *pp) {
msec_sleep(500);
}
a1logd(p->log,3,"Diffuser thread returning\n");
- return rv;
+ return rv1 != inst_ok ? rv1 : rv2;
}
/* Initialise the SPECBOS */
@@ -544,7 +554,7 @@ specbos_init_inst(inst *pp) {
return inst_ok;
}
-static inst_code specbos_measure_set_refresh(specbos *p);
+static inst_code specbos_imp_measure_set_refresh(specbos *p);
static inst_code specbos_imp_set_refresh(specbos *p);
/* Get the ambient diffuser position */
@@ -569,6 +579,29 @@ specbos_get_diffpos(
return inst_ok;
}
+/* Get the target laser state */
+/* (This is not multithread safe) */
+static inst_code
+specbos_get_target_laser(
+ specbos *p, /* Object */
+ int *laser, /* 0 = off, 1 = on */
+ int nd /* nz = no debug message */
+) {
+ char buf[MAX_RD_SIZE];
+ int ec;
+ int lstate;
+
+ if ((ec = specbos_fcommand(p, "*contr:laser?\r", buf, MAX_MES_SIZE, 1.0, 1, 0, nd)) != inst_ok) {
+ return specbos_interp_code((inst *)p, ec);
+ }
+ if (sscanf(buf, "laser: %d ",&lstate) != 1) {
+ a1loge(p->log, 2, "specbos_get_target_laser: failed to parse laser state\n");
+ return inst_protocol_error;
+ }
+ *laser = lstate;
+ return inst_ok;
+}
+
/* Read a single sample */
/* Return the dtp error code */
static inst_code
@@ -649,50 +682,94 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */
amutex_unlock(p->lock);
return rv;
}
+ p->laser = 0;
/* Attempt a refresh display frame rate calibration if needed */
if (p->refrmode != 0 && p->rrset == 0) {
- if ((rv = specbos_measure_set_refresh(p)) != inst_ok) {
+ a1logd(p->log, 1, "specbos: need refresh rate calibration before measure\n");
+ if ((rv = specbos_imp_measure_set_refresh(p)) != inst_ok) {
amutex_unlock(p->lock);
return rv;
}
}
/* Trigger a measurement */
- if ((ec = specbos_fcommand(p, "*init\r", buf, MAX_MES_SIZE, 5.0 * p->measto + 2.0 , 1, 1, 0)) != SPECBOS_OK) {
+ /* (Note that ESC will abort it) */
+ if ((ec = specbos_fcommand(p, "*init\r", buf, MAX_MES_SIZE, 5.0 * p->measto + 10.0 , 1, 1, 0)) != SPECBOS_OK) {
amutex_unlock(p->lock);
return specbos_interp_code((inst *)p, ec);
}
- /* Read the XYZ */
- if ((ec = specbos_fcommand(p, "*fetch:XYZ\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0)) != SPECBOS_OK) {
- amutex_unlock(p->lock);
- return specbos_interp_code((inst *)p, ec);
+
+ if (p->noXYZ) { /* Will fail, so assume it failed */
+ ec = SPECBOS_COMMAND;
+
+ } else { /* Read the XYZ */
+ ec = specbos_fcommand(p, "*fetch:XYZ\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0);
}
- if (sscanf(buf, " X: %lf Y: %lf Z: %lf ",
- &val->XYZ[0], &val->XYZ[1], &val->XYZ[2]) == 3) {
-
- /* This may not change anything since instrument may clamp */
- if (clamp)
- icmClamp3(val->XYZ, val->XYZ);
- val->loc[0] = '\000';
- if (p->mode & inst_mode_ambient) {
- val->mtype = inst_mrt_ambient;
- } else
- val->mtype = inst_mrt_emission;
- val->XYZ_v = 1; /* These are absolute XYZ readings */
- val->sp.spec_n = 0;
- val->duration = 0.0;
- rv = inst_ok;
- } else {
+
+ if (ec == SPECBOS_OK) {
+
+ if (sscanf(buf, " X: %lf Y: %lf Z: %lf ",
+ &val->XYZ[0], &val->XYZ[1], &val->XYZ[2]) != 3) {
+ amutex_unlock(p->lock);
+ a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf);
+ return inst_protocol_error;
+ }
+
+ /* Hmm. Some older firmware versions are reported to not support the */
+ /* "fetch:XYZ" command. Use an alternative if it fails. */
+ } else if (ec == SPECBOS_COMMAND) {
+ double Yxy[3];
+
+ p->noXYZ = 1;
+
+ if ((ec = specbos_fcommand(p, "*fetch:PHOTOmetric\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0))
+ != SPECBOS_OK) {
+ amutex_unlock(p->lock);
+ return specbos_interp_code((inst *)p, ec);
+ }
+ if (sscanf(buf, "Luminance[cd/m^2]: %lf ", &Yxy[0]) != 1) {
+ amutex_unlock(p->lock);
+ a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf);
+ return inst_protocol_error;
+ }
+
+ if ((ec = specbos_fcommand(p, "*fetch:CHROMXY\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0))
+ != SPECBOS_OK) {
+ amutex_unlock(p->lock);
+ return specbos_interp_code((inst *)p, ec);
+ }
+ if (sscanf(buf, "Chrom_x: %lf Chrom_y: %lf ", &Yxy[1], &Yxy[2]) != 2) {
+ amutex_unlock(p->lock);
+ a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf);
+ return inst_protocol_error;
+ }
+ icmYxy2XYZ(val->XYZ, Yxy);
+
+ } else if (ec != SPECBOS_OK) {
amutex_unlock(p->lock);
- a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf);
- return inst_protocol_error;
+ return specbos_interp_code((inst *)p, ec);
}
+ /* This may not change anything since instrument may clamp */
+ if (clamp)
+ icmClamp3(val->XYZ, val->XYZ);
+ val->loc[0] = '\000';
+ if (p->mode & inst_mode_ambient) {
+ val->mtype = inst_mrt_ambient;
+ } else
+ val->mtype = inst_mrt_emission;
+ val->XYZ_v = 1; /* These are absolute XYZ readings */
+ val->sp.spec_n = 0;
+ val->duration = 0.0;
+ rv = inst_ok;
+
+
/* spectrum data is returned only if requested */
if (p->mode & inst_mode_spectral) {
+ int tries, maxtries = 5;
int i, xsize;
char *cp, *ncp;
@@ -700,32 +777,53 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */
/* (Format 9 reportedly doesn't work on the 1201) */
/* The folling works on the 1211 and is reported to work on the 1201 */
- /* Fetch the spectral readings */
- if ((ec = specbos_fcommand(p, "*fetch:sprad\r", buf, MAX_RD_SIZE, 1.0, 2+p->nbands+1, 0, 0)) != SPECBOS_OK) {
- return specbos_interp_code((inst *)p, ec);
- }
-
- val->sp.spec_n = p->nbands;
- val->sp.spec_wl_short = p->wl_short;
- val->sp.spec_wl_long = p->wl_long;
-
- /* Spectral data is in W/nm/m^2 */
- val->sp.norm = 1.0;
- cp = buf;
- for (i = -2; i < val->sp.spec_n; i++) {
- if ((ncp = strchr(cp, '\r')) == NULL) {
- amutex_unlock(p->lock);
- a1logd(p->log, 1, "specbos_read_sample: failed to parse spectral\n");
- return inst_protocol_error;
+ /* Because the specbos doesn't use flow control in its */
+ /* internal serial communications, it may overrun */
+ /* the FT232R buffer, so retry fetching the spectra if */
+ /* we get a comm error or parsing error. */
+ for (tries = 0;;) {
+
+ /* Fetch the spectral readings */
+ ec = specbos_fcommand(p, "*fetch:sprad\r", buf, MAX_RD_SIZE, 4.0, 2+p->nbands+1, 0, 0);
+ tries++;
+ if (ec != SPECBOS_OK) {
+ if (tries > maxtries) {
+ amutex_unlock(p->lock);
+ a1logd(p->log, 1, "specbos_fcommand: failed with 0x%x\n",ec);
+ return specbos_interp_code((inst *)p, ec);
+ }
+ continue; /* Retry the fetch */
}
- *ncp = '\000';
- if (i >= 0) {
- val->sp.spec[i] = 1000.0 * atof(cp); /* Convert to mW/m^2/nm */
- if (p->mode & inst_mode_ambient)
- val->mtype = inst_mrt_ambient;
+
+ val->sp.spec_n = p->nbands;
+ val->sp.spec_wl_short = p->wl_short;
+ val->sp.spec_wl_long = p->wl_long;
+
+ /* Spectral data is in W/nm/m^2 */
+ val->sp.norm = 1.0;
+ cp = buf;
+ for (i = -2; i < val->sp.spec_n; i++) {
+ if ((ncp = strchr(cp, '\r')) == NULL) {
+ a1logd(p->log, 1, "specbos_read_sample: failed to parse spectra at %d/%d\n",i+1,val->sp.spec_n);
+ if (tries > maxtries) {
+ amutex_unlock(p->lock);
+ return inst_protocol_error;
+ }
+ continue; /* Retry the fetch and parse */
+ }
+ *ncp = '\000';
+ if (i >= 0) {
+ a1logd(p->log, 6, "sample %d/%d got %f from '%s'\n",i+1,val->sp.spec_n,atof(cp),cp);
+ val->sp.spec[i] = 1000.0 * atof(cp); /* Convert to mW/m^2/nm */
+ if (p->mode & inst_mode_ambient)
+ val->mtype = inst_mrt_ambient;
+ }
+ cp = ncp+1;
}
- cp = ncp+1;
+ /* We've parsed correctly, so don't retry */
+ break;
}
+ a1logd(p->log, 1, "specbos_read_sample: got total %d samples/%d expected in %d tries\n",i,val->sp.spec_n, tries);
}
amutex_unlock(p->lock);
@@ -778,6 +876,9 @@ double *ref_rate
int ec;
inst_code rv;
+ if (ref_rate != NULL)
+ *ref_rate = 0.0;
+
if (p->model == 1201)
return inst_unsupported;
@@ -822,6 +923,9 @@ double *ref_rate
if (!p->inited)
return inst_no_init;
+ if (ref_rate != NULL)
+ *ref_rate = 0.0;
+
amutex_lock(p->lock);
if ((rv = specbos_imp_measure_refresh(p, &refrate)) != inst_ok) {
amutex_unlock(p->lock);
@@ -832,14 +936,16 @@ double *ref_rate
if (refrate == 0.0)
return inst_misread;
- *ref_rate = refrate;
+ if (ref_rate != NULL)
+ *ref_rate = refrate;
return inst_ok;
}
/* Measure and then set refperiod, refrate if possible */
+/* (Not thread safe) */
static inst_code
-specbos_measure_set_refresh(
+specbos_imp_measure_set_refresh(
specbos *p /* Object */
) {
inst_code rv;
@@ -847,9 +953,7 @@ specbos_measure_set_refresh(
int mul;
double pval;
- amutex_lock(p->lock);
if ((rv = specbos_imp_measure_refresh(p, &refrate)) != inst_ok) {
- amutex_unlock(p->lock);
return rv;
}
@@ -865,14 +969,25 @@ specbos_measure_set_refresh(
p->rrset = 1;
if ((rv = specbos_imp_set_refresh(p)) != inst_ok) {
- amutex_unlock(p->lock);
return rv;
}
- amutex_unlock(p->lock);
return inst_ok;
}
+/* Measure and then set refperiod, refrate if possible */
+static inst_code
+specbos_measure_set_refresh(
+ specbos *p /* Object */
+) {
+ int rv;
+
+ amutex_lock(p->lock);
+ rv = specbos_imp_measure_set_refresh(p);
+ amutex_unlock(p->lock);
+ return rv;
+}
+
/* Return needed and available inst_cal_type's */
static inst_code specbos_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
specbos *p = (specbos *)pp;
@@ -1291,7 +1406,7 @@ specbos_del(inst *pp) {
}
/* Return the instrument mode capabilities */
-void specbos_capabilities(inst *pp,
+static void specbos_capabilities(inst *pp,
inst_mode *pcap1,
inst2_capability *pcap2,
inst3_capability *pcap3) {
@@ -1329,6 +1444,7 @@ inst3_capability *pcap3) {
}
/* Return current or given configuration available measurement modes. */
+/* NOTE that conf_ix values shoudn't be changed, as it is used as a persistent key */
static inst_code specbos_meas_config(
inst *pp,
inst_mode *mmodes,
@@ -1383,7 +1499,7 @@ int *conf_ix
}
/* Check device measurement mode */
-inst_code specbos_check_mode(inst *pp, inst_mode m) {
+static inst_code specbos_check_mode(inst *pp, inst_mode m) {
inst_mode cap;
if (!pp->gotcoms)
@@ -1407,7 +1523,7 @@ inst_code specbos_check_mode(inst *pp, inst_mode m) {
}
/* Set device measurement mode */
-inst_code specbos_set_mode(inst *pp, inst_mode m) {
+static inst_code specbos_set_mode(inst *pp, inst_mode m) {
specbos *p = (specbos *)pp;
int refrmode;
inst_code ev;
@@ -1438,13 +1554,14 @@ inst_code specbos_set_mode(inst *pp, inst_mode m) {
return inst_ok;
}
-inst_disptypesel specbos_disptypesel[3] = {
+static inst_disptypesel specbos_disptypesel[3] = {
{
inst_dtflags_default,
1,
"nl",
"Non-Refresh display",
0,
+ disptech_lcd,
0
},
{
@@ -1453,6 +1570,7 @@ inst_disptypesel specbos_disptypesel[3] = {
"rc", /* sel */
"Refresh display", /* desc */
1, /* refr */
+ disptech_crt, /* disptype */
1 /* ix */
},
{
@@ -1461,6 +1579,7 @@ inst_disptypesel specbos_disptypesel[3] = {
"",
"",
0,
+ disptech_none,
0
}
};
@@ -1527,7 +1646,7 @@ static inst_code set_disp_type(specbos *p, inst_disptypesel *dentry) {
return inst_ok;
}
-/* Set the display type */
+/* Set the display type - refresh or not */
static inst_code specbos_set_disptype(inst *pp, int ix) {
specbos *p = (specbos *)pp;
inst_code ev;
@@ -1576,8 +1695,24 @@ specbos_get_set_opt(inst *pp, inst_opt_type m, ...)
return inst_ok;
}
+ /* Get laser target state. */
+ /* For speed we don't return the real time state, */
+ /* but the state from the last poll */
+ if (m == inst_opt_get_target_state) {
+ va_list args;
+ int *pstate;
+
+ va_start(args, m);
+ pstate = va_arg(args, int *);
+ va_end(args);
+
+ if (pstate != NULL)
+ *pstate = p->laser;
+
+ return inst_ok;
+
/* Set laser target state */
- if (m == inst_opt_set_target_state) {
+ } else if (m == inst_opt_set_target_state) {
va_list args;
int state = 0;
@@ -1586,41 +1721,31 @@ specbos_get_set_opt(inst *pp, inst_opt_type m, ...)
va_end(args);
amutex_lock(p->lock);
- if (state == 2) {
- int lstate = -1;
- if ((ev = specbos_command(p, "*contr:laser?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- a1loge(p->log, 1, "specbos_get_set_opt: failed to send laser? command\n");
- return ev;
- }
- if (sscanf(buf, "laser: %d ",&lstate) != 1) {
+ if (state == 2) { /* Toggle */
+
+ /* Get the current state */
+ if ((ev = specbos_get_target_laser(p, &p->laser, 0)) != inst_ok) {
amutex_unlock(p->lock);
- a1loge(p->log, 1, "specbos_get_set_opt: failed to parse laser state\n");
return ev;
}
- a1logd(p->log, 5, " Laser state = %d\n",lstate);
- if (lstate == 0)
- lstate = 1;
- else if (lstate == 1)
- lstate = 0;
- if (lstate == 0 || lstate == 1) {
- char mes[100];
- sprintf(mes,"*contr:laser %d\r",lstate);
- if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
- }
- }
- } else if (state == 1) {
+ a1logd(p->log, 5, " Laser state = %d\n",p->laser);
+ if (p->laser == 0)
+ state = 1;
+ else if (p->laser == 1)
+ state = 0;
+ }
+ if (state == 1) {
if ((ev = specbos_command(p, "*contr:laser 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
amutex_unlock(p->lock);
return ev;
}
+ p->laser = 1;
} else if (state == 0) {
if ((ev = specbos_command(p, "*contr:laser 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
amutex_unlock(p->lock);
return ev;
}
+ p->laser = 0;
}
amutex_unlock(p->lock);
return inst_ok;