Files
gronod 70111c4272 feat(i1pro2): implement visual LED feedback in chartread (fixes #37)
- Add inst_led_state enum, INST_CAP_LED_STATUS capability, and set_led_state to inst.h / inst.c
- Implement i1pro2_indLEDset, worker thread i1pro2_led_thread, and i1pro_imp_set_led_state in i1pro_imp.c / i1pro_imp.h
- Wire up i1pro_set_led_state and determine_capabilities in i1pro.c for i1Pro 2 (Rev E)
- Integrate -Y l option and lifecycle hooks in chartread.c for calibration, readiness, success, failure, and clean exit
2026-09-05 18:13:31 +01:00

3554 lines
109 KiB
C

/* Spectrometer/Colorimeter target test chart reader */
/*
* Argyll Color Management System
*
* Author: Graeme W. Gill
* Date: 4/10/96
*
* Copyright 1996 - 2013, Graeme W. Gill
* All rights reserved.
*
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
* see the License.txt file for licencing details.
*/
/* This program reads a reflective or transmissive print target chart */
/* using a spectrometer or colorimeter. */
/* TTBD
*
* Should add more detailed usage() messages.
*
* When there is a strip misread, should offer the option
* of skipping the strip.
*
* Should add an -X [xl] mode that reads a simple list of readings
* from a file.
*
* Someone reported that XY mode (spectroscan) didn't work for one paper
* orientation ??
*
* Should fix XY chart read to also allow interruption/save/resume,
* just like the strip reading code.
*
* Should add verbose option to print average & max DE to expected value
* for each patch/strip read.
*
*/
/*
* Nomencalture:
*
* Largely due to how the strip readers name things, the following terms
* are used for how patches are grouped:
*
* Step: One test patch in a pass, usually labelled with a number.
* Pass: One row of patches in a strip. A pass is usually labeled
* with a unique alphabetic label.
* Strip: A group of passes that can be read by a strip reader.
* For an XY instrument, the strip is a complete sheet, and
* a each pass is one column. The rows of an XY chart are
* the step numbers within a pass.
* Sheet: One sheet of paper, containing full and partial strips.
* For an XY instrument, there will be only one strip per sheet.
*
*/
#undef DEBUG
#define COMPORT 1 /* Default com port 1..4 */
#undef TEST_EVENT_CALLBACK /* [und] Test code - report async event callbacks, and implement */
/* beep prompt there. */
#undef USESTRDELTA /* [Und] Use patch delta's for correlation rather than match DE */
/* Doesn't seem to work as well. Why ? */
/* Arbitrary thresholds. Good seems about 15-35, bad 95-130 */
#define ACC_WERR_TH 30.0 /* Worst patch in best row dE warning threshold with accurate ref. */
#define WERR_TH 95.0 /* Worst patch in best row dE warnign threshold */
#ifdef __MINGW32__
# define WINVER 0x0500
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <ctype.h>
#include <string.h>
#ifndef SALONEINSTLIB
#include "copyright.h"
#include "aconfig.h"
#include "numlib.h"
#include "conv.h"
#include "xicc.h"
#include "ui.h"
#include "icc.h"
#else /* SALONEINSTLIB */
#include "sa_config.h"
#include "numsup.h"
#include "cgats.h"
#include "rspl1.h"
#include "xspect.h"
#include "xcolorants.h"
#include "xcal.h"
#include "conv.h"
#include "sa_conv.h"
#endif /* SALONEINSTLIB */
#include "cgats.h"
#include "insttypes.h"
#include "icoms.h"
#include "inst.h"
#include "ccmx.h"
#include "ccss.h"
#ifndef SALONEINSTLIB
#include "dispwin.h"
#include "ccast.h"
#include "dispsup.h"
#endif /* !SALONEINSTLIB */
#include "alphix.h"
#include "sort.h"
#include "instappsup.h"
#include <stdarg.h>
#if defined (NT)
#include <conio.h>
#endif
#ifdef DEBUG
# pragma message("######### DEBUG enabled !!!!! ########")
#endif
#ifdef NEVER /* Not currently used */
/* Convert control chars to ^[A-Z] notation in a string */
static char *
fix_asciiz(char *s) {
static char buf [200];
char *d;
for(d = buf; ;) {
if (*s < ' ' && *s > '\000') {
*d++ = '^';
*d++ = *s++ + '@';
} else
*d++ = *s++;
if (s[-1] == '\000')
break;
}
return buf;
}
#endif /* NEVER */
/* Return the normal Delta E, given two 100 scaled XYZ values */
static double xyzLabDE(double ynorm, double *pat, double *ref) {
int i;
double Lab1[3];
double Lab2[3];
for (i = 0; i < 3; i++) {
Lab1[i] = ynorm * pat[i]/100.0;
Lab2[i] = ref[i]/100.0;
}
icmXYZ2Lab(&icmD50, Lab1, Lab1);
icmXYZ2Lab(&icmD50, Lab2, Lab2);
return icmLabDE(Lab1, Lab2);
}
#ifdef USESTRDELTA
/* Return the -ve correlation of the delta E's between steps */
static double xyzLabcorr(double *pat0, double *ref0,
double *pat1, double *ref1) {
double p0[3], p1[3], pd[3];
double r0[3], r1[3], rd[3];
icmXYZ2Lab(&icmD50, p0, pat0);
icmXYZ2Lab(&icmD50, p1, pat1);
ICMSUB3(pd, p0, p1);
icmXYZ2Lab(&icmD50, r0, ref0);
icmXYZ2Lab(&icmD50, r1, ref1);
ICMSUB3(rd, r0, r1);
return -icmDot3(rd, pd);
}
#endif
/* A chart read color structure. */
/* This can hold all representations simultaniously */
typedef struct {
char *id; /* Id string (e.g. "1") */
char *loc; /* Location string (e.g. "A1") */
int loci; /* Location integer = pass * 256 + step */
int n; /* Number of colorants */
double dev[ICX_MXINKS]; /* Value of colorants */
double eXYZ[3]; /* Expected XYZ values (100.0 scale for ref.) */
int rr; /* nz if reading read (used for tracking unread patches) */
inst_meas_type mtype; /* Measurement type */
inst_meas_cond mcond; /* Measurement conditions */
double XYZ[3]; /* Colorimeter readings (100.0 scale for ref.) */
xspect sp; /* Spectrum. sp.spec_n > 0 if valid, 100 scaled for ref. */
} chcol;
extern int json_ui_out;
int json_ui_out = 0;
static int g_use_leds = 0; /* Enable i1Pro 2 visual LED feedback */
/* Update instrument LED state safely */
static void update_led_state(inst *it, inst_led_state state) {
if (!g_use_leds || it == NULL)
return;
if (it->set_led_state != NULL) {
it->set_led_state(it, state);
}
}
static void compute_patch_metrics(chcol *scb, double *eLab, double *mLab) {
/* Expected Lab */
if (scb->eXYZ[0] != 0.0 || scb->eXYZ[1] != 0.0 || scb->eXYZ[2] != 0.0) {
double scaled_exyz[3];
scaled_exyz[0] = scb->eXYZ[0] / 100.0;
scaled_exyz[1] = scb->eXYZ[1] / 100.0;
scaled_exyz[2] = scb->eXYZ[2] / 100.0;
icmXYZ2Lab(&icmD50, eLab, scaled_exyz);
} else {
eLab[0] = eLab[1] = eLab[2] = 0.0;
}
/* Measured Lab */
{
double scaled_mxyz[3];
scaled_mxyz[0] = scb->XYZ[0] / 100.0;
scaled_mxyz[1] = scb->XYZ[1] / 100.0;
scaled_mxyz[2] = scb->XYZ[2] / 100.0;
icmXYZ2Lab(&icmD50, mLab, scaled_mxyz);
}
}
static void emit_row_json_colors(const char *row_id, int row_index, int total_rows, int patch_count, int nchan, chcol **scbs) {
int i, j;
if (!json_ui_out) return;
fprintf(stdout, "ROW_COLORS_JSON: {\"event\": \"row_complete\", \"row_id\": \"%s\", \"row_index\": %d, \"total_rows\": %d, \"patch_count\": %d, \"patches\": [", row_id ? row_id : "", row_index, total_rows, patch_count);
for (i = 0; i < patch_count; i++) {
chcol *scb = scbs[i];
int is_pad = 0;
double eLab[3], mLab[3];
if (scb->id && strcmp(scb->id, "0") == 0)
is_pad = 1;
compute_patch_metrics(scb, eLab, mLab);
fprintf(stdout, "%s{\"id\": \"%s\", \"loc\": \"%s\", \"is_pad\": %s, \"device\": [", i == 0 ? "" : ", ", scb->id ? scb->id : "", scb->loc ? scb->loc : "", is_pad ? "true" : "false");
for (j = 0; j < nchan; j++) {
fprintf(stdout, "%s%.4f", j == 0 ? "" : ", ", scb->dev[j] * 100.0);
}
fprintf(stdout, "]");
if (scb->eXYZ[0] != 0.0 || scb->eXYZ[1] != 0.0 || scb->eXYZ[2] != 0.0) {
fprintf(stdout, ", \"expected\": {\"XYZ\": [%.4f, %.4f, %.4f], \"Lab\": [%.4f, %.4f, %.4f]}",
scb->eXYZ[0], scb->eXYZ[1], scb->eXYZ[2],
eLab[0], eLab[1], eLab[2]);
}
fprintf(stdout, ", \"measured\": {\"XYZ\": [%.4f, %.4f, %.4f], \"Lab\": [%.4f, %.4f, %.4f]",
scb->XYZ[0], scb->XYZ[1], scb->XYZ[2],
mLab[0], mLab[1], mLab[2]);
if (scb->sp.spec_n > 0) {
fprintf(stdout, ", \"spectral\": {\"bands\": %d, \"start_nm\": %.1f, \"end_nm\": %.1f, \"norm\": %.1f, \"values\": [",
scb->sp.spec_n, scb->sp.spec_wl_short, scb->sp.spec_wl_long, scb->sp.norm);
for (j = 0; j < scb->sp.spec_n; j++) {
fprintf(stdout, "%s%.4f", j == 0 ? "" : ", ", scb->sp.spec[j]);
}
fprintf(stdout, "]}");
}
fprintf(stdout, "}}");
}
fprintf(stdout, "]}\n");
fflush(stdout);
}
/* Convert a base 62 character into a number */
/* (This is used for converting the PASSES_IN_STRIPS string */
/* (Could convert this to using an alphix("0-9A-Za-Z")) */
static int b62_int(char *p) {
int cv, rv;
cv = *p;
if (cv == '\000')
rv = 0;
else if (cv <= '9')
rv = cv - '0';
else if (cv <= 'Z')
rv = cv - 'A' + 10;
else
rv = cv - 'a' + 36;
return rv;
}
/* And save the current results */
static void save_ti3(
instType atype, /* Instrument used to read the chart */
int displ, /* 1 = Use display emissive mode, 2 = display bright rel. */
/* 3 = display white rel. */
int trans, /* Use transmission mode */
xcalstd ucalstd, /* X-Rite calibration standard actually used */
inst_opt_filter fe, /* Optional filter */
int dolab, /* 1 = Save CIE as Lab, 2 = Save CIE as XYZ and Lab */
int npat, /* Total valid patches */
int nchan, /* Number of device chanels */
chcol *cols, /* Internal storage of all the patch colors */
int wpat, /* Set to index of white patch for display */
char *outname, /* Output cgats file base name */
cgats *ocg /* output cgats structure */
) {
int i, j;
int nrpat; /* Number of read patches */
int vpix = 0; /* Valid patch index, if nrpatch > 0 */
int nsetel = 0;
double nn[3] = { 1.0, 1.0, 1.0 };
cgats_set_elem *setel; /* Array of set value elements */
/* Note what instrument the chart was read with */
ocg->add_kword(ocg, 0, "TARGET_INSTRUMENT", inst_name(atype) , NULL);
/* X-Rite calibration standard (If reflective mode) */
if (displ == 0 && trans == 0 && ucalstd != xcalstd_none)
ocg->add_kword(ocg, 0, "DEVCALSTD",xcalstd2str(ucalstd), NULL);
if (fe == inst_opt_filter_pol)
ocg->add_kword(ocg, 0, "INSTRUMENT_FILTER", "POLARIZED", NULL);
else if (fe == inst_opt_filter_D65)
ocg->add_kword(ocg, 0, "INSTRUMENT_FILTER", "D65", NULL);
else if (fe == inst_opt_filter_UVCut)
ocg->add_kword(ocg, 0, "INSTRUMENT_FILTER", "UVCUT", NULL);
/* Count patches actually read */
for (nrpat = i = 0; i < npat; i++) {
if (cols[i].rr) {
vpix = i;
nrpat++;
}
}
/* If we've used a display white relative mode, record the absolute white */
if (displ == 2 || displ == 3) {
char buf[100];
if (cols[wpat].rr == 0) {
error("Can't compute white Y relative display values without reading a white test patch");
}
sprintf(buf,"%f %f %f", cols[wpat].XYZ[0], cols[wpat].XYZ[1], cols[wpat].XYZ[2]);
ocg->add_kword(ocg, 0, "LUMINANCE_XYZ_CDM2",buf, NULL);
/* Normalise to white Y 100 */
if (displ == 2) {
nn[0] = 100.0 / cols[wpat].XYZ[1];
nn[1] = 100.0 / cols[wpat].XYZ[1];
nn[2] = 100.0 / cols[wpat].XYZ[1];
/* Normalise to the white point */
} else {
nn[0] = 100.0 * icmD50.X / cols[wpat].XYZ[0];
nn[1] = 100.0 * icmD50.Y / cols[wpat].XYZ[1];
nn[2] = 100.0 * icmD50.Z / cols[wpat].XYZ[2];
}
}
nsetel += 1; /* For id */
nsetel += 1; /* For loc */
nsetel += nchan; /* For device values */
nsetel += 3; /* For XYZ or Lab */
if (dolab == 2)
nsetel += 3; /* For XYZ and Lab */
/* If we have spectral information, output it too */
if (nrpat > 0 && cols[vpix].sp.spec_n > 0) {
char buf[100];
nsetel += cols[vpix].sp.spec_n; /* Spectral values */
sprintf(buf,"%d", cols[vpix].sp.spec_n);
ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL);
sprintf(buf,"%f", cols[vpix].sp.spec_wl_short);
ocg->add_kword(ocg, 0, "SPECTRAL_START_NM",buf, NULL);
sprintf(buf,"%f", cols[vpix].sp.spec_wl_long);
ocg->add_kword(ocg, 0, "SPECTRAL_END_NM",buf, NULL);
/* Generate fields for spectral values */
for (i = 0; i < cols[vpix].sp.spec_n; i++) {
int nm;
/* Compute nearest integer wavelength */
nm = (int)(cols[vpix].sp.spec_wl_short + ((double)i/(cols[vpix].sp.spec_n-1.0))
* (cols[vpix].sp.spec_wl_long - cols[vpix].sp.spec_wl_short) + 0.5);
sprintf(buf,"SPEC_%03d",nm);
ocg->add_field(ocg, 0, buf, r_t);
}
}
if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * nsetel)) == NULL)
error("Malloc failed!");
/* Overwrite any existing sets */
ocg->t[0].nsets = 0;
/* Write out the patch info to the output CGATS file */
for (i = 0; i < npat; i++) {
int k = 0;
if (cols[i].rr == 0 /* If this patch wasn't read */
|| strcmp(cols[i].id, "0") == 0) /* or it is a padding patch. */
continue; /* Skip it */
setel[k++].c = cols[i].id;
setel[k++].c = cols[i].loc;
for (j = 0; j < nchan; j++)
setel[k++].d = 100.0 * cols[i].dev[j];
if (dolab == 0 || dolab == 2) {
setel[k++].d = cols[i].XYZ[0] * nn[0];
setel[k++].d = cols[i].XYZ[1] * nn[1];
setel[k++].d = cols[i].XYZ[2] * nn[2];
}
if (dolab == 1 || dolab == 2) {
double lab[3];
double xyz[3];
xyz[0] = cols[i].XYZ[0] * nn[0]/100.0;
xyz[1] = cols[i].XYZ[1] * nn[1]/100.0;
xyz[2] = cols[i].XYZ[2] * nn[2]/100.0;
icmXYZ2Lab(&icmD50, lab, xyz);
setel[k++].d = lab[0];
setel[k++].d = lab[1];
setel[k++].d = lab[2];
}
/* Check that the spectral matches, in case we're resuming */
if ( cols[i].sp.spec_n != cols[vpix].sp.spec_n
|| fabs(cols[i].sp.spec_wl_short - cols[vpix].sp.spec_wl_short) > 0.01
|| fabs(cols[i].sp.spec_wl_long - cols[vpix].sp.spec_wl_long) > 0.01 ) {
error("The resumed spectral type seems to have changed!");
}
for (j = 0; j < cols[i].sp.spec_n; j++) {
setel[k++].d = cols[i].sp.spec[j];
}
ocg->add_setarr(ocg, 0, setel);
}
free(setel);
if (ocg->write_name(ocg, outname))
error("Write error : %s",ocg->e.m);
}
#ifdef TEST_EVENT_CALLBACK
void test_event_callback(void *cntx, inst_event_type event) {
a1logd(g_log,0,"Got event_callback with 0x%x\n",event);
if (event == inst_event_scan_ready)
normal_beep();
}
#endif
/* Deal with an instrument error. */
/* Return 0 to retry, 1 to abort */
static int ierror(inst *it, inst_code ic) {
int ch;
empty_con_chars();
printf("Got '%s' (%s) error.\nHit Esc or 'q' to give up, any other key to retry:%s",
it->inst_interp_error(it, ic), it->interp_error(it, ic),fl_end);
do_fflush();
ch = next_con_char();
printf("\n");
if (ch == 0x03 || ch == 0x1b || ch == 'q' || ch == 'Q') /* ^C, Escape or Q */
return 1;
return 0;
}
/* Read all the strips, and return nonzero on abort/error */
static int
read_strips(
instType itype, /* Chart instrument type */
chcol **scols, /* Location sorted pointers to cols (return values) */
instType *atype, /* Return the instrument type used to read the chart */
int npat, /* Total valid patches */
int totpa, /* Total passes (rows) */
int stipa, /* Steps (patches) in pass (Excluding DTP51 Max/Min) */
int *pis, /* Passes in each strip (rows in each sheet), 0 terminated */
alphix *paix, /* Pass (row) index generators */
alphix *saix, /* Step (patch) index generators */
int ixord, /* Index order, 0 = pass then step */
int rstart, /* Random start/chart id */
int rand, /* Random order used - can do auto strip ID & Bi-Dir */
int hex, /* Hexagon test patches */
icompath *ipath, /* Instrument path to open */
flow_control fc, /* flow control */
double plen, /* Patch length in mm (used by DTP20/41) */
double glen, /* Gap length in mm (used by DTP20/41) */
double tlen, /* Trailer length in mm (used by DTP41T) */
int trans, /* Use transmission mode */
int emis, /* Use emissive mode */
int displ, /* 1 = Use display emissive mode, 2 = display bright rel. */
/* 3 = display white rel. */
int ditype, /* Display type selection charater */
inst_opt_filter fe, /* Optional filter */
xcalstd scalstd, /* X-Rite calibration standard to set */
xcalstd *ucalstd, /* X-Rite calibration standard actually used */
int nocal, /* Disable initial calibration */
int disbidi, /* 1 = Disable automatic bi-directional strip recognition, 2 = force enable */
int highres, /* Use high res spectral mode */
char *ccxxname, /* Colorimeter Correction/Colorimeter Calibration name */
icxObserverType obType, /* ccss observer */
xspect custObserver[3], /* If obType = icxOT_custom */
double scan_tol, /* Modify patch consistency tolerance */
int pbypatch, /* Patch by patch measurement */
int xtern, /* Use external (user supplied) values rather than instument read */
/* 1 = Lab, 2 = XYZ */
int spectral, /* Generate spectral info flag */
int uvmode, /* ~~~ i1pro2 test mode ~~~ */
int accurate_expd, /* Expected values can be assumed to be accurate */
int emit_warnings, /* Emit warnings for wrong strip, unexpected value */
int doplot, /* Plot each spectra in patch by patch mode */
a1log *log /* verb, debug & error log */
) {
inst *it = NULL;
inst_mode cap; /* Mode capability */
inst2_capability cap2;
inst3_capability cap3;
int n, i, j;
int rmode = 0; /* Read mode, 0 = spot, 1 = strip, 2 = xy, 3 = chart */
int svdmode = 0; /* Saved mode, 0 = no, 1 = use saved mode */
inst_code rv;
baud_rate br = baud_38400; /* Target baud rate */
int skipp = 0; /* Initial strip readings to skip */
int nextrap = 0; /* Number of extra patches for max and min */
int ch;
char *pfname = NULL;
if (xtern == 0) { /* Use instrument values */
/* Instrument that the chart is set up for */
if (itype == instDTP51) {
skipp = 1; /* First reading is the Max density patch */
nextrap = 2;
}
if ((it = new_inst(ipath, 0, log, DUIH_FUNC_AND_CONTEXT)) == NULL) {
printf("Unknown, inappropriate or no instrument detected\n");
if (pfname != NULL)
free(pfname);
return -1;
}
#ifdef TEST_EVENT_CALLBACK
it->set_event_callback(it, test_event_callback, (void *)it);
#endif
/* Establish communications */
if ((rv = it->init_coms(it, br, fc, 15.0)) != inst_ok) {
printf("Establishing communications with instrument failed with message '%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
/* Set it up the way we want */
if ((rv = it->init_inst(it)) != inst_ok) {
printf("Initialising instrument failed with message '%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
/* For reflective */
if (emis == 0 && trans == 0) {
/* set XRGA conversion */
if (scalstd != xcalstd_none) {
if ((rv = it->get_set_opt(it, inst_opt_set_xcalstd, scalstd)) != inst_ok) {
printf("Warning: Setting calibration standard not supported by instrument\n");
}
}
/* Get actual XRGA conversion */
if (ucalstd != NULL) {
if ((rv = it->get_set_opt(it, inst_opt_get_xcalstd, ucalstd)) != inst_ok) {
*ucalstd = xcalstd_none;
}
}
}
*atype = it->get_itype(it); /* Actual instrument type */
if (*atype != itype)
a1logv(log, 1, "Warning: chart is for %s, using instrument %s\n",inst_name(itype),inst_name(*atype));
{
int ccssset = 0;
inst_mode mode = 0;
it->capabilities(it, &cap, &cap2, &cap3);
if (trans) {
if (!IMODETST(cap, inst_mode_transmission)) {
printf("Need transmission reading capability,\n");
printf("and instrument doesn't support it\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
} else if (emis || displ) {
if (emis) {
if (it->check_mode(it, inst_mode_emis_spot) != inst_ok
&& it->check_mode(it, inst_mode_emis_strip) != inst_ok) {
printf("Need emissive spot or strip reading capability\n");
printf("and instrument doesn't support it\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
} else {
/* Should we allow for non-adaptive mode ? */
if (it->check_mode(it, inst_mode_emis_spot) != inst_ok) {
printf("Need emissive reading capability\n");
printf("and instrument doesn't support it\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
}
} else { /* reflectance */
if (!IMODETST(cap, inst_mode_reflection)) {
printf("Need reflection spot, strip, xy or chart reading capability,\n");
printf("and instrument doesn't support it\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
}
/* Set display type or calibration mode */
if (ditype != 0) {
if (cap2 & inst2_disptype) {
int ix;
if ((ix = inst_get_disptype_index(it, ditype, 0)) < 0) {
printf("Setting display type ix %d failed\n",ix);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if ((rv = it->set_disptype(it, ix)) != inst_ok) {
printf("Setting display type ix %d not supported by instrument\n",ix);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
} else
printf("Display type ignored - instrument doesn't support display type selection\n");
}
if (spectral && !IMODETST(cap, inst_mode_spectral)) {
printf("Warning: Instrument isn't capable of spectral measurement\n");
spectral = 0;
}
/* Colorimeter Correction Matrix */
if (ccxxname[0] != '\000') {
ccss *cs = NULL;
ccmx *cx = NULL;
if ((cx = new_ccmx()) == NULL) {
printf("\nnew_ccmx failed\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (cx->read_ccmx(cx,ccxxname) == 0) {
if ((cap2 & inst2_ccmx) == 0) {
printf("\nInstrument doesn't have Colorimeter Correction Matrix capability\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if ((rv = it->col_cor_mat(it, cx->dtech, cx->cc_cbid, cx->matrix)) != inst_ok) {
printf("\nSetting Colorimeter Correction Matrix failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
cx->del(cx);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
cx->del(cx);
} else {
cx->del(cx);
cx = NULL;
/* CCMX failed, try CCSS */
if ((cs = new_ccss()) == NULL) {
printf("\nnew_ccss failed\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (cs->read_ccss(cs,ccxxname)) {
printf("\nReading CCMX/CCSS File '%s' failed with error %d:'%s'\n",
ccxxname, cs->e.c, cs->e.m);
cs->del(cs);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if ((cap2 & inst2_ccss) == 0) {
printf("\nInstrument doesn't have Colorimeter Calibration Spectral Sample capability\n");
cs->del(cs);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if ((rv = it->get_set_opt(it, inst_opt_set_ccss_obs, obType, custObserver)) != inst_ok) {
printf("\nSetting CCSS observer failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
cs->del(cs);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if ((rv = it->col_cal_spec_set(it, cs->dtech, cs->samples, cs->no_samp)) != inst_ok) {
printf("\nSetting Colorimeter Calibration Spectral Samples failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
cs->del(cs);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
ccssset = 1;
cs->del(cs);
}
}
/* If non-standard observer wasn't set by a CCSS file above */
if (obType != icxOT_default && (cap2 & inst2_ccss) && ccssset == 0) {
if ((rv = it->get_set_opt(it, inst_opt_set_ccss_obs, obType, custObserver)) != inst_ok) {
printf("\nSetting CCSS observer failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
}
/* set reflective mode filters */
if (fe != inst_opt_filter_unknown) {
if ((rv = it->get_set_opt(it, inst_opt_set_filter, fe)) != inst_ok) {
printf("Setting requested filter not supported by instrument\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
}
/* Disable initial calibration of machine if selected */
if (nocal != 0){
if ((rv = it->get_set_opt(it,inst_opt_noinitcalib, 0)) != inst_ok) {
printf("Setting no-initial calibrate failed with '%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
printf("Disable inital-calibrate not supported\n");
}
}
/* If it battery powered, show the status of the battery */
if ((cap2 & inst2_has_battery)) {
double batstat = 0.0;
if ((rv = it->get_set_opt(it, inst_stat_battery, &batstat)) != inst_ok) {
printf("\nGetting instrument battery status failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("The battery charged level is %.0f%%\n",batstat * 100.0);
}
if (scan_tol != 1.0) {
if (cap2 & inst2_has_scan_toll) {
inst_code ev;
if ((ev = it->get_set_opt(it, inst_opt_scan_toll, scan_tol)) != inst_ok) {
printf("\nSetting patch consistency tolerance to %f failed with error :'%s' (%s)\n",
scan_tol, it->inst_interp_error(it, ev), it->interp_error(it, ev));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
} else {
a1logv(log, 1, "Modified patch consistency tolerance ignored - instrument doesn't support it\n");
}
}
/* Should look at instrument type & user spec ??? */
if (trans) {
if (pbypatch
&& it->check_mode(it, inst_mode_trans_spot) == inst_ok) {
mode = inst_mode_trans_spot;
rmode = 0;
} else if (it->check_mode(it, inst_mode_trans_chart) == inst_ok) {
mode = inst_mode_trans_chart;
rmode = 3;
} else if (it->check_mode(it, inst_mode_trans_xy) == inst_ok) {
mode = inst_mode_trans_xy;
rmode = 2;
} else if (it->check_mode(it, inst_mode_trans_strip) == inst_ok) {
mode = inst_mode_trans_strip;
rmode = 1;
} else {
mode = inst_mode_trans_spot;
rmode = 0;
}
} else if (displ) {
/* We assume a display mode will always be spot by spot */
mode = inst_mode_emis_spot;
rmode = 0;
} else if (emis) {
if (pbypatch
&& it->check_mode(it, inst_mode_emis_spot) == inst_ok) {
mode = inst_mode_emis_spot;
rmode = 0;
} else if (it->check_mode(it, inst_mode_emis_strip) == inst_ok) {
mode = inst_mode_emis_strip;
rmode = 1;
} else {
mode = inst_mode_emis_spot;
rmode = 0;
}
} else { /* Reflectance */
inst_stat_savdrd sv = inst_stat_savdrd_none;
/* See if instrument has a saved mode, and if it has data that */
/* could match this chart */
if (IMODETST(cap, inst_mode_s_reflection)) {
a1logv(log, 2, "Instrument has a svaed chart mode\n");
if ((rv = it->get_set_opt(it, inst_stat_saved_readings, &sv)) != inst_ok) {
printf("Getting saved reading status failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (sv & inst_stat_savdrd_chart) {
int no_patches, no_rows, pat_per_row, chart_id, missing_row;
a1logv(log, 2, "There is a saved chart\n");
if ((rv = it->get_set_opt(it, inst_stat_s_chart,
&no_patches, &no_rows, &pat_per_row, &chart_id, &missing_row))
!= inst_ok) {
printf("Getting saved chart details failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (npat != no_patches || totpa != no_rows
|| stipa != pat_per_row || rstart != chart_id) {
printf("Can't use saved chart because it doesn't match\n");
sv &= ~inst_stat_savdrd_chart;
}
if (missing_row >= 0) {
printf("Can't use saved chart because row %d hasn't been read\n",missing_row);
sv &= ~inst_stat_savdrd_chart;
}
}
if (sv & inst_stat_savdrd_xy) {
int nstr;
int no_sheets, no_patches, no_rows, pat_per_row;
/* Count the number of strips (sheets) */
for (nstr = 0; pis[nstr] != 0; nstr++)
;
if ((rv = it->get_set_opt(it, inst_stat_s_xy,
&no_sheets, &no_patches, &no_rows, &pat_per_row)) != inst_ok) {
printf("Getting saved sheet details failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (nstr != no_sheets || npat != no_patches
|| totpa != no_rows || stipa != pat_per_row) {
a1logv(log, 1, "Can't use saved sheets because they don't match chart\n");
a1logv(log, 1, "Got %d sheets, expect %d. Got %d patches, expect %d.\n",
no_sheets,nstr,no_patches,npat);
a1logv(log, 1, "Got %d rows, expect %d. Got %d patches per row, expect %d.\n",
no_rows,totpa,pat_per_row,stipa);
sv &= ~inst_stat_savdrd_xy;
}
}
if (sv & inst_stat_savdrd_strip) {
int no_patches, no_rows, pat_per_row;
if ((rv = it->get_set_opt(it, inst_stat_s_strip,
&no_patches, &no_rows, &pat_per_row)) != inst_ok) {
printf("Getting saved strip details failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (npat != no_patches || totpa != no_rows || stipa != pat_per_row) {
a1logv(log, 1, "Can't use saved strips because they don't match chart\n");
sv &= ~inst_stat_savdrd_strip;
}
}
if (sv & inst_stat_savdrd_spot) {
int no_patches;
if ((rv = it->get_set_opt(it, inst_stat_s_spot, &no_patches)) != inst_ok) {
printf("Getting saved spot details failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (npat != no_patches) {
a1logv(log, 1, "Can't use saved spots because they don't match chart - got %d patches, expect %d\n",no_patches,npat);
sv &= ~inst_stat_savdrd_spot;
}
}
}
if (pbypatch
&& it->check_mode(it, inst_mode_s_ref_spot) == inst_ok
&& (sv & inst_stat_savdrd_spot)) {
mode = inst_mode_s_ref_spot;
svdmode = 1;
rmode = 0;
} else if (it->check_mode(it, inst_mode_s_ref_chart) == inst_ok
&& (sv & inst_stat_savdrd_chart)) {
mode = inst_mode_s_ref_chart;
svdmode = 1;
rmode = 3;
} else if (it->check_mode(it, inst_mode_s_ref_xy) == inst_ok
&& (sv & inst_stat_savdrd_xy)) {
mode = inst_mode_s_ref_xy;
svdmode = 1;
rmode = 2;
} else if (it->check_mode(it, inst_mode_s_ref_strip) == inst_ok
&& (sv & inst_stat_savdrd_strip)) {
mode = inst_mode_s_ref_strip;
svdmode = 1;
rmode = 1;
} else if (it->check_mode(it, inst_mode_s_ref_spot) == inst_ok
&& (sv & inst_stat_savdrd_spot)) {
mode = inst_mode_s_ref_spot;
svdmode = 1;
rmode = 0;
} else if (pbypatch
&& it->check_mode(it, inst_mode_ref_spot) == inst_ok) {
mode = inst_mode_ref_spot;
rmode = 0;
} else if (it->check_mode(it, inst_mode_ref_chart) == inst_ok) {
mode = inst_mode_ref_chart;
rmode = 3;
} else if (it->check_mode(it, inst_mode_ref_xy) == inst_ok) {
mode = inst_mode_ref_xy;
rmode = 2;
} else if (it->check_mode(it, inst_mode_ref_strip) == inst_ok) {
mode = inst_mode_ref_strip;
rmode = 1;
} else {
mode = inst_mode_ref_spot;
rmode = 0;
}
}
if (spectral)
mode |= inst_mode_spectral;
if (highres) {
if (IMODETST(cap, inst_mode_highres)) {
mode |= inst_mode_highres;
} else {
a1logv(log, 1, "high resolution ignored - instrument doesn't support high res. mode\n");
}
}
// ~~~ i1pro2 test code ~~~ */
if (uvmode) {
if (!IMODETST(cap, inst_mode_ref_uv)) {
warning("UV measurement mode requested, but instrument doesn't support this mode");
uvmode = 0;
} else {
mode |= inst_mode_ref_uv;
}
}
if ((rv = it->set_mode(it, mode)) != inst_ok) {
printf("\nSetting instrument mode failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
it->capabilities(it, &cap, &cap2, &cap3);
}
}
/* -------------------------------------------------- */
if (rmode == 3) { /* For chart mode, read all at once */
int chid; /* Chart ID number */
ipatch *vals; /* Array of values */
if (svdmode)
printf("Reading chart from values saved in instrument\n");
else {
/* ~~999 ??? Need to setup trigger and wait for it appropriately ??? */
printf("Reading the whole chart in one go\n");
}
/* Allocate space for patches */
if ((vals = (ipatch *)calloc(sizeof(ipatch), npat)) == NULL)
error("Malloc failed!");
/* Initialise return values */
for (i = 0; i < npat; i++) {
strncpy(vals[i].loc, scols[i]->loc, ICOM_MAX_LOC_LEN-1);
vals[i].loc[ICOM_MAX_LOC_LEN-1] = '\000';
vals[i].XYZ_v = 0;
}
for (;;) { /* retry loop */
if ((rv = it->read_chart(it, npat, totpa, stipa, pis, rstart, vals)) == inst_ok)
break;
if (ierror(it, rv) == 0) /* Ignore */
continue;
break; /* Abort */
}
if (rv != inst_ok) {
free(vals);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("Chart read OK\n");
/* Transfer the values */
/* We assume they are all in the right order */
for (i = 0; i < npat; i++) {
/* Copy XYZ */
if (vals[i].XYZ_v == 0)
error("Instrument didn't return XYZ value for patch %d, loc %s",i,scols[i]->loc);
for (j = 0; j < 3; j++)
scols[i]->XYZ[j] = vals[i].XYZ[j];
/* Copy spectral */
if (vals[i].sp.spec_n > 0) {
scols[i]->sp = vals[i].sp;
}
scols[i]->mtype = vals[i].mtype;
scols[i]->mcond = vals[i].mcond;
scols[i]->rr = 1; /* Has been read */
}
if (json_ui_out) {
int k;
for (k = 0; k < totpa; k++) {
char row_id[32];
sprintf(row_id, "%s", paix->aix(paix, k));
emit_row_json_colors(row_id, k, totpa, stipa, scols[0]->n, &scols[k * stipa]);
}
}
free(vals);
/* -------------------------------------------------- */
/* !!! Hmm. Should really allow user to navigate amongst the sheets, */
/* !!! and skip any sheets already read. */
} else if (rmode == 2) { /* For xy mode, read each sheet */
ipatch *vals;
int nsheets, sheet; /* Total sheets/current sheet (sheet == pass) */
int rpat = npat; /* Remaining total patches */
int pai; /* Overall pass index */
int sti; /* Overall step index */
char *pn[3] = { NULL, NULL, NULL} ; /* Location 1/2/3 Pass name (letters) */
char *sn[3] = { NULL, NULL, NULL} ; /* Location 1/2/3 Step name (numbers) */
int k;
{ /* Figure the maximum number sheets and of patches in a sheet, for allocation */
int lpaist = 0; /* Largest number of passes in strip/sheet */
for (nsheets = 0; pis[nsheets] != 0; nsheets++) {
if (pis[nsheets] > lpaist)
lpaist = pis[nsheets];
}
if ((vals = (ipatch *)calloc(sizeof(ipatch), (lpaist * stipa))) == NULL)
error("Malloc failed!");
}
/* Make sure we can access the instrument table */
if (cap2 & inst2_xy_holdrel) {
it->xy_clear(it);
}
/* XY mode doesn't use the trigger mode */
/* For each pass (==sheet) */
for (sheet = 1, pai = sti = 0; pis[sheet-1] != 0; sheet++) {
int isti = sti; /* Initial sti */
int paist; /* Passes in current Strip (== columns in current sheet) */
int rnpatch; /* Rounded up (inc. padding) Patches in current pass (sheet) */
int npatch; /* Patches in pass (sheet), excluding padding */
int fspipa; /* First pass steps in pass */
int nloc; /* Number of fiducial locations needed */
double ox = 0.0, oy = 0.0; /* Origin */
double ax = 1.0, ay = 0.0; /* pass increment */
double aax = 0.0, aay = 0.0; /* pass offset for hex odd steps */
double px = 0.0, py = 1.0; /* step (==patch) increment */
fspipa = stipa;
paist = pis[sheet-1]; /* columns (letters) in sheet (strip) */
npatch = rnpatch = paist * stipa; /* Total patches including padding */
if (npatch > rpat) { /* This is a non-full pass */
if (paist == 1) {
fspipa -= (npatch - rpat);/* Last patch in first strip */
if (fspipa < 1)
error ("Assert in read_strips, fspipa = %d",fspipa);
}
npatch = rpat; /* Total patches excluding padding */
}
nloc = 3;
if (paist == 1) {
nloc = 2; /* Only one strip, so only 2 locations needed */
if (fspipa == 1)
nloc = 1; /* Only one strip, one patch, so one location */
}
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
pn[0] = paix->aix(paix, pai); /* First pass (letter) */
sn[0] = saix->aix(saix, 0); /* First step (patch) (number) */
pn[1] = paix->aix(paix, pai); /* First pass (letter) */
sn[1] = saix->aix(saix, 0 + fspipa-1); /* Last step (patch) (number) */
pn[2] = paix->aix(paix, pai + paist-1); /* Last pass (letter) */
sn[2] = saix->aix(saix, 0); /* First step (patch) (number) */
empty_con_chars();
if (sheet == 1) {
printf("Please place sheet %d of %d on table, then\n",sheet, nsheets);
} else
printf("\nPlease remove previous sheet, then place sheet %d of %d on table, then\n",sheet, nsheets);
printf("hit return to continue, Esc or 'q' to give up%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
printf("\n");
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
if (cap2 & inst2_xy_holdrel) {
/* Hold table */
for (;;) { /* retry loop */
if ((rv = it->xy_sheet_hold(it)) == inst_ok)
break;
if (ierror(it, rv) == 0) /* Ignore */
continue;
break; /* Abort */
}
if (rv != inst_ok) {
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
}
if (cap2 & inst2_xy_locate) {
int ll;
double x[3], y[3];
/* Allow user location of points */
for (;;) { /* retry loop */
if ((rv = it->xy_locate_start(it)) == inst_ok)
break;
if (ierror(it, rv) == 0) /* Ignore */
continue;
break; /* Abort */
}
if (rv != inst_ok) {
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
/* For each location point */
for (ll = 0; ll < nloc; ll++) {
empty_con_chars();
printf("\nUsing the XY table controls, locate patch %s%s with the sight,\n",
pn[ll], sn[ll]);
printf("then hit return to continue, Esc or 'q' to give up%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
printf("\n");
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
for (;;) { /* retry loop */
if ((rv = it->xy_get_location(it, &x[ll], &y[ll])) == inst_ok)
break;
if (ierror(it, rv) == 0) /* Ignore */
continue;
break; /* Abort */
}
if (rv != inst_ok) {
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
}
/* We're done with user control */
for (;;) { /* retry loop */
if ((rv = it->xy_locate_end(it)) == inst_ok)
break;
if (ierror(it, rv) == 0) /* Ignore */
continue;
break; /* Abort */
}
if (rv != inst_ok) {
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
/* Convert point locations into navigation values */
ox = x[0];
oy = y[0];
if (hex) {
double kk = sqrt(1.0/3.0);
double nn = fspipa - 1.0;
if (((fspipa-1) & 1) == 0) { /* [0] & [1] are lined up */
if (nloc == 3) {
px = (x[1] - x[0])/nn;
py = (y[1] - y[0])/nn;
ax = (x[2] - x[0])/(paist-1);
ay = (y[2] - y[0])/(paist-1);
aax = 0.5 * ax;
aay = 0.5 * ay;
} else if (nloc == 2) {
px = (x[1] - x[0])/nn;
py = (y[1] - y[0])/nn;
aax = kk * py; /* Scale and rotate */
aay = kk * -px;
}
} else { /* [0] & [1] are offset by aa[xy] */
if (nloc == 3) {
ax = (x[2] - x[0])/(paist-1);
ay = (y[2] - y[0])/(paist-1);
aax = 0.5 * ax;
aay = 0.5 * ay;
px = (x[1] - x[0] - aax)/nn;
py = (y[1] - y[0] - aay)/nn;
} else if (nloc == 2) {
px = (nn * (x[1] - x[0]) - kk * (y[1] - y[0]))/(kk * kk + nn * nn);
py = (nn * (y[1] - y[0]) + kk * (x[1] - x[0]))/(kk * kk + nn * nn);
aax = kk * py; /* Scale and rotate */
aay = kk * -px;
}
}
} else { /* Rectangular patches */
if (paist > 1) {
ax = (x[2] - x[0])/(paist-1);
ay = (y[2] - y[0])/(paist-1);
}
if (fspipa > 1) {
px = (x[1] - x[0])/(fspipa-1);
py = (y[1] - y[0])/(fspipa-1);
}
aax = aay = 0.0;
}
}
/* Read the sheets patches */
for (;;) { /* retry loop */
if ((rv = it->read_xy(it, paist, stipa, npatch, pn[0], sn[0],
ox, oy, ax, ay, aax, aay, px, py, vals)) == inst_ok)
break;
if (ierror(it, rv) == 0) /* Ignore */
continue;
break; /* Abort */
}
if (rv != inst_ok) {
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("Sheet %d of %d read OK\n",sheet, nsheets);
/* Transfer the values */
/* We assume they are all in the right order */
for (i = 0; i < npatch; i++, sti++) {
/* Copy XYZ */
if (vals[i].XYZ_v == 0)
error("Instrument didn't return XYZ value for patch %d, loc %s",i,scols[sti]->loc);
for (j = 0; j < 3; j++)
scols[sti]->XYZ[j] = vals[i].XYZ[j];
/* Copy spectral */
if (vals[i].sp.spec_n > 0) {
scols[sti]->sp = vals[i].sp;
}
scols[sti]->rr = 1; /* Has been read */
}
if (json_ui_out) {
int k;
for (k = 0; k < paist; k++) {
char row_id[32];
sprintf(row_id, "%s", paix->aix(paix, pai + k));
emit_row_json_colors(row_id, pai + k, totpa, stipa, scols[0]->n, &scols[(pai + k) * stipa]);
}
}
if (cap2 & inst2_xy_holdrel) {
/* Release table and reset head */
for (;;) { /* retry loop */
if ((rv = it->xy_clear(it)) == inst_ok)
break;
if (ierror(it, rv) == 0) /* Ignore */
continue;
break; /* Abort */
}
if (rv != inst_ok) {
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
}
pai += paist; /* Tracj next first pass in strip */
rpat -= npatch; /* Track remaining patches */
} /* Next pass/sheet */
for (k = 0; k < 3; k++) {
if (pn[k] != NULL)
free(pn[k]);
if (sn[k] != NULL)
free(sn[k]);
}
free(vals);
printf("\nPlease remove last sheet from table\n");
/* -------------------------------------------------- */
} else if (rmode == 1) { /* For strip mode, simply read each strip */
int uswitch = 0; /* 0 if switch can be used, 1 if switch or keyboard */
ipatch *vals; /* Values read for a strip pass */
int incflag = 0; /* 0 = no change, 1 = increment, 2 = inc unread, */
/* -1 = decrement, -2 = done */
int stix; /* Strip index */
int pai; /* Current pass/row in current strip */
int oroi; /* Overall row index */
if (
itype != instDTP20 &&
!rand && disbidi == 2) {
warning("Bi-directional strip recognition may not work without randomized patch locations");
}
/* Do any needed calibration before the user places the instrument on a desired spot */
if (it->needs_calibration(it) & inst_calt_n_dfrble_mask) {
update_led_state(it, inst_led_cal_wait);
if ((rv = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0))
!= inst_ok) {
update_led_state(it, inst_led_off);
printf("\nCalibration failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
update_led_state(it, inst_led_off);
}
/* Enable (switch or user) via uicallback trigger if possible */
if (cap2 & inst2_user_switch_trig) {
rv = it->get_set_opt(it, inst_opt_trig_user_switch);
uswitch = 2;
/* Or use just switch trigger */
} else if (cap2 & inst2_switch_trig) {
rv = it->get_set_opt(it, inst_opt_trig_switch);
uswitch = 1;
/* Or go for user vi uicallback trigger */
} else if (cap2 & inst2_user_trig) {
rv = it->get_set_opt(it, inst_opt_trig_user);
/* Or something is wrong with instrument capabilities */
} else {
printf("\nNo reasonable trigger mode available for this instrument\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (rv != inst_ok) {
printf("\nSetting trigger mode failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
/* Set so that return or any other key triggers, */
/* but retain our abort keys */
inst_set_uih(0x00, 0xff, DUIH_TRIG);
inst_set_uih('f', 'f', DUIH_CMND);
inst_set_uih('F', 'F', DUIH_CMND);
inst_set_uih('b', 'b', DUIH_CMND);
inst_set_uih('B', 'B', DUIH_CMND);
inst_set_uih('n', 'n', DUIH_CMND);
inst_set_uih('N', 'N', DUIH_CMND);
inst_set_uih('g', 'g', DUIH_CMND);
inst_set_uih('G', 'G', DUIH_CMND);
inst_set_uih('d', 'd', DUIH_CMND);
inst_set_uih('D', 'D', DUIH_CMND);
inst_set_uih('q', 'q', DUIH_ABORT);
inst_set_uih('Q', 'Q', DUIH_ABORT);
inst_set_uih(0x03, 0x03, DUIH_ABORT); /* ^c */
inst_set_uih(0x1b, 0x1b, DUIH_ABORT); /* Esc */
/* Allocate space for values from a pass/strip */
if ((vals = (ipatch *)calloc(sizeof(ipatch), (stipa+nextrap))) == NULL)
error("Malloc failed!");
/* Until we're done reading rows */
incflag = 0;
/* Skip to next unread if first has been read */
if (pis[0] != 0 && scols[0]->rr != 0)
incflag = 2;
/* Until we're done reading patches */
for (oroi = stix = pai = 0; pis[0] != 0;) {
char *nn = NULL; /* Pass name */
int guide;
chcol **scb;
int boff = 0; /* Best offset */
int bdir = 0; /* Best overall direction */
int done = 0; /* nz if there are no unread rows */
//printf("\n~1 incflag = %d, oroi %d, pai %d, stix %d\n", incflag, oroi, pai, stix);
/* Increment or decrement to the next row */
if (incflag > 0) {
int s_oroi = oroi;
/* Increment 1 or we get to an unread pass */
for (;;) {
oroi++;
if ((oroi * stipa) >= npat
|| ++pai >= pis[stix]) { /* Carry to next strip */
if ((oroi * stipa) >= npat
|| pis[++stix] == 0) { /* Circle back to first strip & row */
stix = 0;
oroi = 0;
}
pai = 0;
}
//printf("~1 stix = %d, pis[stix] = %d, oroi = %d, rr %d\n",stix, pis[stix],oroi,scols[oroi * stipa]->rr);
// Note we aren't protecting agains a bodgy pis[] value
if (incflag == 1 || oroi == s_oroi || scols[oroi * stipa]->rr == 0)
break;
}
/* Decrement the row */
} else if (incflag < 0) {
oroi--;
if (--pai < 0) { /* Carry */
if (--stix < 0) { /* Carry */
for (oroi = stix = 0; pis[stix] != 0; stix++) {
oroi += pis[stix];
}
stix--;
oroi--;
}
pai = pis[stix]-1;
}
}
incflag = 0;
/* See if there are any unread patches */
for (done = i = 0; i < npat; i += stipa) {
if (scols[i]->rr == 0)
break; /* At least one patch read */
}
if (i >= npat)
done = 1;
//printf("~1 oroi %d, pai %d, stix %d pis[stix] %d, rr = %d\n", oroi, pai, stix, pis[stix],scols[oroi * stipa]->rr);
/* Convert overall pass number index into alpha label */
if (nn != NULL)
free(nn);
nn = paix->aix(paix, oroi);
guide = (pis[stix] - pai) * 5; /* Mechanical guide offset */
for (;;) { /* Until we give up reading this row */
/* Read a strip pass */
printf("\nReady to read strip pass %s%s\n",nn, done ? " (!! ALL ROWS READ !!)" : scols[oroi * stipa]->rr ? " (This row has been read)" : "" );
printf("Press 'f' to move forward, 'b' to move back, 'n' for next unread,\n");
printf(" 'd' when done, Esc or 'q' to quit without saving.\n");
if (uswitch == 1) {
printf("Trigger instrument switch to start reading.%s",fl_end);
} else if (uswitch == 2) {
printf("Trigger instrument switch or any other key to start:%s",fl_end);
} else {
printf("Press any other key to start:%s",fl_end);
}
do_fflush();
update_led_state(it, inst_led_row_ready);
if ((rv = it->read_strip(it, "STRIP", stipa+nextrap, nn, guide, plen, glen, tlen, vals)) != inst_ok
&& (rv & inst_mask) != inst_user_trig) {
#ifdef DEBUG
printf("read_strip returned '%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
#endif /* DEBUG */
/* Deal with user abort or command */
if ((rv & inst_mask) == inst_user_abort) {
int keyc = inst_get_uih_char();
/* Deal with a command */
if (keyc & DUIH_CMND) {
ch = keyc & 0xff;
printf("\n");
if (ch == 'f' || ch == 'F') {
incflag = 1;
break;
} else if (ch == 'b' || ch == 'B') {
incflag = -1;
break;
} else if (ch == 'n' || ch == 'N') {
incflag = 2;
break;
} else { /* Assume 'd' or 'D' */
/* See if there are any unread patches */
for (done = i = 0; i < npat; i += stipa) {
if (scols[i]->rr == 0)
break; /* At least one patch read */
}
if (i >= npat)
done = 1;
if (done) {
incflag = -2;
break;
}
/* Not all rows have been read */
empty_con_chars();
printf("\nDone ? - At least one unread patch (%s, %s), Are you sure [y/n]: %s",
scols[i]->id, scols[i]->loc, fl_end);
do_fflush();
ch = next_con_char();
printf("\n");
if (ch == 'y' || ch == 'Y') {
incflag = -2;
break;
}
continue;
}
/* Deal with a user abort */
} else if (keyc & DUIH_ABORT) {
empty_con_chars();
printf("\n\nStrip read stopped at user request!\n");
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end);
do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n");
if (nn != NULL) free(nn);
free(vals);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
continue;
}
/* Deal with needs calibration */
} else if ((rv & inst_mask) == inst_needs_cal) {
inst_code ev;
if (cap2 & inst2_no_feedback)
bad_beep();
printf("\nStrip read failed because instruments needs calibration\n");
update_led_state(it, inst_led_cal_wait);
ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0);
update_led_state(it, inst_led_off);
if (ev != inst_ok) { /* Abort or fatal error */
if (nn != NULL) free(nn);
free(vals);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
continue;
/* Deal with a bad sensor position */
} else if ((rv & inst_mask) == inst_wrong_config) {
printf("\n\nSpot read failed due to the sensor being in the wrong position\n(%s)\n",it->interp_error(it, rv));
continue;
/* Deal with a misread */
} else if ((rv & inst_mask) == inst_misread) {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback)
bad_beep();
empty_con_chars();
printf("\nStrip read failed due to misread (%s)\n",it->interp_error(it, rv));
printf("Hit Esc to give up, any other key to retry%s:",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n");
if (nn != NULL) free(nn);
free(vals);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
continue;
/* Deal with a communications error */
} else if ((rv & inst_mask) == inst_coms_fail) {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback)
bad_beep();
empty_con_chars();
printf("\nStrip read failed due to communication problem.\n");
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n");
if (nn != NULL) free(nn);
free(vals);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
if ((it->icom->port_type(it->icom) & icomt_serial)
&& !(it->icom->port_attr(it->icom) & icomt_fastserial)) {
/* Allow retrying at a lower baud rate */
int tt = it->last_scomerr(it);
if (tt & (ICOM_BRK | ICOM_FER | ICOM_PER | ICOM_OER)) {
if (br == baud_57600) br = baud_38400;
else if (br == baud_38400) br = baud_9600;
else if (br == baud_9600) br = baud_4800;
else if (br == baud_9600) br = baud_4800;
else if (br == baud_2400) br = baud_1200;
else br = baud_1200;
}
if ((rv = it->init_coms(it, br, fc, 15.0)) != inst_ok) {
#ifdef DEBUG
printf("init_coms returned '%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
#endif /* DEBUG */
if (nn != NULL) free(nn);
free(vals);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
}
continue;
/* Some other error. Treat it as fatal */
} else {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback)
bad_beep();
printf("\nStrip read failed due unexpected error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n");
if (nn != NULL) free(nn);
free(vals);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
continue;
}
/* Successfully read the strip */
/* See which expected row best correlates with the one we've read. */
/* Figure out if there is an "off by one" error for a DTP51 */
} else {
int choroi; /* Check overall row index */
double corr; /* Correlation with expected value */
int loff = 0, hoff = 0; /* DTP51 offset test range */
int toff; /* Test offset */
int dir, dirrg = 1; /* Direction range, 1 = forward, 2 = fwd & bwd */
int boroi = -1; /* Best overall row index */
double bcorr = 1e9; /* Best correlation value */
double werror = 0.0; /* Worst case error in best correlation strip */
double xbcorr = 1e9; /* Expected pass correlation value */
int xboff; /* Expected pass best offset */
int xbdir; /* Expected pass overall pass direction */
double xwerror = 0.0; /* Expected pass worst patcch error */
if (((rand && disbidi == 0) || disbidi == 2)
&& (cap2 & inst2_bidi_scan))
dirrg = 2; /* Enable bi-directional strip recognition */
/* DTP51 has a nasty habit of misaligning test squares by +/- 1 */
/* See if this might have happened */
if (it->dtype == instDTP51) {
loff = -1;
hoff = 1;
}
/* Explore the rows */
for (choroi = 0; choroi < totpa; choroi++) {
/* Explore strip direction */
for (dir = 0; dir < dirrg; dir++) {
double pwerr; /* This rows worst error */
scb = &scols[choroi * stipa];
/* Explore off by +/-1 error for DTP51 */
for (toff = loff; toff <= hoff; toff++) {
double ynorm = 1.0;
/* Compute a Y scaling value to give correlation */
/* a chance for absolute readings */
if (vals[skipp+toff].XYZ_v != 0) {
double refnorm = 0.0;
ynorm = 0.0;
for (i = 0; i < stipa; i++) {
int ix = i+skipp+toff;
if (dir != 0)
ix = stipa - 1 - ix;
refnorm += scb[i]->eXYZ[1];
ynorm += vals[ix].XYZ[1];
}
if (fabs(ynorm) > 1e-6)
ynorm = refnorm/ynorm;
else
ynorm = 1.0;
}
/* Compare just sample patches (not padding Max/Min) */
#ifdef USESTRDELTA
for (pwerr = corr = 0.0, n = 0, i = 0; i < (stipa-1); i++, n++)
#else
for (pwerr = corr = 0.0, n = 0, i = 0; i < stipa; i++, n++)
#endif
{
double vcorr;
int ix = i+skipp+toff, ix1 = ix+1;
if (dir != 0) {
ix = stipa - 1 - ix;
ix1 = stipa - 1 - ix1;
}
if (vals[ix].XYZ_v == 0)
error("Instrument didn't return XYZ value");
#ifdef USESTRDELTA
vcorr = xyzLabcorr(vals[ix].XYZ, scb[i]->eXYZ,
vals[ix1].XYZ, scb[i+1]->eXYZ);
#else
vcorr = xyzLabDE(ynorm, vals[ix].XYZ, scb[i]->eXYZ);
#endif
//printf("DE %f from vals[%d] %f %f %f and scols[%d] %f %f %f\n", vcorr, ix, vals[ix].XYZ[0], vals[ix].XYZ[1], vals[ix].XYZ[2], i + choroi * stipa, scb[i]->eXYZ[0], scb[i]->eXYZ[1], scb[i]->eXYZ[2]);
corr += vcorr;
if (vcorr > pwerr) /* Worsed patch error */
pwerr = vcorr;
}
corr /= (double)n;
#ifdef DEBUG
printf(" Strip %d dir %d offset %d correlation = %f worsterror = %f\n",choroi,dir,toff,corr,pwerr);
#endif
/* If this is the expected strip, */
/* note correlation and best fit to off by 1 and direction */
if (choroi == oroi && corr < xbcorr) {
xbcorr = corr; /* Expected strip correlation */
xboff = toff;
xbdir = dir;
xwerror = pwerr; /* Expected passes worst error */
}
/* Best overall matched strip correlation */
if (corr < bcorr) {
boroi = choroi;
bcorr = corr;
boff = toff;
bdir = dir;
werror = pwerr;
}
}
}
}
if (emit_warnings != 0 && rand && boroi != oroi) { /* Looks like the wrong strip */
char *mm = NULL;
mm = paix->aix(paix, boroi);
#ifdef DEBUG
printf("Strip pass %s (%d) seems to have a better correlation that strip %s (%d)\n",
mm, boroi, nn, oroi);
#endif
if (cap2 & inst2_no_feedback)
bad_beep();
empty_con_chars();
printf("\n(Warning) Seem to have read strip pass %s rather than %s!\n",mm,nn);
printf("Hit Return to use it anyway, any other key to retry, Esc or 'q' to give up:%s",fl_end); do_fflush();
free(mm);
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
printf("\n");
if (nn != NULL) free(nn);
free(vals);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (ch != 0x0d && ch != 0x0a) { /* !(CR or LF) */
update_led_state(it, inst_led_row_fail);
printf("\n");
continue; /* Try again */
}
printf("\n");
/* Switch to state for expected strip */
bcorr = xbcorr;
boff = xboff;
bdir = xbdir;
werror = xwerror;
}
/* Arbitrary threshold. Good seems about 15-35, bad 95-130 */
if (emit_warnings != 0 && ((accurate_expd != 0 && werror >= ACC_WERR_TH)
|| werror >= WERR_TH)) {
#ifdef DEBUG
printf("(Warning) Patch error %f (>35 not good, >75 bad)\n",werror);
#endif
if (cap2 & inst2_no_feedback)
bad_beep();
empty_con_chars();
printf("\nThere is at least one patch with an very unexpected response! (DeltaE %f)\n",werror);
printf("Hit Return to use it anyway, any other key to retry, Esc or 'q' to give up:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n");
if (nn != NULL) free(nn);
free(vals);
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (ch != 0x0d && ch != 0x0a) { /* !Cr */
update_led_state(it, inst_led_row_fail);
printf("\n");
continue;
}
printf("\n");
break;
}
/* Must be OK - save the readings */
if (cap2 & inst2_no_feedback)
good_beep();
update_led_state(it, inst_led_row_success);
printf(" Strip read OK");
if (boff != 0)
printf(" (DTP51 offset fix of %d applied)",boff);
if (bdir != 0)
printf(" (Strip read in reverse direction)");
printf("\n");
break; /* Break out of retry loop */
}
}
/* Save the strip values so that they are returned. */
if (nn != NULL) /* Finished with strip alpha index */
free(nn);
nn = NULL;
/* If we're done */
if (incflag == -2)
break;
/* If we are moving row, rather than having read one. */
if (incflag != 0)
continue;
/* Transfer the values (including DTP51 offset) */
scb = &scols[oroi * stipa];
for (n = 0, i = 0; i < stipa; i++, n++) {
int ix = i+skipp+boff;
if (bdir != 0)
ix = stipa - 1 - ix;
/* Copy XYZ */
if (vals[ix].XYZ_v == 0)
error("Instrument didn't return XYZ value");
for (j = 0; j < 3; j++)
scb[i]->XYZ[j] = vals[ix].XYZ[j];
/* Copy spectral */
if (vals[ix].sp.spec_n > 0) {
scb[i]->sp = vals[ix].sp;
}
scb[i]->rr = 1; /* Has been read */
}
if (json_ui_out) {
char row_id[32];
sprintf(row_id, "%s", paix->aix(paix, oroi));
emit_row_json_colors(row_id, oroi, totpa, stipa, scb[0]->n, scb);
}
incflag = 2; /* Skip to next unread */
} /* Go around to read another row */
update_led_state(it, inst_led_off);
free(vals);
/* -------------------------------------------------- */
/* Spot mode. This will be used if xtern != 0 */
} else {
int pix = 0;
int uswitch = 0; /* nz if switch can be used */
int incflag = 0; /* 0 = no change, 1 = increment, 2 = inc by 10, */
/* 3 = inc next unread, -1 = decrement, -2 = dec by 10 */
/* 4 = goto specific patch */
inst_opt_type omode; /* The option mode used */
ipatch val;
if (xtern == 0) { /* Instrument patch by patch */
/* Do any needed calibration before the user places the instrument on a desired spot */
if (it->needs_calibration(it) & inst_calt_n_dfrble_mask) {
update_led_state(it, inst_led_cal_wait);
if ((rv = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0))
!= inst_ok) {
update_led_state(it, inst_led_off);
printf("\nCalibration failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
update_led_state(it, inst_led_off);
}
/* Enable (switch or user) via uicallback trigger if possible */
if (cap2 & inst2_user_switch_trig) {
omode = inst_opt_trig_user_switch;
rv = it->get_set_opt(it, omode);
uswitch = 1;
/* Or go for user via uicallback trigger */
} else if (cap2 & inst2_user_trig) {
omode = inst_opt_trig_user;
rv = it->get_set_opt(it, omode);
/* Or something is wrong with instrument capabilities */
} else {
printf("\nNo reasonable trigger mode available for this instrument\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
if (rv != inst_ok) {
printf("\nSetting trigger mode failed with error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
/* Setup the keyboard trigger to return our commands */
inst_set_uih('f', 'f', DUIH_CMND);
inst_set_uih('F', 'F', DUIH_CMND);
inst_set_uih('b', 'b', DUIH_CMND);
inst_set_uih('B', 'B', DUIH_CMND);
inst_set_uih('n', 'n', DUIH_CMND);
inst_set_uih('N', 'N', DUIH_CMND);
inst_set_uih('g', 'g', DUIH_CMND);
inst_set_uih('G', 'G', DUIH_CMND);
inst_set_uih('d', 'd', DUIH_CMND);
inst_set_uih('D', 'D', DUIH_CMND);
inst_set_uih('k', 'k', DUIH_CMND);
inst_set_uih('q', 'q', DUIH_ABORT);
inst_set_uih('Q', 'Q', DUIH_ABORT);
inst_set_uih(0xd, 0xd, DUIH_TRIG); /* Return */
inst_set_uih(' ', ' ', DUIH_TRIG);
}
/* Skip to next unread if first has been read */
/* !!! would be nice to skip padding patches !!! */
incflag = 0;
if (npat > 0 && scols[0]->rr != 0)
incflag = 3;
/* Until we're done */
for (;pix < npat;) {
char buf[200], *bp = NULL, *ep = NULL;
char ch = 0;
/* Adjust the location */
if (incflag > 0 && incflag <= 2) { /* Incremente by 1 or 10 */
if (incflag == 2)
pix += 10;
else
pix++;
pix = pix % npat;
} else if (incflag < 0 && incflag >= -2) { /* Decrement by 1 or 10 */
if (incflag == -2)
pix -= 10;
else
pix--;
pix = pix % npat;
if (pix < 0)
pix += npat;
} else if (incflag == 3) { /* Increment to next unread */
int opix = pix;
if (pix >= npat)
pix = 0;
for (;;) {
if (scols[pix]->rr == 0 && strcmp(scols[pix]->id, "0") != 0)
break;
pix++;
if (pix >= npat)
pix = 0;
if (pix == opix)
break;
}
} else if (incflag == 4) { /* Goto specific patch */
printf("\nEnter patch to go to: %s",fl_end); do_fflush();
/* Read in the next line from stdin. */
if (con_fgets(buf, 200) == NULL) {
printf("Error - unrecognised input\n");
} else {
int opix = pix;
/* Skip whitespace */
for (bp = buf; *bp != '\000' && isspace(*bp); bp++)
;
/* Skip non-whitespace */
for (ep = bp; *ep != '\000' && !isspace(*ep); ep++)
;
*ep = '\000';
if (pix >= npat)
pix = 0;
for (;;) {
if (stricmp(scols[pix]->loc, bp) == 0)
break;
pix++;
if (pix >= npat)
pix = 0;
if (pix == opix) {
printf("Patch '%s' not found\n",bp);
break;
}
}
}
}
incflag = 0;
/* See if there are any unread patches */
for (i = 0; i < npat; i++) {
if (scols[i]->rr == 0 && strcmp(scols[i]->id, "0") != 0)
break; /* At least one patch read */
}
if (xtern != 0) { /* User entered values */
printf("\nReady to read patch '%s' at '%s'%s\n",scols[pix]->id, scols[pix]->loc,
i >= npat ? "(All patches read!)" :
strcmp(scols[pix]->id, "0") == 0 ? " (Padding Patch)" :
scols[pix]->rr ? " (Already read)" : "");
printf("Enter %s value (separated by spaces), or\n",
xtern == 1 ? "L*a*b*" : "XYZ");
printf(" 'f' to move forward, 'F' move forward 10,\n");
printf(" 'b' to move back, 'B; to move back 10,\n");
printf(" 'n' for next unread, 'g' to goto patch,\n");
printf(" 'd' when done, 'q' to abort, then press <return>: %s",fl_end);
do_fflush();
/* Read in the next line from stdin. */
if (con_fgets(buf, 200) == NULL) {
printf("Error - unrecognised input\n");
continue;
}
/* Skip whitespace */
for (bp = buf; *bp != '\000' && isspace(*bp); bp++)
;
ch = *bp;
if (ch == '\000') {
printf("Error - unrecognised input\n");
continue;
}
} else { /* Using instrument */
empty_con_chars();
printf("\nReady to read patch '%s' at '%s'%s\n",scols[pix]->id, scols[pix]->loc,
i >= npat ? " (All patches read!)" :
strcmp(scols[pix]->id, "0") == 0 ? " (Padding Patch)" :
scols[pix]->rr ? " (Already read)" : "");
printf("hit 'f' to move forward, 'F' move forward 10,\n");
printf(" 'b' to move back, 'B; to move back 10,\n");
printf(" 'n' for next unread, 'g' to goto patch,\n");
printf(" 'd' when done, 'k' to calibrate, <esc> to abort,\n");
if (uswitch)
printf(" Instrument switch, <return> or <space> to read:%s",fl_end);
else
printf(" <return> or <space> to read:%s",fl_end);
do_fflush();
rv = it->read_sample(it, "SPOT", &val, 1);
/* Deal with reading */
if (rv == inst_ok) {
/* Read OK */
if (cap2 & inst2_no_feedback)
good_beep();
ch = '0';
/* Deal with user trigger via user interface callback function */
} else if ((rv & inst_mask) == inst_user_trig) {
if (cap2 & inst2_no_feedback)
good_beep();
ch = inst_get_uih_char();
/* Deal with a abort or command */
} else if ((rv & inst_mask) == inst_user_abort) {
int keyc = inst_get_uih_char();
/* User issued a command */
if (keyc & DUIH_CMND) {
ch = keyc & 0xff;
printf("\n");
/* User aborted */
} else if (keyc & DUIH_ABORT) {
empty_con_chars();
printf("\n\nSpot read stopped at user request!\n");
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
printf("\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
continue;
}
/* Deal with needs calibration */
} else if ((rv & inst_mask) == inst_needs_cal) {
inst_code ev;
if (cap2 & inst2_no_feedback)
bad_beep();
printf("\nSpot read failed because instruments needs calibration\n");
update_led_state(it, inst_led_cal_wait);
ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0);
update_led_state(it, inst_led_off);
if (ev != inst_ok) { /* Abort or fatal error */
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
continue;
/* Deal with a misread */
} else if ((rv & inst_mask) == inst_misread) {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback)
bad_beep();
empty_con_chars();
printf("\nStrip read failed due to misread (%s)\n",it->interp_error(it, rv));
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
continue;
/* Deal with a communications error */
} else if ((rv & inst_mask) == inst_coms_fail) {
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback)
bad_beep();
empty_con_chars();
printf("\nStrip read failed due to communication problem.\n");
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
if ((it->icom->port_type(it->icom) & icomt_serial)
&& !(it->icom->port_attr(it->icom) & icomt_fastserial)) {
/* Allow retrying at a lower baud rate */
int tt = it->last_scomerr(it);
if (tt & (ICOM_BRK | ICOM_FER | ICOM_PER | ICOM_OER)) {
if (br == baud_57600) br = baud_38400;
else if (br == baud_38400) br = baud_9600;
else if (br == baud_9600) br = baud_4800;
else if (br == baud_9600) br = baud_4800;
else if (br == baud_2400) br = baud_1200;
else br = baud_1200;
}
if ((rv = it->init_coms(it, br, fc, 15.0)) != inst_ok) {
#ifdef DEBUG
printf("init_coms returned '%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
#endif /* DEBUG */
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
}
continue;
} else {
/* Some other error. Treat it as fatal */
update_led_state(it, inst_led_row_fail);
if (cap2 & inst2_no_feedback)
bad_beep();
printf("\nPatch read failed due unexpected error :'%s' (%s)\n",
it->inst_interp_error(it, rv), it->interp_error(it, rv));
printf("Hit Esc or 'q' to give up, any other key to retry:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
update_led_state(it, inst_led_off);
printf("\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
continue;
}
}
if (ch == 'q' || ch == 0x1b || ch == 0x03) { /* q or Esc or ^C */
empty_con_chars();
printf("\nAbort ? - Are you sure ? [y/n]:%s",fl_end); do_fflush();
if ((ch = next_con_char()) == 'y' || ch == 'Y') {
update_led_state(it, inst_led_off);
printf("\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
continue;
} else if (ch == 'k') {
inst_code ev;
update_led_state(it, inst_led_cal_wait);
ev = inst_handle_calibrate(it, inst_calt_available, inst_calc_none, NULL, NULL, 0);
update_led_state(it, inst_led_off);
if (ev != inst_ok) { /* Abort or fatal error */
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
continue;
} else if (ch == 'f') {
incflag = 1;
continue;
} else if (ch == 'F') {
incflag = 2;
continue;
} else if (ch == 'b') {
incflag = -1;
continue;
} else if (ch == 'B') {
incflag = -2;
continue;
} else if (ch == 'n' || ch == 'N') {
incflag = 3;
continue;
} else if (ch == 'g' || ch == 'G') {
incflag = 4;
continue;
} else if (ch == 'd' || ch == 'D') {
int i;
for (i = 0; i < npat; i++) {
if (scols[i]->rr == 0 && strcmp(scols[i]->id, "0") != 0)
break;
}
if (i >= npat)
break; /* None unread, so done */
/* Not all patches have been read */
empty_con_chars();
printf("\nDone ? - At least one unread patch (%s, %s), Are you sure [y/n]: %s",
scols[i]->id, scols[i]->loc, fl_end);
do_fflush();
if ((ch = next_con_char()) == 0x1b) {
printf("\n");
it->del(it);
if (pfname != NULL)
free(pfname);
return -1;
}
printf("\n");
if (ch == 'y' || ch == 'Y')
break;
continue;
/* Read the external sample */
} else if (xtern != 0 && (isdigit(*bp) || ch == '-' || ch == '+' || ch == '.')) {
int i;
/* For each input number */
for (i = 0; *bp != '\000' && i < 3; i++) {
char *tp, *nbp;
/* Find the start of the number */
while(*bp != '\000' && !isdigit(*bp)
&& *bp != '-' && *bp != '+' && *bp != '.')
bp++;
if (!isdigit(*bp) && *bp != '-' && *bp != '+' && *bp != '.')
break;
/* Find the end of the number */
for (tp = bp+1; isdigit(*tp) || *tp == 'e' || *tp == 'E'
|| *tp == '-' || *tp == '+' || *tp == '.'; tp++)
;
if (*tp != '\000')
nbp = tp+1;
else
nbp = tp;
*tp = '\000';
/* Read the number */
scols[pix]->XYZ[i] = atof(bp);
bp = nbp;
}
if (i < 3) { /* Didn't find 3 numbers */
printf("Error - unrecognised input\n");
continue;
}
if (xtern == 1) {
icmLab2XYZ(&icmD50, scols[pix]->XYZ,scols[pix]->XYZ);
scols[pix]->XYZ[0] *= 100.0;
scols[pix]->XYZ[1] *= 100.0;
scols[pix]->XYZ[2] *= 100.0;
}
scols[pix]->rr = 1; /* Has been read */
printf(" Got XYZ value %f %f %f\n",scols[pix]->XYZ[0], scols[pix]->XYZ[1], scols[pix]->XYZ[2]);
/* Advance to next patch. */
incflag = 1;
/* We've read the spot sample */
} else if (xtern == 0 && (ch == '0' || ch == ' ' || ch == '\r')) {
/* Save the reading */
if (val.XYZ_v == 0)
error("Instrument didn't return XYZ value");
for (j = 0; j < 3; j++)
scols[pix]->XYZ[j] = val.XYZ[j];
/* Copy spectral */
if (val.sp.spec_n > 0) {
scols[pix]->sp = val.sp;
}
scols[pix]->rr = 1; /* Has been read */
if (json_ui_out) {
char row_id[32];
int oroi = pix / stipa;
sprintf(row_id, "%s", paix->aix(paix, oroi));
emit_row_json_colors(row_id, oroi, totpa, 1, scols[pix]->n, &scols[pix]);
}
printf(" Patch read OK\n");
/* Advance to next patch. */
incflag = 1;
} else { /* Unrecognised response */
continue;
}
/* Sanity check value */
if (incflag && emit_warnings != 0) {
double werror = 0.0;
werror = xyzLabDE(1.0, scols[pix]->XYZ, scols[pix]->eXYZ);
if ((accurate_expd != 0 && werror >= ACC_WERR_TH) || werror >= WERR_TH) {
#ifdef DEBUG
printf("(Warning) Patch error %f (>35 not good, >75 bad)\n",werror);
#endif
if (cap2 & inst2_no_feedback)
bad_beep();
empty_con_chars();
printf("\nThe patch has %s unexpected response! (DeltaE %f)\n",werror > 80.0 ? "a very " : "an", werror);
incflag = 0; /* Don't auto move forward */
}
}
}
}
/* -------------------------------------------------- */
/* clean up */
update_led_state(it, inst_led_off);
if (it != NULL)
it->del(it);
if (pfname != NULL)
free(pfname);
return 0;
}
void
usage() {
icompaths *icmps = NULL;
inst2_capability cap2 = 0;
fprintf(stderr,"Read Target Test Chart, Version %s\n",ARGYLL_VERSION_STR);
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
fprintf(stderr,"usage: chartread [-options] outfile\n");
fprintf(stderr," -v Verbose mode\n");
fprintf(stderr," -c listno Set communication port from the following list (default %d)\n",COMPORT);
if ((icmps = new_icompaths(g_log)) != NULL) {
icompath **paths;
if ((paths = icmps->paths) != NULL) {
int i;
for (i = 0; ; i++) {
if (paths[i] == NULL)
break;
fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name);
}
} else
fprintf(stderr," ** No ports found **\n");
}
fprintf(stderr," -t Use transmission measurement mode\n");
fprintf(stderr," -u Emit real-time JSON updates to stdout\n");
fprintf(stderr," -d Use display measurement mode (white Y relative results)\n");
cap2 = inst_show_disptype_options(stderr, " -y ", icmps, 0, 0);
fprintf(stderr," -e Emissive for transparency on a light box\n");
fprintf(stderr," -p Measure patch by patch rather than strip\n");
fprintf(stderr," -x [lx] Take external values, either L*a*b* (-xl) or XYZ (-xx).\n");
fprintf(stderr," -n Don't save spectral information (default saves spectral)\n");
fprintf(stderr," -l Save CIE as D50 L*a*b* rather than XYZ\n");
fprintf(stderr," -L Save CIE as D50 L*a*b* as well as XYZ\n");
fprintf(stderr," -r Resume reading partly read chart\n");
fprintf(stderr," -I file.cal Override calibration info from .ti2 in resulting .ti3\n");
fprintf(stderr," -F filter Set filter configuration (if aplicable):\n");
fprintf(stderr," n None (M0)\n");
fprintf(stderr," 5 D50 (M1)\n");
fprintf(stderr," 6 D65\n");
fprintf(stderr," u U.V. Cut (M2)\n");
fprintf(stderr," p Polarising filter (M3)\n");
fprintf(stderr," -A N|A|X|G XRGA conversion (default N)\n");
fprintf(stderr," -N Disable initial calibration of instrument if possible\n");
fprintf(stderr," -B Disable auto bi-directional strip recognition\n");
fprintf(stderr," -b Force auto bi-directional strip recognition\n");
fprintf(stderr," -H Use high resolution spectrum mode (if available)\n");
if (cap2 & inst2_ccmx)
fprintf(stderr," -X file.ccmx Apply Colorimeter Correction Matrix\n");
if (cap2 & inst2_ccss) {
int i;
fprintf(stderr," -X file.ccss Use Colorimeter Calibration Spectral Samples for calibration\n");
fprintf(stderr," -Q observ Choose CIE Observer for CCSS instrument:\n");
#ifdef SALONEINSTLIB
fprintf(stderr," 1931_2 (def), 1964_10, 2015_2, 2015_10\n");
#else /* !SALONEINSTLIB */
fprintf(stderr," 1931_2 (def), 1964_10, 2015_2, 2015_10, S&B 1955_2, shaw, J&V 1978_2 or file.cmf\n");
#endif /* !SALONEINSTLIB */
}
fprintf(stderr," -T ratio Modify strip patch consistency tolerance by ratio\n");
fprintf(stderr," -S Suppress wrong strip & unexpected value warnings\n");
fprintf(stderr," -Y l Enable i1Pro 2 visual LED feedback\n");
fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n");
#ifndef SALONEINSTLIB
fprintf(stderr," -P Plot spectral if patch by patch\n");
#endif
fprintf(stderr," -D [level] Print debug diagnostics to stderr\n");
fprintf(stderr," outfile Base name for input[ti2]/output[ti3] file\n");
if (icmps != NULL)
icmps->del(icmps);
exit(1);
}
int main(int argc, char *argv[]) {
int i, j;
int fa, nfa, mfa; /* current argument we're looking at */
int verb = 0;
int debug = 0;
int comport = COMPORT; /* COM port used */
icompaths *icmps = NULL;
icompath *ipath = NULL;
flow_control fc = fc_nc; /* Default flow control */
instType itype = instUnknown; /* Instrument chart is targeted to */
instType atype = instUnknown; /* Instrument used to read the chart */
int trans = 0; /* Use transmission mode */
int emis = 0; /* Use emissive mode */
int displ = 0; /* 1 = Use display emissive mode, 2 = display bright rel. */
/* 3 = display white rel. */
int ditype = 0; /* Display type selection charater(s) */
inst_opt_filter fe = inst_opt_filter_unknown;
int pbypatch = 0; /* Read patch by patch */
int disbidi = 0; /* 1 = Disable bi-directional strip recognition, 2 = force enable */
int highres = 0; /* Use high res mode if available */
double scan_tol = 1.0; /* Patch consistency tolerance modification */
int xtern = 0; /* Take external values, 1 = Lab, 2 = XYZ */
int spectral = 1; /* Save spectral information */
int uvmode = 0; /* ~~~ i1pro2 test mode ~~~ */
xcalstd scalstd = xcalstd_none; /* X-Rite calibration standard to set */
xcalstd ucalstd = xcalstd_none; /* X-Rite calibration standard actually used */
int accurate_expd = 0; /* Expected value assumed to be accurate */
int emit_warnings = 1; /* Emit warnings for wrong strip, unexpected value */
int dolab = 0; /* 1 = Save CIE as Lab, 2 = Save CIE as XYZ and Lab */
int doresume = 0; /* Resume reading a chart */
int nocal = 0; /* Disable initial calibration */
int doplot = 0; /* Plot spectral of patch by patch */
static char ccxxname[MAXNAMEL+1] = "\000"; /* Colorimeter Correction/Colorimeter Calibration name */
icxObserverType obType = icxOT_default; /* ccss observer */
xspect custObserver[3]; /* If obType = icxOT_custom */
static char inname[MAXNAMEL+10+1] = { 0 }; /* Input cgats file base name */
static char outname[MAXNAMEL+10+1] = { 0 }; /* Output cgats file base name */
cgats *icg; /* input cgats structure */
cgats *ocg; /* output cgats structure */
static char calname[MAXNAMEL+1] = { 0 }; /* User supplied calibration filename */
xcal *cal = NULL; /* Any calibration to be output as well */
int nmask = 0; /* Device colorant mask */
char *pixpat = "A-Z, A-Z"; /* Pass index pattern */
char *sixpat = "0-9,@-9,@-9;1-999"; /* Step index pattern */
alphix *paix, *saix; /* Pass and Step index generators */
int ixord = 0; /* Index order, 0 = pass then step */
int rstart = 0; /* Random start/chart id */
int rand = 0; /* Random patch order, - can use auto strip ID and Bi-Di */
int hex = 0; /* Hexagon pattern layout */
time_t clk = time(0);
struct tm *tsp = localtime(&clk);
char *atm = asctime(tsp); /* Ascii time */
chcol *cols; /* Internal storage of all the patch colors */
chcol **scols; /* Location sorted pointers to cols */
int nchan = 0; /* Number of device chanels */
int npat; /* Number of overall patches */
int *pis; /* Passes in eachstrip, zero terminated */
int stipa; /* Steps in each Pass */
int totpa; /* Total Passes Needed */
int runpat; /* Rounded Up to (totpa * stipa) Number of patches */
int wpat; /* Set to index of white patch for display */
int si; /* Sample id index */
int li; /* Location id index */
int ti; /* Temp index */
int fi; /* Colorspace index */
double plen = 7.366, glen = 2.032, tlen = 18.8; /* Patch, gap and trailer length in mm */
set_exe_path(argv[0]); /* Set global exe_path and error_program */
check_if_not_interactive();
if (argc <= 1)
usage();
/* Process the arguments */
mfa = 1; /* Minimum final arguments */
for (fa = 1;fa < argc;fa++) {
nfa = fa; /* skip to nfa if next argument is used */
if (argv[fa][0] == '-') { /* Look for any flags */
char *na = NULL; /* next argument after flag, null if none */
if (argv[fa][2] != '\000')
na = &argv[fa][2]; /* next is directly after flag */
else {
if ((fa+1+mfa) < argc) {
if (argv[fa+1][0] != '-') {
nfa = fa + 1;
na = argv[nfa]; /* next is seperate non-flag argument */
}
}
}
if (argv[fa][1] == '?')
usage();
/* Verbose */
else if (argv[fa][1] == 'v') {
verb = 1;
g_log->verb = verb;
/* No auto calibration */
} else if (argv[fa][1] == 'N')
nocal = 1;
/* Disable bi-directional strip recognition */
else if (argv[fa][1] == 'B')
disbidi = 1;
/* Force enable bi-directional strip recognition */
else if (argv[fa][1] == 'b')
disbidi = 2;
/* High res mode */
else if (argv[fa][1] == 'H')
highres = 1;
/* Colorimeter Correction Matrix or */
/* or Colorimeter Calibration Spectral Samples */
else if (argv[fa][1] == 'X') {
int ix;
fa = nfa;
if (na == NULL) usage();
strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000';
/* CCSS Spectral Observer type */
} else if (argv[fa][1] == 'Q') {
fa = nfa;
if (na == NULL) usage();
if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */
obType = icxOT_CIE_1931_2;
} else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */
obType = icxOT_CIE_1964_10;
} else if (strcmp(na, "2015_2") == 0) { /* Latest 2 degree */
obType = icxOT_CIE_2015_2;
} else if (strcmp(na, "2015_10") == 0) { /* Latest 10 degree */
obType = icxOT_CIE_2015_10;
#ifndef SALONEINSTLIB
} else if (strcmp(na, "1955_2") == 0) { /* Stiles and Burch 1955 2 degree */
obType = icxOT_Stiles_Burch_2;
} else if (strcmp(na, "1978_2") == 0) { /* Judd and Voss 1978 2 degree */
obType = icxOT_Judd_Voss_2;
} else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */
obType = icxOT_Shaw_Fairchild_2;
#endif /* !SALONEINSTLIB */
} else { /* Assume it's a filename */
obType = icxOT_custom;
if (read_cmf(custObserver, na) != 0)
usage();
}
}
/* Scan tolerance ratio */
else if (argv[fa][1] == 'T') {
fa = nfa;
if (na == NULL)
usage();
scan_tol = atof(na);
/* Suppress warnings */
} else if (argv[fa][1] == 'S') {
emit_warnings = 0;
/* Serial port flow control */
} else if (argv[fa][1] == 'W') {
fa = nfa;
if (na == NULL) usage();
if (na[0] == 'n' || na[0] == 'N')
fc = fc_None;
else if (na[0] == 'h' || na[0] == 'H')
fc = fc_Hardware;
else if (na[0] == 'x' || na[0] == 'X')
fc = fc_XonXOff;
else
usage();
/* Debug coms */
} else if (argv[fa][1] == 'D') {
debug = 1;
if (na != NULL && na[0] >= '0' && na[0] <= '9') {
debug = atoi(na);
fa = nfa;
}
g_log->debug = debug;
/* COM port */
} else if (argv[fa][1] == 'c') {
fa = nfa;
if (na == NULL) usage();
comport = atoi(na);
if (comport < 1 || comport > 99) usage();
/* Request transmission measurement */
} else if (argv[fa][1] == 't') {
emis = 0;
trans = 1;
displ = 0;
/* Request display measurement */
} else if (argv[fa][1] == 'd') {
emis = 0;
trans = 0;
displ = 2;
/* Enable UI JSON output */
} else if (argv[fa][1] == 'u') {
json_ui_out = 1;
/* Request emissive measurement */
} else if (argv[fa][1] == 'e') {
emis = 1;
trans = 0;
displ = 0;
/* Display type */
} else if (argv[fa][1] == 'y') {
fa = nfa;
if (na == NULL) usage();
ditype = na[0];
if (ditype == '_' && na[1] != '\000')
ditype = ditype << 8 | na[1];
/* Request patch by patch measurement */
} else if (argv[fa][1] == 'p') {
pbypatch = 1;
/* Request external values */
} else if (argv[fa][1] == 'x') {
fa = nfa;
if (na == NULL) usage();
if (na[0] == 'l' || na[0] == 'L')
xtern = 1;
else if (na[0] == 'x' || na[0] == 'X')
xtern = 2;
else
usage();
/* Turn off spectral measurement */
} else if (argv[fa][1] == 'n')
spectral = 0;
/* Save as Lab */
else if (argv[fa][1] == 'l')
dolab = 1;
/* Save as Lab */
else if (argv[fa][1] == 'L')
dolab = 2;
/* Resume reading a chart */
else if (argv[fa][1] == 'r')
doresume = 1;
/* Printer calibration info */
else if (argv[fa][1] == 'I') {
fa = nfa;
if (na == NULL) usage();
strncpy(calname,na,MAXNAMEL); calname[MAXNAMEL] = '\000';
}
/* Extra flags */
else if (argv[fa][1] == 'Y') {
fa = nfa;
if (na == NULL)
usage(); /* "Parameter expected after -Y" */
if (na[0] == 'l' || na[0] == 'L') {
g_use_leds = 1;
} else {
usage(); /* "-Y parameter '%c' not recognised",na[0] */
}
}
/* Filter configuration */
else if (argv[fa][1] == 'F') {
fa = nfa;
if (na == NULL) usage();
if (na[0] == 'n' || na[0] == 'N')
fe = inst_opt_filter_none;
else if (na[0] == '5')
fe = inst_opt_filter_D50;
else if (na[0] == '6')
fe = inst_opt_filter_D65;
else if (na[0] == 'u' || na[0] == 'U')
fe = inst_opt_filter_UVCut;
else if (na[0] == 'p' || na[0] == 'P')
fe = inst_opt_filter_pol;
else
usage();
/* XRGA conversion */
} else if (argv[fa][1] == 'A') {
fa = nfa;
if (na == NULL) usage();
if (na[0] == 'N')
scalstd = xcalstd_none;
else if (na[0] == 'A')
scalstd = xcalstd_xrga;
else if (na[0] == 'X')
scalstd = xcalstd_xrdi;
else if (na[0] == 'G')
scalstd = xcalstd_gmdi;
else
usage();
#ifndef SALONEINSTLIB
/* Plot spectral patch by patch */
} else if (argv[fa][1] == 'P') {
doplot = 1;
#endif
} else
usage();
} else
break;
}
/* Get the file name argument */
if (fa >= argc || argv[fa][0] == '-') usage();
strncpy(inname,argv[fa],MAXNAMEL); inname[MAXNAMEL] = '\000';
strcat(inname,".ti2");
strncpy(outname,argv[fa++],MAXNAMEL); outname[MAXNAMEL] = '\000';
strcat(outname,".ti3");
/* See if there is an environment variable ccxx */
if (ccxxname[0] == '\000') {
char *na;
if ((na = getenv("ARGYLL_COLMTER_CAL_SPEC_SET")) != NULL) {
strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000';
} else if ((na = getenv("ARGYLL_COLMTER_COR_MATRIX")) != NULL) {
strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000';
}
}
icg = new_cgats(); /* Create a CGATS structure */
icg->add_other(icg, "CTI2"); /* our special input type is Calibration Target Information 2 */
icg->add_other(icg, "CAL"); /* There may be a calibration too */
if (icg->read_name(icg, inname))
error("CGATS file read error : %s",icg->e.m);
if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0)
error ("Input file isn't a CTI2 format file");
if (icg->ntables < 1)
error ("Input file doesn't contain at least one table");
if ((npat = icg->t[0].nsets) <= 0)
error ("No sets of data in first table");
/* Setup output cgats file */
ocg = new_cgats(); /* Create a CGATS structure */
ocg->add_other(ocg, "CTI3"); /* our special type is Calibration Target Information 3 */
ocg->add_table(ocg, tt_other, 0); /* Start the first table */
ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration Target chart information 3",NULL);
ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll chartread", NULL);
atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
if (displ != 0)
ocg->add_kword(ocg, 0, "DEVICE_CLASS","DISPLAY", NULL); /* What sort of device this is */
else
ocg->add_kword(ocg, 0, "DEVICE_CLASS","OUTPUT", NULL); /* What sort of device this is */
if (itype == instUnknown) {
if ((ti = icg->find_kword(icg, 0, "TARGET_INSTRUMENT")) >= 0) {
if ((itype = inst_enum(icg->t[0].kdata[ti])) == instUnknown)
error ("Unrecognised chart target instrument '%s'", icg->t[0].kdata[ti]);
} else {
itype = instI1Pro; /* Default chart target instrument */
}
}
if ((ti = icg->find_kword(icg, 0, "SINGLE_DIM_STEPS")) >= 0)
ocg->add_kword(ocg, 0, "SINGLE_DIM_STEPS",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "COMP_GREY_STEPS")) >= 0)
ocg->add_kword(ocg, 0, "COMP_GREY_STEPS",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "MULTI_DIM_STEPS")) >= 0)
ocg->add_kword(ocg, 0, "MULTI_DIM_STEPS",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "FULL_SPREAD_PATCHES")) >= 0)
ocg->add_kword(ocg, 0, "FULL_SPREAD_PATCHES",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "ACCURATE_EXPECTED_VALUES")) >= 0
&& strcmp(icg->t[0].kdata[ti], "true") == 0)
accurate_expd = 1;
if ((ti = icg->find_kword(icg, 0, "STEPS_IN_PASS")) < 0)
error ("Input file doesn't contain keyword STEPS_IN_PASS");
stipa = atoi(icg->t[0].kdata[ti]);
/* Old style */
if ((ti = icg->find_kword(icg, 0, "PASSES_IN_STRIPS")) >= 0) {
char *paists = icg->t[0].kdata[ti];
int nstr;
/* Count the number of strips (sheets) */
for (nstr = 0; paists[nstr] != '\000'; nstr++)
;
/* Allocate space for passes in strips */
if ((pis = (int *)calloc(sizeof(int), nstr+1)) == NULL)
error("Malloc failed!");
/* Set the number or passes per strip */
for (i = 0; i < nstr; i++) {
pis[i] = b62_int(&paists[i]);
}
pis[i] = 0;
/* New style */
} else if ((ti = icg->find_kword(icg, 0, "PASSES_IN_STRIPS2")) >= 0) {
char *cp, *paists = icg->t[0].kdata[ti];
int nstr;
/* Count the number of strips (sheets) */
for (nstr = 1, cp = paists; *cp != '\000'; cp++) {
if (*cp == ',') {
nstr++;
*cp = '\000';
}
}
/* Allocate space for passes in strips */
if ((pis = (int *)calloc(sizeof(int), nstr+1)) == NULL)
error("Malloc failed!");
/* Set the number or passes per strip */
for (i = 0, cp = paists; i < nstr; i++) {
pis[i] = atoi(cp);
cp += strlen(cp) + 1;
}
pis[i] = 0;
} else
error ("Input file doesn't contain keyword PASSES_IN_STRIPS");
/* Get specified location indexing patterns */
if ((ti = icg->find_kword(icg, 0, "STRIP_INDEX_PATTERN")) >= 0)
pixpat = icg->t[0].kdata[ti];
if ((ti = icg->find_kword(icg, 0, "PATCH_INDEX_PATTERN")) >= 0)
sixpat = icg->t[0].kdata[ti];
if ((ti = icg->find_kword(icg, 0, "INDEX_ORDER")) >= 0) {
if (strcmp(icg->t[0].kdata[ti], "PATCH_THEN_STRIP") == 0)
ixord = 1;
}
if ((ti = icg->find_kword(icg, 0, "RANDOM_START")) >= 0) {
rstart = atoi(icg->t[0].kdata[ti]);
rand = 1;
} else if ((ti = icg->find_kword(icg, 0, "CHART_ID")) >= 0) {
rstart = atoi(icg->t[0].kdata[ti]);
rand = 0;
}
if ((ti = icg->find_kword(icg, 0, "HEXAGON_PATCHES")) >= 0)
hex = 1;
if ((paix = new_alphix(pixpat)) == NULL)
error("Strip indexing pattern '%s' doesn't parse",pixpat);
if ((saix = new_alphix(sixpat)) == NULL)
error("Patch in strip indexing pattern '%s' doesn't parse",sixpat);
if ((ti = icg->find_kword(icg, 0, "TOTAL_INK_LIMIT")) >= 0)
ocg->add_kword(ocg, 0, "TOTAL_INK_LIMIT",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "FINAL_TOTAL_INK_LIMIT")) >= 0)
ocg->add_kword(ocg, 0, "FINAL_TOTAL_INK_LIMIT",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "TEXT_SPACER_TOTAL_INK_LIMIT")) >= 0)
ocg->add_kword(ocg, 0, "TEXT_SPACER_TOTAL_INK_LIMIT",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "FINAL_TEXT_SPACER_TOTAL_INK_LIMIT")) >= 0)
ocg->add_kword(ocg, 0, "FINAL_TEXT_SPACER_TOTAL_INK_LIMIT",icg->t[0].kdata[ti], NULL);
if (itype == instDTP20
|| itype == instDTP41) { /* DTP20/41 specific info */
if ((ti = icg->find_kword(icg, 0, "PATCH_LENGTH")) < 0)
error ("Input file doesn't contain keyword PATCH_LENGTH");
plen = atof(icg->t[0].kdata[ti]);
if ((ti = icg->find_kword(icg, 0, "GAP_LENGTH")) < 0)
error ("Input file doesn't contain keyword GAP_LENGTH");
glen = atof(icg->t[0].kdata[ti]);
if ((ti = icg->find_kword(icg, 0, "TRAILER_LENGTH")) < 0) {
tlen = 18.0; /* Default for backwards compatibility */
} else
tlen = atof(icg->t[0].kdata[ti]);
}
if (verb) {
printf("Steps in each Pass = %d\n",stipa);
printf("Passes in each Strip = ");
for (i = 0; pis[i] != 0; i++) {
printf("%s%d",i > 0 ? ", " : "", pis[i]);
}
printf("\n");
}
/* Fields we want */
if ((si = icg->find_field(icg, 0, "SAMPLE_ID")) < 0)
error ("Input file doesn't contain field SAMPLE_ID");
if (icg->t[0].ftype[si] != nqcs_t)
error ("Field SAMPLE_ID is wrong type");
ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t);
if ((li = icg->find_field(icg, 0, "SAMPLE_LOC")) < 0)
error ("Input file doesn't contain field SAMPLE_LOC");
if (icg->t[0].ftype[li] != cs_t)
error ("Field SAMPLE_LOC is wrong type");
ocg->add_field(ocg, 0, "SAMPLE_LOC", cs_t);
totpa = (npat + stipa -1)/stipa; /* Total passes for all strips */
runpat = stipa * totpa; /* Rounded up totao number of patches */
if (runpat < npat) /* (If pattern doesn't match patches) */
runpat = npat;
if ((cols = (chcol *)malloc(sizeof(chcol) * runpat)) == NULL)
error("Malloc failed!");
if ((scols = (chcol **)calloc(sizeof(chcol *), runpat)) == NULL)
error("Malloc failed!");
/* Figure out the color space */
if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0)
error ("Input file doesn't contain keyword COLOR_REP");
if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) != 0) {
int i, j, ii;
int chix[ICX_MXINKS]; /* Device chanel indexes */
int xyzix[3]; /* XYZ/Lab chanel indexes */
char *ident; /* Full ident */
char *bident; /* Base ident */
char *xyzfname[3] = { "XYZ_X", "XYZ_Y", "XYZ_Z" };
char *labfname[3] = { "LAB_L", "LAB_A", "LAB_B" };
int gotexyz = 1; /* Flag set if input file already has approx XYZ */
icxColorantLu *clu; /* Xcolorants model based Device -> CIE */
nchan = icx_noofinks(nmask);
ident = icx_inkmask2char(nmask, 1);
bident = icx_inkmask2char(nmask, 0);
/* Device channels */
for (j = 0; j < nchan; j++) {
int imask;
char fname[100];
imask = icx_index2ink(nmask, j);
sprintf(fname,"%s_%s",nmask == ICX_W || nmask == ICX_K ? "GRAY" : bident,
icx_ink2char(imask));
if ((ii = icg->find_field(icg, 0, fname)) < 0)
error ("Input file doesn't contain field %s",fname);
if (icg->t[0].ftype[ii] != r_t)
error ("Field %s is wrong type - expect float",fname);
ocg->add_field(ocg, 0, fname, r_t);
chix[j] = ii;
}
/* Approximate XYZ */
for (j = 0; j < 3; j++) {
if ((ii = icg->find_field(icg, 0, xyzfname[j])) >= 0) {
if (icg->t[0].ftype[ii] != r_t)
error ("Field %s is wrong type - expect float",xyzfname[j]);
xyzix[j] = ii;
} else {
gotexyz = 0;
}
}
/* Measured XYZ and/or Lab */
if (dolab == 0 || dolab == 2) {
for (j = 0; j < 3; j++)
ocg->add_field(ocg, 0, xyzfname[j], r_t);
}
if (dolab == 1 || dolab == 2) {
for (j = 0; j < 3; j++)
ocg->add_field(ocg, 0, labfname[j], r_t);
}
if ((clu = new_icxColorantLu(nmask)) == NULL)
error ("Creation of xcolorant lu object failed");
{
char fname[100];
if (dolab)
sprintf(fname, "%s_LAB", ident);
else
sprintf(fname, "%s_XYZ", ident);
ocg->add_kword(ocg, 0, "COLOR_REP", fname, NULL);
}
/* Read all the test patches in */
for (i = 0; i < npat; i++) {
cols[i].id = ((char *)icg->t[0].fdata[i][si]);
cols[i].loc = ((char *)icg->t[0].fdata[i][li]);
cols[i].n = nchan;
for (j = 0; j < nchan; j++)
cols[i].dev[j] = *((double *)icg->t[0].fdata[i][chix[j]]) / 100.0;
if (gotexyz) {
for (j = 0; j < 3; j++)
cols[i].eXYZ[j] = *((double *)icg->t[0].fdata[i][xyzix[j]]);
} else {
clu->dev_to_XYZ(clu, cols[i].eXYZ, cols[i].dev);
for (j = 0; j < 3; j++)
cols[i].eXYZ[j] *= 100.0;
}
cols[i].XYZ[0] = cols[i].XYZ[1] = cols[i].XYZ[2] = -1.0;
}
for (; i < runpat; i++) {
cols[i].id = cols[i].loc = "-1";
for (j = 0; j < nchan; j++)
cols[i].dev[j] = 0.0;
clu->dev_to_XYZ(clu, cols[i].eXYZ, cols[i].dev);
for (j = 0; j < 3; j++)
cols[i].eXYZ[j] *= 100.0;
cols[i].XYZ[0] = cols[i].XYZ[1] = cols[i].XYZ[2] = -1.0;
}
clu->del(clu);
free(ident);
free(bident);
} else
error ("Input file keyword COLOR_REP has unknown value");
/* Read any user supplied calibration information */
if (calname[0] != '\000') {
if ((cal = new_xcal()) == NULL)
error("new_xcal failed");
if ((cal->read(cal, calname)) != 0)
error("%s",cal->e.m);
}
/* If the user hasn't overridden it, get any calibration in the .ti2 */
if (cal == NULL) { /* No user supplied calibration info */
int oi, tab;
oi = icg->get_oi(icg, "CAL");
for (tab = 0; tab < icg->ntables; tab++) {
if (icg->t[tab].tt == tt_other && icg->t[tab].oi == oi) {
break;
}
}
if (tab < icg->ntables) {
if ((cal = new_xcal()) == NULL) {
error("new_xcal failed");
}
if (cal->read_cgats(cal, icg, tab, inname) != 0) {
error("%s",cal->e.m);
}
}
}
/* If there is calibration information, write it to the .ti3 */
if (cal != NULL) {
if (cal->write_cgats(cal, ocg) != 0) {
error("%s",cal->e.m);
}
cal->del(cal);
cal = NULL;
}
/* Set up the location sorted array of pointers. */
/* If the order is not randomized, we don't care what form */
/* the location identifiers take - i.e. they can be arbitrary. */
{
int badloc = 0;
for (i = 0; i < npat; i++) {
scols[i] = &cols[i];
if ((cols[i].loci = patch_location_order(paix, saix, ixord, cols[i].loc)) < 0)
badloc = 1;
}
for (; i < runpat; i++) { /* Extra on end */
scols[i] = &cols[i];
cols[i].loci = (totpa-1) * (256 - stipa) + i;
/* printf("~~extra = %d, %d\n",cols[i].loci >> 8, cols[i].loci & 255); */
}
/* Reset 'read' flag and all data */
for (i = 0; i < runpat; i++) {
cols[i].rr = 0;
cols[i].XYZ[0] = -1.0;
cols[i].XYZ[1] = -1.0;
cols[i].XYZ[2] = -1.0;
cols[i].sp.spec_n = 0;
}
if (rand && badloc)
error ("Bad location field value '%s' on patch %d", cols[i].loc, i);
if (!badloc) {
#define HEAP_COMPARE(A,B) (A->loci < B->loci)
HEAPSORT(chcol *, scols, npat);
}
}
/* If we're resuming a chartread, fill in all the patches that */
/* have been read. */
if (doresume) {
cgats *rcg; /* output cgats structure */
int nrpat; /* Number of resumed patches */
int lix; /* Patch location index */
int islab = 0; /* nz if Lab, z if XYZ */
int cieix[3]; /* CIE value indexes */
int hasspec = 0; /* nz if has spectral */
xspect sp; /* Parameters of spectrum */
int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */
char *fname[2][3] = { { "XYZ_X", "XYZ_Y", "XYZ_Z" },
{ "LAB_L", "LAB_A", "LAB_B" } };
int k, ii;
char buf[200];
/* Open and look at the .ti3 profile patches file */
rcg = new_cgats(); /* Create a CGATS structure */
rcg->add_other(rcg, "CTI3"); /* our special input type is Calibration Target Information 3 */
rcg->add_other(rcg, "CAL"); /* our special device Calibration state */
if (rcg->read_name(rcg, outname))
error("Unable to read chart being resumed '%s' : %s",outname, rcg->e.m);
if (rcg->ntables == 0 || rcg->t[0].tt != tt_other || rcg->t[0].oi != 0)
error ("Resumed file '%s' isn't a CTI3 format file",outname);
if (rcg->ntables < 1)
error ("Resumed file '%s' doesn't contain at least one table",outname);
if ((lix = rcg->find_field(rcg, 0, "SAMPLE_LOC")) < 0)
error ("Resumed file '%s' doesn't contain SAMPLE_LOC field",outname);
if (rcg->t[0].ftype[lix] != cs_t)
error("Field SAMPLE_LOC is wrong type - corrupted file ?");
/* Get the CIE field indexes */
if (rcg->find_field(rcg, 0, "LAB_L") >= 0)
islab = 1;
for (j = 0; j < 3; j++) {
if ((cieix[j] = rcg->find_field(rcg, 0, fname[islab][j])) < 0)
error("Input file doesn't contain field %s",fname[islab][j]);
if (rcg->t[0].ftype[cieix[j]] != r_t)
error("Field %s is wrong type - corrupted file ?",fname[islab][j]);
}
if ((ii = rcg->find_kword(rcg, 0, "SPECTRAL_BANDS")) >= 0) {
hasspec = 1;
sp.spec_n = atoi(rcg->t[0].kdata[ii]);
if ((ii = rcg->find_kword(rcg, 0, "SPECTRAL_START_NM")) < 0)
error ("Resumed file '%s' doesn't contain keyword SPECTRAL_START_NM",outname);
sp.spec_wl_short = atof(rcg->t[0].kdata[ii]);
if ((ii = rcg->find_kword(rcg, 0, "SPECTRAL_END_NM")) < 0)
error ("Resumed file '%s' doesn't contain keyword SPECTRAL_END_NM",outname);
sp.spec_wl_long = atof(rcg->t[0].kdata[ii]);
/* Find the fields for spectral values */
for (j = 0; j < sp.spec_n; j++) {
int nm;
/* Compute nearest integer wavelength */
nm = (int)(sp.spec_wl_short + ((double)j/(sp.spec_n-1.0))
* (sp.spec_wl_long - sp.spec_wl_short) + 0.5);
sprintf(buf,"SPEC_%03d",nm);
if ((spi[j] = rcg->find_field(rcg, 0, buf)) < 0)
error("Resumed file '%s' doesn't contain field %s",outname,buf);
if (rcg->t[0].ftype[spi[j]] != r_t)
error("Field %s is wrong type - expect float",buf);
}
}
nrpat = rcg->t[0].nsets;
/* Now see if we can match the previously read patches. */
/* We'll use the patch location to do this. */
for (i = 0; i < runpat; i++) {
int k;
for (k = 0; k < nrpat; k++) {
if (strcmp(cols[i].loc, ((char *)rcg->t[0].fdata[k][lix])) == 0)
break;
}
if (k >= nrpat)
continue;
#ifdef DEBUG
printf("Recovering patch '%s' value from .ti3 file\n",cols[i].loc);
#endif
cols[i].XYZ[0] = *((double *)rcg->t[0].fdata[k][cieix[0]]);
cols[i].XYZ[1] = *((double *)rcg->t[0].fdata[k][cieix[1]]);
cols[i].XYZ[2] = *((double *)rcg->t[0].fdata[k][cieix[2]]);
if (islab) {
icmLab2XYZ(&icmD50, cols[i].XYZ, cols[i].XYZ);
cols[i].XYZ[0] *= 100.0;
cols[i].XYZ[1] *= 100.0;
cols[i].XYZ[2] *= 100.0;
}
if (hasspec) {
cols[i].sp.spec_n = sp.spec_n;
cols[i].sp.spec_wl_short = sp.spec_wl_short;
cols[i].sp.spec_wl_long = sp.spec_wl_long;
for (j = 0; j < sp.spec_n; j++)
cols[i].sp.spec[j] = *((double *)rcg->t[0].fdata[k][spi[j]]);
}
cols[i].rr = 1;
}
rcg->del(rcg);
}
/* We can't fiddle white point with spectral data, */
/* so turn spectral off for display with white point relative. */
if (displ == 2 || displ == 3) {
spectral = 0;
/* Check that there is a white patch, so that we can compute Y relative */
/* Read all the test patches in */
if (nmask != ICX_RGB)
error("Don't know how to handle non-RGB display space");
for (wpat = 0; wpat < npat; wpat++) {
if (cols[wpat].dev[0] > 0.9999999 &&
cols[wpat].dev[1] > 0.9999999 &&
cols[wpat].dev[2] > 0.9999999) {
break;
}
}
if (wpat >= npat) { /* Create a white patch */
error("Can't compute white Y relative display values without a white test patch");
}
}
if (!xtern) {
if ((icmps = new_icompaths(g_log)) == NULL)
error("Finding instrument paths failed");
if ((ipath = icmps->get_path(icmps, comport)) == NULL)
error("No instrument at port %d",comport);
}
/* Read all of the strips in */
if (read_strips(itype, scols, &atype, npat, totpa, stipa, pis, paix,
saix, ixord, rstart, rand, hex, ipath, fc, plen, glen, tlen,
trans, emis, displ, ditype, fe, scalstd, &ucalstd, nocal, disbidi, highres,
ccxxname, obType, custObserver,
scan_tol, pbypatch, xtern, spectral, uvmode, accurate_expd,
emit_warnings, doplot,
g_log) == 0) {
/* And save the result */
save_ti3(atype, displ, trans, ucalstd, fe, dolab, npat, nchan, cols, wpat, outname, ocg);
}
free(scols);
if (icmps != NULL)
icmps->del(icmps);
free(pis);
saix->del(saix);
paix->del(paix);
free(cols);
ocg->del(ocg); /* Clean up */
icg->del(icg); /* Clean up */
return 0;
}