summaryrefslogtreecommitdiff
path: root/spectro/ex1.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2015-08-23 12:17:05 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2015-08-23 12:17:05 +0200
commitc0b89ac5bfb90835ef01573267020e42d4fe070c (patch)
tree733c16852d964d84b7565af4bdaff0bcca901b88 /spectro/ex1.c
parent094535c010320967639e8e86f974d878e80baa72 (diff)
Imported Upstream version 1.8.0upstream/1.8.0
Diffstat (limited to 'spectro/ex1.c')
-rwxr-xr-x[-rw-r--r--]spectro/ex1.c2902
1 files changed, 2128 insertions, 774 deletions
diff --git a/spectro/ex1.c b/spectro/ex1.c
index 06f5597..93d0a31 100644..100755
--- a/spectro/ex1.c
+++ b/spectro/ex1.c
@@ -58,155 +58,102 @@
#include "insttypes.h"
#include "conv.h"
#include "icoms.h"
+#include "inst.h"
+#include "rspec.h"
#include "ex1.h"
+#define ENABLE_NONVCAL /* [Def] Enable saving calibration state between program runs in a file */
+
+#undef DEBUG
+#undef PLOT_DEBUG /* Use plot to show readings & processing */
+#undef TEST_DARK_INTERP /* Test adaptive dark interpolation */
+
+#define EX1_MAX_MEAS_TIME 1.0 /* Maximum measurement time to use */
+
+/* ----------------------------------------------------------------- */
+/* High level EX1 commands, implemented at the bottom of the file */
+
+#define EX1_EP 0x1 /* End point to use. Can use 1 and/or 2 */
+#define MIN_CYCLE_TIME 0.009 /* Minimum time per measurement cycle */
+
+#define SENSE_AIM (0.85 * ((1 << 14)-1)) /* Sensor level aim point */
+#define SENSE_SAT (0.95 * ((1 << 14)-1)) /* Saturation level */
+
+#define DCALTOUT (1 * 60 * 60) /* [1 Hour ??] Dark Calibration timeout in seconds */
+
+static int icoms2ex1_err(int se);
static inst_code ex1_interp_code(inst *pp, int ec);
+static char *ex1_interp_native_error(ex1 *p, int ec);
-#define MAX_MES_SIZE 500 /* Maximum normal message reply size */
-#define MAX_RD_SIZE 8000 /* Maximum reading message reply size */
+static void ex1_flush(ex1 *p);
+static int ex1_get_hw_rev(ex1 *p, int *hwrev);
+static int ex1_get_fw_rev(ex1 *p, int *fwrev);
+static int ex1_get_slit_width(ex1 *p, int *swidth);
+static int ex1_get_fiber_width(ex1 *p, int *fibw);
+static int ex1_get_grating(ex1 *p, char **grating);
+static int ex1_get_filter(ex1 *p, char **filter);
+static int ex1_get_coating(ex1 *p, char **coating);
+static int ex1_get_alias(ex1 *p, char **alias);
+static int ex1_get_serno(ex1 *p, char **serno);
+static int ex1_get_wl_coefs(ex1 *p, unsigned int *nocoefs, double **coefs);
+static int ex1_get_nl_coefs(ex1 *p, unsigned int *nocoefs, double **coefs);
+static int ex1_get_ir_coefs(ex1 *p, unsigned int *nocoefs, double **coefs, double *area);
+static int ex1_get_sl_coefs(ex1 *p, unsigned int *nocoefs, double **coefs);
-/* Interpret an icoms error into a EX1 error */
-static int icoms2ex1_err(int se) {
- if (se != ICOM_OK) {
- if (se & ICOM_TO)
- return EX1_TIMEOUT;
- return EX1_COMS_FAIL;
- }
- return EX1_OK;
-}
+#define EX1_TRIGMODE_IMED 0x0 /* Immediate software trigger mode. */
+#define EX1_TRIGMODE_TRIG 0x1 /* Trigger on ext trigger signal after possible delay. */
+#define EX1_TRIGMODE_STROBE 0x2 /* Trigger afer possible delay on internal cont. strobe. */
-/* Debug - dump a command packet at debug level deb1 */
-static void dump_command(ex1 *p, ORD8 *buf, int len, int debl) {
- if (debl < p->log->debug)
- return;
+static int ex1_set_trig_mode(ex1 *p, int trigmode);
- if (len < 64) {
- a1logd(p->log, 4, "Command packet too short (%d bytes)\n",len);
- return;
- }
+#define EX1_INTTIME_MIN 10.0e-6
+#define EX1_INTTIME_MAX 10.0
- if (buf[0] != 0xC1 || buf[1] != 0xC0) {
- a1logd(p->log, 4, "Command missing start bytes (0x%02x, 0x%02x)\n",buf[0],buf[1]);
- }
+static int ex1_set_inttime(ex1 *p, double *qinttime, double inttime);
- // ~~~~999
- // etc.
-}
+#define EX1_DELTIME_MIN 5.0e-6
+#define EX1_DELTIME_MAX 335.5e-3
-/* Do a full command/response exchange with the ex1 */
-/* (This level is not multi-thread safe) */
-/* Return the ex1 error code. */
-static int
-ex1_fcommand(
-ex1 *p,
-char *in, /* In string */
-char *out, /* Out string buffer */
-int bsize, /* Out buffer size */
-double to, /* Timeout in seconds */
-int ntc, /* Number or termination chars */
-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;
+static int ex1_set_trig_delay(ex1 *p, double *qtrigdel, double trigdel);
- // ~~~99
- return EX1_NOT_IMP;
-
- if (ctype == 0)
- tc = "\r\006\025"; /* Return, Ack or Nak */
- else if (ctype == 1)
- tc = "\007\025"; /* Bell or Nak */
- else if (ctype == 2)
- tc = "\r\025"; /* Return or Nak */
-
- 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, "ex1_fcommand: serial i/o failure on write_read '%s' 0x%x\n",icoms_fix(in),se);
- return icoms2ex1_err(se);
- }
-
- /* See if there was an error, and remove any enquire codes */
- 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", 0, buf, 100, NULL, "\r", 1, 1.0)) != 0) {
- if (!nd) a1logd(p->log, 1, "ex1_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in));
- return icoms2ex1_err(se);;
- }
- if (sscanf(buf, "Error Code: %d ",&se) != 1) {
- if (!nd) a1logd(p->log, 1, "ex1_fcommand: failed to parse error code '%s'\n",icoms_fix(buf));
- return EX1_DATA_PARSE_ERROR;
- }
-
- if (!nd) a1logd(p->log, 1, "Got ex1 error code %d\n",se);
- break;
- }
- if (*cp == '\005') /* Got an Enquire */
- continue; /* remove it */
- *dp = *cp;
- dp++;
- }
- out[bsize-1] = '\000';
+#define EX1_STRBPER_MIN 50.0e-6
+#define EX1_STRBPER_MAX 5.0
- if (!nd) a1logd(p->log, 4, "ex1_fcommand: command '%s' returned '%s' bytes %d, err 0x%x\n",
- icoms_fix(in), icoms_fix(out),strlen(out), se);
- return se;
-}
+static int ex1_set_strobe_period(ex1 *p, double *qstbper, double stbper);
-/* Do a normal command/response echange with the ex1. */
-/* (This level is not multi-thread safe) */
-/* Return the inst code */
-static inst_code
-ex1_command(
-struct _ex1 *p,
-char *in, /* In string */
-char *out, /* Out string buffer */
-int bsize, /* Out buffer size */
-double to) { /* Timout in seconds */
- int rv = ex1_fcommand(p, in, out, bsize, to, 1, 0, 0);
- return ex1_interp_code((inst *)p, rv);
-}
+#define EX1_STRB_DISABLE 0x0 /* Continuous strobe disabled */
+#define EX1_STRB_ENABLE 0x1 /* Continuous strobe enabled */
-/* Read another line of response */
-/* (This level is not multi-thread safe) */
-/* Return the inst code */
-static int
-ex1_readresp(
-struct _ex1 *p,
-char *out, /* Out string buffer */
-int bsize, /* Out buffer size */
-double to /* Timeout in seconds */
-) {
- int rv, se;
- char *cp, *tc = "\r\006\025"; /* Return, Ack or Nak */
+static int ex1_set_strobe_enable(ex1 *p, int enable);
- // ~~~999
- return inst_unsupported;
+static int ex1_set_single_strobe_enable(ex1 *p, int enable);
- if ((se = p->icom->read(p->icom, out, bsize, NULL, tc, 1, to)) != 0) {
- a1logd(p->log, 1, "ex1_readresp: serial i/o failure\n");
- return icoms2ex1_err(se);
- }
- return inst_ok;
-}
+#define EX1_AVERAGE_MIN 1
+#define EX1_AVERAGE_MAX 5000
+
+static int ex1_set_average(ex1 *p, int noavg);
+
+#define EX1_BIN_OFF 0x0 /* Full res, 1024 bins */
+#define EX1_BIN_2 0x1 /* 1/2 res, 512 bins */
+#define EX1_BIN_4 0x2 /* 1/4 res, 256 bins */
+#define EX1_BIN_8 0x3 /* 1/8 res, 128 bins */
+
+static int ex1_set_binning(ex1 *p, int bf);
+
+#define EX1_BOXCAR_MIN 0
+#define EX1_BOXCAR_MAX 15
+
+static int ex1_set_boxcar(ex1 *p, int nobox);
+
+static int ex1_measure(ex1 *p, double *raw);
+
+/* ----------------------------------------------------------------- */
/* Establish communications with a ex1 */
/* Return EX1_COMS_FAIL on failure to establish communications */
static inst_code
ex1_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
ex1 *p = (ex1 *) pp;
- char buf[MAX_MES_SIZE];
- baud_rate brt[] = { baud_921600, baud_115200, baud_38400, baud_nc };
instType itype = pp->itype;
int se;
@@ -214,23 +161,30 @@ ex1_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
a1logd(p->log, 2, "ex1_init_coms: called\n");
- amutex_lock(p->lock);
-
if (p->icom->port_type(p->icom) != icomt_usb) {
- amutex_unlock(p->lock);
a1logd(p->log, 1, "ex1_init_coms: wrong communications type for device!\n");
return inst_coms_fail;
}
- // ~~~99 check it is responding.
+ /* Set config, interface, write end point, read end point, read quanta */
+ if ((se = p->icom->set_usb_port(p->icom, 1, EX1_EP, EX1_EP | 0x80, icomuf_none, 0, NULL)) != ICOM_OK) {
+ a1logd(p->log, 1, "ex1_init_coms: set_usbe_port failed ICOM err 0x%x\n",se);
+ return ex1_interp_code((inst *)p, icoms2ex1_err(se));
+ }
- // ~~~99 check the model type
+ /* Should we reset it ? */
- a1logd(p->log, 2, "ex1_init_coms: init coms has suceeded\n");
+ /* Make sure no message is waiting */
+ ex1_flush(p);
+
+ /* Check it is responding */
+ if ((se = ex1_get_hw_rev(p, &p->hwrev)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, se);
+ }
p->gotcoms = 1;
- amutex_unlock(p->lock);
+ a1logd(p->log, 2, "ex1_init_coms: init coms has suceeded\n");
return inst_ok;
}
@@ -240,218 +194,288 @@ ex1_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) {
static inst_code
ex1_init_inst(inst *pp) {
ex1 *p = (ex1 *)pp;
- char mes[100];
- char buf[MAX_MES_SIZE];
inst_code ev = inst_ok;
+ rspec_inf *sconf = &p->sconf;
+ unsigned int count, i;
a1logd(p->log, 2, "ex1_init_inst: called\n");
if (p->gotcoms == 0)
return inst_internal_error; /* Must establish coms before calling init */
-
- amutex_lock(p->lock);
- /* Restore the instrument to it's default settings */
- if ((ev = ex1_command(p, "*conf:default\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok)
- return ev;
-
- /* Set calibration type to auto on ambient cap */
- if ((ev = ex1_command(p, "*para:calibn 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
+ p->lo_secs = 2000000000; /* A very long time */
+ p->max_meastime = EX1_MAX_MEAS_TIME;
+
+ /* Get information about the instrument */
+ if ((ev = ex1_get_alias(p, &p->alias)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ev);
+ if ((ev = ex1_get_hw_rev(p, &p->hwrev)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ev);
+ if ((ev = ex1_get_fw_rev(p, &p->fwrev)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ev);
+ if ((ev = ex1_get_serno(p, &p->serno)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ev);
+ if ((ev = ex1_get_slit_width(p, &p->slitw)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ev);
+ if ((ev = ex1_get_fiber_width(p, &p->fiberw)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ev);
+ if ((ev = ex1_get_grating(p, &p->grating)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ev);
+ if ((ev = ex1_get_filter(p, &p->filter)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ev);
+ if ((ev = ex1_get_coating(p, &p->coating)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ev);
+
+ /* Set the instrument to sane defaults: */
+
+ /* Set trigger mode to software immediate */
+ if ((ev = ex1_set_trig_mode(p, EX1_TRIGMODE_IMED)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
}
- /* Set auto exposure/integration time */
- /* Set calibration type to auto on ambient cap */
- if ((ev = ex1_command(p, "*para:expo 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok
- || (ev = ex1_command(p, "*para:adapt 2\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
+ /* Set integration time to minimum */
+ if ((ev = ex1_set_inttime(p, &p->inttime, EX1_INTTIME_MIN)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
}
- p->measto = 20.0; /* default */
-
- if (p->model == 1211)
- p->measto = 5.0; /* Same overall time as i1d3 ?? */
- else if (p->model == 1201)
- p->measto = 15.0;
+ /* Set trigger delay time to minumum */
+ if ((ev = ex1_set_trig_delay(p, NULL, EX1_DELTIME_MIN)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
+ }
- /* Set maximum integration time to speed up display measurement */
- sprintf(mes, "*conf:maxtin %d\r", (int)(p->measto * 1000.0+0.5));
- if ((ev = ex1_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
+ /* Disable continuous strobe */
+ if ((ev = ex1_set_strobe_enable(p, EX1_STRB_DISABLE)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
}
- /* Set the measurement function to be Radiometric spectrum */
- if ((ev = ex1_command(p, "*conf:func 6\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
+ /* Disable single strobe */
+ if ((ev = ex1_set_single_strobe_enable(p, EX1_STRB_DISABLE)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
}
- /* Set the measurement format to ASCII */
- if ((ev = ex1_command(p, "*conf:form 4\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
+ /* Set averages to 1 */
+ if ((ev = ex1_set_average(p, EX1_AVERAGE_MIN)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
}
- if ((ev = ex1_command(p, "*para:wavbeg?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
+ /* Turn off binning */
+ if ((ev = ex1_set_binning(p, EX1_BIN_OFF)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
}
- if (sscanf(buf, "Predefined start wave: %lf ",&p->wl_short) != 1) {
- amutex_unlock(p->lock);
- a1loge(p->log, 1, "ex1_init_inst: failed to parse start wave\n");
- return ev;
+
+ /* Turn off boxcar filtering */
+ if ((ev = ex1_set_boxcar(p, EX1_BOXCAR_MIN)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
}
- a1logd(p->log, 1, " Short wl range %f\n",p->wl_short);
- if ((ev = ex1_command(p, "*para:wavend?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
+ /* Setup calibration information and measurement config. */
+ clear_rspec_inf(sconf);
+ sconf->log = p->log;
+ sconf->nsen = 1024;
+ sconf->nshgrps = 0;
+ sconf->nilltkgrps = 0;
+ sconf->lightrange.off = 0; /* All of sensor value go to raw */
+ sconf->lightrange.num = sconf->nsen;
+ sconf->nraw = sconf->lightrange.num;
+
+ if ((ev = ex1_get_wl_coefs(p, &sconf->nwlcal, &sconf->wlcal)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
}
- if (sscanf(buf, "Predefined end wave: %lf ",&p->wl_long) != 1) {
- amutex_unlock(p->lock);
- a1loge(p->log, 1, "ex1_init_inst: failed to parse end wave\n");
- return ev;
+ if ((ev = ex1_get_nl_coefs(p, &sconf->nlin, &sconf->lin)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
}
- if (p->wl_long > 830.0) /* Could go to 1000 with 1211 */
- p->wl_long = 830.0;
-
- a1logd(p->log, 1, " Long wl range %f\n",p->wl_long);
+ sconf->lindiv = 1; /* Divide type polynomial correction */
- p->nbands = (int)((p->wl_long - p->wl_short + 1.0)/1.0 + 0.5);
+ /* If we every figure out what the format is, convert it */
+ /* into 2D matrix form and store into sconf. */
+ if ((ev = ex1_get_sl_coefs(p, &p->nstraylight, &p->straylight)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
+ }
- /* Set the wavelength range and resolution */
- sprintf(mes, "*conf:wran %d %d 1\r", (int)(p->wl_short+0.5), (int)(p->wl_long+0.5));
- if ((ev = ex1_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
+ /* Get raw calibration values */
+ if ((ev = ex1_get_ir_coefs(p, &count, &sconf->ecal, &p->emis_area)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ev);
+ }
+ if (count != sconf->nraw) {
+ a1logd(p->log, 1, " Calibration array is unexpected length (is %d, should be %d)\n",
+ count, sconf->nraw);
+ return inst_wrong_setup;
}
+#ifdef PLOT_DEBUG
+ printf("emission cal:\n");
+ plot_ecal(sconf);
+#endif
+
+ /* Hmm. not 100% sure this is correct, but it gives the right */
+ /* sort of numbers.. */
+ for (i = 0; i < sconf->nraw; i++)
+ sconf->ecal[i] /= p->emis_area;
+
+ sconf->ecaltype = rspec_raw;
+
+#ifdef PLOT_DEBUG
+ printf("adjusted emission cal:\n");
+ plot_ecal(sconf);
+#endif
+
+ /* Figure out the calibrated range */
+ rspec_comp_raw_range_from_ecal(sconf);
+
+//printf("~1 raw range = %d - %d\n",sconf->rawrange.off, sconf->rawrange.off + sconf->rawrange.num-1);
+//printf("~1 = %f - %f nm\n",rspec_raw2nm(sconf, sconf->rawrange.off), rspec_raw2nm(sconf, sconf->rawrange.off + sconf->rawrange.num-1));
+
+ sconf->ktype = rspec_gausian;
+// sconf->ktype = rspec_triangle;
+// sconf->ktype = rspec_cubicspline;
+ sconf->wl_space = 2.0;
+ sconf->wl_short = rspec_raw2nm(sconf, sconf->rawrange.off);
+ sconf->wl_short = sconf->wl_space * ceil(sconf->wl_short/sconf->wl_space);
+ if (sconf->wl_short < 350.0)
+ sconf->wl_short = 350.0;
+ sconf->wl_long = rspec_raw2nm(sconf, sconf->rawrange.off + sconf->rawrange.num-1);
+ sconf->wl_long = sconf->wl_space * floor(sconf->wl_long/sconf->wl_space);
+ if (sconf->wl_long > 800.0)
+ sconf->wl_long = 800.0;
+
+ sconf->nwav = (int)floor(sconf->wl_long - sconf->wl_short)/sconf->wl_space + 1;
+
+ a1logd(p->log, 1, " %d Wavelengths %f - %f spacing %f\n",sconf->nwav,
+ sconf->wl_short,sconf->wl_long,sconf->wl_space);
+
+ /* The EX1 is Vis range 350-800 nm */
+ /* so the raw spacing is about 0.45 nm */
+ /* The EX1 has a 100um slit, so FWHM will be 6nm */
+ /* so aim for calibrated wavlength spacing of 2nm */
+
+ rspec_make_resample_filters(sconf);
+#ifdef PLOT_DEBUG
+ plot_resample_filters(sconf);
+#endif
+
+ p->idark_int_time[0] = EX1_INTTIME_MIN;
+ p->idark_int_time[1] = 2.0;
+
+#ifdef ENABLE_NONVCAL
+ /* Restore idarl calibration from the local system */
+ ex1_restore_calibration(p);
+ /* Touch it so that we know when the instrument was last opened */
+ ex1_touch_calibration(p);
+#endif
+
+#ifdef NEVER /* Test linearity iversion */
+ {
+ double v1, v2, v3;
- /* Set to average just 1 reading */
- if ((ev = ex1_command(p, "*conf:aver 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
+ for (v1 = 0.0; v1 < SENSE_SAT; v1 += 1638.0) {
+ v2 = linearize_val_rspec(sconf, v1);
+ v3 = inv_linearize_val_rspec(sconf, v2);
+
+ printf("Sens in %f -> lin %f -> non-lin %f (%f)\n",v1,v2,v3,fabs(v1 - v3));
+ }
}
+#endif /* NEVER */
- if (p->log->verb) {
- int val;
- char *sp;
+ p->conv = new_xsp2cie(icxIT_none, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, icxNoClamp);
- if ((ev = ex1_command(p, "*idn?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
- }
- if ((sp = strrchr(buf, '\r')) != NULL)
- *sp = '\000';
- a1logv(p->log, 1, " Identificaton: %s\n",buf);
-
- if ((ev = ex1_command(p, "*vers?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
- }
-
- if ((sp = strrchr(buf, '\r')) != NULL)
- *sp = '\000';
- a1logv(p->log, 1, " Firmware: %s\n",buf);
-
- if ((ev = ex1_command(p, "*para:spnum?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
- }
- if (sscanf(buf, "spectrometer number: %d ",&val) == 1) {
- a1logv(p->log, 1, " Spectrometer number: %d\n",val);
- }
-
- if ((ev = ex1_command(p, "*para:serno?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
- }
- if (sscanf(buf, "serial number: %d ",&val) == 1) {
- a1logv(p->log, 1, " Serial number: %d\n",val);
- }
+ if (p->conv == NULL)
+ return EX1_INT_CIECONVFAIL;
+
+ if (p->log->verb) {
+ a1logv(p->log, 1, " Model: %s\n",p->alias != NULL ? p->alias : "Unknown");
+ a1logv(p->log, 1, " HW rev: %d\n",p->hwrev);
+ a1logv(p->log, 1, " FW rev: %d\n",p->fwrev);
+ a1logv(p->log, 1, " Serial number: %s\n",p->serno);
+ if (p->slitw != 0)
+ a1logv(p->log, 1, " Slit width: %d microns\n",p->slitw);
+ else
+ a1logv(p->log, 1, " Slit width: Unknown\n");
+ if (p->fiberw != 0)
+ a1logv(p->log, 1, " Fiber width: %d microns\n",p->fiberw);
+ else
+ a1logv(p->log, 1, " Fiber width: Unknown\n");
+ a1logv(p->log, 1, " Grating: %s\n",p->grating != NULL ? p->grating : "Unknown");
+ a1logv(p->log, 1, " Filter: %s\n",p->filter != NULL ? p->filter : "Unknown");
+ a1logv(p->log, 1, " Coating: %s\n",p->coating != NULL ? p->coating : "Unknown");
}
p->inited = 1;
a1logd(p->log, 2, "ex1_init_inst: instrument inited OK\n");
- amutex_unlock(p->lock);
return inst_ok;
}
-static inst_code ex1_imp_measure_set_refresh(ex1 *p);
-static inst_code ex1_imp_set_refresh(ex1 *p);
-
-/* Get the ambient diffuser position */
-/* (This is not multithread safe) */
-static inst_code
-ex1_get_diffpos(
- ex1 *p, /* Object */
- int *pos, /* 0 = display, 1 = ambient */
- int nd /* nz = no debug message */
-) {
- char buf[MAX_RD_SIZE];
+/* Do a raw measurement. */
+/* Will delete existing *praw */
+/* return EX1 error */
+static int ex1_do_meas(ex1 *p, rspec **praw, double *inttime, double duration) {
+ rspec *sens, *raw;
+ int notoav;
int ec;
- /* See if we're in emissive or ambient mode */
- if ((ec = ex1_fcommand(p, "*contr:mhead?\r", buf, MAX_MES_SIZE, 1.0, 1, 0, nd)) != inst_ok) {
- return ex1_interp_code((inst *)p, ec);
+ notoav = (int)ceil(duration/(MIN_CYCLE_TIME + *inttime));
+ if (notoav <= 0)
+ notoav = 1;
+ if (notoav > EX1_AVERAGE_MAX)
+ notoav = EX1_AVERAGE_MAX;
+
+ /* Set integration time */
+ if ((ec = ex1_set_inttime(p, &p->inttime, *inttime)) != EX1_OK) {
+ return ec;
}
- if (sscanf(buf, "mhead: %d ",pos) != 1) {
- a1logd(p->log, 2, "ex1_init_coms: unrecognised measuring head string '%s'\n",icoms_fix(buf));
- return inst_protocol_error;
+ *inttime = p->inttime; /* Quantized integration time */
+
+ /* Set no. of averages */
+ if ((ec = ex1_set_average(p, notoav)) != EX1_OK) {
+ return ec;
}
- return inst_ok;
-}
-/* Get the target laser state */
-/* (This is not multithread safe) */
-static inst_code
-ex1_get_target_laser(
- ex1 *p, /* Object */
- int *laser, /* 0 = off, 1 = on */
- int nd /* nz = no debug message */
-) {
- char buf[MAX_RD_SIZE];
- int ec;
- int lstate;
+ sens = new_rspec(&p->sconf, rspec_sensor, 1);
- if ((ec = ex1_fcommand(p, "*contr:laser?\r", buf, MAX_MES_SIZE, 1.0, 1, 0, nd)) != inst_ok) {
- return ex1_interp_code((inst *)p, ec);
- }
- if (sscanf(buf, "laser: %d ",&lstate) != 1) {
- a1loge(p->log, 2, "ex1_get_target_laser: failed to parse laser state\n");
- return inst_protocol_error;
+ if ((ec = ex1_measure(p, sens->samp[0])) != EX1_OK) {
+ del_rspec(sens);
+ return ec;
}
- *laser = lstate;
- return inst_ok;
+
+ sens->mtype = inst_mrt_emission;
+ sens->inttime = p->inttime;
+
+ /* + Any other processing from sens to raw, */
+ /* i.e. shielded cell tracking, illuminant temp value etc. ? */
+
+ raw = extract_raw_from_sensor_rspec(sens);
+ del_rspec(sens);
+
+ if (*praw != NULL)
+ del_rspec(*praw);
+ *praw = raw;
+
+ return EX1_OK;
}
/* Read a single sample */
-/* Return the dtp error code */
+/* Return the EX1 error code */
static inst_code
ex1_read_sample(
inst *pp,
char *name, /* Strip name (7 chars) */
ipatch *val, /* Pointer to instrument patch value */
-instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */
+instClamping clamp /* NZ if clamp XYZ/Lab to be +ve */
+) {
ex1 *p = (ex1 *)pp;
- char buf[MAX_RD_SIZE];
- int ec;
int user_trig = 0;
int pos = -1;
inst_code rv = inst_protocol_error;
+ rspec_inf *sconf = &p->sconf;
+ rspec *raw = NULL, *wav = NULL;
+ int ec, i;
if (!p->gotcoms)
return inst_no_coms;
if (!p->inited)
return inst_no_init;
- amutex_lock(p->lock);
if (p->trig == inst_opt_trig_user) {
- amutex_unlock(p->lock);
if (p->uicallback == NULL) {
a1logd(p->log, 1, "ex1: inst_opt_trig_user but no uicallback function set!\n");
@@ -473,307 +497,199 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */
/* Notify of trigger */
if (p->uicallback)
p->uicallback(p->uic_cntx, inst_triggered);
- amutex_lock(p->lock);
/* Progromatic Trigger */
} else {
/* Check for abort */
if (p->uicallback != NULL
&& (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) {
- amutex_unlock(p->lock);
return rv; /* Abort */
}
}
-
- /* See if we're in emissive or ambient mode */
- if ((rv = ex1_get_diffpos(p, &pos, 0) ) != inst_ok) {
- amutex_unlock(p->lock);
- return rv;
- }
-
- /* Attempt a refresh display frame rate calibration if needed */
- if (p->refrmode != 0 && p->rrset == 0) {
- a1logd(p->log, 1, "ex1: need refresh rate calibration before measure\n");
- if ((rv = ex1_imp_measure_set_refresh(p)) != inst_ok) {
- amutex_unlock(p->lock);
- return rv;
+ {
+ double inttime, tinttime;
+ int six;
+ double max = 1e6;
+ double blk;
+
+ a1logd(p->log,5,"Starting auto integration time:\n");
+
+ /* Find an integration time small enough to avoid overload */
+ for (inttime = 0.01; max > SENSE_SAT && inttime >= EX1_INTTIME_MIN; inttime *= 0.1) {
+ a1logd(p->log,5,"Trying inttime %f\n",inttime);
+ if ((ec = ex1_do_meas(p, &raw, &inttime, 0.05)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ec);
+ }
+ max = largest_val_rspec(NULL, &six, raw);
+ a1logd(p->log,5,"Max %.0f at six %d, Sat level %.0f\n",max,six,SENSE_SAT);
+ if (max <= SENSE_SAT)
+ break;
+ }
+ if (max >= SENSE_SAT) {
+ a1logd(p->log,1,"Saturated\n");
+ return ex1_interp_code((inst *)p, EX1_RD_SENSORSATURATED);
}
- }
-
- /* Trigger a measurement */
- if ((ec = ex1_fcommand(p, "*init\r", buf, MAX_MES_SIZE, 5.0 * p->measto + 10.0 , 1, 1, 0)) != EX1_OK) {
- amutex_unlock(p->lock);
- return ex1_interp_code((inst *)p, ec);
- }
+ blk = ex1_interp_idark_val(sconf, 0, six, inttime);
+ a1logd(p->log,5,"Black at max = %f\n",blk);
+
+ /* Find an integration time large enough to measure something significant */
+ for (; max < (2.0 * blk)
+ && inttime <= p->max_meastime
+ && inttime <= EX1_INTTIME_MAX;) {
+ tinttime = inttime * 10.0;
+ if (tinttime > p->max_meastime
+ || tinttime > EX1_INTTIME_MAX)
+ break;
+ inttime = tinttime;
+ a1logd(p->log,5,"Trying intttime %f\n",inttime);
+ if ((ec = ex1_do_meas(p, &raw, &inttime, 0.05)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ec);
+ }
+ max = largest_val_rspec(NULL, &six, raw);
+ blk = ex1_interp_idark_val(sconf, 0, six, inttime);
+ a1logd(p->log,5,"Max %.0f, black %.0f\n",max,blk);
+ if (max >= (2.0 * blk))
+ break;
+ }
+ /* If we haven't already tried the maximum time, calculate optimal inttime */
+ if (inttime <= p->max_meastime
+ && max >= (2.0 * blk)) {
+ double llev, blk2;
- if (ec == EX1_OK) {
+ /* Compute the light level for our measurement */
+ llev = linearize_val_rspec(sconf, max - blk)/inttime;
+ a1logd(p->log,5,"Light level = %f\n",llev);
- 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, "ex1_read_sample: failed to parse '%s'\n",buf);
- return inst_protocol_error;
- }
-
- amutex_unlock(p->lock);
- return ex1_interp_code((inst *)p, ec);
- }
+ /* Calculate an initial target integration time to use to estimat black */
+ tinttime = (SENSE_AIM - blk)/(max - blk) * inttime;
+ a1logd(p->log,5,"Initial optimal inttime %f\n",tinttime);
- /* 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;
-
- /* (Format 12 doesn't seem to work on the 1211) */
- /* (Format 9 reportedly doesn't work on the 1201) */
- /* The folling works on the 1211 and is reported to work on the 1201 */
-
- /* Because the ex1 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 = ex1_fcommand(p, "*fetch:sprad\r", buf, MAX_RD_SIZE, 2.0, 2+p->nbands+1, 0, 0);
- tries++;
- if (ec != EX1_OK) {
- if (tries > maxtries) {
- amutex_unlock(p->lock);
- a1logd(p->log, 1, "ex1_fcommand: failed with 0x%x\n",ec);
- return ex1_interp_code((inst *)p, ec);
- }
- continue; /* Retry the fetch */
+ /* Itterate */
+ for (i = 0; i < 5; i++) {
+ blk2 = ex1_interp_idark_val(sconf, 0, six, tinttime);
+ tinttime = linearize_val_rspec(sconf, SENSE_AIM - blk2)/llev;
}
+ a1logd(p->log,5,"Optimal inttime %f\n",tinttime);
+
+ if (tinttime < p->max_meastime) {
+ if (tinttime < EX1_INTTIME_MIN)
+ tinttime = EX1_INTTIME_MIN;
+ if (tinttime > EX1_INTTIME_MAX)
+ tinttime = EX1_INTTIME_MAX;
- val->sp.spec_n = p->nbands;
- val->sp.spec_wl_short = p->wl_short;
- val->sp.spec_wl_long = p->wl_long;
+ /* Do the real measurement */
+ if (inttime <= 1.0) {
+ inttime = tinttime;
- /* 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, "ex1_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;
+ a1logd(p->log,5,"Doing real measurement with inttime %f\n",inttime);
+ if ((ec = ex1_do_meas(p, &raw, &inttime, 1.0)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ec);
}
- 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;
+ max = largest_val_rspec(NULL, &six, raw);
+ a1logd(p->log,5,"Got max %.0f, aimed for %.0f\n",max,SENSE_AIM);
}
- cp = ncp+1;
}
- /* We've parsed correctly, so don't retry */
- break;
}
- a1logd(p->log, 1, "ex1_read_sample: got total %d samples/%d expected in %d tries\n",i,val->sp.spec_n, tries);
- }
- amutex_unlock(p->lock);
-
- if (user_trig)
- return inst_user_trig;
- return rv;
-}
-
-/* Set the instrument to match the current refresh settings */
-/* (Not thread safe) */
-static inst_code
-ex1_imp_set_refresh(ex1 *p) {
- char buf[MAX_MES_SIZE];
- inst_code rv;
-
- if (p->model == 1201)
- return inst_unsupported;
-
- /* Set synchronised read if we should do so */
- if (p->refrmode != 0 && p->refrvalid) {
- char mes[100];
- if ((rv = ex1_command(p, "*conf:cycmod 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- return rv;
+ if (max >= SENSE_SAT) {
+ /* Hmm. Should retry in case light level changed during measurement ? */
+ return ex1_interp_code((inst *)p, EX1_RD_SENSORSATURATED);
}
- sprintf(mes,"*conf:cyctim %f\r",p->refperiod * 1e6);
- if ((rv = ex1_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- return rv;
- }
- a1logd(p->log,5,"ex1_imp_set_refresh set refresh rate to %f Hz\n",1.0/p->refperiod);
- } else {
- if ((rv = ex1_command(p, "*conf:cycmod 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- return rv;
- }
- a1logd(p->log,5,"ex1_imp_set_refresh set non-refresh mode\n");
}
- return inst_ok;
-}
-/* Implementation of read refresh rate */
-/* (Not thread safe) */
-/* Return 0.0 if none detectable */
-static inst_code
-ex1_imp_measure_refresh(
-ex1 *p,
-double *ref_rate
-) {
- char buf[MAX_MES_SIZE], *cp;
- double refperiod = 0.0;
- int ec;
- inst_code rv;
- if (ref_rate != NULL)
- *ref_rate = 0.0;
- if (p->model == 1201)
- return inst_unsupported;
+#ifdef PLOT_DEBUG
+ printf("Raw:\n");
+ plot_rspec1(raw);
+#endif
- /* Make sure the target laser is off */
- if ((rv = ex1_command(p, "*contr:laser 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) {
- return rv;
- }
+ subtract_idark_rspec(raw);
- if ((ec = ex1_fcommand(p, "*contr:cyctim 200 4000\r", buf, MAX_MES_SIZE, 5.0, 1, 2, 0)) != EX1_OK) {
- return ex1_interp_code((inst *)p, ec);
+#ifdef PLOT_DEBUG
+ {
+ rspec *nl = new_rspec_clone(raw);
+ linearize_rspec(raw);
+ printf("non-lin and linearized:\n");
+ plot_rspec2(nl, raw);
+ del_rspec(nl);
}
+#else
+ linearize_rspec(raw);
+#endif
- if ((cp = strchr(buf, 'c')) == NULL)
- cp = buf;
- if (sscanf(cp, "cyctim[ms]: %lf ", &refperiod) != 1) {
- a1logd(p->log, 1, "ex1_read_refrate rate: failed to parse string '%s'\n",icoms_fix(buf));
- *ref_rate = 0.0;
- return inst_misread;
- }
+ emis_calibrate_rspec(raw);
- if (refperiod == 0.0)
- *ref_rate = 0.0;
- else
- *ref_rate = 1000.0/refperiod;
+#ifdef PLOT_DEBUG
+ printf("Calibrated raw:\n");
+ plot_rspec1(raw);
+#endif
- return inst_ok;
-}
+ wav = convert_wav_from_raw_rspec(raw);
+ del_rspec(raw);
-/* Read an emissive refresh rate */
-static inst_code
-ex1_read_refrate(
-inst *pp,
-double *ref_rate
-) {
- ex1 *p = (ex1 *)pp;
- char buf[MAX_MES_SIZE];
- double refrate;
- inst_code rv;
+ inttime_calibrate_rspec(wav);
- if (!p->gotcoms)
- return inst_no_coms;
- if (!p->inited)
- return inst_no_init;
+#ifdef PLOT_DEBUG
+ printf("Wav:\n");
+ plot_rspec1(wav);
+#endif
- if (ref_rate != NULL)
- *ref_rate = 0.0;
+ val->mtype = wav->mtype;
- amutex_lock(p->lock);
- if ((rv = ex1_imp_measure_refresh(p, &refrate)) != inst_ok) {
- amutex_unlock(p->lock);
- return rv;
- }
- amutex_unlock(p->lock);
+ /* Copy spectral in */
+ val->sp.spec_n = sconf->nwav;
+ val->sp.spec_wl_short = sconf->wl_short;
+ val->sp.spec_wl_long = sconf->wl_long;
+ val->sp.norm = 1.0; /* Spectral data is in W/nm/m^2 */
- if (refrate == 0.0)
- return inst_misread;
-
- if (ref_rate != NULL)
- *ref_rate = refrate;
-
- return inst_ok;
-}
-
-/* Measure and then set refperiod, refrate if possible */
-/* (Not thread safe) */
-static inst_code
-ex1_imp_measure_set_refresh(
- ex1 *p /* Object */
-) {
- inst_code rv;
- double refrate = 0.0;
- int mul;
- double pval;
-
- if ((rv = ex1_imp_measure_refresh(p, &refrate)) != inst_ok) {
- return rv;
+ for (i = 0; i < val->sp.spec_n; i++) {
+ val->sp.spec[i] = 1000.0 * wav->samp[0][i];
}
- if (refrate != 0.0) {
- p->refrate = refrate;
- p->refrvalid = 1;
- p->refperiod = 1.0/refrate;
- } else {
- p->refrate = 0.0;
- p->refrvalid = 0;
- p->refperiod = 0.0;
- }
- p->rrset = 1;
+ del_rspec(wav);
- if ((rv = ex1_imp_set_refresh(p)) != inst_ok) {
- return rv;
- }
+ /* Set the XYZ */
+ p->conv->convert(p->conv, val->XYZ, &val->sp);
+ if (clamp)
+ icmClamp3(val->XYZ, val->XYZ);
+ val->XYZ_v = 1;
- return inst_ok;
-}
-/* Measure and then set refperiod, refrate if possible */
-static inst_code
-ex1_measure_set_refresh(
- ex1 *p /* Object */
-) {
- int rv;
- amutex_lock(p->lock);
- rv = ex1_imp_measure_set_refresh(p);
- amutex_unlock(p->lock);
+ if (user_trig)
+ return inst_user_trig;
return rv;
}
/* Return needed and available inst_cal_type's */
static inst_code ex1_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
ex1 *p = (ex1 *)pp;
+ time_t curtime = time(NULL);
inst_cal_type n_cals = inst_calt_none;
inst_cal_type a_cals = inst_calt_none;
+ int idark_valid = p->idark_valid;
- if (p->refrmode != 0) {
- if (p->rrset == 0)
- n_cals |= inst_calt_ref_freq;
- a_cals |= inst_calt_ref_freq;
+ if ((curtime - p->iddate) > DCALTOUT) {
+ a1logd(p->log,2,"Invalidating adaptive dark cal as %d secs from last cal\n",curtime - p->iddate);
+ idark_valid = 0;
}
+ if (!idark_valid
+ || (p->want_dcalib && !p->noinitcalib))
+ n_cals |= inst_calt_em_dark;
+ a_cals |= inst_calt_em_dark;
+
if (pn_cals != NULL)
*pn_cals = n_cals;
if (pa_cals != NULL)
*pa_cals = a_cals;
+ a1logd(p->log,3,"ex1: returning n_cals 0x%x, a_cals 0x%x\n",n_cals, a_cals);
+
return inst_ok;
}
@@ -785,16 +701,16 @@ inst_cal_cond *calc, /* Current condition/desired condition */
char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */
) {
ex1 *p = (ex1 *)pp;
- inst_code ev;
+ rspec_inf *sconf = &p->sconf;
inst_cal_type needed, available;
+ inst_code ev;
+ int ec;
if (!p->gotcoms)
return inst_no_coms;
if (!p->inited)
return inst_no_init;
- id[0] = '\000';
-
if ((ev = ex1_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok)
return ev;
@@ -820,103 +736,197 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */
return inst_unsupported;
}
- if ((*calt & inst_calt_ref_freq) && p->refrmode != 0) {
- inst_code ev = inst_ok;
-
+ /* Adaptive black calibration: */
+ if (*calt & inst_calt_em_dark) {
+ time_t cdate = time(NULL);
+ int i, j, k;
+ rspec *raw;
- if (*calc != inst_calc_emis_80pc) {
- *calc = inst_calc_emis_80pc;
+ if ((*calc & inst_calc_cond_mask) != inst_calc_man_em_dark) {
+ *calc = inst_calc_man_em_dark;
return inst_cal_setup;
}
- /* Do refresh display rate calibration */
- if ((ev = ex1_measure_set_refresh(p)) != inst_ok)
- return ev;
+ a1logd(p->log,2,"\nDoing emis adapative black calibration\n");
- *calt &= ~inst_calt_ref_freq;
- }
- return inst_ok;
-}
+ /* Bracket the dark level and interpolate. */
-/* Return the last calibrated refresh rate in Hz. Returns: */
-static inst_code ex1_get_refr_rate(inst *pp,
-double *ref_rate
-) {
- ex1 *p = (ex1 *)pp;
- if (p->refrvalid) {
- *ref_rate = p->refrate;
- return inst_ok;
- } else if (p->rrset) {
- *ref_rate = 0.0;
- return inst_misread;
- }
- return inst_needs_cal;
-}
+ if ((ec = ex1_do_meas(p, &sconf->idark[0], &p->idark_int_time[0], 1.0)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ec);
+ }
-/* Set the calibrated refresh rate in Hz. */
-/* Set refresh rate to 0.0 to mark it as invalid */
-/* Rates outside the range 5.0 to 150.0 Hz will return an error */
-static inst_code ex1_set_refr_rate(inst *pp,
-double ref_rate
-) {
- ex1 *p = (ex1 *)pp;
- inst_code rv;
+ if ((ec = ex1_do_meas(p, &sconf->idark[1], &p->idark_int_time[1], 1.0)) != EX1_OK) {
+ return ex1_interp_code((inst *)p, ec);
+ }
- a1logd(p->log,5,"ex1_set_refr_rate %f Hz\n",ref_rate);
+ p->idark_valid = 1;
+ p->want_dcalib = 0;
+ p->iddate = cdate;
+ *calt &= ~inst_calt_em_dark;
+
+#ifdef PLOT_DEBUG /* Use plot to show readings & processing */
+ printf("idark calib:\n");
+ plot_rspec2(sconf->idark[0], sconf->idark[1]);
+#endif
+
+ /* Test accuracy of dark level interpolation */
+#ifdef TEST_DARK_INTERP
+ {
+ double tinttime;
+ rspec *ref, *interp;
+
+ for (tinttime = EX1_INTTIME_MIN; ; tinttime *= 2.0) {
+ if (tinttime >= EX1_INTTIME_MAX)
+ tinttime = EX1_INTTIME_MAX;
+
+ if ((ec = ex1_do_meas(p, &ref, &tinttime, 1.0)) != EX1_OK)
+ return ex1_interp_code((inst *)p, ec);
- if (ref_rate != 0.0 && (ref_rate < 5.0 || ref_rate > 150.0))
- return inst_bad_parameter;
+ interp = ex1_interp_idark(sconf, tinttime);
- p->refrate = ref_rate;
- if (ref_rate == 0.0)
- p->refrvalid = 0;
- else {
- p->refperiod = 1.0/ref_rate;
- p->refrvalid = 1;
- }
- p->rrset = 1;
+ fprintf(stderr,"Low gain ref vs. interp dark offset for inttime %f:\n",tinttime);
+ plot_rspec2(ref, interp);
- /* Set the instrument to given refresh rate */
- amutex_lock(p->lock);
- if ((rv = ex1_imp_set_refresh(p)) != inst_ok) {
- amutex_unlock(p->lock);
- return rv;
+ del_rspec(interp);
+ del_rspec(ref);
+
+ if ((tinttime * 1.1) > EX1_INTTIME_MAX)
+ break;
+ }
+ }
+#endif /* NEVER */
+
+#ifdef ENABLE_NONVCAL
+ /* Save the idark calibration to a file */
+ ex1_save_calibration(p);
+#endif
}
- amutex_unlock(p->lock);
return inst_ok;
}
-
/* Error codes interpretation */
static char *
ex1_interp_error(inst *pp, int ec) {
-// ex1 *p = (ex1 *)pp;
+ char *rv;
+ ex1 *p = (ex1 *)pp;
+
ec &= inst_imask;
+
+ /* See if it's an native error code */
+ if ((rv = ex1_interp_native_error(p, ec)) != NULL)
+ return rv;
+
switch (ec) {
- case EX1_INTERNAL_ERROR:
- return "Internal software error";
case EX1_TIMEOUT:
return "Communications timeout";
case EX1_COMS_FAIL:
return "Communications failure";
case EX1_UNKNOWN_MODEL:
- return "Not a JETI ex1";
+ return "Not an EX1";
+ case EX1_SHORT_WRITE:
+ return "Short USB write";
+ case EX1_SHORT_READ:
+ return "Short USB read";
+ case EX1_LONG_READ:
+ return "Long USB read";
+ case EX1_DATA_CHSUM_ERROR:
+ return "Data checksum error";
case EX1_DATA_PARSE_ERROR:
return "Data from ex1 didn't parse as expected";
+ case EX1_RD_SENSORSATURATED:
+ return "Sensor is saturated";
- case EX1_OK:
- return "No device error";
+ /* Internal software errors */
+ case EX1_INTERNAL_ERROR:
+ return "Internal software error";
case EX1_NOT_IMP:
return "Not implemented";
+ case EX1_MEMORY:
+ return "Memory allocation failed";
+ case EX1_INT_THREADFAILED:
+ return "Thread failed";
+ case EX1_INTTIME_RANGE:
+ return "Integration time is out of range";
+ case EX1_DELTIME_RANGE:
+ return "Trigger delat time is out of range";
+ case EX1_STROBE_RANGE:
+ return "Multi strobe period time is out of range";
+ case EX1_AVERAGE_RANGE:
+ return "Number to average is out of range";
+ case EX1_BOXCAR_RANGE:
+ return "Boxcar filtering is out of range";
+ case EX1_INT_CAL_SAVE:
+ return "Saving calibration file failed";
+ case EX1_INT_CAL_RESTORE:
+ return "Restoring calibration file failed";
+ case EX1_INT_CAL_TOUCH:
+ return "Touching calibration file failed";
+ case EX1_INT_CIECONVFAIL:
+ return "Creating spectral to XYZ conversion failed";
+
+ /* Hardware errors */
+ case EX1_NO_WL_CAL:
+ return "Instrument doesn't contain wavelength calibration";
+ case EX1_NO_IR_CAL:
+ return "Instrument doesn't contain irradiance calibration";
default:
return "Unknown error code";
}
}
+/* EX1 Native error codes interpretation */
+/* Return NULL if not recognised */
+static char *
+ex1_interp_native_error(ex1 *p, int ec) {
+ switch (ec) {
+ case EX1_OK:
+ return "No device error";
+ case EX1_UNSUP_PROTOCOL:
+ return "Invalid/unsupported protocol";
+ case EX1_MES_UNKN:
+ return "Unknown message type";
+ case EX1_MES_BAD_CHSUM:
+ return "Bad message checksum";
+ case EX1_MES_TOO_LARGE:
+ return "Message is too large";
+ case EX1_MES_PAYLD_LEN:
+ return "Payload length doesn't match message type";
+ case EX1_MES_PAYLD_INV:
+ return "Payload data is invalid";
+ case EX1_DEV_NOT_RDY:
+ return "Device not ready for message type";
+ case EX1_MES_UNK_CHSUM:
+ return "Unknown checksum type";
+ case EX1_DEV_UNX_RST:
+ return "Unexpected device reset";
+ case EX1_TOO_MANY_BUSSES:
+ return "Too many command sources";
+ case EX1_OUT_OF_MEM:
+ return "Device is out of memory";
+ case EX1_NO_INF:
+ return "Information doesn't exist";
+ case EX1_DEV_INT_ERR:
+ return "Device internal error";
+ case EX1_DECRYPT_ERR:
+ return "Could not decrypt";
+ case EX1_FIRM_LAYOUT:
+ return "Firmware layout is invalid";
+ case EX1_PACKET_SIZE:
+ return "Data packet size is not 64 bytes";
+ case EX1_HW_REV_INCPT:
+ return "HW rev. is incompatible with firmware";
+ case EX1_FLASH_MAP:
+ return "Flash map is incompatible with firmware";
+ case EX1_DEFERRED:
+ return "Operation/Response deffered";
+ default:
+ return NULL;
+ }
+}
/* Convert a machine specific error code into an abstract dtp code */
static inst_code
@@ -928,21 +938,64 @@ ex1_interp_code(inst *pp, int ec) {
case EX1_OK:
return inst_ok;
-// return inst_internal_error | ec;
-
-// return inst_coms_fail | ec;
+ case EX1_INTERNAL_ERROR:
+ case EX1_MEMORY:
+ case EX1_INT_THREADFAILED:
+ case EX1_INTTIME_RANGE:
+ case EX1_DELTIME_RANGE:
+ case EX1_STROBE_RANGE:
+ case EX1_AVERAGE_RANGE:
+ case EX1_BOXCAR_RANGE:
+ case EX1_INT_CAL_SAVE:
+ case EX1_INT_CAL_RESTORE:
+ case EX1_INT_CAL_TOUCH:
+ case EX1_INT_CIECONVFAIL:
+ return inst_internal_error | ec;
-// return inst_unknown_model | ec;
+ case EX1_TIMEOUT:
+ case EX1_COMS_FAIL:
+ case EX1_SHORT_WRITE:
+ case EX1_SHORT_READ:
+ case EX1_LONG_READ:
+ return inst_coms_fail | ec;
-// return inst_protocol_error | ec;
+ case EX1_UNKNOWN_MODEL:
+ return inst_unknown_model | ec;
-// return inst_wrong_config | ec;
+ case EX1_DATA_CHSUM_ERROR:
+ case EX1_DATA_PARSE_ERROR:
+ return inst_protocol_error | ec;
// return inst_bad_parameter | ec;
+// return inst_wrong_config | ec;
-// return inst_misread | ec;
-
-// return inst_hardware_fail | ec;
+ case EX1_NO_WL_CAL:
+ case EX1_NO_IR_CAL:
+
+ /* Native instrument error */
+ case EX1_UNSUP_PROTOCOL:
+ case EX1_MES_UNKN:
+ case EX1_MES_BAD_CHSUM:
+ case EX1_MES_TOO_LARGE:
+ case EX1_MES_PAYLD_LEN:
+ case EX1_MES_PAYLD_INV:
+ case EX1_DEV_NOT_RDY:
+ case EX1_MES_UNK_CHSUM:
+ case EX1_DEV_UNX_RST:
+ case EX1_TOO_MANY_BUSSES:
+ case EX1_OUT_OF_MEM:
+ case EX1_NO_INF:
+ case EX1_DEV_INT_ERR:
+ case EX1_DECRYPT_ERR:
+ case EX1_FIRM_LAYOUT:
+ case EX1_PACKET_SIZE:
+ case EX1_HW_REV_INCPT:
+ case EX1_FLASH_MAP:
+ case EX1_DEFERRED:
+ return inst_hardware_fail | ec;
+
+ case EX1_RD_SENSORSATURATED:
+ return inst_misread | ec;
case EX1_NOT_IMP:
return inst_unsupported | ec;
@@ -955,9 +1008,36 @@ static void
ex1_del(inst *pp) {
if (pp != NULL) {
ex1 *p = (ex1 *)pp;
+
+#ifdef ENABLE_NONVCAL
+ ex1_touch_calibration(p);
+#endif
+
if (p->icom != NULL)
p->icom->del(p->icom);
- amutex_del(p->lock);
+ if (p->cbuf != NULL)
+ free(p->cbuf);
+ if (p->alias != NULL)
+ free(p->alias);
+ if (p->serno != NULL)
+ free(p->serno);
+ if (p->grating != NULL)
+ free(p->grating);
+ if (p->filter != NULL)
+ free(p->filter);
+ if (p->coating != NULL)
+ free(p->coating);
+
+ free_rspec_inf(&p->sconf);
+
+ if (p->straylight != NULL)
+ free(p->straylight);
+ if (p->emis_coef != NULL)
+ free(p->emis_coef);
+
+ if (p->conv != NULL)
+ p->conv->del(p->conv);
+
free(p);
}
}
@@ -972,27 +1052,14 @@ inst3_capability *pcap3) {
inst2_capability cap2 = 0;
cap1 |= inst_mode_emis_tele
-// | inst_mode_ambient // ?? is it
| inst_mode_colorimeter
| inst_mode_spectral
- | inst_mode_emis_refresh_ovd
- | inst_mode_emis_norefresh_ovd
;
- /* can inst2_has_sensmode, but not report it asynchronously */
cap2 |= inst2_prog_trig
| inst2_user_trig
- | inst2_disptype
- | inst2_has_target /* Has a laser target */
;
-// ~~~~9999
- if (p->model != 1201) {
- cap2 |= inst2_emis_refr_meas;
- cap2 |= inst2_set_refresh_rate;
- cap2 |= inst2_get_refresh_rate;
- }
-
if (pcap1 != NULL)
*pcap1 = cap1;
if (pcap2 != NULL)
@@ -1011,38 +1078,15 @@ int *conf_ix
) {
ex1 *p = (ex1 *)pp;
inst_code ev;
- inst_mode mval;
- int pos;
+ inst_mode mval = 0;
if (mmodes != NULL)
*mmodes = inst_mode_none;
if (cconds != NULL)
*cconds = inst_calc_unknown;
- if (conf_ix == NULL
- || *conf_ix < 0
- || *conf_ix > 1) {
- /* Return current configuration measrement modes */
- amutex_lock(p->lock);
- if ((ev = ex1_get_diffpos(p, &pos, 0)) != inst_ok) {
- amutex_unlock(p->lock);
- return ev;
- }
- amutex_unlock(p->lock);
- } else {
- /* Return given configuration measurement modes */
- pos = *conf_ix;
- }
-
- if (pos == 1) {
- mval = inst_mode_emis_ambient;
- } else if (pos == 0) {
- mval |= inst_mode_emis_tele;
- }
-
/* Add the extra dependent and independent modes */
- mval |= inst_mode_emis_refresh_ovd
- | inst_mode_emis_norefresh_ovd
+ mval |= inst_mode_emis_tele
| inst_mode_colorimeter
| inst_mode_spectral;
@@ -1051,7 +1095,7 @@ int *conf_ix
/* Return configuration index returned */
if (conf_ix != NULL)
- *conf_ix = pos;
+ *conf_ix = 0; /* There is only one */
return inst_ok;
}
@@ -1072,8 +1116,7 @@ static inst_code ex1_check_mode(inst *pp, inst_mode m) {
return inst_unsupported;
/* Only tele emission mode supported */
- if (!IMODETST(m, inst_mode_emis_tele)
- && !IMODETST(m, inst_mode_emis_ambient)) {
+ if (!IMODETST(m, inst_mode_emis_tele)) {
return inst_unsupported;
}
@@ -1091,144 +1134,18 @@ static inst_code ex1_set_mode(inst *pp, inst_mode m) {
p->mode = m;
-
- if (p->model != 1201) { /* Can't set refresh mode on 1201 */
-
- /* Effective refresh mode may change */
- refrmode = p->refrmode;
- if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */
- refrmode = 0;
- } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) {
- refrmode = 1;
- }
-
- if (p->refrmode != refrmode) {
- p->rrset = 0; /* This is a hint we may have swapped displays */
- p->refrvalid = 0;
- }
- p->refrmode = refrmode;
- }
-
- return inst_ok;
-}
-
-static inst_disptypesel ex1_disptypesel[3] = {
- {
- inst_dtflags_default,
- 1,
- "nl",
- "Non-Refresh display",
- 0,
- disptech_lcd,
- 0
- },
- {
- inst_dtflags_none, /* flags */
- 2, /* cbid */
- "rc", /* sel */
- "Refresh display", /* desc */
- 1, /* refr */
- disptech_crt, /* disptype */
- 1 /* ix */
- },
- {
- inst_dtflags_end,
- 0,
- "",
- "",
- 0,
- disptech_none,
- 0
- }
-};
-
-/* Get mode and option details */
-static inst_code ex1_get_disptypesel(
-inst *pp,
-int *pnsels, /* Return number of display types */
-inst_disptypesel **psels, /* Return the array of display types */
-int allconfig, /* nz to return list for all configs, not just current. */
-int recreate /* nz to re-check for new ccmx & ccss files */
-) {
- ex1 *p = (ex1 *)pp;
- inst_code rv = inst_ok;
-
- if ((!allconfig && (p->mode & inst_mode_ambient)) /* If set to Ambient */
- || p->model == 1201) { /* Or 1201, return empty list */
-
- if (pnsels != NULL)
- *pnsels = 0;
-
- if (psels != NULL)
- *psels = NULL;
-
- return inst_ok;
- }
-
-
- if (pnsels != NULL)
- *pnsels = 2;
-
- if (psels != NULL)
- *psels = ex1_disptypesel;
-
- return inst_ok;
-}
-
-/* Given a display type entry, setup for that type */
-static inst_code set_disp_type(ex1 *p, inst_disptypesel *dentry) {
- inst_code rv;
- int refrmode;
-
- refrmode = dentry->refr;
-
- a1logd(p->log,5,"ex1 set_disp_type refmode %d\n",refrmode);
-
- if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */
- refrmode = 0;
- } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) {
- refrmode = 1;
- }
-
- if (p->refrmode != refrmode)
- p->rrset = 0; /* This is a hint we may have swapped displays */
- p->refrmode = refrmode;
-
- amutex_lock(p->lock);
- if ((rv = ex1_imp_set_refresh(p)) != inst_ok) {
- amutex_unlock(p->lock);
- return rv;
- }
- amutex_unlock(p->lock);
-
return inst_ok;
}
-/* Set the display type - refresh or not */
-static inst_code ex1_set_disptype(inst *pp, int ix) {
- ex1 *p = (ex1 *)pp;
- inst_code ev;
- inst_disptypesel *dentry;
+/* Set the noinitcalib mode */
+static void ex1_set_noinitcalib(ex1 *p, int v, int losecs) {
- if (!p->gotcoms)
- return inst_no_coms;
- if (!p->inited)
- return inst_no_init;
-
- if (p->model == 1201) /* No display type to select on 1201 */
- return inst_unsupported;
-
- if (ix < 0 || ix >= 2)
- return inst_unsupported;
-
- a1logd(p->log,5,"ex1 ex1_set_disptype ix %d\n",ix);
- dentry = &ex1_disptypesel[ix];
-
- if ((ev = set_disp_type(p, dentry)) != inst_ok) {
- return ev;
+ /* Ignore disabling init calib if more than losecs since instrument was open */
+ if (v && losecs != 0 && p->lo_secs >= losecs) {
+ a1logd(p->log,3,"initcalib disable ignored because %d >= %d secs\n",p->lo_secs,losecs);
+ return;
}
-
- return inst_ok;
+ p->noinitcalib = v;
}
/*
@@ -1241,13 +1158,27 @@ static inst_code
ex1_get_set_opt(inst *pp, inst_opt_type m, ...)
{
ex1 *p = (ex1 *)pp;
- char buf[MAX_MES_SIZE];
inst_code ev = inst_ok;
a1logd(p->log, 5, "ex1_get_set_opt: opt type 0x%x\n",m);
+ if (m == inst_opt_initcalib) { /* default */
+ ex1_set_noinitcalib(p, 0, 0);
+ return inst_ok;
+
+ } else if (m == inst_opt_noinitcalib) {
+ va_list args;
+ int losecs = 0;
+
+ va_start(args, m);
+ losecs = va_arg(args, int);
+ va_end(args);
+
+ ex1_set_noinitcalib(p, 1, losecs);
+ return inst_ok;
+
/* Record the trigger mode */
- if (m == inst_opt_trig_prog
+ } else if (m == inst_opt_trig_prog
|| m == inst_opt_trig_user) {
p->trig = m;
return inst_ok;
@@ -1269,6 +1200,14 @@ extern ex1 *new_ex1(icoms *icom, instType itype) {
return NULL;
}
+ /* Allocate a default communication buffer */
+ if ((p->cbuf = calloc(1, 64)) == NULL) {
+ a1loge(icom->log, 1, "new_ex1: malloc failed!\n");
+ free(p);
+ return NULL;
+ }
+ p->cbufsz = 64;
+
p->log = new_a1log_d(icom->log);
p->init_coms = ex1_init_coms;
@@ -1277,23 +1216,1438 @@ extern ex1 *new_ex1(icoms *icom, instType itype) {
p->meas_config = ex1_meas_config;
p->check_mode = ex1_check_mode;
p->set_mode = ex1_set_mode;
- p->get_disptypesel = ex1_get_disptypesel;
- p->set_disptype = ex1_set_disptype;
p->get_set_opt = ex1_get_set_opt;
p->read_sample = ex1_read_sample;
- p->read_refrate = ex1_read_refrate;
p->get_n_a_cals = ex1_get_n_a_cals;
p->calibrate = ex1_calibrate;
- p->get_refr_rate = ex1_get_refr_rate;
- p->set_refr_rate = ex1_set_refr_rate;
p->interp_error = ex1_interp_error;
p->del = ex1_del;
p->icom = icom;
- p->itype = icom->itype;
+ p->itype = itype;
- amutex_init(p->lock);
+ p->want_dcalib = 1; /* Always do an initial dark calibration */
return p;
}
+/* =============================================================================== */
+/* Calibration info save/restore */
+
+int ex1_save_calibration(ex1 *p) {
+ int ev = EX1_OK;
+ int i;
+ char fname[100]; /* Name */
+ calf x;
+ int argyllversion = ARGYLL_VERSION;
+ int ss;
+
+ snprintf(fname, 99, ".ex1_%s.cal", p->serno);
+
+ if (calf_open(&x, p->log, fname, 1)) {
+ x.ef = 2;
+ goto done;
+ }
+
+ ss = sizeof(ex1);
+
+ /* Some file identification */
+ calf_wints(&x, &argyllversion, 1);
+ calf_wints(&x, &ss, 1);
+ calf_wstrz(&x, p->serno);
+
+ /* Save the black calibration if it's valid */
+ calf_wints(&x, &p->idark_valid, 1);
+ calf_wtime_ts(&x, &p->iddate, 1);
+ calf_wrspec(&x, p->sconf.idark[0]);
+ calf_wrspec(&x, p->sconf.idark[1]);
+
+ a1logd(p->log,3,"nbytes = %d, Checkum = 0x%x\n",x.nbytes,x.chsum);
+ calf_wints(&x, (int *)(&x.chsum), 1);
+
+ if (calf_done(&x))
+ x.ef = 3;
+
+ done:;
+ if (x.ef != 0) {
+ a1logd(p->log,2,"Writing calibration file failed with %d\n",x.ef);
+ ev = EX1_INT_CAL_SAVE;
+ } else {
+ a1logd(p->log,2,"Writing calibration file succeeded\n");
+ }
+
+ return ev;
+}
+
+/* Restore the all modes calibration from the local system */
+int ex1_restore_calibration(ex1 *p) {
+ int ev = EX1_OK;
+ int i, j;
+ char fname[100]; /* Name */
+ calf x;
+ int argyllversion;
+ int ss, nbytes, chsum1, chsum2;
+ char *serno = NULL;
+
+ snprintf(fname, 99, ".ex1_%s.cal", p->serno);
+
+ if (calf_open(&x, p->log, fname, 0)) {
+ x.ef = 2;
+ goto done;
+ }
+
+ /* Last modified time */
+ p->lo_secs = x.lo_secs;
+
+ /* Do a dumy read to check the checksum, then a real read */
+ for (x.rd = 0; x.rd < 2; x.rd++) {
+ calf_rewind(&x);
+
+ /* Check the file identification */
+ calf_rints2(&x, &argyllversion, 1);
+ calf_rints2(&x, &ss, 1);
+ calf_rstrz2(&x, &serno);
+
+ if (x.ef != 0
+ || argyllversion != ARGYLL_VERSION
+ || ss != (sizeof(ex1))
+ || strcmp(serno, p->serno) != 0) {
+ a1logd(p->log,2,"Identification didn't verify\n");
+ if (x.ef == 0)
+ x.ef = 4;
+ goto done;
+ }
+
+ /* Read the black calibration if it's valid */
+ calf_rints(&x, &p->idark_valid, 1);
+ calf_rtime_ts(&x, &p->iddate, 1);
+ calf_rrspec(&x, &p->sconf.idark[0], &p->sconf);
+ calf_rrspec(&x, &p->sconf.idark[1], &p->sconf);
+
+ /* Check the checksum */
+ chsum1 = x.chsum;
+ nbytes = x.nbytes;
+ calf_rints2(&x, &chsum2, 1);
+
+ if (x.ef != 0
+ || chsum1 != chsum2) {
+ a1logd(p->log,2,"Checksum didn't verify, bytes %d, got 0x%x, expected 0x%x\n",nbytes,chsum1, chsum2);
+ if (x.ef == 0)
+ x.ef = 5;
+ goto done;
+ }
+ }
+
+ a1logd(p->log,5,"ex1_restore_calibration done\n");
+ done:;
+
+ free(serno);
+ if (calf_done(&x))
+ x.ef = 3;
+
+ if (x.ef != 0) {
+ a1logd(p->log,2,"Reading calibration file failed with %d\n",x.ef);
+ ev = EX1_INT_CAL_RESTORE;
+ }
+
+ return ev;
+}
+
+int ex1_touch_calibration(ex1 *p) {
+ int ev = EX1_OK;
+ char fname[100]; /* Name */
+ int rv;
+
+ snprintf(fname, 99, ".ex1_%s.cal", p->serno);
+
+ if (calf_touch(p->log, fname)) {
+ a1logd(p->log,2,"Touching calibration file time failed with\n");
+ return EX1_INT_CAL_TOUCH;
+ }
+
+ return EX1_OK;
+}
+
+/* =============================================================================== */
+/* EX1 lower level communications */
+
+/* Interpret an icoms error into a EX1 error */
+static int icoms2ex1_err(int se) {
+ if (se != ICOM_OK) {
+ if (se & ICOM_TO)
+ return EX1_TIMEOUT;
+ return EX1_COMS_FAIL;
+ }
+ return EX1_OK;
+}
+
+/* Message format definitions */
+
+#define EX1_FLAG_RESP 0x0001 /* Response to an earlier request */
+#define EX1_FLAG_ACK 0x0002 /* Acknowldgement response */
+#define EX1_FLAG_RQACK 0x0004 /* Request for acknowldgement */
+#define EX1_FLAG_NACK 0x0008 /* Negative acknowldgement response */
+#define EX1_FLAG_EXPTN 0x0010 /* Exception occured */
+#define EX1_FLAG_PVDEP 0x0020 /* Protocol version is deprecated */
+
+#define EX1_CHSUM_NONE 0x0 /* No checksum */
+#define EX1_CHSUM_MD5 0x1 /* MD5 checksum */
+
+#define EX1_TLC_MASK 0xFFF00000 /* Top-level category message type mask */
+#define EX1_TLC_GENERAL 0x00000000 /* General device characteristics */
+#define EX1_TLC_SPECTRO 0x00100000 /* Spectrometer feature */
+#define EX1_TLC_GPIO 0x00200000 /* GPIO feature */
+#define EX1_TLC_STROBE 0x00300000 /* Strobe feature */
+#define EX1_TLC_TEMP 0x00400000 /* Temperature feature */
+
+static char *cmdtstring(unsigned int cmd);
+
+/* Debug - dump a command packet at debug level deb1 */
+static void dump_command(ex1 *p, ORD8 *buf, int len, int deblev) {
+ unsigned int pver = 0; /* Protocol version */
+ unsigned int flags = 0;
+ unsigned int merrno = 0;
+ char *es;
+ unsigned int mestype = 0;
+ unsigned int tlc = 0; /* Top level message category */
+ unsigned int regarding = 0;
+ unsigned int chstype = EX1_CHSUM_NONE;
+ unsigned int imdatlen = 0;
+ unsigned int bremain = 0;
+ unsigned int pll = 0; /* Explicit payload length */
+ int fo; /* Footer offset */
+
+ if (deblev < p->log->debug)
+ return;
+
+ if (len < 44) {
+ a1logd(p->log, 0, " Command packet too short (%d bytes)\n",len);
+ return;
+ }
+
+ /* First comes the header */
+ if (buf[0] != 0xC1 || buf[1] != 0xC0) {
+ a1logd(p->log, 0, " Start bytes wrong (0x%02x, 0x%02x)\n",buf[0],buf[1]);
+ }
+
+ pver = read_ORD16_le(buf + 2);
+ if (pver < 0x1000) {
+ a1logd(p->log, 0, " Unknown protocol version (0x%x)\n",pver);
+ return;
+ }
+ a1logd(p->log, 0, " Protocol version: 0x%x\n",pver);
+
+ flags = read_ORD16_le(buf + 4);
+ a1logd(p->log, 0, " Flags: 0x%x\n",flags);
+
+ if (flags & EX1_FLAG_RESP)
+ a1logd(p->log, 0, " Response to an earlier request\n");
+ if (flags & EX1_FLAG_ACK)
+ a1logd(p->log, 0, " Acknowldgement response\n");
+ if (flags & EX1_FLAG_RQACK)
+ a1logd(p->log, 0, " Request for acknowldgement\n");
+ if (flags & EX1_FLAG_NACK)
+ a1logd(p->log, 0, " Negative acknowldgement response\n");
+ if (flags & EX1_FLAG_EXPTN)
+ a1logd(p->log, 0, " Exception occured\n");
+ if (flags & EX1_FLAG_PVDEP)
+ a1logd(p->log, 0, " Protocol version is deprecated request\n");
+
+ merrno = read_ORD16_le(buf + 6);
+ a1logd(p->log, 0, " Error no.: 0x%x\n",merrno);
+ if ((es = ex1_interp_native_error(p, merrno)) != NULL)
+ a1logd(p->log, 0, " '%s'\n",es);
+
+ mestype = read_ORD32_le(buf + 8);
+ a1logd(p->log, 0, " Mes. Type: 0x%x = %s\n",mestype, cmdtstring(mestype));
+ tlc = mestype & EX1_TLC_MASK;
+ switch(tlc) {
+ case EX1_TLC_GENERAL:
+ a1logd(p->log, 0, " General device characteristics\n");
+ break;
+ case EX1_TLC_SPECTRO:
+ a1logd(p->log, 0, " Spectrometer feature\n");
+ break;
+ case EX1_TLC_GPIO:
+ a1logd(p->log, 0, " GPIO feature\n");
+ break;
+ case EX1_TLC_STROBE:
+ a1logd(p->log, 0, " Strobe feature\n");
+ break;
+ case EX1_TLC_TEMP:
+ a1logd(p->log, 0, " Temperature feature\n");
+ break;
+ }
+
+ regarding = read_ORD32_le(buf + 12);
+ a1logd(p->log, 0, " Regarding: 0x%x\n",regarding);
+
+ chstype = read_ORD8(buf + 22);
+ a1logd(p->log, 0, " checksum: 0x%x\n",chstype);
+ if (chstype == EX1_CHSUM_NONE)
+ a1logd(p->log, 0, " none\n");
+ else if (chstype == EX1_CHSUM_MD5)
+ a1logd(p->log, 0, " MD5\n");
+
+ imdatlen = read_ORD8(buf + 23);
+ a1logd(p->log, 0, " immediate data %d bytes%s\n",imdatlen, imdatlen > 16 ? " (illegal)" : " ");
+
+ if (imdatlen > 0)
+ a1logd(p->log, 0, " 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"
+ " 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31],
+ buf[32], buf[33], buf[34], buf[35], buf[36], buf[37], buf[38], buf[39]);
+
+ bremain = read_ORD32_le(buf + 40);
+ a1logd(p->log, 0, " bytes remaining %d\n",bremain);
+
+ if (bremain < 20) {
+ a1logd(p->log, 0, " - too small for chsum & footer\n");
+ return;
+ }
+
+ if ((44 + bremain) > len) {
+ a1logd(p->log, 0, " - too large for for message size (%d available)\n",len - 44);
+ return;
+ }
+
+ /* Now comes the payload */
+ pll = bremain - 20; /* Payload length */
+
+ if (pll > 0) {
+ adump_bytes(p->log, " ", buf + 44, 0, pll);
+ }
+
+ /* Then the checksum */
+ if (chstype == EX1_CHSUM_NONE) {
+ a1logd(p->log, 0, " checksum not used\n");
+ } else if (chstype == EX1_CHSUM_MD5) {
+ icmMD5 *md5;
+
+ if ((md5 = new_icmMD5()) == NULL) {
+ a1logd(p->log, 0, " new_icmMD5 failed\n");
+ } else {
+ ORD8 chsum[16];
+ int i;
+
+ md5->add(md5, buf, 44 + pll);
+ md5->get(md5, chsum);
+ for (i = 0; i < 16; i++) {
+ if (chsum[i] != buf[44+pll+i])
+ break;
+ }
+ if (i < 16)
+ a1logd(p->log, 0, " MD5 checksum error\n");
+ else
+ a1logd(p->log, 0, " MD5 checksum OK\n");
+ md5->del(md5);
+ }
+ } else {
+ a1logd(p->log, 0, " checksum not checked (unknown type)\n");
+ }
+
+ /* Finally the footer */
+ fo = 44 + pll + 16;
+ if (buf[fo] != 0xC5 || buf[fo + 1] != 0xC4 || buf[fo + 2] != 0xC3 || buf[fo + 3] != 0xC2) {
+ a1logd(p->log, 0, " Footer error (0x%02x 0x%02x 0x%02x 0x%02x)\n",
+ buf[fo],buf[fo+1],buf[fo+2],buf[fo+3]);
+ }
+}
+
+/* Do a full command/response exchange with the ex1 */
+/* (This level is not multi-thread safe) */
+/* Return the ex1 error code. */
+static int
+ex1_command(
+ex1 *p,
+unsigned int cmd, /* 32 bit command or query code */
+ORD8 *in, /* Input data */
+int ilen, /* data length */
+ORD8 *out, /* Output payload - if NULL, assume it's a command */
+int olen, /* Expected returned data length */
+int *prlen, /* Actual data returned. If NULL, expect exactly olen */
+double to, /* Timeout in seconds */
+int nd /* nz to disable debug messages */
+) {
+ int se, rv;
+ ORD8 *buf;
+ int bsize = 64; /* Size needed */
+ int rwbytes = 0;
+ int stime;
+
+ int slen = 64; /* Send length */
+ int toff; /* Tail offset */
+
+ int rlen = 64; /* Receive length */
+ unsigned int pver = 0; /* Protocol version */
+ unsigned int flags = 0;
+ unsigned int merrno = 0;
+ unsigned int mestype = 0;
+ unsigned int tlc = 0; /* Top level message category */
+ unsigned int regarding = 0;
+ unsigned int chstype = EX1_CHSUM_MD5;
+ unsigned int imdatlen = 0;
+ unsigned int bremain = 0;
+ unsigned int pll = 0; /* Explicit payload length */
+ unsigned int ardlen = 0; /* Actual read data length (payload) */
+ int fo; /* Footer offset */
+
+ if (in == NULL)
+ ilen = 0;
+
+ if (out == NULL)
+ olen = 0;
+
+ a1logd(p->log,6,"ex1_command: 0x%x '%s' ilen %d olen %d\n", cmd, cmdtstring(cmd), ilen, olen);
+
+ if (p->log->debug >= 7 && ilen > 0) {
+ adump_bytes(p->log, " ", in, 0, ilen);
+ }
+
+ stime = msec_time();
+
+ if (ilen > 16) { /* Too big for immediate data */
+ slen += ilen;
+ }
+
+ if (out != NULL && olen > 16) { /* Too big for immediate data */
+ rlen += olen;
+ }
+
+ /* Make sure out buffer is big enough to send or revieve */
+ bsize = slen > rlen ? slen : rlen;
+ if (bsize > p->cbufsz) {
+ if ((p->cbuf = realloc(p->cbuf, bsize)) == NULL) {
+ rv = EX1_MEMORY;
+ goto done;
+ }
+ p->cbufsz = bsize;
+ }
+ buf = p->cbuf;
+
+ /* Header */
+ buf[0] = 0xC1;
+ buf[1] = 0xC0;
+
+ /* Protocol version */
+ write_ORD16_le(buf + 2, 0x1100);
+
+ /* If it's a command, request an acknowledge */
+ flags = 0;
+ if (out == NULL) {
+ flags |= EX1_FLAG_RQACK;
+ }
+ write_ORD16_le(buf + 4, flags);
+
+ /* Error number */
+ write_ORD16_le(buf + 6, 0);
+
+ /* Command */
+ write_ORD32_le(buf + 8, cmd);
+
+ /* Regarding identifier (not used) */
+ write_ORD32_le(buf + 12, 0);
+
+ /* Checksum type 0 = none */
+ write_ORD8(buf + 22, chstype);
+
+ /* Reserved bytes */
+ memset(buf + 16, 0, 6);
+
+ /* Immediate data */
+ if (ilen <= 16) {
+ write_ORD8(buf + 23, ilen);
+ if (ilen > 0)
+ memcpy(buf + 24, in, ilen);
+ if (ilen < 16)
+ memset(buf + 24 + ilen, 0, 16 - ilen);
+
+ /* Bytes remaining = checksum + footer = 20 */
+ write_ORD32_le(buf + 40, 20);
+ toff = 44;
+ pll = 0;
+
+ /* or Payload */
+ } else {
+ write_ORD8(buf + 23, 0);
+ write_ORD32_le(buf + 40, ilen + 20);
+ if (ilen > 0)
+ memcpy(buf + 44, in, ilen);
+ toff = 44 + ilen;
+ pll = ilen;
+ }
+
+ /* Checksum bytes */
+
+ if (chstype == EX1_CHSUM_MD5) {
+ icmMD5 *md5;
+
+ if ((md5 = new_icmMD5()) == NULL) {
+ a1logd(p->log, 1, "new_icmMD5 failed\n");
+ merrno = EX1_INTERNAL_ERROR;
+ } else {
+ ORD8 chsum[16];
+ int i;
+
+ md5->add(md5, buf, toff);
+ md5->get(md5, chsum);
+ for (i = 0; i < 16; i++)
+ buf[44+pll+i] = chsum[i];
+ md5->del(md5);
+ }
+ } else {
+ memset(buf + toff, 0, 16);
+ }
+
+ /* Finally the footer */
+ buf[toff + 16 + 0] = 0xC5;
+ buf[toff + 16 + 1] = 0xC4;
+ buf[toff + 16 + 2] = 0xC3;
+ buf[toff + 16 + 3] = 0xC2;
+
+ if (p->log->debug >= 8) {
+ a1logd(p->log,1,"\nex1_command: SENDING:\n");
+ dump_command(p, buf, slen, p->log->debug);
+ }
+
+ /* Send the command */
+ se = p->icom->usb_write(p->icom, NULL, EX1_EP, buf, slen, &rwbytes, 1.0);
+
+ if ((rv = icoms2ex1_err(se)) != EX1_OK) {
+ a1logd(p->log,1,"ex1_command: send failed with ICOM err 0x%x\n",se);
+ goto done;
+ }
+
+ if (rwbytes != slen) {
+ a1logd(p->log,1,"ex1_command: send %d/%d bytes - short\n",rwbytes, slen);
+ rv = EX1_SHORT_WRITE;
+ goto done;
+ }
+
+ /* - - - - - - - - - - - - - - */
+
+ /* Get the expected reply to a query, or an acknowledgement to a command */
+ se = p->icom->usb_read(p->icom, NULL, EX1_EP | 0x80, buf, 64, &rwbytes, to);
+
+ if ((rv = icoms2ex1_err(se)) != EX1_OK) {
+ a1logd(p->log,1,"ex1_command: read failed with ICOM err 0x%x\n",se);
+ goto done;
+ }
+
+ if (p->log->debug >= 8) {
+ a1logd(p->log,1,"\nex1_command: RECIEVING:\n");
+ dump_command(p, buf, rwbytes, p->log->debug);
+ }
+
+ if (rwbytes != 64) {
+ a1logd(p->log,1,"ex1_command: read %d/%d bytes - short\n",rwbytes, 64);
+ rv = EX1_SHORT_READ;
+ goto done;
+ }
+
+ /* Parse and check the reply: */
+
+ /* First comes the header */
+ if (buf[0] != 0xC1 || buf[1] != 0xC0) {
+ a1logd(p->log, 1, "ex1_command: start bytes wrong (0x%02x, 0x%02x)\n",buf[0],buf[1]);
+ rv = EX1_DATA_PARSE_ERROR;
+ goto done;
+ }
+
+ pver = read_ORD16_le(buf + 2);
+ if (pver < 0x1000) {
+ a1logd(p->log, 1, "Unknown protocol version (0x%x)\n",pver);
+ rv = EX1_DATA_PARSE_ERROR;
+ goto done;
+ }
+
+ flags = read_ORD16_le(buf + 4);
+ merrno = read_ORD16_le(buf + 6);
+ mestype = read_ORD32_le(buf + 8);
+ regarding = read_ORD32_le(buf + 12);
+ chstype = read_ORD8(buf + 22);
+ imdatlen = read_ORD8(buf + 23);
+ bremain = read_ORD32_le(buf + 40);
+
+ /* If there has been an error, return it */
+ if (merrno != EX1_OK) {
+ rv = merrno;
+ goto done;
+ }
+
+ if (bremain < 20) {
+ a1logd(p->log, 1, "Bytes remaining %d is too small for chsum & footer\n",bremain);
+ rv = EX1_DATA_PARSE_ERROR;
+ goto done;
+ }
+
+ /* Explicit playload length */
+ pll = bremain - 20; /* Payload length */
+
+ if (pll > 0 && imdatlen > 0) {
+ a1logd(p->log, 1, "Got both immediate payoad %d bytes and explicit %d bytes\n",imdatlen,pll);
+ rv = EX1_DATA_PARSE_ERROR;
+ goto done;
+ }
+
+ /* Payload is immediate */
+ if (imdatlen > 0) {
+ if (imdatlen > olen) {
+ a1logd(p->log, 1, "Got %d bytes payload when expecting %d\n",imdatlen, olen);
+ rv = EX1_LONG_READ;
+ goto done;
+ }
+ memcpy(out, buf + 24, imdatlen);
+ if (prlen != NULL)
+ *prlen = imdatlen;
+ ardlen = imdatlen;
+ }
+
+ /* If there is an explicit payload, read the remaining bytes */
+ else if (pll > 0) {
+ /* Make sure buffer is big enough for read */
+ bsize = pll + 64;
+ if (bsize > p->cbufsz) {
+ if ((p->cbuf = realloc(p->cbuf, bsize)) == NULL) {
+ rv = EX1_MEMORY;
+ goto done;
+ }
+ p->cbufsz = bsize;
+ }
+ buf = p->cbuf;
+
+ se = p->icom->usb_read(p->icom, NULL, EX1_EP | 0x80, buf + 64, pll, &rwbytes, to);
+
+// adump_bytes(p->log, " ", buf + 44, 0, pll);
+
+ if (rwbytes != pll) {
+ a1logd(p->log,1,"ex1_command: read %d/%d bytes - short\n",rwbytes, pll);
+ rv = EX1_SHORT_READ;
+ goto done;
+ }
+
+ if (rwbytes > olen) {
+ a1logd(p->log, 1, "Got %d bytes payload when expecting %d\n",rwbytes, olen);
+ rv = EX1_LONG_READ;
+ goto done;
+ }
+ memcpy(out, buf + 44, pll);
+ if (prlen != NULL)
+ *prlen = pll;
+ ardlen = pll;
+ }
+
+ /* Then the checksum */
+ if (chstype == EX1_CHSUM_MD5) {
+ icmMD5 *md5;
+
+ if ((md5 = new_icmMD5()) == NULL) {
+ a1logd(p->log, 1, "new_icmMD5 failed\n");
+ rv = EX1_INTERNAL_ERROR;
+ goto done;
+ } else {
+ ORD8 chsum[16];
+ int i;
+
+ md5->add(md5, buf, 44 + pll);
+ md5->get(md5, chsum);
+ for (i = 0; i < 16; i++) {
+ if (chsum[i] != buf[44+pll+i])
+ break;
+ }
+ if (i < 16) {
+ a1logd(p->log, 1, "MD5 checksum failed\n");
+ md5->del(md5);
+ rv = EX1_DATA_CHSUM_ERROR;
+ goto done;
+ }
+ md5->del(md5);
+ }
+ }
+
+ /* If we were expecting an exact ammount */
+ if (prlen == NULL && ardlen != olen) {
+ a1logd(p->log, 1, "Got %d bytes payload when expecting %d\n",ardlen, olen);
+ rv = EX1_SHORT_READ;
+ goto done;
+ }
+
+ /* Finally the footer */
+ fo = 44 + pll + 16;
+ if (buf[fo] != 0xC5 || buf[fo + 1] != 0xC4 || buf[fo + 2] != 0xC3 || buf[fo + 3] != 0xC2) {
+ a1logd(p->log, 1, "Footer error (0x%02x 0x%02x 0x%02x 0x%02x)\n",
+ buf[fo],buf[fo+1],buf[fo+2],buf[fo+3]);
+ rv = EX1_DATA_PARSE_ERROR;
+ goto done;
+ }
+
+ if (p->log->debug >= 7 && out != NULL && olen > 0) {
+ adump_bytes(p->log, " ", out, 0, olen);
+ }
+
+ done:;
+ a1logd(p->log,6,"ex1_command: returning 0x%x (%d msec)\n", rv, msec_time()-stime);
+
+ return rv;
+}
+
+#define EX1_CMD_GET_HW_REV 0x00000080
+
+/* Flush the pipe in case we have an unexpected reply waiting for us. */
+/* Note that the instrument will return a measurement initiated from */
+/* a previous session if it finishes in this session, i.e. closing */
+/* the instrument doesn't abort what it is doing ! */
+/* (This helps on MSWin, but stuffs up Linux) */
+static void
+ex1_flush(
+ex1 *p
+) {
+#ifdef NEVER
+ char buf[8096];
+ int debugl = p->log->debug;
+ p->log->debug = 0;
+ p->icom->usb_resetep(p->icom, EX1_EP);
+ p->icom->usb_read(p->icom, NULL, EX1_EP | 0x80, buf, 8096, NULL, 0.1);
+ ex1_command(p, EX1_CMD_GET_HW_REV, NULL, 0, buf, 1, NULL, 0.1, 0);
+ p->icom->usb_resetep(p->icom, EX1_EP);
+ p->log->debug = debugl;
+#endif
+}
+
+/* ------------------------------------------------------------------- */
+/* Implement specific commands */
+
+/* Unimplemented commands/queries */
+#define EX1_CMD_RESETDEFAULTS 0x00000001
+
+#define EX1_CMD_GET_NO_USER_STR 0x00000300
+#define EX1_CMD_GET_USER_STR_SZ 0x00000301
+#define EX1_CMD_GET_USER_STR 0x00000302
+
+/* - - - - - - */
+
+//#define EX1_CMD_GET_HW_REV 0x00000080
+
+/* Return Hardware revision number */
+/* Return the ex1 error code. */
+static int ex1_get_hw_rev(ex1 *p, int *hwrev) {
+ ORD8 buf[1];
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_HW_REV, NULL, 0, buf, 1, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ *hwrev = read_ORD8(buf);
+
+ return EX1_OK;
+}
+
+
+#define EX1_CMD_GET_FW_REV 0x00000090
+
+/* Return Firmware revision number */
+/* Return the ex1 error code. */
+static int ex1_get_fw_rev(ex1 *p, int *fwrev) {
+ ORD8 buf[2];
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_FW_REV, NULL, 0, buf, 2, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ *fwrev = read_ORD16_le(buf);
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_GET_SLITW 0x001B0200
+
+/* Get slit width in microns */
+/* Return 0 if info. not available */
+/* Return the ex1 error code. */
+static int ex1_get_slit_width(ex1 *p, int *swidth) {
+ ORD8 buf[2];
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_SLITW, NULL, 0, buf, 2, NULL, 1.0, 0)) != EX1_OK) {
+ if (ec != EX1_NO_INF)
+ return ec;
+ *swidth = 0;
+ } else {
+ *swidth = read_ORD16_le(buf);
+ }
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_GET_FIBW 0x001B0300
+
+/* Get fiber diameter in microns */
+/* Return the ex1 error code. */
+static int ex1_get_fiber_width(ex1 *p, int *fibw) {
+ ORD8 buf[2];
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_FIBW, NULL, 0, buf, 2, NULL, 1.0, 0)) != EX1_OK) {
+ if (ec != EX1_NO_INF)
+ return ec;
+ *fibw = 0;
+ } else {
+ *fibw = read_ORD16_le(buf);
+ }
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_GET_GRATING 0x001B0400
+
+/* Return grating string, or NULL if none */
+/* (free after use) */
+/* Return the ex1 error code. */
+static int ex1_get_grating(ex1 *p, char **grating) {
+ int bread;
+ int len = 32; /* We assume a max of 32 bytes */
+ int ec;
+
+ if ((*grating = malloc(len+1)) == NULL)
+ return EX1_MEMORY;
+ if ((ec = ex1_command(p, EX1_CMD_GET_GRATING, NULL, 0, (ORD8 *)*grating, len, &bread, 2.0, 0))
+ != EX1_OK) {
+ if (ec != EX1_NO_INF)
+ return ec;
+ free(*grating);
+ *grating = NULL;
+ } else {
+ (*grating)[bread] = '\000';
+ }
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_GET_FILTER 0x001B0500
+
+/* Return filter string, or NULL if none */
+/* (free after use) */
+/* Return the ex1 error code. */
+static int ex1_get_filter(ex1 *p, char **filter) {
+ int bread;
+ int len = 32; /* We assume a max of 32 bytes */
+ int ec;
+
+ if ((*filter = malloc(len+1)) == NULL)
+ return EX1_MEMORY;
+ if ((ec = ex1_command(p, EX1_CMD_GET_FILTER, NULL, 0, (ORD8 *)*filter, len, &bread, 2.0, 0))
+ != EX1_OK) {
+ if (ec != EX1_NO_INF)
+ return ec;
+ free(*filter);
+ *filter = NULL;
+ } else {
+ (*filter)[bread] = '\000';
+ }
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_GET_COATING 0x001B0600
+
+/* Return coating string, or NULL if none */
+/* (free after use) */
+/* Return the ex1 error code. */
+static int ex1_get_coating(ex1 *p, char **coating) {
+ int bread;
+ int len = 32; /* We assume a max of 32 bytes */
+ int ec;
+
+ if ((*coating = malloc(len+1)) == NULL)
+ return EX1_MEMORY;
+ if ((ec = ex1_command(p, EX1_CMD_GET_COATING, NULL, 0, (ORD8 *)*coating, len, &bread, 2.0, 0))
+ != EX1_OK) {
+ if (ec != EX1_NO_INF)
+ return ec;
+ free(*coating);
+ *coating = NULL;
+ } else {
+ (*coating)[bread] = '\000';
+ }
+
+ return EX1_OK;
+}
+
+
+#define EX1_CMD_GET_ALIAS_LEN 0x00000201
+#define EX1_CMD_GET_ALIAS 0x00000200
+
+/* Return device alias string, or NULL if none */
+/* (free after use) */
+/* Return the ex1 error code. */
+static int ex1_get_alias(ex1 *p, char **alias) {
+ ORD8 buf[3];
+ int bread;
+ int len;
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_ALIAS_LEN, NULL, 0, buf, 1, NULL, 2.0, 0)) != EX1_OK) {
+ return ec;
+ }
+
+ len = read_ORD8(buf);
+ if (len == 0) {
+ *alias = NULL;
+ return inst_ok;
+ }
+ if ((*alias = malloc(len+1)) == NULL) {
+ return EX1_MEMORY;
+ }
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_ALIAS, NULL, 0, (ORD8 *)*alias, len, &bread, 2.0, 0))
+ != EX1_OK) {
+ if (ec != EX1_NO_INF) {
+ return ec;
+ }
+ free(*alias);
+ *alias = NULL;
+ } else {
+ (*alias)[bread] = '\000';
+ }
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_GET_SERNO_LEN 0x00000101
+#define EX1_CMD_GET_SERNO 0x00000100
+
+/* Return device serial string, or NULL if none */
+/* (free after use) */
+/* Return the ex1 error code. */
+static int ex1_get_serno(ex1 *p, char **serno) {
+ ORD8 buf[3];
+ int bread;
+ int len;
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_SERNO_LEN, NULL, 0, buf, 1, NULL, 2.0, 0)) != EX1_OK) {
+ return ec;
+ }
+
+ len = read_ORD8(buf);
+ if (len == 0) {
+ *serno = NULL;
+ return inst_ok;
+ }
+ if ((*serno = malloc(len+1)) == NULL) {
+ return EX1_MEMORY;
+ }
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_SERNO, NULL, 0, (ORD8 *)*serno, len, &bread, 2.0, 0))
+ != EX1_OK) {
+ if (ec != EX1_NO_INF) {
+ return ec;
+ }
+ free(*serno);
+ *serno = NULL;
+ } else {
+ (*serno)[bread] = '\000';
+ }
+
+ return EX1_OK;
+}
+
+/* - - - - - - */
+
+#define EX1_CMD_GET_WL_COEF_COUNT 0x00180100
+#define EX1_CMD_GET_WL_COEF 0x00180101
+
+/* Get wavelength cooeficients. */
+/* (free after use) */
+/* Return the ex1 error code. */
+static int ex1_get_wl_coefs(ex1 *p, unsigned int *nocoefs, double **coefs) {
+ ORD8 buf[4];
+ unsigned int i, no;
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_WL_COEF_COUNT, NULL, 0, buf, 1, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ no = read_ORD8(buf);
+
+ /* There should be 4, but 2 is the minimum */
+ if (no < 2) {
+ return EX1_NO_WL_CAL;
+ }
+ if ((*coefs = malloc(sizeof(double) * no)) == NULL) {
+ return EX1_MEMORY;
+ }
+
+ for (i = 0; i < no; i++) {
+ write_ORD8(buf, i);
+ if ((ec = ex1_command(p, EX1_CMD_GET_WL_COEF, buf, 1, buf, 4, NULL, 1.0, 0)) != EX1_OK) {
+ *nocoefs = 0;
+ free(*coefs);
+ return ec;
+ }
+ (*coefs)[i] = IEEE754todouble(read_ORD32_le(buf));
+ }
+ *nocoefs = no;
+
+ if (p->log->debug >= 6) {
+ a1logd(p->log,1,"ex1: no. wavelength calib coefs = %d\n",no);
+ for (i = 0; i < no; i++) {
+ a1logd(p->log,1," [%d] = %e\n",i,(*coefs)[i]);
+ }
+ }
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_GET_NL_COEF_COUNT 0x00181100
+#define EX1_CMD_GET_NL_COEF 0x00181101
+
+/* Get non-linearity coefficient */
+/* (free after use) */
+/* Return the ex1 error code. */
+static int ex1_get_nl_coefs(ex1 *p, unsigned int *nocoefs, double **coefs) {
+ ORD8 buf[4];
+ unsigned int i, no;
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_NL_COEF_COUNT, NULL, 0, buf, 1, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ no = read_ORD8(buf);
+
+ if (no == 0) {
+ *nocoefs = 0;
+ *coefs = NULL;
+ return EX1_OK;
+ }
+ if ((*coefs = malloc(sizeof(double) * no)) == NULL) {
+ return EX1_MEMORY;
+ }
+
+ for (i = 0; i < no; i++) {
+ write_ORD8(buf, i);
+ if ((ec = ex1_command(p, EX1_CMD_GET_NL_COEF, buf, 1, buf, 4, NULL, 1.0, 0)) != EX1_OK) {
+ free(*coefs);
+ *nocoefs = 0;
+ *coefs = NULL;
+ return ec;
+ }
+ (*coefs)[i] = IEEE754todouble(read_ORD32_le(buf));
+ }
+ *nocoefs = no;
+
+ if (p->log->debug >= 6) {
+ a1logd(p->log,1,"ex1: no. linearity calib coefs = %d\n",no);
+ for (i = 0; i < no; i++) {
+ a1logd(p->log,1," [%d] = %e\n",i,(*coefs)[i]);
+ }
+ }
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_GET_IR_COEF_COUNT 0x00182002
+#define EX1_CMD_GET_IR_COEF 0x00182001
+#define EX1_CMD_GET_IR_AREA 0x00182003
+
+/* Get Irradiance calibration coefficients and collection area */
+/* (free after use) */
+/* Return the ex1 error code. */
+static int ex1_get_ir_coefs(ex1 *p, unsigned int *nocoefs, double **coefs, double *area) {
+ ORD8 buf[4], *tbuf;
+ unsigned int i, no;
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_IR_COEF_COUNT, NULL, 0, buf, 4, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ no = read_ORD32_le(buf);
+
+ if (no == 0) {
+ *nocoefs = 0;
+ *coefs = NULL;
+ return EX1_NO_IR_CAL;
+ }
+ if ((tbuf = malloc(4 * no)) == NULL) {
+ return EX1_MEMORY;
+ }
+
+ if ((*coefs = malloc(sizeof(double) * no)) == NULL) {
+ free(tbuf);
+ return EX1_MEMORY;
+ }
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_IR_COEF, NULL, 0, tbuf, 4 * no, NULL, 1.0, 0)) != EX1_OK) {
+ free(*coefs);
+ *nocoefs = 0;
+ *coefs = NULL;
+ *area = 0.0;
+ return ec;
+ }
+ for (i = 0; i < no; i++) {
+ (*coefs)[i] = IEEE754todouble(read_ORD32_le(tbuf + 4 * i));
+ }
+ *nocoefs = no;
+ free(tbuf);
+
+ if (p->log->debug >= 6) {
+ a1logd(p->log,1,"ex1: no. Irradiance calib coefs = %d\n",no);
+ for (i = 0; (i+3) < no; i += 4) {
+ a1logd(p->log,1," [%d] = %e, %e %e %e\n",i,
+ (*coefs)[i], (*coefs)[i+1], (*coefs)[i+2], (*coefs)[i+3]);
+ }
+ }
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_IR_AREA, NULL, 0, buf, 4, NULL, 1.0, 0)) != EX1_OK) {
+ if (ec != EX1_NO_INF) {
+ return ec;
+ }
+ *area = 0.0;
+ } else {
+ *area = IEEE754todouble(read_ORD32_le(buf));
+ }
+
+ a1logd(p->log,1,"ex1: Irradiance collection area = %f\n",*area);
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_GET_SL_COEF_COUNT 0x00183100
+#define EX1_CMD_GET_SL_COEF 0x00183101
+
+/* Get stray light coefficient */
+/* (free after use) */
+/* Return the ex1 error code. */
+static int ex1_get_sl_coefs(ex1 *p, unsigned int *nocoefs, double **coefs) {
+ ORD8 buf[4];
+ unsigned int i, no;
+ int ec;
+
+ if ((ec = ex1_command(p, EX1_CMD_GET_SL_COEF_COUNT, NULL, 0, buf, 1, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ no = read_ORD8(buf);
+
+ if (no == 0) {
+ *nocoefs = 0;
+ *coefs = NULL;
+ return EX1_OK;
+ }
+ if ((*coefs = malloc(sizeof(double) * no)) == NULL) {
+ return EX1_MEMORY;
+ }
+
+ for (i = 0; i < no; i++) {
+ write_ORD8(buf, i);
+ if ((ec = ex1_command(p, EX1_CMD_GET_SL_COEF, buf, 1, buf, 4, NULL, 1.0, 0)) != EX1_OK) {
+ free(*coefs);
+ *nocoefs = 0;
+ *coefs = NULL;
+ if (ec != EX1_NO_INF)
+ return ec;
+ else
+ return EX1_OK;
+ }
+ (*coefs)[i] = IEEE754todouble(read_ORD32_le(buf));
+ }
+ *nocoefs = no;
+
+ if (p->log->debug >= 6) {
+ a1logd(p->log,1,"ex1: no. stray light calib coefs = %d\n",no);
+ for (i = 0; i < no; i++) {
+ a1logd(p->log,1," [%d] = %e\n",i,(*coefs)[i]);
+ }
+ }
+
+ return EX1_OK;
+}
+
+
+/* - - - - - - */
+
+#define EX1_CMD_SET_TRIGMODE 0x00110110
+
+/* Set trigger mode */
+static int ex1_set_trig_mode(ex1 *p, int trigmode) {
+ ORD8 buf[1];
+ int ec;
+
+ write_ORD8(buf, trigmode);
+ if ((ec = ex1_command(p, EX1_CMD_SET_TRIGMODE, buf, 1, NULL, 0, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ return EX1_OK;
+}
+
+#define EX1_CMD_SET_INTTIME 0x00110010
+
+/* Set integration time, min 10 usec, max 10 sec */
+/* Return qualtized integration time in qinttime if != NULL */
+static int ex1_set_inttime(ex1 *p, double *qinttime, double inttime) {
+ ORD8 buf[4];
+ unsigned int iinttime;
+ int ec;
+
+ inttime = floor(inttime * 1e6 + 0.5); /* To int usec */
+ if (inttime < (EX1_INTTIME_MIN * 1e6) || inttime > (EX1_INTTIME_MAX * 1e6))
+ return EX1_INTTIME_RANGE;
+
+ iinttime = (unsigned int)inttime;
+ write_ORD32_le(buf, iinttime);
+
+ if ((ec = ex1_command(p, EX1_CMD_SET_INTTIME, buf, 4, NULL, 0, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+
+ if (qinttime != NULL)
+ *qinttime = inttime/1e6;
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_SET_DELTIME 0x00110510
+
+/* Set trigger delay time, min 5 usec, max 355.5 msec */
+/* Return quantized trigger delay time in qtrigdel if != NULL */
+static int ex1_set_trig_delay(ex1 *p, double *qtrigdel, double trigdel) {
+ ORD8 buf[4];
+ unsigned int itrigdel;
+ int ec;
+
+ trigdel = floor(trigdel * 1e6 + 0.5); /* To int usec */
+ if (trigdel < (EX1_DELTIME_MIN * 1e6) || trigdel > (EX1_DELTIME_MAX * 1e6))
+ return EX1_DELTIME_RANGE;
+
+ itrigdel = (unsigned int)trigdel;
+ write_ORD32_le(buf, itrigdel);
+
+ if ((ec = ex1_command(p, EX1_CMD_SET_DELTIME, buf, 4, NULL, 0, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+
+ if (qtrigdel != NULL)
+ *qtrigdel = trigdel/1e6;
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_SET_STRBPER 0x00310010
+
+/* Set continuous strobe period, min 50 usec, max 5 sec */
+/* Return quantized strobe period in qinttime if != NULL */
+static int ex1_set_strobe_period(ex1 *p, double *qstbper, double stbper) {
+ ORD8 buf[4];
+ unsigned int istbper;
+ int ec;
+
+ stbper = floor(stbper * 1e6 + 0.5); /* To int usec */
+ if (stbper < (EX1_STRBPER_MIN * 1e6) || stbper > (EX1_STRBPER_MAX * 1e6))
+ return EX1_STROBE_RANGE;
+
+ istbper = (unsigned int)stbper;
+ write_ORD32_le(buf, istbper);
+
+ if ((ec = ex1_command(p, EX1_CMD_SET_STRBPER, buf, 4, NULL, 0, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+
+ if (qstbper != NULL)
+ *qstbper = stbper/1e6;
+
+ return EX1_OK;
+}
+
+#define EX1_CMD_SET_STRBEN 0x00310011
+
+/* Set continuous strobe enable/disable mode */
+static int ex1_set_strobe_enable(ex1 *p, int enable) {
+ ORD8 buf[1];
+ int ec;
+
+ write_ORD8(buf, enable);
+ if ((ec = ex1_command(p, EX1_CMD_SET_STRBEN, buf, 1, NULL, 0, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ return EX1_OK;
+}
+
+#define EX1_CMD_SET_SING_STRBEN 0x00300012
+
+/* Set single strobe enable/disable mode */
+static int ex1_set_single_strobe_enable(ex1 *p, int enable) {
+ ORD8 buf[1];
+ int ec;
+
+ write_ORD8(buf, enable);
+ if ((ec = ex1_command(p, EX1_CMD_SET_SING_STRBEN, buf, 1, NULL, 0, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ return EX1_OK;
+}
+
+#define EX1_CMD_SET_AVERAGE 0x00120010
+
+/* Set number of scans to average, range 1 - 5000 */
+static int ex1_set_average(ex1 *p, int noavg) {
+ ORD8 buf[2];
+ int ec;
+
+ if (noavg < EX1_AVERAGE_MIN || noavg > EX1_AVERAGE_MAX)
+ return EX1_AVERAGE_RANGE;
+
+ write_ORD16_le(buf, noavg);
+ if ((ec = ex1_command(p, EX1_CMD_SET_AVERAGE, buf, 2, NULL, 0, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ p->noaverage = noavg;
+ return EX1_OK;
+}
+
+#define EX1_CMD_SET_BINNING 0x00110290
+
+/* Set binning factor. */
+static int ex1_set_binning(ex1 *p, int bf) {
+ ORD8 buf[1];
+ int ec;
+
+ write_ORD8(buf, bf);
+ if ((ec = ex1_command(p, EX1_CMD_SET_BINNING, buf, 1, NULL, 0, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ return EX1_OK;
+}
+
+#define EX1_CMD_SET_BOXCAR 0x00121010
+
+/* Set boxcar filtering width, 0 - 15 */
+static int ex1_set_boxcar(ex1 *p, int nobox) {
+ ORD8 buf[1];
+ int ec;
+
+ if (nobox < EX1_BOXCAR_MIN || nobox > EX1_BOXCAR_MAX)
+ return EX1_BOXCAR_RANGE;
+
+ write_ORD8(buf, nobox);
+ if ((ec = ex1_command(p, EX1_CMD_SET_BOXCAR, buf, 1, NULL, 0, NULL, 1.0, 0)) != EX1_OK) {
+ return ec;
+ }
+ return EX1_OK;
+}
+
+/* - - - - - - */
+
+#define EX1_CMD_GET_COR_SPEC 0x00101000
+
+/* Get corrected spectrum (temp and pattern noise corrected). */
+/* raw must be double[1024] */
+/* Return the ex1 error code. */
+static int ex1_measure(ex1 *p, double *raw) {
+ ORD8 buf[2048];
+ unsigned int i, no = 1024;
+ int ec;
+ double to;
+ int stime;
+
+ to = 1.0 + p->noaverage * (MIN_CYCLE_TIME + p->inttime) * 1.1;
+
+//printf("~1 ex1_measure: to = %f from %d x %f\n",to,p->noaverage,p->inttime);
+
+ stime = msec_time();
+ if ((ec = ex1_command(p, EX1_CMD_GET_COR_SPEC, NULL, 0, buf, no * 2, NULL, to, 0)) != EX1_OK) {
+ return ec;
+ }
+//printf("Measure took %d msec\n", msec_time()-stime);
+
+ for (i = 0; i < no; i++)
+ raw[i] = (double)read_ORD16_le(buf + 2 * i);
+
+ if (p->log->debug >= 6) {
+ a1logd(p->log,1,"ex1: spectrum:\n");
+ for (i = 0; (i+3) < no; i += 4) {
+ a1logd(p->log,1," [%d] = %.0f, %.0f %.0f %.0f\n",
+ i, raw[i], raw[i+1], raw[i+2], raw[i+3]);
+ }
+ }
+ return EX1_OK;
+}
+
+/* - - - - - - */
+
+/* Return command description */
+static char *cmdtstring(unsigned int cmd) {
+ switch (cmd) {
+
+ case EX1_CMD_RESETDEFAULTS:
+ return "Reset to defaults";
+ case EX1_CMD_GET_NO_USER_STR:
+ return "Get number of user strings";
+ case EX1_CMD_GET_USER_STR_SZ:
+ return "Get maximum user string size";
+ case EX1_CMD_GET_USER_STR:
+ return "Get user string";
+ case EX1_CMD_GET_HW_REV:
+ return "Get Hardware Revision";
+ case EX1_CMD_GET_FW_REV:
+ return "Get Firmware revision";
+ case EX1_CMD_GET_ALIAS_LEN:
+ return "Get Alias string length";
+ case EX1_CMD_GET_ALIAS:
+ return "Get Alias string";
+ case EX1_CMD_GET_SERNO_LEN:
+ return "Get Serial number length";
+ case EX1_CMD_GET_SERNO:
+ return "Get Serial number";
+ case EX1_CMD_GET_SLITW:
+ return "Get Slit width";
+ case EX1_CMD_GET_FIBW:
+ return "Get Fiber width";
+ case EX1_CMD_GET_GRATING:
+ return "Get Grating string";
+
+ case EX1_CMD_GET_WL_COEF_COUNT:
+ return "Get Wavelenght coefficient count";
+ case EX1_CMD_GET_WL_COEF:
+ return "Get Wavelenght coefficient";
+ case EX1_CMD_GET_NL_COEF_COUNT:
+ return "Get Linearity coefficient count";
+ case EX1_CMD_GET_NL_COEF:
+ return "Get Linearity coefficient";
+ case EX1_CMD_GET_IR_COEF_COUNT:
+ return "Get Irradiance coefficient count";
+ case EX1_CMD_GET_IR_COEF:
+ return "Get Irradiance coefficient";
+ case EX1_CMD_GET_IR_AREA:
+ return "Get Irradiance collection area";
+ case EX1_CMD_GET_SL_COEF_COUNT:
+ return "Get Stray light coefficient count";
+ case EX1_CMD_GET_SL_COEF:
+ return "Get Stray light coefficient";
+
+ case EX1_CMD_SET_TRIGMODE:
+ return "Set Trigger mode";
+ case EX1_CMD_SET_INTTIME:
+ return "Set Intergration time";
+ case EX1_CMD_SET_DELTIME:
+ return "Set Trigger delay time";
+ case EX1_CMD_SET_STRBPER:
+ return "Set Strobe period";
+ case EX1_CMD_SET_STRBEN:
+ return "Set Strobe enable";
+ case EX1_CMD_SET_SING_STRBEN:
+ return "Set Single Strobe enable";
+ case EX1_CMD_SET_AVERAGE:
+ return "Set Averaging count";
+ case EX1_CMD_SET_BINNING:
+ return "Set Binning multiple";
+ case EX1_CMD_SET_BOXCAR:
+ return "Set Boxcar filtering width";
+
+ case EX1_CMD_GET_COR_SPEC:
+ return "Measure corrected spectral values";
+
+ default:
+ return "Unknown";
+ }
+}
+
+