diff options
Diffstat (limited to 'spectro/specbos.c')
-rw-r--r-- | spectro/specbos.c | 345 |
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; |