diff options
Diffstat (limited to 'spectro')
101 files changed, 7387 insertions, 2574 deletions
diff --git a/spectro/IntsLib_Readme.txt b/spectro/IntsLib_Readme.txt index 7d9fb0c..c8df3d3 100644 --- a/spectro/IntsLib_Readme.txt +++ b/spectro/IntsLib_Readme.txt @@ -3,17 +3,6 @@ create the instlib.zip archive. To build it: -If you are on Linux or OS X, you first need to -build libusb 1.0A, ie:: - - cd libusb1 - sh autogen.sh - make - cp libusb/libusb-1.0A.a . - cd .. - -(The libraries are pre-built for MSWin) - To build the standalone instrument lib, you need to edit the Makefile to #include the appropriate Makefile.XXX for your operating system, and then diff --git a/spectro/Jamfile b/spectro/Jamfile index 9eb292f..94a2f70 100644 --- a/spectro/Jamfile +++ b/spectro/Jamfile @@ -127,7 +127,7 @@ if $(USE_DEMOINST) = true && [ GLOB [ NormPaths . ] : demoinst.c ] { INST_SRCS += demoinst.c ; } -Library libinst : inst.c insttypes.c icoms.c disptechs.c rspec.c $(INST_SRCS) ; +Library libinst : inst.c insttypes.c icoms.c disptechs.c rspec.c xrga.c $(INST_SRCS) ; # Display access library ObjectKeep mongoose.c ; diff --git a/spectro/Makefile.SA b/spectro/Makefile.SA index 2d57742..5913126 100644 --- a/spectro/Makefile.SA +++ b/spectro/Makefile.SA @@ -39,12 +39,12 @@ WIN_STDHDRS = $(INCFLAG)usb$(SLASH)driver all:: libinst$(SUFLIB) libinstappsup$(SUFLIB) spotread$(SUFEXE) oeminst$(SUFEXE) INSTHEADERS = dtp20.h dtp22.h dtp41.h dtp51.h dtp92.h ss.h ss_imp.h i1disp.h i1d3.h i1pro.h i1pro_imp.h munki.h munki_imp.h hcfr.h huey.h colorhug.h spyd2.h specbos.h kleink10.h -INSOBJS = dtp20$(SUFOBJ) dtp22$(SUFOBJ) dtp41$(SUFOBJ) dtp51$(SUFOBJ) dtp92$(SUFOBJ) ss$(SUFOBJ) ss_imp$(SUFOBJ) i1disp$(SUFOBJ) i1d3$(SUFOBJ) i1pro$(SUFOBJ) i1pro_imp$(SUFOBJ) munki$(SUFOBJ) munki_imp$(SUFOBJ) hcfr$(SUFOBJ) huey$(SUFOBJ) colorhug$(SUFOBJ) spyd2$(SUFOBJ) specbos$(SUFOBJ) kleink10$(SUFOBJ) +INSOBJS = dtp20$(SUFOBJ) dtp22$(SUFOBJ) dtp41$(SUFOBJ) dtp51$(SUFOBJ) dtp92$(SUFOBJ) ss$(SUFOBJ) ss_imp$(SUFOBJ) i1disp$(SUFOBJ) i1d3$(SUFOBJ) i1pro$(SUFOBJ) i1pro_imp$(SUFOBJ) munki$(SUFOBJ) munki_imp$(SUFOBJ) hcfr$(SUFOBJ) huey$(SUFOBJ) colorhug$(SUFOBJ) spyd2$(SUFOBJ) specbos$(SUFOBJ) kleink10$(SUFOBJ) ex1$(SUFOBJ) smcube$(SUFOBJ) -HEADERS = pollem.h conv.h aglob.h hidio.h icoms.h inst.c inst.h insttypeinst.h insttypes.h disptechs.h $(INSTHEADERS) usbio.h xspect.h rspl1.h sort.h xdg_bds.h ccss.h ccmx.h pars.h cgats.h instappsup.h usb$(SLASH)driver$(SLASH)driver_api.h +HEADERS = pollem.h conv.h sa_conv.h aglob.h hidio.h icoms.h inst.c inst.h insttypeinst.h insttypes.h disptechs.h rspec.h xrga.h $(INSTHEADERS) usbio.h xspect.h rspl1.h sort.h xdg_bds.h ccss.h ccmx.h pars.h cgats.h instappsup.h usb$(SLASH)driver$(SLASH)driver_api.h # libinst objects -OBJS = conv$(SUFOBJ) aglob$(SUFOBJ) inst$(SUFOBJ) numsup$(SUFOBJ) rspl1$(SUFOBJ) icoms$(SUFOBJ) usbio$(SUFOBJ) hidio$(SUFOBJ) insttypes$(SUFOBJ) disptechs$(SUFOBJ) pollem$(SUFOBJ) xspect$(SUFOBJ) xdg_bds$(SUFOBJ) ccss$(SUFOBJ) ccmx$(SUFOBJ) pars$(SUFOBJ) cgats$(SUFOBJ) $(INSOBJS) +OBJS = conv$(SUFOBJ) sa_conv$(SUFOBJ) aglob$(SUFOBJ) inst$(SUFOBJ) numsup$(SUFOBJ) rspl1$(SUFOBJ) icoms$(SUFOBJ) usbio$(SUFOBJ) hidio$(SUFOBJ) insttypes$(SUFOBJ) disptechs$(SUFOBJ) rspec$(SUFOBJ) xrga$(SUFOBJ) pollem$(SUFOBJ) xspect$(SUFOBJ) xdg_bds$(SUFOBJ) ccss$(SUFOBJ) ccmx$(SUFOBJ) pars$(SUFOBJ) cgats$(SUFOBJ) $(INSOBJS) # instrument library @@ -52,6 +52,9 @@ OBJS = conv$(SUFOBJ) aglob$(SUFOBJ) inst$(SUFOBJ) numsup$(SUFOBJ) rspl1$(SUFOBJ) conv$(SUFOBJ): conv.c $(HEADERS) $(CC) conv.c +sa_conv$(SUFOBJ): sa_conv.c $(HEADERS) + $(CC) sa_conv.c + aglob$(SUFOBJ): aglob.c $(HEADERS) $(CC) aglob.c @@ -76,6 +79,12 @@ hidio$(SUFOBJ): hidio.c $(HEADERS) insttypes$(SUFOBJ): insttypes.c $(HEADERS) $(CC) insttypes.c +rspec$(SUFOBJ): rspec.c $(HEADERS) + $(CC) rspec.c + +xrga$(SUFOBJ): xrga.c $(HEADERS) + $(CC) xrga.c + disptechs$(SUFOBJ): disptechs.c $(HEADERS) $(CC) disptechs.c @@ -148,6 +157,12 @@ specbos$(SUFOBJ): specbos.c $(HEADERS) kleink10$(SUFOBJ): kleink10.c $(HEADERS) $(CC) kleink10.c +ex1$(SUFOBJ): ex1.c $(HEADERS) + $(CC) ex1.c + +smcube$(SUFOBJ): smcube.c $(HEADERS) + $(CC) smcube.c + oemarch$(SUFOBJ): oemarch.c $(HEADERS) $(CC) oemarch.c diff --git a/spectro/afiles b/spectro/afiles index 984bac5..8891ce7 100644 --- a/spectro/afiles +++ b/spectro/afiles @@ -26,6 +26,7 @@ mongoose.c insttypes.h insttypes.c insttypeinst.h +dev.h inst.c inst.h disptechs.h @@ -87,8 +88,12 @@ spec2cie.c average.c rspec.h rspec.c +xrga.h +xrga.c conv.h conv.c +sa_conv.h +sa_conv.c aglob.h aglob.c xdg_bds.h diff --git a/spectro/average.c b/spectro/average.c index 5a8fb3f..e961262 100644 --- a/spectro/average.c +++ b/spectro/average.c @@ -23,6 +23,7 @@ */ + #undef DEBUG #define verbo stdout @@ -34,10 +35,15 @@ #include "copyright.h" #include "aconfig.h" #include "numlib.h" +#include "sort.h" #include "cgats.h" #include "xicc.h" #include "insttypes.h" +static double average(double *vals, int nvals); +static double median(double *vals, int nvals); +static void geommed(double res[3], double vals[][3], int nvals); + void usage(char *diag, ...) { int i; fprintf(stderr,"Average or merge values in .ti3 like files, Version %s\n",ARGYLL_VERSION_STR); @@ -52,6 +58,10 @@ void usage(char *diag, ...) { fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); fprintf(stderr,"usage: average [-options] input1.ti3 input2.ti3 ... output.ti3\n"); fprintf(stderr," -v Verbose\n"); + fprintf(stderr," -e Median rather than average\n"); + fprintf(stderr," -g Geometric Median of PCS in encoded space\n"); + fprintf(stderr," -L Geometric Median of PCS in L*a*b* space\n"); + fprintf(stderr," -X Geometric Median of PCS in XYZ space\n"); fprintf(stderr," -m Merge rather than average\n"); fprintf(stderr," input1.ti3 First input file\n"); fprintf(stderr," input2.ti3 Second input file\n"); @@ -69,6 +79,8 @@ struct _inpinfo { int main(int argc, char *argv[]) { int fa,nfa; /* current argument we're looking at */ int verb = 0; + int domedian = 0; /* Median rather than average */ + int dogeom = 0; /* Do geometric median of PCS, 2 = Lab, 3 = PCS */ int domerge = 0; /* Merge rather than average */ int ninps = 0; /* Number of input files */ @@ -78,10 +90,12 @@ int main(int argc, char *argv[]) { cgats_set_elem *setel; /* Array of set value elements */ int *flags; /* Point to destination of set */ - int nchan; /* Number of device channels */ + int nchan = 0; /* Number of device channels */ int chix[ICX_MXINKS]; /* Device chanel indexes */ - int pcsix[3]; /* PCS chanel indexes */ - int isLab = 0; + int npcs = 0; + + int haspcs[2] = { 0 }; /* Has Lab, XYZ */ + int pcsix[3][3]; /* Lab, XYZ chanel indexes */ int i, j, n; @@ -110,13 +124,33 @@ int main(int argc, char *argv[]) { if (argv[fa][1] == '?') usage("Usage requested"); + /* Median */ + else if (argv[fa][1] == 'e') { + domedian = 1; + } + + /* Geometric Median of PCS */ + else if (argv[fa][1] == 'g') { + dogeom = 1; + } + + /* Geometric Median of PCS in L*a*b* */ + else if (argv[fa][1] == 'L') { + dogeom = 2; + } + + /* Geometric Median of PCS in XYZ */ + else if (argv[fa][1] == 'X') { + dogeom = 3; + } + /* Merge */ - else if (argv[fa][1] == 'm' || argv[fa][1] == 'M') { + else if (argv[fa][1] == 'm') { domerge = 1; } /* Verbosity */ - else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { + else if (argv[fa][1] == 'v') { verb = 1; } @@ -192,8 +226,9 @@ int main(int argc, char *argv[]) { ocg->add_field(ocg, n, inps[0].c->t[n].fsym[i], inps[0].c->t[n].ftype[i]); } + /* If more than one file, must be merging or averaging between files */ if (ninps > 1) { - /* Duplicate all of the data */ + /* Duplicate all of the data or first file to output file */ if ((setel = (cgats_set_elem *)malloc( sizeof(cgats_set_elem) * inps[0].c->t[n].nfields)) == NULL) error("Malloc failed!"); @@ -207,11 +242,11 @@ int main(int argc, char *argv[]) { } /* Figure out the indexes of the device channels */ - { + if (inps[0].c->find_kword(inps[0].c, 0, "COLOR_REP") < 0) { + warning("Input file '%s' doesn't contain keyword COLOR_REP", inps[0].name); + } else { int ti; char *buf; - char *xyzfname[3] = { "XYZ_X", "XYZ_Y", "XYZ_Z" }; - char *labfname[3] = { "LAB_L", "LAB_A", "LAB_B" }; char *outc; int nmask; char *bident; @@ -227,13 +262,6 @@ int main(int argc, char *argv[]) { error("COLOR_REP '%s' invalid", inps[0].c->t[0].kdata[ti]); *outc++ = '\000'; - if (strcmp(outc, "XYZ") == 0) { - isLab = 0; - } else if (strcmp(outc, "LAB") == 0) { - isLab = 1; - } else - error("COLOR_REP '%s' invalid (Neither XYZ nor LAB)", inps[0].c->t[0].kdata[ti]); - if ((nmask = icx_char2inkmask(buf)) == 0) { error ("File '%s' keyword COLOR_REP has unknown device value '%s'",inps[0].name,buf); } @@ -256,19 +284,32 @@ int main(int argc, char *argv[]) { error ("Field %s is wrong type",fname); chix[j] = ii; } - - /* Find PCS fields */ - for (j = 0; j < 3; j++) { - int ii; - - if ((ii = inps[0].c->find_field(inps[0].c, 0, isLab ? labfname[j] : xyzfname[j])) < 0) - error ("Input file doesn't contain field %s",isLab ? labfname[j] : xyzfname[j]); - if (inps[0].c->t[0].ftype[ii] != r_t) - error ("Field %s is wrong type",isLab ? labfname[j] : xyzfname[j]); - pcsix[j] = ii; + } + + /* Figure out the indexes of the PCS channels, if any */ + { + int npcs; + char *fname[2][3] = { { "LAB_L", "LAB_A", "LAB_B" }, + { "XYZ_X", "XYZ_Y", "XYZ_Z" } }; + + /* For Lab and XYZ */ + for (j = 0; j < 2; j++) { + for (npcs = 0; npcs < 3; npcs++) { + int ii; + + if ((ii = inps[0].c->find_field(inps[0].c, 0, fname[j][npcs])) < 0) + break; /* Try next or give up */ + + if (inps[0].c->t[0].ftype[ii] != r_t) + error ("Field %s is wrong type",fname[j][npcs]); + pcsix[j][npcs] = ii; + } + if (npcs == 3) + haspcs[j] = 1; } - free(bident); } + if (!haspcs[0] && !haspcs[1]) + warning("No PCS fields found - hope that's OK!"); if (!domerge && verb) { printf("Averaging the following fields:"); @@ -294,7 +335,8 @@ int main(int argc, char *argv[]) { printf("\n"); } - /* Get ready to add more values to output */ + /* Get ready to add more values to output, */ + /* for merging or averaging within one file. */ if ((setel = (cgats_set_elem *)malloc( sizeof(cgats_set_elem) * inps[0].c->t[0].nfields)) == NULL) error("Malloc failed!"); @@ -302,13 +344,24 @@ int main(int argc, char *argv[]) { /* If averaging values within the one file */ if (ninps == 1) { int *valdone; - double npatches; - int k; + int npat; + double *vlist; + double (*v3list)[3] = NULL; + int k, e; n = 0; /* Output set index */ if ((valdone = (int *)calloc(inps[0].c->t[0].nsets, sizeof(int))) == NULL) error("Malloc failed!"); + if ((vlist = (double *)calloc(inps[0].c->t[0].nsets, sizeof(double))) == NULL) + error("Malloc failed!"); + + if (dogeom && (haspcs[0] || haspcs[1])) { + if ((v3list = (double (*)[3])calloc(inps[0].c->t[0].nsets, 3 * sizeof(double))) == NULL) + error("Malloc failed!"); + } + + /* For each patch */ for (i = 0; i < inps[0].c->t[0].nsets; i++) { if (valdone[i]) @@ -316,49 +369,8 @@ int main(int argc, char *argv[]) { inps[0].c->get_setarr(inps[0].c, 0, i, setel); ocg->add_setarr(ocg, 0, setel); - npatches = 1.0; - - /* Locate and patches with matching device values */ - for (k = i+1; k < inps[0].c->t[0].nsets; k++) { - - /* Check if the device values match */ - for (j = 0; j < nchan; j++) { - double diff; - - diff = *((double *)inps[0].c->t[0].fdata[i][chix[j]]) - - *((double *)inps[0].c->t[0].fdata[k][chix[j]]); - - if (fabs(diff) > 0.001) { - break; - } - } - if (j < nchan) { - continue; - } - /* Add all the non-device real field values */ - for (j = 0; j < inps[0].c->t[0].nfields; j++) { - int jj; - - /* Only real types */ - if (inps[0].c->t[0].ftype[j] != r_t) - continue; - - /* Not device channels */ - for (jj = 0; jj < nchan; jj++) { - if (chix[jj] == j) - break; - } - if (jj < nchan) - continue; - - *((double *)ocg->t[0].fdata[n][j]) - += *((double *)inps[0].c->t[0].fdata[k][j]); - } - npatches++; - valdone[k] = 1; - } - /* Average them out */ + /* For each non-device real field values */ for (j = 0; j < inps[0].c->t[0].nfields; j++) { int jj; @@ -374,20 +386,102 @@ int main(int argc, char *argv[]) { if (jj < nchan) continue; - *((double *)ocg->t[0].fdata[n][j]) /= npatches; + /* Locate any patches (including starting patch) with matching device values */ + npat = 0; + for (k = i; k < inps[0].c->t[0].nsets; k++) { + + /* Check if the device values match */ + for (e = 0; e < nchan; e++) { + double diff; + + diff = *((double *)inps[0].c->t[0].fdata[i][chix[e]]) + - *((double *)inps[0].c->t[0].fdata[k][chix[e]]); + + if (fabs(diff) > 0.001) { + break; + } + } + if (e < nchan) { + continue; + } + + vlist[npat++] = *((double *)inps[0].c->t[0].fdata[k][j]); + valdone[k] = 1; + } + if (domedian) + *((double *)ocg->t[0].fdata[n][j]) = median(vlist, npat); + else + *((double *)ocg->t[0].fdata[n][j]) = average(vlist, npat); + } + + /* Override per-component average/median if PCS Geometric Median */ + if (dogeom && (haspcs[0] || haspcs[1])) { + double res[3]; + + /* For Lab and XYZ */ + for (j = 0; j < 2; j++) { + + if (haspcs[j] == 0) + continue; + + /* Locate any patches (including starting patch) with matching device values */ + npat = 0; + for (k = i; k < inps[0].c->t[0].nsets; k++) { + + /* Check if the device values match */ + for (e = 0; e < nchan; e++) { + double diff; + + diff = *((double *)inps[0].c->t[0].fdata[i][chix[e]]) + - *((double *)inps[0].c->t[0].fdata[k][chix[e]]); + + if (fabs(diff) > 0.001) { + break; + } + } + if (e < nchan) { + continue; + } + + v3list[npat][0] = *((double *)inps[0].c->t[0].fdata[k][pcsix[j][0]]); + v3list[npat][1] = *((double *)inps[0].c->t[0].fdata[k][pcsix[j][1]]); + v3list[npat][2] = *((double *)inps[0].c->t[0].fdata[k][pcsix[j][2]]); + + if (j == 0 && dogeom == 3) /* Lab and want XYZ */ + icmLab2XYZ(&icmD50_100, v3list[npat], v3list[npat]); + else if (j == 1 && dogeom == 2) /* XYZ and want Lab */ + icmXYZ2Lab(&icmD50_100, v3list[npat], v3list[npat]); + + npat++; + } + geommed(res, v3list, npat); + + if (j == 0 && dogeom == 3) + icmXYZ2Lab(&icmD50_100, res, res); + else if (j == 1 && dogeom == 2) + icmLab2XYZ(&icmD50_100, res, res); + + *((double *)ocg->t[0].fdata[n][pcsix[j][0]]) = res[0]; + *((double *)ocg->t[0].fdata[n][pcsix[j][1]]) = res[1]; + *((double *)ocg->t[0].fdata[n][pcsix[j][2]]) = res[2]; + } } n++; /* One more output set */ } + if (v3list != NULL) + free(v3list); + free(vlist); free(valdone); /* Averaging patches between identical files, */ /* or concatenating (merging) several files */ } else { - /* Process all the other input files */ + + /* Check/process all the other input files */ for (n = 1; n < ninps; n++) { - /* Check all the fields match */ + /* Check all the fields match the first file */ if (inps[0].c->t[0].nfields != inps[n].c->t[0].nfields) error ("File '%s' has %d fields, file '%s has %d", inps[n].name, inps[n].c->t[0].nfields, inps[0].name, inps[0].c->t[0].nfields); @@ -405,15 +499,15 @@ int main(int argc, char *argv[]) { } } else { /* Averaging */ - /* Check the number of values matches */ + + /* Check the number of patches matches the first file */ if (inps[0].c->t[0].nsets != inps[n].c->t[0].nsets) error ("File '%s' has %d sets, file '%s has %d", inps[n].name, inps[n].c->t[0].nsets, inps[0].name, inps[0].c->t[0].nsets); - - /* Add the numeric field values to corresponding output */ + /* For all the patches: */ for (i = 0; i < inps[n].c->t[0].nsets; i++) { - /* Check that the device values match */ + /* Check that the device values match the first file */ for (j = 0; j < nchan; j++) { double diff; diff = *((double *)inps[0].c->t[0].fdata[i][chix[j]]) @@ -423,53 +517,100 @@ int main(int argc, char *argv[]) { error ("File '%s' set %d has field '%s' value that differs from '%s'", inps[n].name, i+1, inps[n].c->t[0].fsym[j], inps[0].name); } - - /* Add all the non-device real field values */ - for (j = 0; j < inps[0].c->t[0].nfields; j++) { - int jj; - - /* Only real types */ - if (inps[0].c->t[0].ftype[j] != r_t) - continue; - - /* Not device channels */ - for (jj = 0; jj < nchan; jj++) { - if (chix[jj] == j) - break; - } - if (jj < nchan) - continue; - - *((double *)ocg->t[0].fdata[i][j]) - += *((double *)inps[n].c->t[0].fdata[i][j]); - } } } } - - /* If averaging, divide out the number of files */ + + /* If averaging */ if (!domerge) { + int npat; + double *vlist; + double (*v3list)[3] = NULL; + + if ((vlist = (double *)calloc(ninps, sizeof(double))) == NULL) + error("Malloc failed!"); + + if (dogeom && (haspcs[0] || haspcs[1])) { + if ((v3list = (double (*)[3])calloc(inps[0].c->t[0].nsets, 3 * sizeof(double))) == NULL) + error("Malloc failed!"); + } + + /* For all the non-device real field values */ + for (j = 0; j < inps[0].c->t[0].nfields; j++) { + int jj; + + /* Only real types */ + if (inps[0].c->t[0].ftype[j] != r_t) + continue; + + /* Not device channels */ + for (jj = 0; jj < nchan; jj++) { + if (chix[jj] == j) + break; + } + if (jj < nchan) + continue; - for (i = 0; i < inps[n].c->t[0].nsets; i++) { - - for (j = 0; j < inps[0].c->t[0].nfields; j++) { - int jj; - - /* Only real types */ - if (inps[0].c->t[0].ftype[j] != r_t) - continue; - - /* Not device channels */ - for (jj = 0; jj < nchan; jj++) { - if (chix[jj] == j) - break; + /* For each patch */ + for (i = 0; i < inps[n].c->t[0].nsets; i++) { + + /* For all input files */ + npat = 0; + for (n = 0; n < ninps; n++) { + vlist[npat++] = *((double *)inps[n].c->t[0].fdata[i][j]); } - if (jj < nchan) - continue; + + if (domedian) + *((double *)ocg->t[0].fdata[i][j]) = median(vlist, npat); + else + *((double *)ocg->t[0].fdata[i][j]) = average(vlist, npat); + } + } + + /* Override per-component average/median if PCS Geometric Median */ + if (dogeom && (haspcs[0] || haspcs[1])) { + double res[3]; + + /* For each patch */ + for (i = 0; i < inps[n].c->t[0].nsets; i++) { + + /* For Lab and XYZ */ + for (j = 0; j < 2; j++) { + + if (haspcs[j] == 0) + continue; - *((double *)ocg->t[0].fdata[i][j]) /= (double)ninps; + /* For all input files */ + npat = 0; + for (n = 0; n < ninps; n++) { + v3list[npat][0] = *((double *)inps[n].c->t[0].fdata[i][pcsix[j][0]]); + v3list[npat][1] = *((double *)inps[n].c->t[0].fdata[i][pcsix[j][1]]); + v3list[npat][2] = *((double *)inps[n].c->t[0].fdata[i][pcsix[j][2]]); + + if (j == 0 && dogeom == 3) /* Lab and want XYZ */ + icmLab2XYZ(&icmD50_100, v3list[npat], v3list[npat]); + else if (j == 1 && dogeom == 2) /* XYZ and want Lab */ + icmXYZ2Lab(&icmD50_100, v3list[npat], v3list[npat]); + + npat++; + } + geommed(res, v3list, npat); + + if (j == 0 && dogeom == 3) + icmXYZ2Lab(&icmD50_100, res, res); + else if (j == 1 && dogeom == 2) + icmLab2XYZ(&icmD50_100, res, res); + + *((double *)ocg->t[0].fdata[i][pcsix[j][0]]) = res[0]; + *((double *)ocg->t[0].fdata[i][pcsix[j][1]]) = res[1]; + *((double *)ocg->t[0].fdata[i][pcsix[j][2]]) = res[2]; + } } } + + if (v3list != NULL) + free(v3list); + free(vlist); } } @@ -490,6 +631,69 @@ int main(int argc, char *argv[]) { } +static double average(double *vals, int nvals) { + double rv; + int i; + + for (rv = 0.0, i = 0; i < nvals; i++) + rv += vals[i]; + + if (nvals > 0) + rv /= (double)nvals; + + return rv; +} + +static double median(double *vals, int nvals) { + if (nvals < 3) + return average(vals, nvals); + +#define HEAP_COMPARE(A,B) (A < B) + HEAPSORT(double,vals,nvals); + + if ((nvals & 1) != 0) + return vals[nvals/2]; + else + return 0.5 * (vals[nvals/2] + vals[nvals/2-1]); +} +/* Compute Geometric Median of PCS values */ +/* using Weiszfeld's algorithm. */ +static void geommed(double res[3], double vals[][3], int nvals) { + int i, j; + + /* Start with mean value */ + icmSet3(res, 0.0); + for (i = 0; i < nvals; i++) + icmAdd3(res, res, vals[i]); + icmScale3(res, res, 1.0/(double)nvals); + +//printf("\n~1 average = %f %f %f\n", res[0], res[1], res[2]); + + /* Itterate to approach Geometric Mean */ + for (j = 0; j < 20; j++) { + double tv[3], tt; + int k; + + icmSet3(tv, 0.0); + tt = 0.0; + for (k = i = 0; i < nvals; i++) { + double norm = icmNorm33(vals[i], res); + if (norm < 1e-6) + continue; + tv[0] += vals[i][0]/norm; + tv[1] += vals[i][1]/norm; + tv[2] += vals[i][2]/norm; + tt += 1.0/norm; + k++; +//printf("Norm = %f, tv = %f %f %f, tt = %f\n",norm, tv[0], tv[1], tv[2], tt); + } + if (k > 0) + icmScale3(res, tv, 1.0/tt); +//printf("~1 res = %f %f %f\n", res[0], res[1], res[2]); + } + +//printf("~1 geomm = %f %f %f\n", res[0], res[1], res[2]); +} diff --git a/spectro/base64.c b/spectro/base64.c index 388d121..8e07967 100644 --- a/spectro/base64.c +++ b/spectro/base64.c @@ -4,6 +4,9 @@ * * Very simple & concise base64 encoder/decoder * + */ + +/* * Author: Graeme W. Gill * * Copyright 2014, Graeme W. Gill diff --git a/spectro/base64.h b/spectro/base64.h index 726f20a..29ca140 100644 --- a/spectro/base64.h +++ b/spectro/base64.h @@ -1,9 +1,13 @@ +#ifndef BASE64_H + /* * Argyll Color Correction System * * Very simple & concise base64 encoder/decoder - * + */ + +/* * Author: Graeme W. Gill * * Copyright 2014, Graeme W. Gill @@ -13,6 +17,9 @@ * see the License2.txt file for licencing details. */ +#ifdef __cplusplus + extern "C" { +#endif /* The maximum encoded length given decoded length */ #define EBASE64LEN(len) (((len) * 4 + 2)/3) @@ -30,4 +37,9 @@ void ebase64(int *dlen, char *dst, unsigned char *src, int slen); /* We assume that the destination buffer is long enough at DBASE64LEN */ void dbase64(int *dlen, unsigned char *dst, char *src); +#ifdef __cplusplus + } +#endif +#define BASE64_H +#endif /* BASE64_H */ diff --git a/spectro/ccwin.c b/spectro/ccwin.c index 1e9a18d..a89bc65 100644 --- a/spectro/ccwin.c +++ b/spectro/ccwin.c @@ -454,7 +454,7 @@ icmFile *ccwin_get_profile(dispwin *p, char *name, int mxlen) { /* Change the window color. */ /* Return 1 on error, 2 on window being closed */ -/* inst_license, inst_licensenc or inst_tamper on licening problem */ +/* inst_license, inst_licensenc, inst_tamper or inst_syscompat on licening problem */ static int ccwin_set_color( dispwin *p, double r, double g, double b /* Color values 0.0 - 1.0 */ @@ -487,9 +487,9 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ /* For video encoding the extra bits of precision are created by bit shifting */ /* rather than scaling, so we need to scale the fp value to account for this. */ - if (p->pdepth > 8) - p->r_rgb[j] = (p->s_rgb[j] * 255 * (1 << (p->pdepth - 8))) - /((1 << p->pdepth) - 1.0); + if (p->edepth > 8) + p->r_rgb[j] = (p->s_rgb[j] * 255 * (1 << (p->edepth - 8))) + /((1 << p->edepth) - 1.0); } } @@ -798,8 +798,11 @@ int ddebug /* >0 to print debug statements to stderr */ p->ncix = 1; - p->pdepth = 8; /* Assume this by API */ - p->edepth = 8; + p->fdepth = 8; /* Assume this by API */ + p->rdepth = p->fdepth; /* Assumed */ + p->ndepth = p->rdepth; /* Assumed */ + p->nent = 0; /* No ramdac */ + p->edepth = 8; /* Assumed */ /* Basic object is initialised, so create connection to ChromeCast */ if ((ws = new_chws(cc_id, width, height, hoff, voff, verb, ddebug)) == NULL) { diff --git a/spectro/ccxxmake.c b/spectro/ccxxmake.c index 418affc..740eb0a 100644 --- a/spectro/ccxxmake.c +++ b/spectro/ccxxmake.c @@ -500,7 +500,7 @@ int main(int argc, char *argv[]) { fa = nfa; if (na == NULL) usage(0,"Paramater expected following -W"); if (na[0] == 'n' || na[0] == 'N') - fc = fc_none; + fc = fc_None; else if (na[0] == 'h' || na[0] == 'H') fc = fc_Hardware; else if (na[0] == 'x' || na[0] == 'X') @@ -1441,11 +1441,12 @@ int main(int argc, char *argv[]) { strcat(colname, ")"); } if (description == NULL) { - if ((description = malloc(strlen(colname) + strlen(displayname) + 4)) == NULL) + char *disp = displayname != NULL ? displayname : dtinfo->desc; + if ((description = malloc(strlen(colname) + strlen(disp) + 4)) == NULL) error("Malloc failed"); strcpy(description, colname); strcat(description, " & "); - strcat(description, displayname); + strcat(description, disp); } if (refrmode < 0) diff --git a/spectro/chartread.c b/spectro/chartread.c index 9cf6c22..e1624e1 100644 --- a/spectro/chartread.c +++ b/spectro/chartread.c @@ -1,7 +1,8 @@ +/* Spectrometer/Colorimeter target test chart reader */ + /* * Argyll Color Correction System - * Spectrometer/Colorimeter target test chart reader * * Author: Graeme W. Gill * Date: 4/10/96 @@ -58,6 +59,11 @@ #define COMPORT 1 /* Default com port 1..4 */ +#undef TEST_EVENT_CALLBACK /* Report async event callbacks, and implement beep prompt there. */ + +#undef USESTRDELTA /* [Und] Use patch delat's for correlation rather than match DE */ + /* Doesn't seem to work as well. Why ? */ + #ifdef __MINGW32__ # define WINVER 0x0500 #endif @@ -69,22 +75,36 @@ #include <time.h> #include <ctype.h> #include <string.h> +#ifndef SALONEINSTLIB #include "copyright.h" #include "aconfig.h" -#include "cgats.h" #include "numlib.h" -#include "icc.h" #include "xicc.h" -#include "insttypes.h" #include "conv.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 "ui.h" #include "ccast.h" #include "dispsup.h" +#endif /* !SALONEINSTLIB */ #include "alphix.h" #include "sort.h" #include "instappsup.h" @@ -132,6 +152,24 @@ static double xyzLabDE(double ynorm, double *pat, double *ref) { 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 { @@ -169,6 +207,15 @@ static int b62_int(char *p) { return rv; } +#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) { @@ -211,6 +258,8 @@ int displ, /* 1 = Use display emissive mode, 2 = display bright rel. */ /* 3 = display white rel. */ int dtype, /* 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, /* Disable automatic bi-directional strip recognition */ int highres, /* Use high res spectral mode */ @@ -224,6 +273,7 @@ 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; @@ -251,6 +301,11 @@ a1log *log /* verb, debug & error log */ printf("Unknown, inappropriate or no instrument detected\n"); 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", @@ -276,6 +331,24 @@ a1log *log /* verb, debug & error log */ 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)); @@ -297,8 +370,8 @@ a1log *log /* verb, debug & error log */ } else if (emis || displ) { if (emis) { - if (!IMODETST(cap, inst_mode_emis_spot) - && !IMODETST(cap, inst_mode_emis_strip)) { + 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); @@ -306,7 +379,7 @@ a1log *log /* verb, debug & error log */ } } else { /* Should we allow for non-adaptive mode ? */ - if (!IMODETST(cap, inst_mode_emis_spot)) { + 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); @@ -314,7 +387,7 @@ a1log *log /* verb, debug & error log */ } } - } else { + } 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"); @@ -461,20 +534,17 @@ a1log *log /* verb, debug & error log */ /* Should look at instrument type & user spec ??? */ if (trans) { - if (pbypatch && IMODETST(cap, inst_mode_trans_spot) + if (pbypatch && it->check_mode(it, inst_mode_trans_spot) == inst_ok) { mode = inst_mode_trans_spot; rmode = 0; - } else if (IMODETST(cap, inst_mode_trans_chart) - && it->check_mode(it, inst_mode_trans_chart) == inst_ok) { + } else if (it->check_mode(it, inst_mode_trans_chart) == inst_ok) { mode = inst_mode_trans_chart; rmode = 3; - } else if (IMODETST(cap, inst_mode_trans_xy) - && it->check_mode(it, inst_mode_trans_xy) == inst_ok) { + } else if (it->check_mode(it, inst_mode_trans_xy) == inst_ok) { mode = inst_mode_trans_xy; rmode = 2; - } else if (IMODETST(cap, inst_mode_trans_strip) - && it->check_mode(it, inst_mode_trans_strip) == inst_ok) { + } else if (it->check_mode(it, inst_mode_trans_strip) == inst_ok) { mode = inst_mode_trans_strip; rmode = 1; } else { @@ -482,23 +552,24 @@ a1log *log /* verb, debug & error log */ rmode = 0; } } else if (displ) { +printf("~1 using displ mode\n"); /* We assume a display mode will always be spot by spot */ mode = inst_mode_emis_spot; rmode = 0; } else if (emis) { - if (pbypatch && IMODETST(cap, inst_mode_emis_spot) +printf("~1 using emis mode\n"); + if (pbypatch && it->check_mode(it, inst_mode_emis_spot) == inst_ok) { mode = inst_mode_emis_spot; rmode = 0; - } else if (IMODETST(cap, inst_mode_emis_strip) - && it->check_mode(it, inst_mode_emis_strip) == inst_ok) { + } 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 { + } else { /* Reflectance */ inst_stat_savdrd sv = inst_stat_savdrd_none; /* See if instrument has a saved mode, and if it has data that */ @@ -602,58 +673,50 @@ a1log *log /* verb, debug & error log */ } if (pbypatch - && IMODETST(cap, inst_mode_s_ref_spot) && 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 (IMODETST(cap, inst_mode_s_ref_chart) - && it->check_mode(it, inst_mode_s_ref_chart) == inst_ok + } 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 (IMODETST(cap, inst_mode_s_ref_xy) - && it->check_mode(it, inst_mode_s_ref_xy) == inst_ok + } 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 (IMODETST(cap, inst_mode_s_ref_strip) - && it->check_mode(it, inst_mode_s_ref_strip) == inst_ok + } 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 (IMODETST(cap, inst_mode_s_ref_spot) - && it->check_mode(it, inst_mode_s_ref_spot) == inst_ok + } 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 && IMODETST(cap, inst_mode_ref_spot) + } else if (pbypatch && it->check_mode(it, inst_mode_ref_spot) == inst_ok) { mode = inst_mode_ref_spot; rmode = 0; - } else if (IMODETST(cap, inst_mode_ref_chart) - && it->check_mode(it, inst_mode_ref_chart) == inst_ok) { + } else if (it->check_mode(it, inst_mode_ref_chart) == inst_ok) { mode = inst_mode_ref_chart; rmode = 3; - } else if (IMODETST(cap, inst_mode_ref_xy) - && it->check_mode(it, inst_mode_ref_xy) == inst_ok) { + } else if (it->check_mode(it, inst_mode_ref_xy) == inst_ok) { mode = inst_mode_ref_xy; rmode = 2; - } else if (IMODETST(cap, inst_mode_ref_strip) - && it->check_mode(it, inst_mode_ref_strip) == inst_ok) { + } else if (it->check_mode(it, inst_mode_ref_strip) == inst_ok) { mode = inst_mode_ref_strip; rmode = 1; @@ -1076,6 +1139,12 @@ a1log *log /* verb, debug & error log */ int pai; /* Current pass in current strip */ int oroi; /* Overall row index */ + if ( + itype != instDTP20 && + !rand && disbidi == 0) { + warning("Can't do bi-directional strip recognition 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) { if ((rv = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0)) @@ -1168,6 +1237,7 @@ a1log *log /* verb, debug & error log */ 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 || scols[oroi * stipa]->rr == 0 || oroi == s_oroi) break; } @@ -1261,8 +1331,8 @@ a1log *log /* verb, debug & error log */ /* Not all rows have been read */ empty_con_chars(); - printf("\nDone ? - At least one unread patch (%s), Are you sure [y/n]: ", - scols[i]->loc); + printf("\nDone ? - At least one unread patch (%s, %s), Are you sure [y/n]: ", + scols[i]->id, scols[i]->loc); fflush(stdout); ch = next_con_char(); printf("\n"); @@ -1333,7 +1403,8 @@ a1log *log /* verb, debug & error log */ return -1; } printf("\n"); - if (it->icom->port_type(it->icom) == icomt_serial) { + 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)) { @@ -1386,9 +1457,9 @@ a1log *log /* verb, debug & error log */ double werror = 0.0; /* Worst case error in best correlation strip */ double xbcorr = 1e6; /* Expected pass correlation value */ - int xboff; /* Expected pass offset */ + int xboff; /* Expected pass best offset */ int xbdir; /* Expected pass overall pass direction */ - double xwerror = 0.0; /* Expected pass worst error in best strip */ + double xwerror = 0.0; /* Expected pass worst patcch error */ if (rand && disbidi == 0 && (cap2 & inst2_bidi_scan)) dirrg = 2; /* Enable bi-directional strip recognition */ @@ -1426,17 +1497,29 @@ a1log *log /* verb, debug & error log */ } /* Compare just sample patches (not padding Max/Min) */ - for (pwerr = corr = 0.0, n = 0, i = 0; i < stipa; i++, n++) { +#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; - if (dir != 0) + 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) + if (vcorr > pwerr) /* Worsed patch error */ pwerr = vcorr; } corr /= (double)n; @@ -1444,16 +1527,16 @@ a1log *log /* verb, debug & error log */ printf(" Strip %d dir %d offset %d correlation = %f\n",choroi,dir,toff,corr); #endif - /* Expected strip correlation and */ - /* best fir to off by 1 and direction */ + /* If this is the expected strip, */ + /* note correlation and best fit to off by 1 and direction */ if (choroi == oroi && corr < xbcorr) { - xbcorr = corr; + xbcorr = corr; /* Expected strip correlation */ xboff = toff; xbdir = dir; xwerror = pwerr; /* Expected passes worst error */ } - /* Best matched strip correlation */ + /* Best overall matched strip correlation */ if (corr < bcorr) { boroi = choroi; bcorr = corr; @@ -1624,6 +1707,7 @@ a1log *log /* verb, debug & error log */ 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 */ @@ -1637,7 +1721,7 @@ a1log *log /* verb, debug & error log */ incflag = 3; /* Until we're done */ - for(;pix < npat;) { + for (;pix < npat;) { char buf[200], *bp = NULL, *ep = NULL; char ch = 0; @@ -1716,7 +1800,7 @@ a1log *log /* verb, debug & error log */ } if (xtern != 0) { /* User entered values */ - printf("\nReady to read patch '%s'%s\n",scols[pix]->loc, + 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)" : ""); @@ -1747,7 +1831,7 @@ a1log *log /* verb, debug & error log */ empty_con_chars(); - printf("\nReady to read patch '%s'%s\n",scols[pix]->loc, + 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)" : ""); @@ -1755,7 +1839,7 @@ a1log *log /* verb, debug & error log */ 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, <esc> to abort,\n"); + printf(" 'd' when done, 'k' to calibrate, <esc> to abort,\n"); if (uswitch) printf(" Instrument switch, <return> or <space> to read:"); @@ -1772,7 +1856,7 @@ a1log *log /* verb, debug & error log */ good_beep(); ch = '0'; - /* Deal with user trigger */ + /* Deal with user trigger via user interface callback function */ } else if ((rv & inst_mask) == inst_user_trig) { if (cap2 & inst2_no_feedback) good_beep(); @@ -1841,7 +1925,8 @@ a1log *log /* verb, debug & error log */ return -1; } printf("\n"); - if (it->icom->port_type(it->icom) == icomt_serial) { + 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)) { @@ -1891,6 +1976,15 @@ a1log *log /* verb, debug & error log */ } printf("\n"); continue; + } else if (ch == 'k') { + inst_code ev; + + ev = inst_handle_calibrate(it, inst_calt_available, inst_calc_none, NULL, NULL, 0); + if (ev != inst_ok) { /* Abort or fatal error */ + it->del(it); + return -1; + } + continue; } else if (ch == 'f') { incflag = 1; continue; @@ -1920,8 +2014,8 @@ a1log *log /* verb, debug & error log */ /* Not all patches have been read */ empty_con_chars(); - printf("\nDone ? - At least one unread patch (%s), Are you sure [y/n]: ", - scols[i]->loc); + printf("\nDone ? - At least one unread patch (%s, %s), Are you sure [y/n]: ", + scols[i]->id, scols[i]->loc); fflush(stdout); if ((ch = next_con_char()) == 0x1b) { printf("\n"); @@ -1996,6 +2090,10 @@ a1log *log /* verb, debug & error log */ } scols[pix]->rr = 1; /* Has been read */ printf(" Patch read OK\n"); + + if (doplot && val.sp.spec_n > 0) + xspect_plot_w(&val.sp, NULL, NULL, 0); + /* Advance to next patch. */ incflag = 1; } else { /* Unrecognised response */ @@ -2018,7 +2116,7 @@ usage() { 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(NULL)) != NULL) { + if ((icmps = new_icompaths(g_log)) != NULL) { icompath **paths; if ((paths = icmps->paths) != NULL) { int i; @@ -2046,6 +2144,7 @@ usage() { fprintf(stderr," p Polarising filter\n"); fprintf(stderr," 6 D65\n"); fprintf(stderr," u U.V. Cut\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," -H Use high resolution spectrum mode (if available)\n"); @@ -2055,12 +2154,19 @@ usage() { 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\n"); +#else /* !SALONEINSTLIB */ fprintf(stderr," 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2\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 U Test i1pro2 UV measurement mode\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) @@ -2092,11 +2198,14 @@ int main(int argc, char *argv[]) { 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 */ char ccxxname[MAXNAMEL+1] = "\000"; /* Colorimeter Correction/Colorimeter Calibration name */ icxObserverType obType = icxOT_default; /* ccss observer */ static char inname[MAXNAMEL+1] = { 0 }; /* Input cgats file base name */ @@ -2191,12 +2300,14 @@ int main(int argc, char *argv[]) { obType = icxOT_CIE_1931_2; } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */ obType = icxOT_CIE_1964_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 usage(); } @@ -2217,7 +2328,7 @@ int main(int argc, char *argv[]) { fa = nfa; if (na == NULL) usage(); if (na[0] == 'n' || na[0] == 'N') - fc = fc_none; + fc = fc_None; else if (na[0] == 'h' || na[0] == 'H') fc = fc_Hardware; else if (na[0] == 'x' || na[0] == 'X') @@ -2320,6 +2431,27 @@ int main(int argc, char *argv[]) { 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 + /* Extra flags */ } else if (argv[fa][1] == 'Y') { if (na == NULL) @@ -2392,7 +2524,7 @@ int main(int argc, char *argv[]) { if ((itype = inst_enum(icg->t[0].kdata[ti])) == instUnknown) error ("Unrecognised chart target instrument '%s'", icg->t[0].kdata[ti]); } else { - itype = instDTP41; /* Default chart target instrument */ + itype = instI1Pro; /* Default chart target instrument */ } } @@ -2508,10 +2640,6 @@ int main(int argc, char *argv[]) { tlen = atof(icg->t[0].kdata[ti]); } - if (itype != instDTP20 && !rand && disbidi == 0) { - warning("Can't do bi-directional strip recognition without randomize patch locations"); - } - if (verb) { printf("Steps in each Pass = %d\n",stipa); printf("Passes in each Strip = "); @@ -2537,6 +2665,8 @@ int main(int argc, char *argv[]) { 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) @@ -2684,29 +2814,40 @@ int main(int argc, char *argv[]) { cal = NULL; } - /* Set up the location sorted array of pointers */ - for (i = 0; i < npat; i++) { - scols[i] = &cols[i]; - if ((cols[i].loci = patch_location_order(paix, saix, ixord, cols[i].loc)) < 0) - error ("Bad location field value '%s' on patch %d", cols[i].loc, i); - } - for (; i < runpat; i++) { /* Extra on end */ - scols[i] = &cols[i]; - cols[i].loci = (totpa-1) * (256 - stipa) + i; + /* 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; - } + /* 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); + HEAPSORT(chcol *, scols, npat); + } + } /* If we're resuming a chartread, fill in all the patches that */ /* have been read. */ @@ -2840,17 +2981,20 @@ int main(int argc, char *argv[]) { } } - 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); + 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, dtype, fe, nocal, disbidi, highres, ccxxname, obType, + trans, emis, displ, dtype, fe, scalstd, &ucalstd, nocal, disbidi, highres, + ccxxname, obType, scan_tol, pbypatch, xtern, spectral, uvmode, accurate_expd, - emit_warnings, g_log) == 0) { + emit_warnings, doplot, g_log) == 0) { /* And save the result */ int nrpat; /* Number of read patches */ @@ -2861,6 +3005,10 @@ int main(int argc, char *argv[]) { /* 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); + /* Count patches actually read */ for (nrpat = i = 0; i < npat; i++) { if (cols[i].rr) { diff --git a/spectro/colorhug.c b/spectro/colorhug.c index 7397314..fefe766 100644 --- a/spectro/colorhug.c +++ b/spectro/colorhug.c @@ -1138,8 +1138,7 @@ int *cbid) { * error if it hasn't been initialised. */ static inst_code -colorhug_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +colorhug_get_set_opt(inst *pp, inst_opt_type m, ...) { colorhug *p = (colorhug *)pp; inst_code ev = inst_ok; @@ -1184,7 +1183,17 @@ colorhug_get_set_opt(inst *pp, inst_opt_type m, ...) return colorhug_set_LEDs(p, mask); } - return inst_unsupported; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/colorhug.h b/spectro/colorhug.h index 90fff6f..8617705 100644 --- a/spectro/colorhug.h +++ b/spectro/colorhug.h @@ -20,6 +20,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Note: update colorhug_interp_error() and colorhug_interp_code() in colorhug.c */ /* if anything of these #defines are added or subtracted */ @@ -94,6 +98,10 @@ struct _colorhug { /* Constructor */ extern colorhug *new_colorhug(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif + #define COLORHUG_H #endif /* COLORHUG_H */ diff --git a/spectro/conv.c b/spectro/conv.c index 94023db..284a30c 100644 --- a/spectro/conv.c +++ b/spectro/conv.c @@ -64,7 +64,7 @@ #include "conv.h" #include "icoms.h" -#ifdef __APPLE__ +#ifdef UNIX_APPLE //#include <stdbool.h> #include <sys/sysctl.h> #include <sys/param.h> @@ -81,7 +81,7 @@ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 # include <objc/objc-auto.h> #endif -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #undef DEBUG @@ -131,6 +131,8 @@ int next_con_char(void) { return c; } +#ifdef NEVER // Don't need this now. + /* Horrible hack to poll stdin when we're not interactive. */ /* This has the drawback that the char and returm must be */ /* written in one operation for the character to be recognised - */ @@ -143,9 +145,12 @@ static int th_read_char(void *pp) { DWORD bread; if ((stdinh = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) { + *rp = 0; /* We've started */ return 0; } + *rp = 0; /* We've started */ + if (ReadFile(stdinh, buf, 1, &bread, NULL) && bread == 1 && buf[0] != '\r' && buf[0] != '\n') { @@ -154,32 +159,65 @@ static int th_read_char(void *pp) { return 0; } +#endif /* NEVER */ /* If there is one, return the next character from the keyboard, else return 0 */ /* (If not_interactive, always returns 0) */ int poll_con_char(void) { if (not_interactive) { /* Can't assume that it's the console */ + +#ifdef NEVER // Use better approach below. athread *getch_thread = NULL; - char c = 0; + volatile char c = 0xff; /* This is pretty horrible. The problem is that we can't use */ /* any of MSWin's async file read functions, because we */ /* have no way of ensuring that the STD_INPUT_HANDLE has been */ /* opened with FILE_FLAG_OVERLAPPED. Used a thread instead... */ - /* ReOpenFile() would fix this, but it's not available in WinXP, only Visa+ :-( */ - if ((getch_thread = new_athread(th_read_char, &c)) != NULL) { + /* ReOpenFile() would in theory fix this, but it's not available in WinXP, only Visa+, */ + /* and aparently doesn't work on stdin anyway! :-( */ + if ((getch_thread = new_athread(th_read_char, (char *)&c)) != NULL) { HANDLE stdinh; if ((stdinh = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) { return 0; } - Sleep(100); /* We just hope 1 msec is enough for the thread to start */ - CancelIo(stdinh); + /* Wait for the thread to start */ + while (c == 0xff) { + Sleep(10); /* We just hope 1 msec is enough for the thread to start */ + } + Sleep(10); /* Give it time to read */ + CancelIo(stdinh); /* May not work since ReadFile() is on a different thread ? */ getch_thread->del(getch_thread); return c; } +#else /* ! NEVER */ + /* This approach is very flakey from the console, but seems */ + /* to work reliably when operated progromatically. */ + HANDLE stdinh; + char buf[10] = { 0 }, c; + DWORD bread; + + if ((stdinh = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) { + return 0; + } +// printf("Waiting\n"); + if (WaitForSingleObject(stdinh, 1) == WAIT_OBJECT_0) { +// printf("stdin signalled\n"); + +// FlushFileBuffers(stdinh); +// FlushConsoleInputBuffer(stdinh); + if (ReadFile(stdinh, buf, 1, &bread, NULL)) { + int i; +// fprintf(stderr,"Read %d chars 0x%x 0x%x 0x%x\n",bread,buf[0],buf[1], buf[2]); + if (buf[0] != '\r' && buf[0] != '\n') + return buf[0]; + return 0; + } + } +#endif /* !NEVER */ return 0; } @@ -192,10 +230,23 @@ int poll_con_char(void) { } /* Suck all characters from the keyboard */ -/* (If not_interactive, does nothing) */ +/* (If not_interactive, does nothing ?) */ void empty_con_chars(void) { if (not_interactive) { + HANDLE stdinh; + char buf[100] = { 0 }, c; + DWORD bread; + + if ((stdinh = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) + return; + for (;;) { + if (WaitForSingleObject(stdinh, 1) == WAIT_OBJECT_0) { + ReadFile(stdinh, buf, 100, &bread, NULL); + } else { + break; + } + } return; } @@ -211,51 +262,6 @@ void sleep(unsigned int secs) { Sleep(secs * 1000); } -/* Sleep for the given number of msec */ -void msec_sleep(unsigned int msec) { - Sleep(msec); -} - -/* Return the current time in msec since */ -/* the first invokation of msec_time() */ -/* (Is this based on timeGetTime() ? ) */ -unsigned int msec_time() { - unsigned int rv; - static unsigned int startup = 0; - - rv = GetTickCount(); - if (startup == 0) - startup = rv; - - return rv - startup; -} - -/* Return the current time in usec */ -/* since the first invokation of usec_time() */ -/* Return -1.0 if not available */ -double usec_time() { - double rv; - LARGE_INTEGER val; - static double scale = 0.0; - static LARGE_INTEGER startup; - - if (scale == 0.0) { - if (QueryPerformanceFrequency(&val) == 0) - return -1.0; - scale = 1000000.0/val.QuadPart; - QueryPerformanceCounter(&val); - startup.QuadPart = val.QuadPart; - - } else { - QueryPerformanceCounter(&val); - } - val.QuadPart -= startup.QuadPart; - - rv = val.QuadPart * scale; - - return rv; -} - static athread *beep_thread = NULL; static int beep_delay; static int beep_freq; @@ -552,126 +558,6 @@ void empty_con_chars(void) { tcflush(STDIN_FILENO, TCIFLUSH); } -/* Sleep for the given number of msec */ -/* (Note that OS X 10.9+ App Nap can wreck this, unless */ -/* it is turned off.) */ -void msec_sleep(unsigned int msec) { -#ifdef NEVER - if (msec > 1000) { - unsigned int secs; - secs = msec / 1000; - msec = msec % 1000; - sleep(secs); - } - usleep(msec * 1000); -#else - struct timespec ts; - - ts.tv_sec = msec / 1000; - ts.tv_nsec = (msec % 1000) * 1000000; - nanosleep(&ts, NULL); -#endif -} - - -#if defined(__APPLE__) && !defined(CLOCK_MONOTONIC) - -#include <mach/mach_time.h> - -unsigned int msec_time() { - mach_timebase_info_data_t timebase; - static uint64_t startup = 0; - uint64_t time; - double msec; - - time = mach_absolute_time(); - if (startup == 0) - startup = time; - - mach_timebase_info(&timebase); - time -= startup; - msec = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e6); - - return (unsigned int)floor(msec + 0.5); -} - -/* Return the current time in usec */ -/* since the first invokation of usec_time() */ -double usec_time() { - mach_timebase_info_data_t timebase; - static uint64_t startup = 0; - uint64_t time; - double usec; - - time = mach_absolute_time(); - if (startup == 0) - startup = time; - - mach_timebase_info(&timebase); - time -= startup; - usec = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e3); - - return usec; -} - -#else - -/* Return the current time in msec */ -/* since the first invokation of msec_time() */ -unsigned int msec_time() { - unsigned int rv; - static struct timespec startup = { 0, 0 }; - struct timespec cv; - - clock_gettime(CLOCK_MONOTONIC, &cv); - - /* Set time to 0 on first invocation */ - if (startup.tv_sec == 0 && startup.tv_nsec == 0) - startup = cv; - - /* Subtract, taking care of carry */ - cv.tv_sec -= startup.tv_sec; - if (startup.tv_nsec > cv.tv_nsec) { - cv.tv_sec--; - cv.tv_nsec += 1000000000; - } - cv.tv_nsec -= startup.tv_nsec; - - /* Convert nsec to msec */ - rv = cv.tv_sec * 1000 + cv.tv_nsec / 1000000; - - return rv; -} - -/* Return the current time in usec */ -/* since the first invokation of usec_time() */ -double usec_time() { - double rv; - static struct timespec startup = { 0, 0 }; - struct timespec cv; - - clock_gettime(CLOCK_MONOTONIC, &cv); - - /* Set time to 0 on first invocation */ - if (startup.tv_sec == 0 && startup.tv_nsec == 0) - startup = cv; - - /* Subtract, taking care of carry */ - cv.tv_sec -= startup.tv_sec; - if (startup.tv_nsec > cv.tv_nsec) { - cv.tv_sec--; - cv.tv_nsec += 1000000000; - } - cv.tv_nsec -= startup.tv_nsec; - - /* Convert to usec */ - rv = cv.tv_sec * 1000000.0 + cv.tv_nsec/1000; - - return rv; -} - -#endif - /* - - - - - - - - - - - - - - - - - - - - - - - - */ int acond_timedwait_imp(pthread_cond_t *cond, pthread_mutex_t *lock, int msec) { @@ -699,7 +585,7 @@ int acond_timedwait_imp(pthread_cond_t *cond, pthread_mutex_t *lock, int msec) { /* Set the current threads priority */ int set_interactive_priority() { -#ifdef __APPLE__ +#ifdef UNIX_APPLE #ifdef NEVER int rv = 0; struct task_category_policy tcatpolicy; @@ -725,7 +611,7 @@ int set_interactive_priority() { // a1logd(g_log, 8, "set_interactive_priority: set to important got %d\n",rv); return rv; #endif /* NEVER */ -#else /* !APPLE */ +#else /* !UNIX_APPLE */ int rv; struct sched_param param; param.sched_priority = 32; @@ -734,11 +620,11 @@ int set_interactive_priority() { rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); // a1logd(g_log, 8, "set_interactive_priority: set got %d\n",rv); return rv; -#endif /* !APPLE */ +#endif /* !UNIX_APPLE */ } int set_normal_priority() { -#ifdef __APPLE__ +#ifdef UNIX_APPLE #ifdef NEVER int rv = 0; struct task_category_policy tcatpolicy; @@ -763,7 +649,7 @@ int set_normal_priority() { // a1logd(g_log, 8, "set_normal_priority: set to standard got %d\n",rv); return rv; #endif /* NEVER */ -#else /* !APPLE */ +#else /* !UNIX_APPLE */ struct sched_param param; param.sched_priority = 0; int rv; @@ -771,7 +657,7 @@ int set_normal_priority() { rv = pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m); // a1logd(g_log, 8, "set_normal_priority: reset got %d\n",rv); return rv; -#endif /* !APPLE */ +#endif /* !UNIX_APPLE */ } #endif /* NEVER */ @@ -786,7 +672,7 @@ static int beep_msec; static int delayed_beep(void *pp) { msec_sleep(beep_delay); a1logd(g_log,8, "msec_beep activate\n"); -#ifdef __APPLE__ +#ifdef UNIX_APPLE # if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 AudioServicesPlayAlertSound(kUserPreferredAlert); # else @@ -812,7 +698,7 @@ void msec_beep(int delay, int freq, int msec) { a1logw(g_log, "msec_beep: Delayed beep failed to create thread\n"); } else { a1logd(g_log,8, "msec_beep activate\n"); -#ifdef __APPLE__ +#ifdef UNIX_APPLE # if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 AudioServicesPlayAlertSound(kUserPreferredAlert); # else @@ -980,7 +866,7 @@ int create_parent_directories(char *path) { #endif /* defined(UNIX) */ /* - - - - - - - - - - - - - - - - - - - - - - - - */ -#if defined(__APPLE__) || defined(NT) +#if defined(UNIX_APPLE) || defined(NT) /* Thread to monitor and kill the named processes */ static int th_kkill_nprocess(void *pp) { @@ -1109,7 +995,7 @@ int kill_nprocess(char **pname, a1log *log) { #endif /* NT */ -#if defined(__APPLE__) +#if defined(UNIX_APPLE) /* Kill a list of named process. */ /* Kill the first process found, then return */ @@ -1188,411 +1074,8 @@ int kill_nprocess(char **pname, a1log *log) { return rv; } -#endif /* __APPLE__ */ - -#endif /* __APPLE__ || NT */ - -/* ============================================================= */ - -/* A very small subset of icclib, copied to here. */ -/* This is just enough to support the standalone instruments */ -#ifdef SALONEINSTLIB - -sa_XYZNumber sa_D50 = { - 0.9642, 1.0000, 0.8249 -}; - -sa_XYZNumber sa_D65 = { - 0.9505, 1.0000, 1.0890 -}; - -void sa_SetUnity3x3(double mat[3][3]) { - int i, j; - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) { - if (i == j) - mat[j][i] = 1.0; - else - mat[j][i] = 0.0; - } - } - -} - -void sa_Cpy3x3(double dst[3][3], double src[3][3]) { - int i, j; - - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) - dst[j][i] = src[j][i]; - } -} - -void sa_MulBy3x3(double out[3], double mat[3][3], double in[3]) { - double tt[3]; - - tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1] + mat[0][2] * in[2]; - tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1] + mat[1][2] * in[2]; - tt[2] = mat[2][0] * in[0] + mat[2][1] * in[1] + mat[2][2] * in[2]; +#endif /* UNIX_APPLE */ - out[0] = tt[0]; - out[1] = tt[1]; - out[2] = tt[2]; -} - -void sa_Mul3x3_2(double dst[3][3], double src1[3][3], double src2[3][3]) { - int i, j, k; - double td[3][3]; /* Temporary dest */ - - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) { - double tt = 0.0; - for (k = 0; k < 3; k++) - tt += src1[j][k] * src2[k][i]; - td[j][i] = tt; - } - } - - /* Copy result out */ - for (j = 0; j < 3; j++) - for (i = 0; i < 3; i++) - dst[j][i] = td[j][i]; -} - - -/* Matrix Inversion by Richard Carling from "Graphics Gems", Academic Press, 1990 */ -#define det2x2(a, b, c, d) (a * d - b * c) - -static void adjoint( -double out[3][3], -double in[3][3] -) { - double a1, a2, a3, b1, b2, b3, c1, c2, c3; - - /* assign to individual variable names to aid */ - /* selecting correct values */ - - a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2]; - a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2]; - a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2]; - - /* row column labeling reversed since we transpose rows & columns */ - - out[0][0] = det2x2(b2, b3, c2, c3); - out[1][0] = - det2x2(a2, a3, c2, c3); - out[2][0] = det2x2(a2, a3, b2, b3); - - out[0][1] = - det2x2(b1, b3, c1, c3); - out[1][1] = det2x2(a1, a3, c1, c3); - out[2][1] = - det2x2(a1, a3, b1, b3); - - out[0][2] = det2x2(b1, b2, c1, c2); - out[1][2] = - det2x2(a1, a2, c1, c2); - out[2][2] = det2x2(a1, a2, b1, b2); -} - -static double sa_Det3x3(double in[3][3]) { - double a1, a2, a3, b1, b2, b3, c1, c2, c3; - double ans; - - a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2]; - a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2]; - a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2]; - - ans = a1 * det2x2(b2, b3, c2, c3) - - b1 * det2x2(a2, a3, c2, c3) - + c1 * det2x2(a2, a3, b2, b3); - return ans; -} - -#define SA__SMALL_NUMBER 1.e-8 - -int sa_Inverse3x3(double out[3][3], double in[3][3]) { - int i, j; - double det; - - /* calculate the 3x3 determinant - * if the determinant is zero, - * then the inverse matrix is not unique. - */ - det = sa_Det3x3(in); - - if ( fabs(det) < SA__SMALL_NUMBER) - return 1; - - /* calculate the adjoint matrix */ - adjoint(out, in); - - /* scale the adjoint matrix to get the inverse */ - for (i = 0; i < 3; i++) - for(j = 0; j < 3; j++) - out[i][j] /= det; - return 0; -} - -#undef SA__SMALL_NUMBER -#undef det2x2 - -/* - - - - - - - - - - - - - - - - - - - - - - - - */ -/* Transpose a 3x3 matrix */ -void sa_Transpose3x3(double out[3][3], double in[3][3]) { - int i, j; - if (out != in) { - for (i = 0; i < 3; i++) - for (j = 0; j < 3; j++) - out[i][j] = in[j][i]; - } else { - double tt[3][3]; - for (i = 0; i < 3; i++) - for (j = 0; j < 3; j++) - tt[i][j] = in[j][i]; - for (i = 0; i < 3; i++) - for (j = 0; j < 3; j++) - out[i][j] = tt[i][j]; - } -} - -/* Scale a 3 vector by the given ratio */ -void sa_Scale3(double out[3], double in[3], double rat) { - out[0] = in[0] * rat; - out[1] = in[1] * rat; - out[2] = in[2] * rat; -} - -/* Clamp a 3 vector to be +ve */ -void sa_Clamp3(double out[3], double in[3]) { - int i; - for (i = 0; i < 3; i++) - out[i] = in[i] < 0.0 ? 0.0 : in[i]; -} - -/* Return the normal Delta E given two Lab values */ -double sa_LabDE(double *Lab0, double *Lab1) { - double rv = 0.0, tt; - - tt = Lab0[0] - Lab1[0]; - rv += tt * tt; - tt = Lab0[1] - Lab1[1]; - rv += tt * tt; - tt = Lab0[2] - Lab1[2]; - rv += tt * tt; - - return sqrt(rv); -} - -/* CIE XYZ to perceptual CIE 1976 L*a*b* */ -void -sa_XYZ2Lab(icmXYZNumber *w, double *out, double *in) { - double X = in[0], Y = in[1], Z = in[2]; - double x,y,z,fx,fy,fz; - - x = X/w->X; - y = Y/w->Y; - z = Z/w->Z; - - if (x > 0.008856451586) - fx = pow(x,1.0/3.0); - else - fx = 7.787036979 * x + 16.0/116.0; - - if (y > 0.008856451586) - fy = pow(y,1.0/3.0); - else - fy = 7.787036979 * y + 16.0/116.0; - - if (z > 0.008856451586) - fz = pow(z,1.0/3.0); - else - fz = 7.787036979 * z + 16.0/116.0; - - out[0] = 116.0 * fy - 16.0; - out[1] = 500.0 * (fx - fy); - out[2] = 200.0 * (fy - fz); -} - -/* - - - - - - - - - - - - - - - - - - - - - - - - */ -/* A sub-set of ludecomp code from numlib */ - -int sa_lu_decomp(double **a, int n, int *pivx, double *rip) { - int i, j; - double *rscale, RSCALE[10]; - - if (n <= 10) - rscale = RSCALE; - else - rscale = dvector(0, n-1); - - for (i = 0; i < n; i++) { - double big; - for (big = 0.0, j=0; j < n; j++) { - double temp; - temp = fabs(a[i][j]); - if (temp > big) - big = temp; - } - if (fabs(big) <= DBL_MIN) { - if (rscale != RSCALE) - free_dvector(rscale, 0, n-1); - return 1; - } - rscale[i] = 1.0/big; - } - - for (*rip = 1.0, j = 0; j < n; j++) { - double big; - int k, bigi = 0; - - for (i = 0; i < j; i++) { - double sum; - sum = a[i][j]; - for (k = 0; k < i; k++) - sum -= a[i][k] * a[k][j]; - a[i][j] = sum; - } - - for (big = 0.0, i = j; i < n; i++) { - double sum, temp; - sum = a[i][j]; - for (k = 0; k < j; k++) - sum -= a[i][k] * a[k][j]; - a[i][j] = sum; - temp = rscale[i] * fabs(sum); - if (temp >= big) { - big = temp; - bigi = i; - } - } - - if (j != bigi) { - { - double *temp; - temp = a[bigi]; - a[bigi] = a[j]; - a[j] = temp; - } - *rip = -(*rip); - rscale[bigi] = rscale[j]; - } - - pivx[j] = bigi; - if (fabs(a[j][j]) <= DBL_MIN) { - if (rscale != RSCALE) - free_dvector(rscale, 0, n-1); - return 1; - } - - if (j != (n-1)) { - double temp; - temp = 1.0/a[j][j]; - for (i = j+1; i < n; i++) - a[i][j] *= temp; - } - } - if (rscale != RSCALE) - free_dvector(rscale, 0, n-1); - return 0; -} - -void sa_lu_backsub(double **a, int n, int *pivx, double *b) { - int i, j; - int nvi; - - for (nvi = -1, i = 0; i < n; i++) { - int px; - double sum; - - px = pivx[i]; - sum = b[px]; - b[px] = b[i]; - if (nvi >= 0) { - for (j = nvi; j < i; j++) - sum -= a[i][j] * b[j]; - } else { - if (sum != 0.0) - nvi = i; - } - b[i] = sum; - } - - for (i = (n-1); i >= 0; i--) { - double sum; - sum = b[i]; - for (j = i+1; j < n; j++) - sum -= a[i][j] * b[j]; - b[i] = sum/a[i][i]; - } -} - -int sa_lu_invert(double **a, int n) { - int i, j; - double rip; - int *pivx, PIVX[10]; - double **y; - - if (n <= 10) - pivx = PIVX; - else - pivx = ivector(0, n-1); - - if (sa_lu_decomp(a, n, pivx, &rip)) { - if (pivx != PIVX) - free_ivector(pivx, 0, n-1); - return 1; - } - - y = dmatrix(0, n-1, 0, n-1); - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - y[i][j] = a[i][j]; - } - } - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) - a[i][j] = 0.0; - a[i][i] = 1.0; - sa_lu_backsub(y, n, pivx, a[i]); - } - - free_dmatrix(y, 0, n-1, 0, n-1); - if (pivx != PIVX) - free_ivector(pivx, 0, n-1); - - return 0; -} - -int sa_lu_psinvert(double **out, double **in, int m, int n) { - int rv = 0; - double **tr; - double **sq; - - tr = dmatrix(0, n-1, 0, m-1); - matrix_trans(tr, in, m, n); - - if (m > n) { - sq = dmatrix(0, n-1, 0, n-1); - if ((rv = matrix_mult(sq, n, n, tr, n, m, in, m, n)) == 0) { - if ((rv = sa_lu_invert(sq, n)) == 0) { - rv = matrix_mult(out, n, m, sq, n, n, tr, n, m); - } - } - free_dmatrix(sq, 0, n-1, 0, n-1); - } else { - sq = dmatrix(0, m-1, 0, m-1); - if ((rv = matrix_mult(sq, m, m, in, m, n, tr, n, m)) == 0) { - if ((rv = sa_lu_invert(sq, m)) == 0) { - rv = matrix_mult(out, n, m, tr, n, m, sq, m, m); - } - } - free_dmatrix(sq, 0, m-1, 0, m-1); - } - - free_dmatrix(tr, 0, n-1, 0, m-1); - return rv; -} - - -#endif /* SALONEINSTLIB */ -/* ============================================================= */ +#endif /* UNIX_APPLE || NT */ diff --git a/spectro/conv.h b/spectro/conv.h index 4e14dd9..a6adcbf 100644 --- a/spectro/conv.h +++ b/spectro/conv.h @@ -32,7 +32,7 @@ # include <io.h> #endif -#if defined (UNIX) || defined(__APPLE__) +#if defined(UNIX) # include <unistd.h> # include <glob.h> # include <pthread.h> @@ -57,19 +57,8 @@ int poll_con_char(void); /* (If not_interactive, does nothing) */ void empty_con_chars(void); -/* Sleep for the given number of msec */ -void msec_sleep(unsigned int msec); - -/* Return the current time in msec since */ -/* the first invokation of msec_time() */ -unsigned int msec_time(); - -/* Return the current time in usec */ -/* the first invokation of usec_time() */ -double usec_time(); - /* Activate the system beeper after a delay */ -/* (Note frequancy and duration may not be honoured on all systems) */ +/* (Note frequency and duration may not be honoured on all systems) */ void msec_beep(int delay, int freq, int msec); void normal_beep(); /* Emit a "normal" beep */ @@ -149,7 +138,7 @@ struct _athread { #if defined (NT) HANDLE th; /* Thread */ #endif -#if defined (UNIX) || defined(__APPLE__) +#if defined(UNIX) pthread_t thid; /* Thread ID */ #endif int finished; /* Set when the thread returned */ @@ -197,7 +186,7 @@ struct _kkill_nproc_ctx { void (*del)(struct _kkill_nproc_ctx *p); }; typedef struct _kkill_nproc_ctx kkill_nproc_ctx; -#if defined(__APPLE__) || defined(NT) +#if defined(UNIX_APPLE) || defined(NT) /* Kill a list of named processes. NULL for last */ /* return < 0 if this fails. */ @@ -209,65 +198,10 @@ int kill_nprocess(char **pname, a1log *log); /* Call ctx->del() when done */ kkill_nproc_ctx *kkill_nprocess(char **pname, a1log *log); -#endif /* __APPLE__ || NT */ +#endif /* UNIX_APPLE || NT */ #include "xdg_bds.h" -/* - - - - - - - - - - - - - - - - - - -- */ -/* A very small subset of icclib */ -#ifdef SALONEINSTLIB - -typedef struct { - double X; - double Y; - double Z; -} sa_XYZNumber; - -typedef enum { - sa_SigXYZData = 0x58595A20L, /* 'XYZ ' */ - sa_SigLabData = 0x4C616220L /* 'Lab ' */ -} sa_ColorSpaceSignature; - -extern sa_XYZNumber sa_D50; -extern sa_XYZNumber sa_D65; -void sa_SetUnity3x3(double mat[3][3]); -void sa_Cpy3x3(double out[3][3], double mat[3][3]); -void sa_MulBy3x3(double out[3], double mat[3][3], double in[3]); -void sa_Mul3x3_2(double dst[3][3], double src1[3][3], double src2[3][3]); -int sa_Inverse3x3(double out[3][3], double in[3][3]); -void sa_Transpose3x3(double out[3][3], double in[3][3]); -void sa_Scale3(double out[3], double in[3], double rat); -double sa_LabDE(double *in0, double *in1); - - -#define icmXYZNumber sa_XYZNumber -#define icColorSpaceSignature sa_ColorSpaceSignature -#define icSigXYZData sa_SigXYZData -#define icSigLabData sa_SigLabData -#define icmD50 sa_D50 -#define icmD65 sa_D65 -#define icmSetUnity3x3 sa_SetUnity3x3 -#define icmCpy3x3 sa_Cpy3x3 -#define icmMulBy3x3 sa_MulBy3x3 -#define icmMul3x3_2 sa_Mul3x3_2 -#define icmInverse3x3 sa_Inverse3x3 -#define icmTranspose3x3 sa_Transpose3x3 -#define icmScale3 sa_Scale3 -#define icmClamp3 sa_Clamp3 -#define icmLabDE sa_LabDE -#define icmXYZ2Lab sa_XYZ2Lab - -/* A subset of numlib */ - -int sa_lu_psinvert(double **out, double **in, int m, int n); - -#define lu_psinvert sa_lu_psinvert - -#endif /* SALONEINSTLIB */ - -/* - - - - - - - - - - - - - - - - - - -- */ - - #ifdef __cplusplus } #endif diff --git a/spectro/cubecal.h b/spectro/cubecal.h index 60f7380..93f1ada 100644 --- a/spectro/cubecal.h +++ b/spectro/cubecal.h @@ -1,6 +1,7 @@ /* * Calibration Table for SwatchMate Cube + * This is #included in smcube.c * * Copyright 2015 Graeme W. Gill * All rights reserved diff --git a/spectro/dev.h b/spectro/dev.h new file mode 100644 index 0000000..0b62de3 --- /dev/null +++ b/spectro/dev.h @@ -0,0 +1,41 @@ + +#ifndef DEV_H + +/* + * Abstract base class for all devices handled here. + */ + +/* + * Argyll Color Correction System + * + * Author: Graeme W. Gill + * Date: 17/8/2016 + * + * Copyright 2016 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- + * see the License2.txt file for licencing details. + * + */ + +#include "icoms.h" /* libinst Includes this functionality */ +#include "conv.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Device base object. */ +#define DEV_OBJ_BASE \ + a1log *log; /* Pointer to debug & error logging class */ \ + icoms *icom; /* Device coms object */ \ + instType itype; /* Device type determined by driver */ \ + +/* The base object type */ +struct _dev { + DEV_OBJ_BASE + }; typedef struct _dev dev; + +#define DEV_H +#endif /* DEV_H */ diff --git a/spectro/dispcal.c b/spectro/dispcal.c index 97337cd..65e2f10 100644 --- a/spectro/dispcal.c +++ b/spectro/dispcal.c @@ -62,8 +62,13 @@ Change white point gamut clipping to be a measurement search rather than computing from primary XYZ ? - Add bell at end of calibration ? + Handling of white and black device clipping is not so good. + White clipping isn't characterized very well due to sparse sampling, + and moncurve tends to smooth over the clip inflection point, + making it innacurate. This particularly hurts the black point + accuracy, leading to raised or crushed blacks. + Add bell at end of calibration ? Add option to plot graph of native and calibrated RGB ? @@ -1012,7 +1017,7 @@ static void init_csamp(csamp *p, calx *x, int doupdate, int verify, int psrand, p->_no = p->no = no; - if ((p->s = (csp *)malloc(p->_no * sizeof(csp))) == NULL) + if ((p->s = (csp *)calloc(p->_no, sizeof(csp))) == NULL) error("csamp malloc failed"); /* Compute v and txyz */ @@ -1715,7 +1720,7 @@ int main(int argc, char *argv[]) { set_exe_path(argv[0]); /* Set global exe_path and error_program */ check_if_not_interactive(); -#if defined(__APPLE__) +#if defined(UNIX_APPLE) { SInt32 MacMajVers, MacMinVers, MacBFVers; @@ -1939,7 +1944,7 @@ int main(int argc, char *argv[]) { fa = nfa; if (na == NULL) usage(0,"Paramater expected following -W"); if (na[0] == 'n' || na[0] == 'N') - fc = fc_none; + fc = fc_None; else if (na[0] == 'h' || na[0] == 'H') fc = fc_Hardware; else if (na[0] == 'x' || na[0] == 'X') @@ -2572,7 +2577,7 @@ int main(int argc, char *argv[]) { #ifdef MEAS_RES if (doreport == 1) { if (sigbits == 0) { - warning("Unable to determine Video LUT entry bit depth"); + warning("Unable to determine effective Video LUT entry bit depth"); } else { printf("Effective Video LUT entry depth seems to be %d bits\n",sigbits); } @@ -3701,8 +3706,9 @@ int main(int argc, char *argv[]) { else printf(" Current Brightness = %.2f\n", tcols[2].XYZ[1]); - printf(" Target 50%% Level = %.3f, Current = %.3f, error = % .1f%%\n", + printf(" Target 50%% Level = %.3f, Current = %.3f (Aprox. Gamma %.2f), error = % .1f%%\n", tarh, tcols[1].XYZ[1], + mgamma, 100.0 * (tcols[1].XYZ[1] - tarh)/tarw); printf(" Target Near Black = %.4f, Current = %.4f, error = % .1f%%\n", @@ -4354,7 +4360,7 @@ int main(int argc, char *argv[]) { 0.2, /* Background relative to reference white */ 80.0, /* Display is 80 cd/m^2 */ 0.0, 0.01, x.nwh, /* 0% flare and 1% glare same white point */ - 0); + 0, 1.0); break; case gt_Rec709: @@ -4365,7 +4371,7 @@ int main(int argc, char *argv[]) { 0.2, /* Background relative to reference white */ 1000.0/3.1415, /* Luminance of white in the Image field (cd/m^2) */ 0.0, 0.01, x.nwh, /* 0% flare and 1% glare same white point */ - 0); + 0, 1.0); break; default: @@ -4378,7 +4384,7 @@ int main(int argc, char *argv[]) { 0.2, /* Background relative to reference white */ x.twh[1], /* Target white level (cd/m^2) */ 0.0, 0.01, x.nwh, /* 0% flare and 1% glare same white point */ - 0); + 0, 1.0); /* Compute the normalisation values */ x.svc->XYZ_to_cam(x.svc, Jab, x.nwh); /* Relative white point */ @@ -4532,7 +4538,10 @@ int main(int argc, char *argv[]) { } #endif - dr->reset_targ_w(dr); /* Reset white drift target at start of main cal. */ + /* If native white and white drift compensation enabled, */ + /* reset white drift target at start of main cal. */ + if (x.nat && asgrey.s[0].v == 1.0 && wdrift) + dr->reset_targ_w(dr); /* Now we go into the main verify & refine loop */ for (it = (verify == 2) ? mxits : 0; @@ -5485,6 +5494,29 @@ int main(int argc, char *argv[]) { if ((wr_icco = new_icc()) == NULL) error("Write: Creation of ICC object failed"); + /* Set the header: */ + { + icmHeader *wh = wr_icco->header; + + /* Values that must be set before writing */ + wh->deviceClass = icSigDisplayClass; + wh->colorSpace = icSigRgbData; /* Display is RGB */ + wh->pcs = icSigXYZData; /* XYZ for matrix based profile */ + wh->renderingIntent = icRelativeColorimetric; /* For want of something */ + + wh->manufacturer = icmSigUnknownType; + wh->model = icmSigUnknownType; +#ifdef NT + wh->platform = icSigMicrosoft; +#endif +#ifdef UNIX_APPLE + wh->platform = icSigMacintosh; +#endif +#if defined(UNIX_X11) + wh->platform = icmSig_nix; +#endif + } + /* Lookup white and black points */ { int j; @@ -5561,9 +5593,9 @@ int main(int argc, char *argv[]) { printf("RGB 1 through matrix = XYZ %f %f %f, Lab %f %f %f\n", xyz[0], xyz[1], xyz[2], lab[0], lab[1], lab[2]); } #endif - /* Adapt matrix */ + /* Chromatic Adaptation matrix */ icmAry2XYZ(swp, wp); - wr_icco->chromAdaptMatrix(wr_icco, ICM_CAM_MULMATRIX, icmD50, swp, mat); + wr_icco->chromAdaptMatrix(wr_icco, ICM_CAM_MULMATRIX, NULL, mat, icmD50, swp); #ifdef NEVER { double rgb[3], xyz[3], lab[3]; @@ -5603,9 +5635,9 @@ int main(int argc, char *argv[]) { printf("RGB cal through matrix = XYZ %f %f %f, Lab %f %f %f\n", xyz[0], xyz[1], xyz[2], lab[0], lab[1], lab[2]); } #endif - /* Adapt matrix */ + /* Chromatic Adaptation matrix */ icmAry2XYZ(swp, wp); - wr_icco->chromAdaptMatrix(wr_icco, ICM_CAM_MULMATRIX, icmD50, swp, mat); + wr_icco->chromAdaptMatrix(wr_icco, ICM_CAM_MULMATRIX, NULL, mat, icmD50, swp); #ifdef NEVER { double rgb[3], xyz[3], lab[3]; @@ -5619,30 +5651,7 @@ int main(int argc, char *argv[]) { } } - /* Add all the tags required */ - - /* The header: */ - { - icmHeader *wh = wr_icco->header; - - /* Values that must be set before writing */ - wh->deviceClass = icSigDisplayClass; - wh->colorSpace = icSigRgbData; /* Display is RGB */ - wh->pcs = icSigXYZData; /* XYZ for matrix based profile */ - wh->renderingIntent = icRelativeColorimetric; /* For want of something */ - - wh->manufacturer = icmSigUnknownType; - wh->model = icmSigUnknownType; -#ifdef NT - wh->platform = icSigMicrosoft; -#endif -#ifdef __APPLE__ - wh->platform = icSigMacintosh; -#endif -#if defined(UNIX_X11) - wh->platform = icmSig_nix; -#endif - } + /* Add all the other tags required */ /* Profile Description Tag: */ { @@ -5715,6 +5724,7 @@ int main(int argc, char *argv[]) { wr_icco, icSigLuminanceTag, icSigXYZArrayType)) == NULL) error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err); + /* (Only Y is used according to the ICC spec.) */ wo->size = 1; wo->allocate((icmBase *)wo); /* Allocate space */ wo->data[0].X = 0.0; diff --git a/spectro/dispread.c b/spectro/dispread.c index 8a78fcc..4c96b76 100644 --- a/spectro/dispread.c +++ b/spectro/dispread.c @@ -271,7 +271,7 @@ int main(int argc, char *argv[]) { /* 0X = use current color management cLut (MadVR) */ /* 1X = disable color management cLUT (MadVR) */ double cal[3][MAX_CAL_ENT]; /* Display calibration */ - int ncal = 256; /* number of cal entries used */ + int ncal = 256; /* Default number of cal entries used */ cgats *icg; /* input cgats structure */ cgats *ocg; /* output cgats structure */ time_t clk = time(0); @@ -572,7 +572,7 @@ int main(int argc, char *argv[]) { fa = nfa; if (na == NULL) usage(0,"Parameter expected after -W"); if (na[0] == 'n' || na[0] == 'N') - fc = fc_none; + fc = fc_None; else if (na[0] == 'h' || na[0] == 'H') fc = fc_Hardware; else if (na[0] == 'x' || na[0] == 'X') @@ -855,8 +855,9 @@ int main(int argc, char *argv[]) { if ((ncal = ccg->t[0].nsets) <= 0) error ("No data in set of file '%s'",calname); - if (ncal != 256) - error ("Expect 256 data sets in file '%s'",calname); + if (ncal < 2 || ncal > MAX_CAL_ENT) + error("Data set size %d is out of range for '%s'",ncal,calname); + if (ncal > MAX_CAL_ENT) error ("Cant handle %d data sets in file '%s', max is %d",ncal,calname,MAX_CAL_ENT); diff --git a/spectro/dispsup.c b/spectro/dispsup.c index 57a598e..281912d 100644 --- a/spectro/dispsup.c +++ b/spectro/dispsup.c @@ -281,18 +281,28 @@ a1log *log /* Verb, debug & error log */ itype = p->get_itype(p); /* Actual type */ p->capabilities(p, &cap, &cap2, &cap3); - if (tele && !IMODETST(cap, inst_mode_emis_tele)) { + if (tele && p->check_mode(p, inst_mode_emis_tele) != inst_ok) { printf("Want telephoto measurement capability but instrument doesn't support it\n"); printf("so falling back to emissive spot mode.\n"); tele = 0; } - if (!tele && !IMODETST(cap, inst_mode_emis_spot)) { + if (!tele && p->check_mode(p, inst_mode_emis_spot) != inst_ok) { printf("Want emissive spot measurement capability but instrument doesn't support it\n"); printf("so switching to telephoto spot mode.\n"); tele = 1; } + if (( tele && p->check_mode(p, inst_mode_emis_tele) != inst_ok) + || (!tele && p->check_mode(p, inst_mode_emis_spot) != inst_ok)) { + printf("Need %s emissive measurement capability,\n", tele ? "telephoto" : "spot"); + printf("but instrument doesn't support it\n"); + a1logd(p->log,1,"Need %s emissive measurement capability but device doesn't support it,\n", + tele ? "telephoto" : "spot"); + p->del(p); + return -1; + } + /* Set to emission mode to read a display */ if (tele) mode = inst_mode_emis_tele; @@ -434,6 +444,7 @@ static int disprd_read_imp( ipatch val; /* Return value */ int ch; /* Character */ int cal_type; + inst_calc_id_type idtype; char id[CALIDLEN]; inst2_capability cap2; @@ -521,7 +532,7 @@ static int disprd_read_imp( return 3; } /* Do calibrate, but ignore return code. Press on regardless. */ - if ((rv = p->it->calibrate(p->it, &calt, &calc, id)) != inst_ok) { + if ((rv = p->it->calibrate(p->it, &calt, &calc, &idtype, id)) != inst_ok) { a1logd(p->log,1,"warning, frequency calibrate failed with '%s' (%s)\n", p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv)); } @@ -538,7 +549,7 @@ static int disprd_read_imp( return 3; } /* Do calibrate, but ignore return code. Press on regardless. */ - if ((rv = p->it->calibrate(p->it, &calt, &calc, id)) != inst_ok) { + if ((rv = p->it->calibrate(p->it, &calt, &calc, &idtype, id)) != inst_ok) { a1logd(p->log,1,"warning, display integration calibrate failed with '%s' (%s)\n", p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv)); } @@ -681,7 +692,8 @@ static int disprd_read_imp( return 1; } printf("\n"); - if (p->it->icom->port_type(p->it->icom) == icomt_serial) { + if ((p->it->icom->port_type(p->it->icom) & icomt_serial) + && !(p->it->icom->port_attr(p->it->icom) & icomt_fastserial)) { /* Allow retrying at a lower baud rate */ int tt = p->it->last_scomerr(p->it); if (tt & (ICOM_BRK | ICOM_FER | ICOM_PER | ICOM_OER)) { @@ -1198,7 +1210,7 @@ int disprd_ambient( p->it->capabilities(p->it, &cap, &cap2, &cap3); } - if (!IMODETST(cap, inst_mode_emis_ambient)) { + if (p->it->check_mode(p->it, inst_mode_emis_ambient) != inst_ok) { printf("Need ambient measurement capability,\n"); printf("but instrument doesn't support it\n"); return 8; @@ -1297,7 +1309,7 @@ int disprd_ambient( setup_display_calibrate, &dwi, 0); setup_display_calibrate(p->it,inst_calc_none, &dwi); if (rv != inst_ok) { /* Abort or fatal error */ - return 1; + return ((rv & inst_mask) == inst_user_abort) ? 1 : 2; } continue; @@ -1336,7 +1348,8 @@ int disprd_ambient( return 1; } printf("\n"); - if (p->it->icom->port_type(p->it->icom) == icomt_serial) { + if ((p->it->icom->port_type(p->it->icom) & icomt_serial) + && !(p->it->icom->port_attr(p->it->icom) & icomt_fastserial)) { /* Allow retrying at a lower baud rate */ int tt = p->it->last_scomerr(p->it); if (tt & (ICOM_BRK | ICOM_FER | ICOM_PER | ICOM_OER)) { @@ -2146,21 +2159,21 @@ static int config_inst_displ(disprd *p) { p->it->capabilities(p->it, &cap, &cap2, &cap3); - if (p->tele && !IMODETST(cap, inst_mode_emis_tele)) { + if (p->tele && p->it->check_mode(p->it, inst_mode_emis_tele) != inst_ok) { printf("Want telephoto measurement capability but instrument doesn't support it\n"); printf("so falling back to spot mode.\n"); a1logd(p->log,1,"No telephoto mode so falling back to spot mode.\n"); p->tele = 0; } - if (!p->tele && !IMODETST(cap, inst_mode_emis_spot)) { + if (!p->tele && p->it->check_mode(p->it, inst_mode_emis_spot) != inst_ok) { printf("Want emissive spot measurement capability but instrument doesn't support it\n"); printf("so switching to telephoto spot mode.\n"); p->tele = 1; } - if (( p->tele && !IMODETST(cap, inst_mode_emis_tele)) - || (!p->tele && !IMODETST(cap, inst_mode_emis_spot))) { + if (( p->tele && p->it->check_mode(p->it, inst_mode_emis_tele) != inst_ok) + || (!p->tele && p->it->check_mode(p->it, inst_mode_emis_spot) != inst_ok)) { printf("Need %s emissive measurement capability,\n", p->tele ? "telephoto" : "spot"); printf("but instrument doesn't support it\n"); a1logd(p->log,1,"Need %s emissive measurement capability but device doesn't support it,\n", @@ -2685,7 +2698,7 @@ a1log *log /* Verb, debug & error log */ printf("Calibrate failed with '%s' (%s)\n", p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv)); p->del(p); - if (errc != NULL) *errc = 2; + if (errc != NULL) *errc = ((rv & inst_mask) == inst_user_abort) ? 1 : 2; return NULL; } } diff --git a/spectro/dispsup.h b/spectro/dispsup.h index bca3070..07a66e2 100644 --- a/spectro/dispsup.h +++ b/spectro/dispsup.h @@ -91,9 +91,6 @@ typedef struct { } col; -/* Maximum number of entries to setup for calibration */ -#define MAX_CAL_ENT 4096 - /* Display reading context */ struct _disprd { diff --git a/spectro/disptechs.c b/spectro/disptechs.c index 70e150f..bade777 100644 --- a/spectro/disptechs.c +++ b/spectro/disptechs.c @@ -22,14 +22,15 @@ #include <ctype.h> #include <string.h> #include <time.h> +#include "numsup.h" #ifndef SALONEINSTLIB #include "copyright.h" #include "aconfig.h" #include "icc.h" #else #include "sa_config.h" +#include "sa_conv.h" #endif /* !SALONEINSTLIB */ -#include "numsup.h" #include "conv.h" #include "disptechs.h" diff --git a/spectro/dispwin.c b/spectro/dispwin.c index fffbaee..368f707 100644 --- a/spectro/dispwin.c +++ b/spectro/dispwin.c @@ -31,6 +31,10 @@ * Should add dithering support to overcome 8 bit limitations of * non-RAMDAC access or limited RAMDAC depth. (How do we easily * determine the latter ??) + * + * For X11/XRANDR, should we check for and save/restore CscMatrix property ?? + * - or should we assumed that the use intends to use this to manually calibrate the display ?? + * See <http://us.download.nvidia.com/XFree86/Linux-x86/364.12/README/xrandrextension.html#CscMatrix> */ #include <stdio.h> @@ -69,7 +73,7 @@ # endif #endif -#ifdef __APPLE__ +#ifdef UNIX_APPLE /* Note that the new ColorSync API is defined in @@ -113,14 +117,14 @@ typedef float CGFloat; #endif #endif /* !NSINTEGER_DEFINED */ -#include <IOKit/Graphics/IOGraphicsLib.h> +#include <IOKit/graphics/IOGraphicsLib.h> #if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1060 /* This wasn't declared in 10.6, although it is needed */ CFUUIDRef CGDisplayCreateUUIDFromDisplayID (uint32_t displayID); #endif /* < 10.6 */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #define VERIFY_TOL (1.0/255.0) #undef DISABLE_RANDR /* Disable XRandR code */ @@ -388,7 +392,7 @@ disppath **get_displays() { #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE /* Note :- some recent releases of OS X have a feature which */ /* automatically adjusts the screen brigtness with ambient level. */ /* We may have to find a way of disabling this during calibration and profiling. */ @@ -548,7 +552,7 @@ disppath **get_displays() { } free(dids); -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) int i, j, k; @@ -636,8 +640,8 @@ disppath **get_displays() { /* Look at all the screens outputs */ for (jj = j = 0; j < scrnres->noutput; j++) { - XRROutputInfo *outi; - XRRCrtcInfo *crtci; + XRROutputInfo *outi = NULL; + XRRCrtcInfo *crtci = NULL; if ((outi = XRRGetOutputInfo(mydisplay, scrnres, scrnres->outputs[j])) == NULL) { debugrr("XRRGetOutputInfo failed\n"); @@ -649,12 +653,13 @@ disppath **get_displays() { if (outi->connection == RR_Disconnected || outi->crtc == None) { + XRRFreeOutputInfo(outi); continue; } /* Check that the VideoLUT's are accessible */ { - XRRCrtcGamma *crtcgam; + XRRCrtcGamma *crtcgam = NULL; debugrr("Checking XRandR 1.2 VideoLUT access\n"); if ((crtcgam = XRRGetCrtcGamma(mydisplay, outi->crtc)) == NULL @@ -666,8 +671,11 @@ disppath **get_displays() { disps = NULL; j = scrnres->noutput; i = dcount; + XRRFreeOutputInfo(outi); continue; /* Abort XRandR 1.2 */ } + if (crtcgam != NULL) + XRRFreeGamma(crtcgam); } #ifdef NEVER { @@ -692,6 +700,7 @@ disppath **get_displays() { if ((disps = (disppath **)calloc(sizeof(disppath *), 1 + 1)) == NULL) { debugrr("get_displays failed on malloc\n"); XRRFreeCrtcInfo(crtci); + XRRFreeOutputInfo(outi); XRRFreeScreenResources(scrnres); XCloseDisplay(mydisplay); return NULL; @@ -701,6 +710,7 @@ disppath **get_displays() { sizeof(disppath *) * (ndisps + 2))) == NULL) { debugrr("get_displays failed on malloc\n"); XRRFreeCrtcInfo(crtci); + XRRFreeOutputInfo(outi); XRRFreeScreenResources(scrnres); XCloseDisplay(mydisplay); return NULL; @@ -711,6 +721,7 @@ disppath **get_displays() { if ((disps[ndisps] = calloc(sizeof(disppath),1)) == NULL) { debugrr("get_displays failed on malloc\n"); XRRFreeCrtcInfo(crtci); + XRRFreeOutputInfo(outi); XRRFreeScreenResources(scrnres); XCloseDisplay(mydisplay); free_disppaths(disps); @@ -741,6 +752,7 @@ disppath **get_displays() { if ((disps[ndisps]->description = strdup(desc2)) == NULL) { debugrr("get_displays failed on malloc\n"); XRRFreeCrtcInfo(crtci); + XRRFreeOutputInfo(outi); XRRFreeScreenResources(scrnres); XCloseDisplay(mydisplay); free_disppaths(disps); @@ -756,6 +768,7 @@ disppath **get_displays() { if ((disps[ndisps]->name = strdup(dnbuf)) == NULL) { debugrr("get_displays failed on malloc\n"); XRRFreeCrtcInfo(crtci); + XRRFreeOutputInfo(outi); XRRFreeScreenResources(scrnres); XCloseDisplay(mydisplay); free_disppaths(disps); @@ -809,6 +822,7 @@ disppath **get_displays() { if ((disps[ndisps]->edid = malloc(sizeof(unsigned char) * ret_len)) == NULL) { debugrr("get_displays failed on malloc\n"); XRRFreeCrtcInfo(crtci); + XRRFreeOutputInfo(outi); XRRFreeScreenResources(scrnres); XCloseDisplay(mydisplay); free_disppaths(disps); @@ -833,7 +847,6 @@ disppath **get_displays() { } XRRFreeOutputInfo(outi); } - XRRFreeScreenResources(scrnres); } XSetErrorHandler(NULL); @@ -1136,15 +1149,16 @@ void free_a_disppath(disppath *path) { /* ----------------------------------------------- */ -/* For VideoLUT/RAMDAC use, we assume that the number of entries in the RAMDAC */ -/* meshes perfectly with the display raster depth, so that we can */ -/* figure out how to apportion device values. We fail if they don't */ -/* seem to mesh. */ +/* For VideoLUT/RAMDAC use, we assume that the frame buffer */ +/* may map through some intermediate hardware or lookup */ +/* into a RAMDAC index. */ /* !!! Would be nice to add an error message return to dispwin and */ /* !!! pass errors back to it so that the detail can be reported */ /* !!! to the user. */ +static void dispwin_dump_ramdac(FILE *fp, ramdac *r); + /* Get RAMDAC values. ->del() when finished. */ /* Return NULL if not possible */ static ramdac *dispwin_get_ramdac(dispwin *p) { @@ -1168,9 +1182,11 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { debugr("dispwin_get_ramdac failed on malloc()\n"); return NULL; } - r->pdepth = p->pdepth; - r->nent = (1 << p->pdepth); - r->clone = dispwin_clone_ramdac; + r->fdepth = p->fdepth; + r->rdepth = p->rdepth; + r->ndepth = p->ndepth; + r->nent = p->nent; + r->clone = dispwin_clone_ramdac; r->setlin = dispwin_setlin_ramdac; r->del = dispwin_del_ramdac; @@ -1186,9 +1202,9 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } /* GetDeviceGammaRamp() is hard coded for 3 x 256 entries (Quantize) */ - if (r->nent != 256) { + if (256 != r->nent) { free(r); - debugr2((errout,"GetDeviceGammaRamp() is hard coded for nent == 256, and we've got nent = %d!\n",r->nent)); + debugr2((errout,"GetDeviceGammaRamp number of entries %d inconsistent with expected value %d\n",256,p->nent)); return NULL; } @@ -1204,7 +1220,7 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE unsigned int nent; CGGammaValue vals[3][16385]; @@ -1220,8 +1236,8 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { return NULL; } - if (nent != (1 << p->pdepth)) { - debugr2((errout,"CGGetDisplayTransferByTable number of entries %d mismatches screen depth %d\n",nent,p->pdepth)); + if (nent != p->nent) { + debugr2((errout,"CGGetDisplayTransferByTable number of entries %u inconsistent with previous value %d\n",nent,p->nent)); return NULL; } @@ -1231,8 +1247,10 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { return NULL; } - r->pdepth = p->pdepth; - r->nent = (1 << p->pdepth); + r->fdepth = p->fdepth; + r->rdepth = p->rdepth; + r->ndepth = p->ndepth; + r->nent = p->nent; r->clone = dispwin_clone_ramdac; r->setlin = dispwin_setlin_ramdac; r->del = dispwin_del_ramdac; @@ -1252,7 +1270,7 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { r->v[j][i] = vals[j][i]; } } -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) unsigned short vals[3][16384]; @@ -1275,13 +1293,8 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { nent = crtcgam->size; - if (nent > 16384) { - debugr("XRRGetCrtcGammaSize has more entries than we can handle\n"); - return NULL; - } - - if (nent != (1 << p->pdepth)) { - debugr2((errout,"XRRGetCrtcGammaSize number of entries %d mismatches screen depth %d bits\n",nent,(1 << p->pdepth))); + if (nent != p->nent) { + debugr2((errout,"XRRGetCrtcGammaSize number of entries %d inconsistent with previous value\n",nent,p->nent)); return NULL; } @@ -1331,8 +1344,8 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { return NULL; } - if (nent > 16384) { - debugr("XF86VidModeGetGammaRampSize has more entries than we can handle\n"); + if (nent != p->nent) { + debugr2((errout,"XF86VidModeGetGammaRampSize number of entries %d inconsistent with previous value\n",nent,p->nent)); return NULL; } @@ -1340,11 +1353,6 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { debugr("XF86VidModeGetGammaRamp failed\n"); return NULL; } - - if (nent != (1 << p->pdepth)) { - debugr2((errout,"CGGetDisplayTransferByTable number of entries %d mismatches screen depth %d bits\n",nent,(1 << p->pdepth))); - return NULL; - } } /* Allocate a ramdac */ @@ -1353,9 +1361,11 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { return NULL; } - r->pdepth = p->pdepth; - r->nent = (1 << p->pdepth); - r->clone = dispwin_clone_ramdac; + r->fdepth = p->fdepth; + r->rdepth = p->rdepth; + r->ndepth = p->ndepth; + r->nent = p->nent; + r->clone = dispwin_clone_ramdac; r->setlin = dispwin_setlin_ramdac; r->del = dispwin_del_ramdac; for (j = 0; j < 3; j++) { @@ -1379,7 +1389,7 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { return r; } -#ifdef __APPLE__ +#ifdef UNIX_APPLE /* Various support functions */ #if __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 @@ -1643,7 +1653,7 @@ static void *cur_colorsync_ref(dispwin *p) { return cspr; } -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ /* Set the RAMDAC values. */ /* Return nz if not possible */ @@ -1666,6 +1676,7 @@ static int dispwin_set_ramdac(dispwin *p, ramdac *r, int persist) { for (j = 0; j < 3; j++) { for (i = 0; i < r->nent; i++) { double vv = r->v[j][i]; + if (vv < 0.0) vv = 0.0; else if (vv > 1.0) @@ -1676,12 +1687,15 @@ static int dispwin_set_ramdac(dispwin *p, ramdac *r, int persist) { if (SetDeviceGammaRamp(p->hdc, vals) == 0) { debugr2((errout,"dispwin_set_ramdac failed on SetDeviceGammaRamp() with error %d\n",GetLastError())); +#ifdef NEVER + dispwin_dump_ramdac(stderr, r); +#endif return 1; } GdiFlush(); #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE { /* Transient first */ CGGammaValue vals[3][16384]; @@ -2115,7 +2129,7 @@ static int dispwin_set_ramdac(dispwin *p, ramdac *r, int persist) { } #endif /* < 10.6 */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) unsigned short vals[3][16384]; @@ -2214,6 +2228,24 @@ ramdac *dispwin_clone_ramdac(ramdac *r) { return nr; } +/* Debug dump ramdac */ +static void dispwin_dump_ramdac(FILE *fp, ramdac *r) { + int i, j; + + fprintf(fp,"Ramdac fdepth %d, rdepth %d, ndepth %d, nent %d\n", + r->fdepth, r->rdepth, r->ndepth, r->nent); + + for (i = 0; i < r->nent; i++) { + int note = 0; + for (j = 0; j < 3; j++) { + if (r->v[j][i] < 0.0 || r->v[j][i] > 1.0 + || (i > 0 && r->v[j][i] < r->v[j][i-1])) + note = 1; + } + fprintf(fp," %d: %f %f %f%s\n",i, r->v[0][i], r->v[1][i], r->v[2][i], note ? " #" : ""); + } +} + /* Set the ramdac values to linear */ void dispwin_setlin_ramdac(ramdac *r) { int i, j; @@ -2522,7 +2554,7 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { } #endif /* OS X || Linux */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { @@ -2685,7 +2717,7 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { return 0; } #endif /* 10.6 and prior */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) && defined(USE_UCMM) { @@ -2862,7 +2894,7 @@ int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { } } #endif /* OS X || Linux */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 { char *dpath; /* Un-install file path */ @@ -2980,7 +3012,7 @@ int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { return 0; } #endif /* 10.6 and prior */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) && defined(USE_UCMM) { @@ -3043,7 +3075,7 @@ icmFile *dispwin_get_profile(dispwin *p, char *name, int mxlen) { } #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { char *dpath; /* Read file path */ @@ -3187,7 +3219,7 @@ icmFile *dispwin_get_profile(dispwin *p, char *name, int mxlen) { return rd_fp; } #endif /* 10.5 and prior */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) && defined(USE_UCMM) /* Try and get the currently installed profile from ucmm */ @@ -3454,7 +3486,7 @@ static void dispwin_uninstall_signal_handlers(dispwin *p) { /* ----------------------------------------------- */ /* Test patch window specific declarations */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE @class DWWin; @class DWView; @@ -3467,6 +3499,8 @@ typedef struct { #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 NSColorSpace *nscs; /* Colorspace from profile */ #endif + NSRect rect; /* Size and position to create window */ + int err; /* Error code */ } osx_cntx_t; // - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3545,6 +3579,15 @@ unsigned char emptyCursor[43] = { @end +/* Function called back by main thread to trigger a drawRect */ + +static void doSetNeedsDisplay(void *cntx) { + osx_cntx_t *cx = (osx_cntx_t *)cntx; + + [cx->view setNeedsDisplay: YES ]; + + cx->err = 0; +} // - - - - - - - - - - - - - - - - - - - - - - - - - @interface DWWin : NSWindow { @@ -3582,7 +3625,9 @@ unsigned char emptyCursor[43] = { @end /* Create our window */ -static void create_my_win(NSRect rect, osx_cntx_t *cx) { +/* We run this on the main thread using a custom message */ +static void create_my_win(void *cntx) { + osx_cntx_t *cx = (osx_cntx_t *)cntx; dispwin *p = cx->p; SInt32 MacMajVers, MacMinVers, MacBFVers; void *cspr = NULL; /* ColorSync profile ref. */ @@ -3607,7 +3652,7 @@ static void create_my_win(NSRect rect, osx_cntx_t *cx) { } /* Create Window */ - cx->window = [[DWWin alloc] initWithContentRect: rect + cx->window = [[DWWin alloc] initWithContentRect: cx->rect styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: YES @@ -3626,7 +3671,8 @@ static void create_my_win(NSRect rect, osx_cntx_t *cx) { /* Moves the window to the front of the screen list within its level, */ /* and show the window (i.e. make it "key") */ - /* Trigger crash on OS X 10.11 El Capitan ? */ + /* Trigger warning on OS X 10.11 El Capitan ? */ + /* (Doesn't happen using 1.6.3 which ran everything in the main thread.) */ [cx->window makeKeyAndOrderFront: nil]; /* Use a null color transform to ensure device values */ @@ -3686,9 +3732,10 @@ static void create_my_win(NSRect rect, osx_cntx_t *cx) { } #endif + cx->err = 0; } -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ /* ----------------------------------------------- */ @@ -3726,22 +3773,24 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ /* For video encoding the extra bits of precision are created by bit shifting */ /* rather than scaling, so we need to scale the fp value to account for this. */ - if (p->pdepth > 8) - p->r_rgb[j] = (p->s_rgb[j] * 255 * (1 << (p->pdepth - 8))) - /((1 << p->pdepth) - 1.0); + if (p->edepth > 8) + p->r_rgb[j] = (p->s_rgb[j] * 255 * (1 << (p->edepth - 8))) + /((1 << p->edepth) - 1.0); } } //if (p->out_tvenc) { //printf(" %d: 8 bit tv = s_rgb %f %f %f\n",j, p->s_rgb[0], p->s_rgb[1], p->s_rgb[2]); -//printf(" %d: %d bitraster r_rgb %f %f %f\n",j, p->pdepth,p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]); +//printf(" %d: %d bitraster r_rgb %f %f %f\n",j, p->edepth,p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]); //} /* Use ramdac for high precision native output. */ /* The ramdac is used to hold the lsb that the frame buffer */ /* doesn't hold. */ if ((p->native & 1) == 1) { - double prange = p->r->nent - 1.0; + double frange = (1 << p->fdepth) - 1.0; + double rrange = (1 << p->rdepth) - 1.0; + double nrange = p->nent - 1.0; p->r->setlin(p->r); /* In case something else altered this */ @@ -3756,9 +3805,66 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ if (p->out_tvenc && p->edepth > 8) vv = (vv * 255 * (1 << (p->edepth - 8)))/((1 << p->edepth) - 1.0); - tt = (int)(vv * prange + 0.5); - p->r_rgb[j] = (double)tt/prange; /* RAMDAC output Quantized value */ + /* Determine the ramdac index that the quantized frame buffer */ + /* value will make use of */ +#ifdef NT + /* Assume all depths match, or linear mapping between them */ + tt = (int)(vv * frange + 0.5); /* Frame buffer value */ + p->r_rgb[j] = (double)tt/frange; /* Double frame buffer value */ + tt = (int)(tt/frange * rrange + 0.5); /* expected RAMDAC index */ + tt = (int)(tt/rrange * nrange + 0.5); /* actual RAMDAC index */ +#endif +#ifdef UNIX_APPLE + /* We assume linear mapping with perfect rounding between rdepth and ndepth */ + tt = (int)(vv * frange + 0.5); /* Frame buffer value */ + p->r_rgb[j] = (double)tt/frange; /* Double frame buffer value */ + tt = (int)(tt/frange * rrange + 0.5); /* expected RAMDAC index */ + tt = (int)(tt/rrange * nrange + 0.5); /* actual RAMDAC index */ +#endif +#if defined(UNIX_X11) + /* We assume linear mapping with perfect rounding between rdepth and ndepth */ + tt = (int)(vv * frange + 0.5); /* Frame buffer value */ + p->r_rgb[j] = (double)tt/frange; /* Double frame buffer value */ + tt = p->rmap[j][tt]; /* expected RAMDAC index */ + tt = (int)(tt/rrange * nrange + 0.5); /* actual RAMDAC index */ +#endif + +#ifdef NEVER // Just set entry we think will get hit p->r->v[j][tt] = vv; +#else + + /* Set the three entries around target and create ramp either side, + to allow for some video cards not having a precise + definition of what value translates to what frame buffer value. */ + { + int i; + double maxv = 1.0; + + if (p->out_tvenc && p->edepth > 8) + maxv = (maxv * 255 * (1 << (p->edepth - 8)))/((1 << p->edepth) - 1.0); + + if ((tt-1) == 0) { + p->r->v[j][tt-1] = vv; + } else { + for (i = 0; i <= (tt-1); i++) + p->r->v[j][i] = vv * i/(tt-1); + } + + p->r->v[j][tt] = vv; + + if ((tt+1) == (p->r->nent-1)) { + p->r->v[j][tt+1] = vv; + } else { + for (i = tt+1; i < p->r->nent; i++) + p->r->v[j][i] = vv + (maxv - vv) * (i - (tt+1))/((p->r->nent-1) - (tt+1)); + } + +#ifdef NEVER + for (i = 0; i < p->r->nent; i++) + printf("~1 %d, %d -> %f\n",j,i,p->r->v[j][i]); +#endif + } +#endif //printf(" cell[%d] = r_rgb %f, cell val %f\n",tt, p->r_rgb[j], vv); } @@ -3796,7 +3902,9 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ #endif p->colupd++; -//printf("~1 set color %f %f %f\n", p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]); + + debugr2((errout,"dispwin_set_color about to paint color %f %f %f\n", + p->r_rgb[0], p->r_rgb[1], p->r_rgb[2])); /* Trigger a WM_PAINT */ if (!InvalidateRect(p->hwnd, NULL, FALSE)) { @@ -3810,13 +3918,14 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ while (p->colupd != p->colupde && p->cberror == 0) { msec_sleep(10); } -//printf("~1 paint done\n"); + + debugr2((errout,"dispwin_set_color paint done\n")); } #endif /* NT */ /* - - - - - - - - - - - - - - */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE if (p->winclose) { return 2; @@ -3840,11 +3949,7 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ if ((stat = GetCurrentProcess(&cpsn)) != noErr) { debugr2((errout,"GetCurrentProcess returned error %d\n",stat)); } else { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 - if ((stat = TransformProcessType(&cpsn, kProcessTransformToForegroundApplication)) != noErr) { - debugr2((errout,"TransformProcessType returned error %d\n",stat)); - } -#endif /* OS X 10.3 */ + // [window makeGetAndOrderFront:] ?? if ((stat = SetFrontProcess(&cpsn)) != noErr) { debugr2((errout,"SetFrontProcess returned error %d\n",stat)); } @@ -3852,40 +3957,56 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ p->btf = 1; } - /* Trigger an update that fills window with r_rgb[] */ - [((osx_cntx_t *)(p->osx_cntx))->view setNeedsDisplay: YES ]; + /* Prepare to wait for events */ + ui_aboutToWait(); + + debugr2((errout,"dispwin_set_color about to paint color %f %f %f\n", + p->r_rgb[0], p->r_rgb[1], p->r_rgb[2])); + +// [((osx_cntx_t *)(p->osx_cntx))->view setNeedsDisplay: YES ]; + + /* Run the window creation in the main thread and wait for it */ + ui_runInMainThreadAndWait((void *)p->osx_cntx, doSetNeedsDisplay); + + /* Wait for any events generated by paint to complete */ + ui_waitForEvents(); + + debugr2((errout,"dispwin_set_color paint done\n")); if (tpool != nil) [tpool release]; -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ /* - - - - - - - - - - - - - - */ #if defined(UNIX_X11) { - Colormap mycmap; - XColor col; - int vali[3]; + unsigned int vali[3]; + unsigned long fbval; /* Indicate that we've got activity to the X11 Screensaver */ XResetScreenSaver(p->mydisplay); - /* Quantize to 16 bit color */ + /* Quantize to frame buffer component depth */ for (j = 0; j < 3; j++) - vali[j] = (int)(65535.0 * p->r_rgb[j] + 0.5); + vali[j] = (int)(((1 << p->fdepth)-1.0) * p->r_rgb[j] + 0.5); - mycmap = DefaultColormap(p->mydisplay, p->myscreen); - col.red = vali[0]; - col.green = vali[1]; - col.blue = vali[2]; - XAllocColor(p->mydisplay, mycmap, &col); - XSetForeground(p->mydisplay, p->mygc, col.pixel); + /* Compose frame buffer pixel value */ + fbval = (vali[0] << p->shift[0]) + | (vali[1] << p->shift[1]) + | (vali[2] << p->shift[2]); + XSetForeground(p->mydisplay, p->mygc, fbval); + + debugr2((errout,"dispwin_set_color about to paint color %f %f %f\n", + p->r_rgb[0], p->r_rgb[1], p->r_rgb[2])); XFillRectangle(p->mydisplay, p->mywindow, p->mygc, p->tx, p->ty, p->tw, p->th); XSync(p->mydisplay, False); /* Make sure it happens */ + + debugr2((errout,"dispwin_set_color paint done\n")); } #endif /* UNXI X11 */ @@ -4029,6 +4150,7 @@ dispwin *p if (p->mth != NULL) { /* Message thread */ p->mth->del(p->mth); } + p->hwnd = NULL; } if (p->hdc != NULL) @@ -4038,7 +4160,7 @@ dispwin *p /* -------------------------------------------------- */ /* -------------------------------------------------- */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE if (p->nowin == 0) { /* We have a window up */ restore_display(p); if (p->osx_cntx != NULL) { /* And we've allocated a context */ @@ -4061,7 +4183,7 @@ dispwin *p // ~~ // CGDisplayShowCursor(p->ddid); -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ /* -------------------------------------------------- */ /* -------------------------------------------------- */ @@ -4071,22 +4193,43 @@ dispwin *p if (p->mydisplay != NULL) { if (p->nowin == 0) { /* We have a window up */ - XFreeGC(p->mydisplay, p->mygc); - XDestroyWindow(p->mydisplay, p->mywindow); + if (p->mygc != 0) + XFreeGC(p->mydisplay, p->mygc); + if (p->mywindow != 0) + XDestroyWindow(p->mydisplay, p->mywindow); } XCloseDisplay(p->mydisplay); + p->mydisplay = NULL; + } + { + int j; + for (j = 0; j < 3; j++) { + if (p->rmap[j] != NULL) { + free(p->rmap[j]); + p->rmap[j] = NULL; + } + } } debugr("finished\n"); + if (p->edid != NULL) + free(p->edid); + #endif /* UNXI X11 */ /* -------------------------------------------------- */ - if (p->name != NULL) + if (p->name != NULL) { free(p->name); - if (p->description != NULL) + p->name = NULL; + } + if (p->description != NULL) { free(p->description); - if (p->callout != NULL) + p->description = NULL; + } + if (p->callout != NULL) { free(p->callout); + p->callout = NULL; + } free(p); } @@ -4234,6 +4377,10 @@ static LRESULT CALLBACK MainWndProc( #endif /* NT */ +#ifdef UNIX_APPLE + +#endif /* UNIX_APPLE */ + #if defined(UNIX_X11) /* None */ #endif /* UNXI X11 */ @@ -4241,7 +4388,8 @@ static LRESULT CALLBACK MainWndProc( /* ----------------------------------------------- */ #ifdef NT -/* Thread to handle message processing, so that there is no delay */ +/* Thread to create the window if it doesn'r exist, */ +/* and handle message processing, so that there is no delay */ /* when the main thread is doing other things. */ int win_message_thread(void *pp) { dispwin *p = (dispwin *)pp; @@ -4333,7 +4481,7 @@ int win_message_thread(void *pp) { } if (UnregisterClass(p->AppName, NULL) == 0) { - warning("UnregisterClass failed, lasterr = %d\n",GetLastError()); + warning("UnregisterClass failed, lasterr = %d",GetLastError()); } p->hwnd = NULL; /* Signal it's been deleted */ @@ -4400,7 +4548,10 @@ int ddebug /* >0 to print debug statements to stderr */ ) { dispwin *p = NULL; - debug("new_dispwin called\n"); +#ifndef DEBUG + if (ddebug) +#endif + fprintf(errout, "new_dispwin called\n"); #if defined(UNIX_X11) && defined(USE_UCMM) dispwin_checkfor_colord(); /* Make colord functions available */ @@ -4444,6 +4595,8 @@ int ddebug /* >0 to print debug statements to stderr */ /* Basic object is initialised, so create a window */ + ui_UsingGUI(); + /* -------------------------------------------------- */ #ifdef NT { @@ -4511,7 +4664,7 @@ int ddebug /* >0 to print debug statements to stderr */ p->wh = he; /* It's a bit difficult to know how windows defines the display */ - /* depth. Microsofts doco is fuzzy, and typical values */ + /* depth. Microsoft's doco is fuzzy, and typical values */ /* for BITSPIXEL and PLANES are confusing (What does "32" and "1" */ /* mean ?) NUMCOLORS seems to be -1 on my box, and perhaps */ /* is only applicable to up to 256 paletized colors. The doco */ @@ -4530,12 +4683,17 @@ int ddebug /* >0 to print debug statements to stderr */ } bpp = GetDeviceCaps(p->hdc, COLORRES); if (bpp <= 0) - p->pdepth = 8; /* Assume this is so */ + p->fdepth = 8; /* Assume this is so */ else - p->pdepth = bpp/3; + p->fdepth = bpp/3; + p->rdepth = p->fdepth; /* Assume this is so */ + p->ndepth = 8; /* Assume this is so */ + p->nent = (1 << p->ndepth); p->edepth = 16; + debugr2((errout,"new_dispwin: fdepth %d, rdepth %d, ndepth %d, edepth %d\n", p->fdepth,p->rdepth,p->ndepth,p->edepth)); + if (nowin == 0) { /* We use a thread to process the window messages, so that */ @@ -4549,6 +4707,9 @@ int ddebug /* >0 to print debug statements to stderr */ p->wi = wi; p->he = he; + debugr2((errout,"new_dispwin about to create window\n")); + + /* Create the window and then process events */ if ((p->mth = new_athread(win_message_thread, (void *)p)) == NULL) { debugr2((errout, "new_dispwin: new_athread failed\n")); dispwin_del(p); @@ -4556,6 +4717,9 @@ int ddebug /* >0 to print debug statements to stderr */ } /* Wait for thread to run */ + /* (Hmm. This doesn't actually gurantee that our window has */ + /* been created yet ? */ + // ~~ should sync by sending a custom message and getting notified ? */ while (p->inited == 0) { msec_sleep(20); } @@ -4565,6 +4729,8 @@ int ddebug /* >0 to print debug statements to stderr */ dispwin_del(p); return NULL; } + + debugr2((errout,"new_dispwin window created\n")); } /* Install the signal handler to ensure cleanup */ @@ -4575,7 +4741,7 @@ int ddebug /* >0 to print debug statements to stderr */ /* -------------------------------------------------- */ /* -------------------------------------------------- */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE if ((p->name = strdup(disp->name)) == NULL) { debugr2((errout,"new_dispwin: Malloc failed\n")); @@ -4584,31 +4750,13 @@ int ddebug /* >0 to print debug statements to stderr */ } p->ddid = disp->ddid; /* Display we're working on */ - /* Hmm. Could we use CGDisplayGammaTableCapacity() instead ? */ #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { CGDisplayModeRef dispmode; CFStringRef pixenc; - int cap = CGDisplayGammaTableCapacity(p->ddid); int fbdepth = 0; - debugr2((errout,"new_dispwin: CGDisplayGammaTableCapacity = %d\n",cap)); - - /* Compute GammaTable depth */ - { - for (p->pdepth = 1; p->pdepth < 17; p->pdepth++) { - if ((1 << p->pdepth) == cap) - break; - } - if (p->pdepth >= 17) { - debugr2((errout,"new_dispwin: failed to extract depth from GammaTableCapacity %d\n",cap)); - dispwin_del(p); - return NULL; - } - debugr2((errout,"new_dispwin: found pixel depth %d bits\n",p->pdepth)); - } - /* Get frame buffer depth for sanity check, but don't actually make used of it */ - + /* Get frame buffer depth */ dispmode = CGDisplayCopyDisplayMode(p->ddid); pixenc = CGDisplayModeCopyPixelEncoding(dispmode); @@ -4625,6 +4773,9 @@ int ddebug /* >0 to print debug statements to stderr */ else if (CFStringCompare(pixenc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) fbdepth = 5; + else + fbdepth = 8; /* Assume */ + #ifndef DEBUG if (p->ddebug) #endif @@ -4637,26 +4788,56 @@ int ddebug /* >0 to print debug statements to stderr */ CFRelease(pixenc); CGDisplayModeRelease(dispmode); - if (p->pdepth != fbdepth) { - static int warned = 0; - if (!warned) { - warning("new_dispwin: frame buffer depth %d != GammaTable depth %d\n",fbdepth, p->pdepth); - warned = 1; + p->fdepth = fbdepth; + p->rdepth = p->fdepth; /* We don't know of any HW between frame buffer and VideoLUT */ + + /* Get CGDisplayGammaTable size */ + p->nent = CGDisplayGammaTableCapacity(p->ddid); + + /* Compute GammaTable/VideoLUT/RAMDAC depth */ + { + for (p->ndepth = 1; p->ndepth < 17; p->ndepth++) { + if ((1 << p->ndepth) >= p->nent) + break; + } + if (p->ndepth >= 17) { + debugr2((errout,"new_dispwin: failed to extract depth from GammaTableCapacity %d\n",p->nent)); + dispwin_del(p); + return NULL; + } + debugr2((errout,"new_dispwin: found actual VideoLUT depth %d bits\n",p->ndepth)); + } + + if (p->rdepth > p->ndepth) { + debugr2((errout,"new_dispwin: Frame buffer depth %d > VideoLUT depth %d\n",p->rdepth, p->ndepth)); + dispwin_del(p); + return NULL; + } + + if (p->rdepth != p->ndepth) { + if (!p->warned) { + warning("new_dispwin: Frame buffer depth %d doesn't matcv VideoLUT %d",p->rdepth, p->ndepth); + p->warned = 1; } } } #else - p->pdepth = CGDisplayBitsPerSample(p->ddid); + /* Life is simple */ + p->fdepth = CGDisplayBitsPerSample(p->ddid); + p->rdepth = p->fdepth; + p->ndepth = p->rdepth; + p->nent = (1 << p->ndepth); #endif p->edepth = 16; /* By experiment it seems to be 16 bits too */ + debugr2((errout,"new_dispwin: fdepth %d, rdepth %d, ndepth %d, edepth %d\n", p->fdepth,p->rdepth,p->ndepth,p->edepth)); + if (nowin == 0) { /* Create a window */ osx_cntx_t *cx; CGSize sz; /* Display size in mm */ int wi, he; /* Width and height in pixels */ int xo, yo; /* Window location */ - NSRect wrect; debugr2((errout, "new_dispwin: About to open display '%s'\n",disp->name)); @@ -4716,22 +4897,33 @@ int ddebug /* >0 to print debug statements to stderr */ p->ww = wi; p->wh = he; - wrect.origin.x = xo; - wrect.origin.y = yo; - wrect.size.width = wi; - wrect.size.height = he; + cx->rect.origin.x = xo; + cx->rect.origin.y = yo; + cx->rect.size.width = wi; + cx->rect.size.height = he; + + debugr2((errout,"new_dispwin about to create window\n")); + + /* Prepare to wait for events */ + ui_aboutToWait(); - create_my_win(wrect, cx); + /* Run the window creation in the main thread and wait for it */ + ui_runInMainThreadAndWait((void *)cx, create_my_win); + + /* Wait for events generated by window creation to complete */ + ui_waitForEvents(); if (tpool != nil) [tpool release]; p->winclose = 0; + + debugr2((errout,"new_dispwin window created\n")); } /* Install the signal handler to ensure cleanup */ dispwin_install_signal_handlers(p); -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ /* -------------------------------------------------- */ /* -------------------------------------------------- */ @@ -4755,6 +4947,8 @@ int ddebug /* >0 to print debug statements to stderr */ Window rootwindow; char *appname = "TestWin"; Visual *myvisual; + XVisualInfo template, *vinfo; + int nitems = 0; XSetWindowAttributes myattr; XEvent myevent; XTextProperty myappname; @@ -4817,15 +5011,184 @@ int ddebug /* >0 to print debug statements to stderr */ memmove(p->edid, disp->edid, p->edid_len); } - //p->pdepth = DefaultDepth(p->mydisplay, p->myscreen)/3; +#ifdef NEVER +#pragma message("######### dispwin.c DirectColor test is defined! ########") + + /* To test DirectColor Visual when it's not the default:*/ + debugr2((errout,"new_dispwin: Testing DirectColor Visual\n")); + + // test DirectColor visual + template.class = DirectColor; + if ((vinfo = XGetVisualInfo(p->mydisplay, VisualClassMask, &template, &nitems)) == NULL) { + debugr2((errout,"new_dispwin: Unable to find a DirectColor Visual\n")); + dispwin_del(p); + return NULL; + } + myvisual = vinfo->visual; - // Hmm. Should we explicitly get the root window visual, - // since our test window inherits it from root ? +#else myvisual = DefaultVisual(p->mydisplay, p->myscreen); - p->pdepth = myvisual->bits_per_rgb; - p->edepth = 16; +#endif + + /* Expect TrueColor (fixed/no map) or DirectColor (read/write map) Visual - */ + /* anything else is not suitable for high quality color. */ + + /* Get the VisualInfo */ + template.visualid = myvisual->visualid; + vinfo = XGetVisualInfo(p->mydisplay, VisualIDMask, &template, &nitems); + + if (nitems < 1) { + debugr2((errout,"new_dispwin: Failed to get XGetVisualInfo of defalt Visual\n")); + dispwin_del(p); + return NULL; + } + + if (vinfo->class != TrueColor && vinfo->class != DirectColor) { + debugr2((errout,"new_dispwin: Default Visual is not TrueColor or DirectColor\n")); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + /* Compute per component frame buffer depth */ + { + for (p->fdepth = 1; p->fdepth < 17; p->fdepth++) { + if ((1 << p->fdepth) >= myvisual->map_entries) + break; + } + if (p->fdepth >= 17) { + debugr2((errout,"new_dispwin: failed to extract depth from Visual bits_per_rgb %d\n",myvisual->map_entries)); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + } + p->rdepth = myvisual->bits_per_rgb; /* X11 colormap entry size */ + p->edepth = 16; /* Assumed */ + + /* Check that vinfo->red_mask, green_mask & blue_mask all have */ + /* myvisual->map_entries number of bits set, and determine the shift. */ + { + unsigned long bit; + int depth; + + if (vinfo->red_mask == 0 || vinfo->green_mask == 0 || vinfo->blue_mask == 0) { + debugr2((errout,"new_dispwin: one of default Visual r/g/b masks is 0\n")); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + for (p->shift[0] = 0, bit = 1; (bit & vinfo->red_mask) == 0; p->shift[0]++, bit <<= 1) + ; + + for (depth = 0; (bit & vinfo->red_mask) != 0; depth++, bit <<= 1) + ; + + if (depth != p->fdepth) { + debugr2((errout,"new_dispwin: Default Visual red mask 0x%x is not %d bits\n",vinfo->red_mask,p->fdepth)); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + for (p->shift[1] = 0, bit = 1; (bit & vinfo->green_mask) == 0; p->shift[1]++, bit <<= 1) + ; + + for (depth = 0; (bit & vinfo->green_mask) != 0; depth++, bit <<= 1) + ; + + if (depth != p->fdepth) { + debugr2((errout,"new_dispwin: Default Visual green mask 0x%x is not %d bits\n",vinfo->red_mask,p->fdepth)); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + for (p->shift[2] = 0, bit = 1; (bit & vinfo->blue_mask) == 0; p->shift[2]++, bit <<= 1) + ; + + for (depth = 0; (bit & vinfo->blue_mask) != 0; depth++, bit <<= 1) + ; + + if (depth != p->fdepth) { + debugr2((errout,"new_dispwin: Default Visual blue mask 0x%x is not %d bits\n",vinfo->red_mask,p->fdepth)); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + } + + /* Check the VideoLUT depth */ +#if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR) + if (p->crtc != 0) { /* Using Xrandr 1.2 */ + + if ((p->nent = XRRGetCrtcGammaSize(p->mydisplay, p->crtc)) <= 0) { + p->nent = 0; + } + } else +#endif /* randr >= V 1.2 */ + { + p->nent = 0; + + if (XF86VidModeQueryExtension(p->mydisplay, &evb, &erb) != 0) { + int nent = -1; + + /* Some propietary multi-screen drivers (ie. TwinView & MergedFB) */ + /* don't implement the XVidMode extenstion properly. */ + if (XSetErrorHandler(null_error_handler) == 0) { + debugr("new_dispwin failed on XSetErrorHandler\n"); + XSetErrorHandler(NULL); /* Restore handler */ + XFree(vinfo); + dispwin_del(p); + return NULL; + } + if (XF86VidModeGetGammaRampSize(p->mydisplay, p->myrscreen, &nent) != 0 + && nent != -1) { + p->nent = nent; + } + XSetErrorHandler(NULL); /* Restore handler */ + } + } + + if (p->nent == 0) { + p->ndepth = 0; + + if (!p->warned) { + warning("new_dispwin: VideoLUT is not accessible"); + p->warned = 1; + } + + } else { + if (p->nent > 16384) { + debugr("VideoLUT has more entries than we can handle\n"); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + /* Compute actual ramdac depth */ + for (p->ndepth = 1; p->ndepth < 17; p->ndepth++) { + if ((1 << p->ndepth) >= p->nent) + break; + } + + if (p->nent != (1 << p->rdepth)) { + if (!p->warned) { + warning("new_dispwin: Expected VideoLUT depth %d doesn't match actual %d",p->rdepth, p->ndepth); + p->warned = 1; + } + } + } + + debugr2((errout,"new_dispwin: %s fdepth %d, rdepth %d, ndepth %d, edepth %d, r/g/b shifts %d %d %d\n", vinfo->class != TrueColor ? "TreuColor" : "DirectColor", p->fdepth,p->rdepth,p->ndepth,p->edepth, p->shift[0], p->shift[1], p->shift[2])); if (nowin == 0) { /* Create a window */ + unsigned long attrmask = 0; + XWindowAttributes mywa; + Colormap mycmap = None; + int ncolors, i; + rootwindow = RootWindow(p->mydisplay, p->myscreen); myforeground = BlackPixel(p->mydisplay, p->myscreen); @@ -4887,6 +5250,53 @@ int ddebug /* >0 to print debug statements to stderr */ else myattr.override_redirect = False; + attrmask |= CWBackPixel | CWBitGravity /* Attributes Valumask */ + | CWWinGravity | CWBackingStore | CWOverrideRedirect; + + /* For a DirectColor Visual, set the X11 color map */ + if (vinfo->class == DirectColor) { + Colormap mycmap = None; + XColor *colors; + int ncolors = (1 << p->fdepth), i; + + debugr2((errout,"new_dispwin: setting DirectColor colormap\n")); + + if ((mycmap = XCreateColormap(p->mydisplay, rootwindow, myvisual, AllocAll)) == None) { + debugr2((errout,"new_dispwin: XCreateColormap failed\n")); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + if ((colors = malloc(sizeof(XColor) * ncolors)) == NULL) { + debugr2((errout,"new_dispwin: Malloc failed for XColors\n")); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + /* Set a linear mapping */ + for (i = 0; i < ncolors; i++) { + colors[i].pixel = i << p->shift[0] | i << p->shift[1] | i << p->shift[2]; + colors[i].red = + colors[i].green = + colors[i].blue = (unsigned short) (65535.0 * i/(ncolors-1.0) + 0.5); + colors[i].flags = DoRed | DoGreen | DoBlue; + } + + if (!XStoreColors(p->mydisplay, mycmap, colors, ncolors)) { + debugr2((errout,"new_dispwin: DirectColor XStoreColors failed\n")); + free(colors); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + free(colors); + + myattr.colormap = mycmap; + attrmask |= CWColormap; + } + debugr("Opening window\n"); p->mywindow = XCreateWindow( p->mydisplay, rootwindow, @@ -4894,9 +5304,9 @@ int ddebug /* >0 to print debug statements to stderr */ 0, /* Border width */ CopyFromParent, /* Depth */ InputOutput, /* Class */ - CopyFromParent, /* Visual */ - CWBackPixel | CWBitGravity /* Attributes Valumask */ - | CWWinGravity | CWBackingStore | CWOverrideRedirect, +// CopyFromParent, /* Visual */ + myvisual, /* Visual */ + attrmask, /* Attributes Valumask */ &myattr /* Attribute details */ ); @@ -4908,10 +5318,12 @@ int ddebug /* >0 to print debug statements to stderr */ p->mydisplay, p->mywindow, &mywattributes) == 0) { debugr("new_dispwin: XGetWindowAttributes failed\n"); + XFree(vinfo); dispwin_del(p); return NULL; } - p->pdepth = mywattributes.depth/3; + p->fdepth = mywattributes.depth/3; + p->rdepth = p->fdepth; #endif /* Setup TextProperty */ @@ -4926,6 +5338,9 @@ int ddebug /* >0 to print debug statements to stderr */ &mywmhints, NULL); /* No class hints */ + // ~1 should free myappname, but there doesn't seem to be + // a XFreeXTextProperty(&myappname); ??? + /* Set aditional properties */ { Atom optat; @@ -4975,8 +5390,8 @@ int ddebug /* >0 to print debug statements to stderr */ XSelectInput(p->mydisplay,p->mywindow, ExposureMask); + debugr2((errout,"new_dispwin about to raise window\n")); XMapRaised(p->mydisplay,p->mywindow); - debug("Raised window\n"); /* ------------------------------------------------------- */ /* Suspend any screensavers if we can */ @@ -5065,7 +5480,7 @@ int ddebug /* >0 to print debug statements to stderr */ } /* Deal with any pending events */ - debug("About to enter main loop\n"); + debugr("About to enter main loop\n"); while(XPending(p->mydisplay) > 0) { XNextEvent(p->mydisplay, &myevent); switch(myevent.type) { @@ -5079,10 +5494,94 @@ int ddebug /* >0 to print debug statements to stderr */ break; } } + + /* Deal with Colormaps */ + debugr2((errout,"new_dispwin: window created - dealling with colormap\n")); + + if (!XGetWindowAttributes(p->mydisplay, p->mywindow, &mywa)) { + debugr2((errout,"new_dispwin: XGetWindowAttributes failed\n")); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + mycmap = mywa.colormap; + ncolors = (1 << p->fdepth); + + for (i = 0; i < 3; i++) { + if ((p->rmap[i] = malloc(sizeof(int) * ncolors)) == NULL) { + debugr2((errout,"new_dispwin: Malloc failed for rmap[%d]\n",i)); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + } + + /* Get the X11 colormaps, so that we know how to translate */ + /* between the frame buffer pixel value and the ramdac */ + /* index number. */ + if (mycmap != None) { + XColor *colors; + + debugr2((errout,"new_dispwin: getting colormap\n")); + + if ((colors = malloc(sizeof(XColor) * ncolors)) == NULL) { + debugr2((errout,"new_dispwin: Malloc failed for XColors\n")); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + for (i = 0; i < ncolors; i++) { + colors[i].pixel = i << p->shift[0] | i << p->shift[1] | i << p->shift[2]; + colors[i].flags = DoRed | DoGreen | DoBlue; + } + + if (!XQueryColors(p->mydisplay, mycmap, colors, ncolors)) { + debugr2((errout,"new_dispwin: DirectColor XQueryColors failed\n")); + free(colors); + XFree(vinfo); + dispwin_del(p); + return NULL; + } + + /* Map from frame buffer value to ramdac index */ + for (i = 0; i < ncolors; i++) { + p->rmap[0][i] = (int)(colors[i].red/65535.0 * ((1 << p->rdepth)-1.0) + 0.5); + p->rmap[1][i] = (int)(colors[i].green/65535.0 * ((1 << p->rdepth)-1.0) + 0.5); + p->rmap[2][i] = (int)(colors[i].blue/65535.0 * ((1 << p->rdepth)-1.0) + 0.5); + +//printf("%d: %d %d %d\n",i,p->rmap[0][i],p->rmap[1][i],p->rmap[2][i]); + } + + free(colors); + + /* Assume a default linear mapping */ + } else { + debugr2((errout,"new_dispwin: assuming a linear colormap\n")); + for (i = 0; i < ncolors; i++) { + p->rmap[0][i] = (int)(i/((1 << p->fdepth)-1.0) * ((1 << p->rdepth)-1.0) + 0.5); + p->rmap[1][i] = (int)(i/((1 << p->fdepth)-1.0) * ((1 << p->rdepth)-1.0) + 0.5); + p->rmap[2][i] = (int)(i/((1 << p->fdepth)-1.0) * ((1 << p->rdepth)-1.0) + 0.5); + } + + if (p->fdepth != p->rdepth) { + static int warned = 0; + if (!warned) { + warning("new_dispwin: frame buffer depth %d != VideoLUT depth %d",p->fdepth, p->rdepth); + warned = 1; + } + } + } + + debugr2((errout,"new_dispwin: dealt with colormap\n")); + } else { /* Install the signal handler to ensure cleanup */ dispwin_install_signal_handlers(p); } + + XFree(vinfo); vinfo = NULL; } #endif /* UNIX X11 */ /* -------------------------------------------------- */ @@ -5750,7 +6249,7 @@ main(int argc, char *argv[]) { if (fa < argc) { strncpy(calname,argv[fa++],MAXNAMEL); calname[MAXNAMEL] = '\000'; if (installprofile == 0 && loadprofile == 0 && verify == 0) - loadfile = 1; /* Load the given profile into the videoLUT */ + loadfile = 1; /* Load the given profile into the VideoLUT */ } #if defined(UNIX_X11) @@ -5949,7 +6448,7 @@ main(int argc, char *argv[]) { /* Should we load calfile instead of installed profile if it's present ??? */ if (loadprofile) { if (calname[0] != '\000') - warning("Profile '%s' provided as argument is being ignored!\n",calname); + warning("Profile '%s' provided as argument is being ignored!",calname); /* Get the current displays profile */ debug2((errout,"Loading calibration from display profile '%s'\n",dw->name)); @@ -5974,7 +6473,7 @@ main(int argc, char *argv[]) { is_ok_icc = 1; /* The profile is OK */ if ((wo = (icmVideoCardGamma *)icco->read_tag(icco, icSigVideoCardGammaTag)) == NULL) { - warning("No vcgt tag found in profile - assuming linear\n"); + warning("No vcgt tag found in profile - assuming linear"); for (i = 0; i < dw->r->nent; i++) { iv = i/(dw->r->nent-1.0); dw->r->v[0][i] = iv; @@ -6008,7 +6507,7 @@ main(int argc, char *argv[]) { } else { /* See if it's a .cal file */ int ncal; int ii, fi, ri, gi, bi; - double cal[3][256]; + double cal[3][MAX_CAL_ENT]; int out_tvenc = 0; /* nz to use (16-235)/255 video encoding */ icco->del(icco); /* Don't need these now */ @@ -6032,8 +6531,8 @@ main(int argc, char *argv[]) { if ((ncal = ccg->t[0].nsets) <= 0) error("No data in set of file '%s'",calname); - if (ncal != 256) - error("Expect 256 data sets in file '%s'",calname); + if (ncal < 2 || ncal > MAX_CAL_ENT) + error("Data set size %d is out of range for '%s'",ncal,calname); if ((fi = ccg->find_kword(ccg, 0, "DEVICE_CLASS")) < 0) error("Calibration file '%s' doesn't contain keyword COLOR_REP",calname); diff --git a/spectro/dispwin.h b/spectro/dispwin.h index b5e14ce..47fd256 100644 --- a/spectro/dispwin.h +++ b/spectro/dispwin.h @@ -73,11 +73,11 @@ WINSHLWAPI LPWSTR WINAPI PathFindFileNameW(LPCWSTR); #endif /* NT */ -#ifdef __APPLE__ /* Assume OS X Cocoa */ +#ifdef UNIX_APPLE /* Assume OS X Cocoa */ #include <Carbon/Carbon.h> /* To declare CGDirectDisplayID */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) #include <X11/Xlib.h> @@ -114,9 +114,9 @@ typedef struct { char monid[128]; /* Monitor ID */ int prim; /* NZ if primary display monitor */ #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE CGDirectDisplayID ddid; -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) int screen; /* Screen to select */ int uscreen; /* Underlying screen */ @@ -158,9 +158,17 @@ struct _ramdac { /* Should have separate frame buffer depth + representation to account */ /* for floating point frame buffers, even though this isn't currently used. */ - int pdepth; /* Frame buffer depth into RAMDAC, usually 8 */ - int nent; /* Number of entries, = 2^pdepth */ - double *v[3]; /* 2^pdepth entries for RGB, values 0.0 - 1.0 */ + int fdepth; /* Frame buffer depth, typically 8, could be more. */ + int rdepth; /* Expected ramdac index depth. May be different to fdepth */ + /* if there is another level of mapping between the frame buffer */ + /* and ramdac, i.e. X11 DirectorColor Colormap. */ + + int ndepth; /* Actual ramdac depth, typically = rdepth */ + int nent; /* Number of entries, = 2^ndepth, typically = 2^rdepth, */ + /* but may be different for some video cards. */ + /* Will be 0 if ramdac is not accessible */ + + double *v[3]; /* nent entries for RGB, values 0.0 - 1.0 */ /* Clone ourselves */ struct _ramdac *(*clone)(struct _ramdac *p); @@ -255,12 +263,12 @@ struct _dispwin { #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE CGDirectDisplayID ddid; void *osx_cntx; /* OSX specific info */ int btf; /* Flag, nz if window has been brought to the front once */ int winclose; /* Flag, set to nz if window was closed */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) Display *mydisplay; @@ -271,6 +279,9 @@ struct _dispwin { unsigned char *edid; /* 128 or 256 bytes of monitor EDID, NULL if none */ int edid_len; /* 128 or 256 */ + int shift[3]; /* Bit shift to create RGB value from components */ + int *rmap[3]; /* Map of fdepth to rdepth values */ + #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 /* Xrandr stuff - output is connected 1:1 to a display */ RRCrtc crtc; /* Associated crtc */ @@ -308,10 +319,21 @@ struct _dispwin { volatile int cberror; /* NZ if error detected in a callback routine */ int ddebug; /* >0 to print debug to stderr */ + int warned; /* Warning message has been issued */ + /* public: */ - int pdepth; /* Frame buffer plane depth of display */ - int edepth; /* Notional ramdac entry size in bits. (Bits actually used may be less) */ - /* This is used to scale out_tvenc appropriately */ + int fdepth; /* Frame buffer depth, typically 8, could be more */ + int rdepth; /* Expected ramdac index depth. May be different to fdepth */ + /* if there is another level of mapping between the frame buffer */ + /* and ramdac, i.e. X11 DirectorColor Colormap. */ + + int ndepth; /* Actual ramdac depth, typically = rdepth */ + int nent; /* Number of entries, = s^ndepth, typically = 2^rdepth, */ + /* but may be different for some video cards. */ + /* Will be 0 if ramdac is not accessible */ + + int edepth; /* Notional frame buffer/ramdac entry size in bits. (Bits actually used */ + /* may be less). This is just used to scale out_tvenc appropriately. */ /* Get RAMDAC values. ->del() when finished. */ /* Return NULL if not possible */ diff --git a/spectro/dtp20.c b/spectro/dtp20.c index 6af2484..a125a53 100644 --- a/spectro/dtp20.c +++ b/spectro/dtp20.c @@ -72,6 +72,7 @@ #include "conv.h" #include "icoms.h" #include "dtp20.h" +#include "xrga.h" static inst_code dtp20_interp_code(inst *pp, int ec); static inst_code activate_mode(dtp20 *p); @@ -345,12 +346,27 @@ dtp20_init_inst(inst *pp) { dtp20 *p = (dtp20 *)pp; char buf[MAX_MES_SIZE]; inst_code rv = inst_ok; + char *envv; a1logd(p->log, 2, "dtp20_init_inst: called\n"); if (p->gotcoms == 0) return inst_no_coms; /* Must establish coms before calling init */ + + p->native_calstd = xcalstd_xrdi; + p->target_calstd = xcalstd_native; /* Default to native calibration standard*/ + + /* Honour Environment override */ + if ((envv = getenv("ARGYLL_XCALSTD")) != NULL) { + if (strcmp(envv, "XRGA") == 0) + p->target_calstd = xcalstd_xrga; + else if (strcmp(envv, "XRDI") == 0) + p->target_calstd = xcalstd_xrdi; + else if (strcmp(envv, "GMDI") == 0) + p->target_calstd = xcalstd_gmdi; + } + /* Reset it (without disconnecting USB or clearing stored data) */ if ((rv = dtp20_command(p, "0PR\r", buf, MAX_MES_SIZE, 2.0)) != inst_ok) return rv; @@ -534,7 +550,8 @@ ipatch *vals) { /* Pointer to array of values */ tp += strlen(tp) + 1; } - if (p->mode & inst_mode_spectral) { + if (p->mode & inst_mode_spectral + || XCALSTD_NEEDED(p->target_calstd, p->native_calstd)) { /* Gather the results in Spectral reflectance */ if ((ev = dtp20_command(p, "0318CF\r", buf, MAX_RD_SIZE, 0.5)) != inst_ok) @@ -575,6 +592,11 @@ ipatch *vals) { /* Pointer to array of values */ } a1logv(p->log, 1, "All saved strips read\n"); + + /* Apply any XRGA conversion */ + ipatch_convert_xrga(vals, npatch, xcalstd_nonpol, p->target_calstd, p->native_calstd, + instClamp); + return inst_ok; } @@ -762,7 +784,8 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ } - if (p->mode & inst_mode_spectral) { + if (p->mode & inst_mode_spectral + || XCALSTD_NEEDED(p->target_calstd, p->native_calstd)) { /* Gather the results in Spectral reflectance */ if ((ev = dtp20_command(p, "0318CF\r", buf, MAX_RD_SIZE, 0.5)) != inst_ok) @@ -813,6 +836,11 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ } } + + /* Apply any XRGA conversion */ + ipatch_convert_xrga(vals, npatch, xcalstd_nonpol, p->target_calstd, p->native_calstd, + instClamp); + if (user_trig) return inst_user_trig; return inst_ok; @@ -993,7 +1021,8 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ val->duration = 0.0; - if (p->mode & inst_mode_spectral) { + if (p->mode & inst_mode_spectral + || XCALSTD_NEEDED(p->target_calstd, p->native_calstd)) { int j; /* Set to read spectral reflectance */ @@ -1051,6 +1080,9 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } } + /* Apply any XRGA conversion */ + ipatch_convert_xrga(val, 1, xcalstd_nonpol, p->target_calstd, p->native_calstd, clamp); + if (user_trig) return inst_user_trig; return inst_ok; @@ -1086,6 +1118,7 @@ static inst_code dtp20_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { dtp20 *p = (dtp20 *)pp; @@ -1098,6 +1131,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = dtp20_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -1136,6 +1170,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ; *cp = '\000'; strcpy(id, buf); + *idtype = inst_calc_id_ref_sn; *calc = inst_calc_man_ref_white; return inst_cal_setup; } @@ -1686,7 +1721,7 @@ inst_opt_type m, /* Requested status type */ } /* !! It's not clear if there is a way of knowing */ - /* whether the instrument has a UV filter. */ + /* whether the instrument has a UV filter !! */ /* Use default implementation of other inst_opt_type's */ { diff --git a/spectro/dtp20.h b/spectro/dtp20.h index c575c67..a8b6862 100644 --- a/spectro/dtp20.h +++ b/spectro/dtp20.h @@ -37,6 +37,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Fake Error codes */ #define DTP20_INTERNAL_ERROR 0x81 /* Internal software error */ #define DTP20_COMS_FAIL 0x82 /* Communication failure */ @@ -128,12 +132,17 @@ struct _dtp20 { int savix; /* Index of last saved spot reading read */ + xcalstd native_calstd; /* Instrument native calibration standard */ + xcalstd target_calstd; /* Returned calibration standard */ + }; typedef struct _dtp20 dtp20; /* Constructor */ extern dtp20 *new_dtp20(icoms *icom, instType itype); - +#ifdef __cplusplus + } +#endif #define DTP20_H #endif /* DTP20_H */ diff --git a/spectro/dtp22.c b/spectro/dtp22.c index 2cb3634..37249f0 100644 --- a/spectro/dtp22.c +++ b/spectro/dtp22.c @@ -32,6 +32,11 @@ and agreed to support. */ +/* + Would like to add a thread to return status of + switch, so that we can fully run this in progromatic trigger mode. + */ + #include <stdio.h> #include <stdlib.h> #include <ctype.h> @@ -51,6 +56,7 @@ #include "conv.h" #include "icoms.h" #include "dtp22.h" +#include "xrga.h" /* Default flow control (Instrument doesn't support HW flow control) */ #define DEFFC fc_XonXOff @@ -186,7 +192,7 @@ dtp22_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } else if (fc == fc_Hardware) { fcc = "0104CF\r"; } else { - fc = fc_none; + fc = fc_None; fcc = "0004CF\r"; } @@ -209,40 +215,37 @@ dtp22_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* The tick to give up on */ etime = msec_time() + (long)(1000.0 * tout + 0.5); - while (msec_time() < etime) { - a1logd(p->log, 4, "dtp22_init_coms: Trying different baud rates (%u msec to go)\n", - etime - msec_time()); - - /* Until we time out, find the correct baud rate */ - for (i = ci; msec_time() < etime;) { + /* Until we time out, find the correct baud rate */ + for (i = ci; msec_time() < etime;) { + + a1logd(p->log, 4, "dtp22_init_coms: Trying %s baud, %d msec to go\n", + baud_rate_to_str(brt[i]), etime- msec_time()); - if ((se = p->icom->set_ser_port(p->icom, fc_none, brt[i], parity_none, - stop_1, length_8)) != ICOM_OK) { - a1logd(p->log, 1, "dtp22_init_coms: set_ser_port failed ICOM err 0x%x\n",se); - return dtp22_interp_code((inst *)p, icoms2dtp22_err(se)); - } - if (((ev = dtp22_command(p, "\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) - != inst_coms_fail) - break; /* We've got coms */ - - /* Check for user abort */ - if (p->uicallback != NULL) { - inst_code ev; - if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { - a1logd(p->log, 1, "dtp22_init_coms: user aborted\n"); - return ev; - } + if ((se = p->icom->set_ser_port(p->icom, fc_None, brt[i], parity_none, + stop_1, length_8)) != ICOM_OK) { + a1logd(p->log, 1, "dtp22_init_coms: set_ser_port failed ICOM err 0x%x\n",se); + return dtp22_interp_code((inst *)p, icoms2dtp22_err(se)); + } + if (((ev = dtp22_command(p, "\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) + != inst_coms_fail) + goto got_coms; /* We've got coms or user abort */ + + /* Check for user abort */ + if (p->uicallback != NULL) { + inst_code ev; + if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { + a1logd(p->log, 1, "dtp22_init_coms: user aborted\n"); + return ev; } - if (++i >= 5) - i = 0; } - break; /* Got coms */ + if (++i >= 5) + i = 0; } + /* We haven't established comms */ + return inst_coms_fail; - if (msec_time() >= etime) { /* We haven't established comms */ - return inst_coms_fail; - } + got_coms:; /* Set the handshaking */ if ((ev = dtp22_command(p, fcc, buf, MAX_MES_SIZE, 0.2)) != inst_ok) @@ -287,6 +290,7 @@ dtp22_init_inst(inst *pp) { dtp22 *p = (dtp22 *)pp; char buf[MAX_MES_SIZE], *bp; inst_code ev = inst_ok; + char *envv; int i; a1logd(p->log, 2, "dtp22_init_inst: called\n"); @@ -294,6 +298,19 @@ dtp22_init_inst(inst *pp) { if (p->gotcoms == 0) return inst_internal_error; /* Must establish coms before calling init */ + p->native_calstd = xcalstd_xrdi; + p->target_calstd = xcalstd_native; /* Default to native calibration standard*/ + + /* Honour Environment override */ + if ((envv = getenv("ARGYLL_XCALSTD")) != NULL) { + if (strcmp(envv, "XRGA") == 0) + p->target_calstd = xcalstd_xrga; + else if (strcmp(envv, "XRDI") == 0) + p->target_calstd = xcalstd_xrdi; + else if (strcmp(envv, "GMDI") == 0) + p->target_calstd = xcalstd_gmdi; + } + /* Warm reset it */ if ((ev = dtp22_command(p, "0PR\r", buf, MAX_MES_SIZE, 2.0)) != inst_ok) return ev; @@ -333,7 +350,7 @@ dtp22_init_inst(inst *pp) { /* - - - - - - - - - - - - - - - - - - - - - - - - */ /* Get some information about the instrument */ - if ((ev = dtp22_command(p, "GI\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) { + if ((ev = dtp22_command(p, "GI\r", buf, MAX_MES_SIZE, 0.5)) != inst_ok) { a1logd(p->log, 1, "dtp22: GI command failed with ICOM err 0x%x\n",ev); return ev; } @@ -577,7 +594,8 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ val->sp.spec_n = 0; val->duration = 0.0; - if (p->mode & inst_mode_spectral) { + if (p->mode & inst_mode_spectral + || XCALSTD_NEEDED(p->target_calstd, p->native_calstd)) { int j; char *fmt; @@ -605,6 +623,9 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ val->sp.norm = 100.0; } + /* Apply any XRGA conversion */ + ipatch_convert_xrga(val, 1, xcalstd_nonpol, p->target_calstd, p->native_calstd, clamp); + if (user_trig) return inst_user_trig; return inst_ok; @@ -640,6 +661,7 @@ inst_code dtp22_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { dtp22 *p = (dtp22 *)pp; @@ -654,6 +676,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = dtp22_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -683,7 +706,8 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (*calt & inst_calt_ref_white) { /* White calibration */ - sprintf(id, "Serial no. %d",p->plaqueno); + *idtype = inst_calc_id_ref_sn; + sprintf(id, "%d",p->plaqueno); if ((*calc & inst_calc_cond_mask) != inst_calc_man_ref_whitek) { *calc = inst_calc_man_ref_whitek; ev = inst_cal_setup; @@ -1046,6 +1070,37 @@ dtp22_get_set_opt(inst *pp, inst_opt_type m, ...) { dtp22 *p = (dtp22 *)pp; + /* Set xcalstd */ + if (m == inst_opt_set_xcalstd) { + xcalstd standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd); + va_end(args); + + p->target_calstd = standard; + + return inst_ok; + } + + /* Get the current effective xcalstd */ + if (m == inst_opt_get_xcalstd) { + xcalstd *standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd *); + va_end(args); + + if (p->target_calstd == xcalstd_native) + *standard = p->native_calstd; /* If not overridden */ + else + *standard = p->target_calstd; /* Overidden std. */ + + return inst_ok; + } + /* Record the trigger mode */ if (m == inst_opt_trig_prog || m == inst_opt_trig_user @@ -1054,7 +1109,17 @@ dtp22_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } - return inst_unsupported; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/dtp22.h b/spectro/dtp22.h index 95dce16..af565e8 100644 --- a/spectro/dtp22.h +++ b/spectro/dtp22.h @@ -35,6 +35,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Note: update dtp22_interp_error() and dtp22_interp_code() in dtp22.c */ /* if anything of these #defines are added or subtracted */ @@ -95,11 +99,17 @@ struct _dtp22 { int noutocalib; /* Don't mode change or auto calibrate */ inst_opt_type trig; /* Reading trigger mode */ + xcalstd native_calstd; /* Instrument native calibration standard */ + xcalstd target_calstd; /* Returned calibration standard */ + }; typedef struct _dtp22 dtp22; /* Constructor */ extern dtp22 *new_dtp22(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define DTP22_H #endif /* DTP22_H */ diff --git a/spectro/dtp41.c b/spectro/dtp41.c index 44ac6c6..97a3db1 100644 --- a/spectro/dtp41.c +++ b/spectro/dtp41.c @@ -53,6 +53,7 @@ #include "conv.h" #include "icoms.h" #include "dtp41.h" +#include "xrga.h" /* Default flow control */ #define DEFFC fc_XonXOff @@ -169,7 +170,7 @@ dtp41_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } else if (fc == fc_Hardware) { fcc = "0104CF\r"; } else { - fc = fc_none; + fc = fc_None; fcc = "0004CF\r"; } @@ -192,36 +193,35 @@ dtp41_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* The tick to give up on */ etime = msec_time() + (long)(1000.0 * tout + 0.5); - while (msec_time() < etime) { + /* Until we time out, find the correct baud rate */ + for (i = ci; msec_time() < etime;) { + a1logd(p->log, 4, "dtp41_init_coms: Trying %s baud, %d msec to go\n", + baud_rate_to_str(brt[i]), etime- msec_time()); + if ((se = p->icom->set_ser_port(p->icom, fc_None, brt[i], parity_none, + stop_1, length_8)) != ICOM_OK) { + a1logd(p->log, 1, "dtp41_init_coms: set_ser_port failed ICOM err 0x%x\n",se); + return dtp41_interp_code((inst *)p, icoms2dtp41_err(se)); + } + if (((ev = dtp41_command(p, "\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) + != inst_coms_fail) + goto got_coms; /* We've got coms or user abort */ - /* Until we time out, find the correct baud rate */ - for (i = ci; msec_time() < etime;) { - if ((se = p->icom->set_ser_port(p->icom, fc_none, brt[i], parity_none, - stop_1, length_8)) != ICOM_OK) { - a1logd(p->log, 1, "dtp41_init_coms: set_ser_port failed ICOM err 0x%x\n",se); - return dtp41_interp_code((inst *)p, icoms2dtp41_err(se)); - } - if (((ev = dtp41_command(p, "\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) - != inst_coms_fail) - break; /* We've got coms */ - - /* Check for user abort */ - if (p->uicallback != NULL) { - inst_code ev; - if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { - a1logd(p->log, 1, "dtp41_init_coms: user aborted\n"); - return inst_user_abort; - } + + /* Check for user abort */ + if (p->uicallback != NULL) { + inst_code ev; + if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { + a1logd(p->log, 1, "dtp41_init_coms: user aborted\n"); + return inst_user_abort; } - if (++i >= 9) - i = 0; } - break; /* Got coms */ + if (++i >= 9) + i = 0; } + /* We haven't established comms */ + return inst_coms_fail; - if (msec_time() >= etime) { /* We haven't established comms */ - return inst_coms_fail; - } + got_coms:; /* set the protocol to RCI */ if ((ev = dtp41_command(p, "0012CF\r", buf, MAX_MES_SIZE, 1.5)) != inst_ok) @@ -319,6 +319,7 @@ static inst_code dtp41_init_inst(inst *pp) { dtp41 *p = (dtp41 *)pp; static char tbuf[100], buf[MAX_MES_SIZE]; + char *envv; inst_code ev = inst_ok; a1logd(p->log, 2, "dtp41_init_inst: called\n"); @@ -326,6 +327,19 @@ dtp41_init_inst(inst *pp) { if (p->gotcoms == 0) return inst_internal_error; /* Must establish coms before calling init */ + p->native_calstd = xcalstd_xrdi; + p->target_calstd = xcalstd_native; /* Default to native calibration standard*/ + + /* Honour Environment override */ + if ((envv = getenv("ARGYLL_XCALSTD")) != NULL) { + if (strcmp(envv, "XRGA") == 0) + p->target_calstd = xcalstd_xrga; + else if (strcmp(envv, "XRDI") == 0) + p->target_calstd = xcalstd_xrdi; + else if (strcmp(envv, "GMDI") == 0) + p->target_calstd = xcalstd_gmdi; + } + /* Resetting instrument resets the baud rate, so do manual reset. */ /* Set emulation mode to DTP41 */ @@ -603,7 +617,9 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ tp += strlen(tp) + 1; } - if (p->mode & inst_mode_spectral) { + if (p->mode & inst_mode_spectral + || (XCALSTD_NEEDED(p->target_calstd, p->native_calstd) + && ((p->mode & inst_mode_illum_mask) != inst_mode_transmission))) { /* Gather the results in Spectral reflectance */ if ((ev = dtp41_command(p, "0403TS\r", buf, MAX_RD_SIZE, 0.5 + npatch * 0.1)) != inst_ok) @@ -639,6 +655,11 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ tp += strlen(tp) + 1; } } + + /* Apply any XRGA conversion */ + ipatch_convert_xrga(vals, npatch, xcalstd_nonpol, p->target_calstd, p->native_calstd, + instClamp); + if (user_trig) return inst_user_trig; return inst_ok; @@ -790,7 +811,9 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ val->sp.spec_n = 0; val->duration = 0.0; - if (p->mode & inst_mode_spectral) { + if (p->mode & inst_mode_spectral + || (XCALSTD_NEEDED(p->target_calstd, p->native_calstd) + && ((p->mode & inst_mode_illum_mask) != inst_mode_transmission))) { int j; /* Gather the results in Spectral reflectance */ @@ -840,6 +863,9 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ if ((ev = dtp41_command(p, "0113CF\r", buf, MAX_MES_SIZE, 1.5)) != inst_ok) return ev; + /* Apply any XRGA conversion */ + ipatch_convert_xrga(val, 1, xcalstd_nonpol, p->target_calstd, p->native_calstd, clamp); + if (user_trig) return inst_user_trig; return inst_ok; @@ -881,6 +907,7 @@ inst_code dtp41_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { dtp41 *p = (dtp41 *)pp; @@ -892,6 +919,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = dtp41_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -919,7 +947,6 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ return inst_unsupported; } - if ((p->mode & inst_mode_illum_mask) == inst_mode_transmission) { if (*calt & inst_calt_trans_white) { @@ -1236,6 +1263,37 @@ dtp41_get_set_opt(inst *pp, inst_opt_type m, ...) inst_code rv = inst_ok; static char buf[MAX_MES_SIZE]; + /* Set xcalstd */ + if (m == inst_opt_set_xcalstd) { + xcalstd standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd); + va_end(args); + + p->target_calstd = standard; + + return inst_ok; + } + + /* Get the current effective xcalstd */ + if (m == inst_opt_get_xcalstd) { + xcalstd *standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd *); + va_end(args); + + if (p->target_calstd == xcalstd_native) + *standard = p->native_calstd; /* If not overridden */ + else + *standard = p->target_calstd; /* Overidden std. */ + + return inst_ok; + } + if (!p->gotcoms) return inst_no_coms; if (!p->inited) @@ -1259,7 +1317,17 @@ dtp41_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } - return inst_unsupported; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/dtp41.h b/spectro/dtp41.h index 529f94c..018ed5c 100644 --- a/spectro/dtp41.h +++ b/spectro/dtp41.h @@ -37,6 +37,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Fake Error codes */ #define DTP41_INTERNAL_ERROR 0x61 /* Internal software error */ #define DTP41_COMS_FAIL 0x62 /* Communication failure */ @@ -101,10 +105,17 @@ struct _dtp41 { int need_cal; /* needs calibration */ inst_opt_type trig; /* Reading trigger mode */ + xcalstd native_calstd; /* Instrument native calibration standard */ + xcalstd target_calstd; /* Returned calibration standard */ + }; typedef struct _dtp41 dtp41; /* Constructor */ extern dtp41 *new_dtp41(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif + #define DTP41_H #endif /* DTP41_H */ diff --git a/spectro/dtp51.c b/spectro/dtp51.c index ab1999d..0b2ecab 100644 --- a/spectro/dtp51.c +++ b/spectro/dtp51.c @@ -202,7 +202,7 @@ dtp51_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } else if (fc == fc_Hardware) { fcc = "0104CF\r"; } else { - fc = fc_none; + fc = fc_None; fcc = "0004CF\r"; } @@ -225,37 +225,36 @@ dtp51_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* The tick to give up on */ etime = msec_time() + (long)(1000.0 * tout + 0.5); - while (msec_time() < etime) { - - /* Until we time out, find the correct baud rate */ - for (i = ci; msec_time() < etime;) { - if ((se = p->icom->set_ser_port(p->icom, fc_none, brt[i], parity_none, - stop_1, length_8)) != ICOM_OK) { - a1logd(p->log, 1, "dtp51_init_coms: set_ser_port failed ICOM err 0x%x\n",se); - return dtp51_interp_code((inst *)p, icoms2dtp51_err(se)); - } + /* Until we time out, find the correct baud rate */ + for (i = ci; msec_time() < etime;) { + a1logd(p->log, 4, "dtp51_init_coms: Trying %s baud, %d msec to go\n", + baud_rate_to_str(brt[i]), etime- msec_time()); + if ((se = p->icom->set_ser_port(p->icom, fc_None, brt[i], parity_none, + stop_1, length_8)) != ICOM_OK) { + a1logd(p->log, 1, "dtp51_init_coms: set_ser_port failed ICOM err 0x%x\n",se); + return dtp51_interp_code((inst *)p, icoms2dtp51_err(se)); + } - if (((ev = dtp51_command(p, "\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) - != inst_coms_fail) - break; /* We've got coms */ + if (((ev = dtp51_command(p, "\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) + != inst_coms_fail) + goto got_coms; /* We've got coms or user abort */ - /* Check for user abort */ - if (p->uicallback != NULL) { - inst_code ev; - if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { - a1logd(p->log, 1, "dtp22_init_coms: user aborted\n"); - return inst_user_abort; - } + /* Check for user abort */ + if (p->uicallback != NULL) { + inst_code ev; + if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { + a1logd(p->log, 1, "dtp22_init_coms: user aborted\n"); + return inst_user_abort; } - if (++i >= 5) - i = 0; } - break; /* Got coms */ + if (++i >= 5) + i = 0; } - if (msec_time() >= etime) { /* We haven't established comms */ - return inst_coms_fail; - } + /* We haven't established comms */ + return inst_coms_fail; + + got_coms:; /* Set the handshaking */ if ((ev = dtp51_command(p, fcc, buf, MAX_MES_SIZE, 1.5)) != inst_ok) @@ -595,6 +594,7 @@ inst_code dtp51_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { dtp51 *p = (dtp51 *)pp; @@ -606,6 +606,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = dtp51_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -857,8 +858,7 @@ inst_code dtp51_set_mode(inst *pp, inst_mode m) { * was assume that all of these can be done before initialisation. */ static inst_code -dtp51_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +dtp51_get_set_opt(inst *pp, inst_opt_type m, ...) { dtp51 *p = (dtp51 *)pp; static char buf[MAX_MES_SIZE]; @@ -868,7 +868,31 @@ dtp51_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } - return inst_unsupported; + /* Get the current effective xcalstd */ + if (m == inst_opt_get_xcalstd) { + xcalstd *standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd *); + va_end(args); + + *standard = p->native_calstd; + + return inst_ok; + } + + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ @@ -896,5 +920,7 @@ extern dtp51 *new_dtp51(icoms *icom, instType itype) { p->icom = icom; p->itype = itype; + p->native_calstd = xcalstd_xrdi; /* Not alterable */ + return p; } diff --git a/spectro/dtp51.h b/spectro/dtp51.h index 329c5d6..804b8ae 100644 --- a/spectro/dtp51.h +++ b/spectro/dtp51.h @@ -35,6 +35,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Fake Error codes */ #define DTP51_INTERNAL_ERROR 0x61 /* Internal software error */ #define DTP51_COMS_FAIL 0x62 /* Communication failure */ @@ -91,12 +95,16 @@ struct _dtp51 { int need_cal; /* needs calibration */ inst_opt_type trig; /* Reading trigger mode */ - }; typedef struct _dtp51 dtp51; + xcalstd native_calstd; /* Instrument native calibration standard */ + +}; typedef struct _dtp51 dtp51; /* Constructor */ extern dtp51 *new_dtp51(icoms *icom, instType itype); - +#ifdef __cplusplus + } +#endif #define DTP51_H #endif /* DTP51_H */ diff --git a/spectro/dtp92.c b/spectro/dtp92.c index 3b6dacd..33b8fa0 100644 --- a/spectro/dtp92.c +++ b/spectro/dtp92.c @@ -58,7 +58,7 @@ #include "dtp92.h" /* Default flow control */ -#define DEFFC fc_none +#define DEFFC fc_None #define DEF_TIMEOUT 0.5 #define MED_TIMEOUT 2.5 @@ -147,7 +147,8 @@ dtp92_fcommand( icoms_fix(in), icoms_fix(out),rv); #ifdef IGNORE_NEEDS_OFFSET_DRIFT_CAL_ERR - if (strcmp(in, "0PR\r") == 0 && rv == DTP92_NEEDS_OFFSET_DRIFT_CAL) { + if ((strcmp(in, "0PR\r") == 0 + || strcmp(in, "EFC\r") == 0) && rv == DTP92_NEEDS_OFFSET_DRIFT_CAL) { static int warned = 0; if (!warned) { a1logw(p->log,"dtp92: Got error NEEDS_OFFSET_DRIFT_CAL on instrument reset - being ignored."); @@ -239,7 +240,7 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } else if (fc == fc_Hardware) { fcc = "0104CF\r"; } else { - fc = fc_none; + fc = fc_None; fcc = "0004CF\r"; } @@ -262,41 +263,42 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* The tick to give up on */ etime = msec_time() + (long)(1000.0 * tout + 0.5); - while (msec_time() < etime) { + /* Until we time out, find the correct baud rate */ + for (i = ci; msec_time() < etime;) { + a1logd(p->log, 4, "dtp92_init_coms: Trying %s baud, %d msec to go\n", + baud_rate_to_str(brt[i]), etime- msec_time()); + if ((se = p->icom->set_ser_port(p->icom, fc_None, brt[i], parity_none, + stop_1, length_8)) != ICOM_OK) { + a1logd(p->log, 1, "dtp92_init_coms: set_ser_port failed ICOM err 0x%x\n",se); + return dtp92_interp_code((inst *)p, icoms2dtp92_err(se)); + } - a1logd(p->log, 4, "dtp92_init_coms: Trying different baud rates (%u msec to go)\n", - etime - msec_time()); + /* Throw one response away, to work around some USB<->Serial quirks */ + p->icom->write_read(p->icom, "\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.1); - /* Until we time out, find the correct baud rate */ - for (i = ci; msec_time() < etime;) { - if ((se = p->icom->set_ser_port(p->icom, fc_none, brt[i], parity_none, - stop_1, length_8)) != ICOM_OK) { - a1logd(p->log, 1, "dtp92_init_coms: set_ser_port failed ICOM err 0x%x\n",se); - return dtp92_interp_code((inst *)p, icoms2dtp92_err(se)); - } + if (((ev = dtp92_command(p, "\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) & inst_mask) + != inst_coms_fail) + goto got_coms; /* We've got coms or user abort */ - if (((ev = dtp92_command(p, "\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) & inst_mask) - != inst_coms_fail) - break; /* We've got coms or user abort */ - - /* Check for user abort */ - if (p->uicallback != NULL) { - inst_code ev; - if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { - a1logd(p->log, 1, "dtp92_init_coms: user aborted\n"); - return inst_user_abort; - } + /* Check for user abort */ + if (p->uicallback != NULL) { + inst_code ev; + if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { + a1logd(p->log, 1, "dtp92_init_coms: user aborted\n"); + return inst_user_abort; } - - if (++i >= 5) - i = 0; } - break; /* Got coms */ - } - if (msec_time() >= etime) { /* We haven't established comms */ - return inst_coms_fail; + if (++i >= 5) + i = 0; } + /* We haven't established comms */ + return inst_coms_fail; + + got_coms:; + + /* Throw one response away, to work around some USB<->Serial quirks */ + p->icom->write_read(p->icom, "\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.1); /* Set the handshaking */ if ((ev = dtp92_command(p, fcc, buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) @@ -317,6 +319,7 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* Loose a character (not sure why) */ p->icom->write_read(p->icom, "\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.1); + #else /* !ENABLE_SERIAL */ a1logd(p->log, 1, "dtp92: Failed to find serial connection to instrument\n"); return inst_coms_fail; @@ -621,6 +624,14 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ if (sscanf(buf, " X%*c %lf\t Y%*c %lf\t Z%*c %lf ", &val->XYZ[0], &val->XYZ[1], &val->XYZ[2]) == 3) { + /* Hmm. The DTP92 seems to return strange X & Z values if the light */ + /* level is zero, and it's black level is out of calibration */ + /* (i.e. internally it has -ve Y ?) */ + if (val->XYZ[1] == 0.0 && val->XYZ[0] == 999.99) + val->XYZ[0] = 0.0; + if (val->XYZ[1] == 0.0 && val->XYZ[2] == 999.99) + val->XYZ[2] = 0.0; + /* Apply the colorimeter correction matrix */ icmMulBy3x3(val->XYZ, p->ccmat, val->XYZ); @@ -778,6 +789,7 @@ static inst_code dtp92_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { dtp92 *p = (dtp92 *)pp; @@ -790,6 +802,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = dtp92_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -1356,8 +1369,7 @@ int *cbid) { * error if it hasn't been initialised. */ static inst_code -dtp92_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +dtp92_get_set_opt(inst *pp, inst_opt_type m, ...) { dtp92 *p = (dtp92 *)pp; char buf[MAX_MES_SIZE]; inst_code ev = inst_ok; @@ -1369,12 +1381,17 @@ dtp92_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } - if (!p->gotcoms) - return inst_no_coms; - if (!p->inited) - return inst_no_init; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); - return inst_unsupported; + return rv; + } } /* Constructor */ diff --git a/spectro/dtp92.h b/spectro/dtp92.h index 317fb7a..8a6885e 100644 --- a/spectro/dtp92.h +++ b/spectro/dtp92.h @@ -35,6 +35,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Note: update dtp92_interp_error() and dtp92_interp_code() in dtp92.c */ /* if anything of these #defines are added or subtracted */ @@ -100,6 +104,9 @@ struct _dtp92 { /* Constructor */ extern dtp92 *new_dtp92(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define DTP92_H #endif /* DTP92_H */ diff --git a/spectro/ex1.c b/spectro/ex1.c index b336cbc..5f4eae2 100644 --- a/spectro/ex1.c +++ b/spectro/ex1.c @@ -328,7 +328,9 @@ ex1_init_inst(inst *pp) { //printf("~1 raw range = %d - %d\n",sconf->rawrange.off, sconf->rawrange.off + sconf->rawrange.num-1); //printf("~1 = %f - %f nm\n",rspec_raw2nm(sconf, sconf->rawrange.off), rspec_raw2nm(sconf, sconf->rawrange.off + sconf->rawrange.num-1)); - sconf->ktype = rspec_gausian; + sconf->ktype = rspec_gausian; /* Default */ +// sconf->ktype = rspec_lanczos2; +// sconf->ktype = rspec_lanczos3; // sconf->ktype = rspec_triangle; // sconf->ktype = rspec_cubicspline; sconf->wl_space = 2.0; @@ -702,6 +704,7 @@ inst_code ex1_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { ex1 *p = (ex1 *)pp; @@ -1160,8 +1163,7 @@ static void ex1_set_noinitcalib(ex1 *p, int v, int losecs) { * error if it hasn't been initialised. */ static inst_code -ex1_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +ex1_get_set_opt(inst *pp, inst_opt_type m, ...) { ex1 *p = (ex1 *)pp; inst_code ev = inst_ok; @@ -1194,7 +1196,17 @@ ex1_get_set_opt(inst *pp, inst_opt_type m, ...) if (!p->inited) return inst_no_init; - return inst_unsupported; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/ex1.h b/spectro/ex1.h index d69b7ca..a278777 100644 --- a/spectro/ex1.h +++ b/spectro/ex1.h @@ -37,6 +37,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Communication errors */ #define EX1_TIMEOUT 0xFF02 /* Communication timeout */ #define EX1_COMS_FAIL 0xFF03 /* Communication failure */ @@ -143,6 +147,9 @@ struct _ex1 { /* Constructor */ extern ex1 *new_ex1(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define EX1_H #endif /* EX1_H */ diff --git a/spectro/hcfr.c b/spectro/hcfr.c index a6c457e..22b5170 100644 --- a/spectro/hcfr.c +++ b/spectro/hcfr.c @@ -366,7 +366,7 @@ hcfr_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { inst_code ev = inst_ok; icomuflags usbflags = icomuf_no_open_clear | icomuf_detach; -#if defined(__APPLE__) && !defined(__ppc__) +#if defined(UNIX_APPLE) && !defined(__ppc__) /* Except on Intel OS X 10.4/5 for some reasone. */ /* It would be good if the HCFR had a better USB implementation... */ usbflags &= ~icomuf_no_open_clear; @@ -919,7 +919,17 @@ hcfr_get_set_opt(inst *pp, inst_opt_type m, ...) { return inst_ok; } - return inst_unsupported; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/hcfr.h b/spectro/hcfr.h index 8910cec..2420fdf 100644 --- a/spectro/hcfr.h +++ b/spectro/hcfr.h @@ -35,11 +35,14 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Required minimum firmware version */ #define HCFR_FIRMWARE_MAJOR_VERSION 5 #define HCFR_FIRMWARE_MINOR_VERSION 0 - /* Command byte contents. (A value of 0x00 won't be tranmsitted properly) */ /* 0xff = get firmware version */ @@ -99,6 +102,9 @@ struct _hcfr { /* Constructor */ extern hcfr *new_hcfr(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define HCFR_H #endif /* HCFR_H */ diff --git a/spectro/hidio.c b/spectro/hidio.c index c930755..2d8649f 100644 --- a/spectro/hidio.c +++ b/spectro/hidio.c @@ -178,7 +178,7 @@ int hid_get_paths(icompaths *p) { pdidd->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); dinfod.cbSize = sizeof(SP_DEVINFO_DATA); for (i = 0; ; i++) { - instType itype; + devType itype; if (SetupDiEnumDeviceInterfaces(hdinfo, NULL, &HidGuid, i, &did) == 0) { if (GetLastError() == ERROR_NO_MORE_ITEMS) { @@ -269,7 +269,7 @@ int hid_get_paths(icompaths *p) { } #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE # if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { CFAllocatorContext alocctx; @@ -308,7 +308,7 @@ int hid_get_paths(icompaths *p) { CFNumberRef vref, pref; /* HID Vendor and Product ID propeties */ CFNumberRef lidpref; /* Location ID properties */ unsigned int vid = 0, pid = 0, lid = 0; - instType itype; + devType itype; IOHIDDeviceRef ioob = values[i]; /* HID object found */ if ((vref = IOHIDDeviceGetProperty(ioob, CFSTR(kIOHIDVendorIDKey))) != NULL) { @@ -376,7 +376,7 @@ int hid_get_paths(icompaths *p) { CFNumberRef vref, pref; /* HID Vendor and Product ID propeties */ CFNumberRef lidpref; /* Location ID properties */ unsigned int vid = 0, pid = 0, lid = 0; - instType itype; + devType itype; if ((ioob = IOIteratorNext(mit)) == 0) break; @@ -426,7 +426,7 @@ int hid_get_paths(icompaths *p) { IOObjectRelease(mit); /* Release the itterator */ } #endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) @@ -483,14 +483,14 @@ int hid_get_paths(icompaths *p) { #endif /* NEVER */ #endif /* UNIX_X11 */ - a1logd(p->log, 8, "icoms_get_paths: returning %d paths and ICOM_OK\n",p->npaths); + a1logd(p->log, 8, "icoms_get_paths: returning %d paths and ICOM_OK\n",p->ndpaths[dtix_combined]); return ICOM_OK; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE /* HID Interrupt callback for OS X */ /* This seems to only get called when the run loop is active. */ @@ -516,7 +516,7 @@ UInt32 size a1logd(p->log, 8, "HID callback has no run loop\n"); } -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -571,7 +571,7 @@ char **pnames /* List of process names to try and kill before opening */ } #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE # if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { /* Open the device */ @@ -644,7 +644,7 @@ char **pnames /* List of process names to try and kill before opening */ } } #endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ p->is_open = 1; a1logd(p->log, 8, "hid_open_port: HID port is now open\n"); @@ -669,7 +669,7 @@ void hid_close_port(icoms *p) { CloseHandle(p->hidd->fh); #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE # if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 if (IOHIDDeviceClose(p->hidd->ioob, kIOHIDOptionsTypeNone) != kIOReturnSuccess) { a1loge(p->log, ICOM_SYS, "hid_close_port: closing HID port '%s' failed",p->name); @@ -701,7 +701,7 @@ void hid_close_port(icoms *p) { } p->hidd->device = NULL; #endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ p->is_open = 0; a1logd(p->log, 8, "hid_close_port: has been released and closed\n"); @@ -770,7 +770,7 @@ icoms_hid_read(icoms *p, } #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE # if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { IOReturn result; @@ -899,7 +899,7 @@ printf("~1 IOHIDDeviceGet returned 0x%x\n",result); } #endif // NEVER #endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ if (breadp != NULL) *breadp = bread; @@ -968,7 +968,7 @@ icoms_hid_write(icoms *p, } #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE # if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { IOReturn result; @@ -1039,7 +1039,7 @@ printf("~1 IOHIDDeviceSetReportWithCallback returned 0x%x\n",result); } } #endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ if (bwrittenp != NULL) *bwrittenp = bwritten; @@ -1101,7 +1101,7 @@ int hid_copy_hid_idevice(icoms *d, icompath *s) { return ICOM_SYS; } #endif -#if defined(__APPLE__) +#if defined(UNIX_APPLE) # if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 d->hidd->ioob = s->hidd->ioob; CFRetain(d->hidd->ioob); @@ -1109,7 +1109,7 @@ int hid_copy_hid_idevice(icoms *d, icompath *s) { d->hidd->ioob = s->hidd->ioob; IOObjectRetain(d->hidd->ioob); #endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined (UNIX_X11) #endif return ICOM_OK; @@ -1124,7 +1124,7 @@ void hid_del_hid_idevice(struct hid_idevice *hidd) { if (hidd->dpath != NULL) free(hidd->dpath); #endif -#if defined(__APPLE__) +#if defined(UNIX_APPLE) # if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 if (hidd->ioob != 0) CFRelease(hidd->ioob); @@ -1132,7 +1132,7 @@ void hid_del_hid_idevice(struct hid_idevice *hidd) { if (hidd->ioob != 0) IOObjectRelease(hidd->ioob); #endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined (UNIX_X11) #endif free(hidd); diff --git a/spectro/hidio.h b/spectro/hidio.h index 90a741b..87edd43 100644 --- a/spectro/hidio.h +++ b/spectro/hidio.h @@ -1,7 +1,7 @@ #ifndef HIDIO_H - /* General USB HID I/O support */ +/* General USB HID I/O support */ /* * Argyll Color Correction System @@ -16,16 +16,17 @@ * see the License2.txt file for licencing details. */ + /* These routines supliement the class code in ntio.c and unixio.c */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE #include <sys/param.h> #include <IOKit/IOKitLib.h> #include <IOKit/IOCFPlugIn.h> #include <IOKit/hid/IOHIDLib.h> #include <IOKit/hid/IOHIDKeys.h> #include <CoreFoundation/CoreFoundation.h> -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #ifdef __cplusplus extern "C" { @@ -59,7 +60,7 @@ struct hid_idevice { HANDLE fh; /* File handle for write/read */ OVERLAPPED ols; /* Overlapped structure for write/read */ #endif -#if defined(__APPLE__) +#if defined(UNIX_APPLE) # if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 int lid; /* Location ID */ IOHIDDeviceRef ioob; /* Object to open */ diff --git a/spectro/huey.c b/spectro/huey.c index b09ed2c..2c82b4c 100644 --- a/spectro/huey.c +++ b/spectro/huey.c @@ -885,7 +885,8 @@ huey_check_unlock( if ((ev = huey_command(p, i1d_status, buf, buf, 1.0,1.0)) != inst_ok) return ev; - if (strncmp((char *)buf, "Locked", 6) == 0) { + /* Hmm. Some Lenovo Huey's say they are unlocked, even when they are not. */ + if (p->lenovo || strncmp((char *)buf, "Locked", 6) == 0) { memset(buf, 0, 7); if (p->lenovo) strcpy((char *)buf,"huyL"); @@ -902,6 +903,7 @@ huey_check_unlock( if (strncmp((char *)buf, "huL002", 6) != 0 /* Lenovo Huey ? */ && strncmp((char *)buf, "ECCM2 ", 6) != 0 /* Lenovo Thinkpad W530 Huey ? */ + && strncmp((char *)buf, "ECCM3 ", 6) != 0 /* Lenovo Thinkpad W530 Huey ? */ && strncmp((char *)buf, "Cir001", 6) != 0) { /* Huey */ a1logd(p->log,1,"huey_check_unlock: unknown model '%s'\n",buf); return huey_interp_code((inst *)p, HUEY_UNKNOWN_MODEL); @@ -920,7 +922,7 @@ huey_read_all_regs( inst_code ev; int i; - a1logd(p->log,2,"huey_check_unlock: about to read all the registers\n"); + a1logd(p->log,2,"huey_read_all_regs: about to read all the registers\n"); /* Serial number */ if ((ev = huey_rdreg_word(p, &p->ser_no, 0) ) != inst_ok) @@ -988,7 +990,7 @@ huey_read_all_regs( return ev; a1logd(p->log,3,"Integration time = %d\n",p->int_clocks); - a1logd(p->log,2,"huey_check_unlock: all registers read OK\n"); + a1logd(p->log,2,"huey_read_all_regs: all registers read OK\n"); return inst_ok; } @@ -1671,8 +1673,7 @@ int *cbid) { * error if it hasn't been initialised. */ static inst_code -huey_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +huey_get_set_opt(inst *pp, inst_opt_type m, ...) { huey *p = (huey *)pp; inst_code ev = inst_ok; @@ -1717,7 +1718,17 @@ huey_get_set_opt(inst *pp, inst_opt_type m, ...) return huey_set_LEDs(p, mask); } - return inst_unsupported; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/huey.h b/spectro/huey.h index aec96a0..f13056b 100644 --- a/spectro/huey.h +++ b/spectro/huey.h @@ -37,6 +37,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Note: update huey_interp_error() and huey_interp_code() in huey.c */ /* if anything of these #defines are added or subtracted */ @@ -131,6 +135,9 @@ struct _huey { /* Constructor */ extern huey *new_huey(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define HUEY_H #endif /* HUEY_H */ diff --git a/spectro/i1d3.c b/spectro/i1d3.c index df7d3f0..4562a52 100644 --- a/spectro/i1d3.c +++ b/spectro/i1d3.c @@ -2464,6 +2464,8 @@ i1d3_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* On Linux, the i1d3 doesn't seem to close properly, and won't re-open - */ /* something to do with detaching the default HID driver ?? */ #if defined(UNIX_X11) + usbflags |= icomuf_detach; + usbflags |= icomuf_no_open_clear; usbflags |= icomuf_reset_before_close; #endif /* Open as an HID if available */ @@ -2485,7 +2487,7 @@ i1d3_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* Set config, interface, write end point, read end point */ /* ("serial" end points aren't used - the i1d3 uses USB control messages) */ /* We need to detatch the HID driver on Linux */ - if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, usbflags | icomuf_detach, 0, NULL)) + if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, usbflags, 0, NULL)) != ICOM_OK) { a1logd(p->log, 1, "i1d3_init_coms: set_usb_port failed ICOM err 0x%x\n",se); return i1d3_interp_code((inst *)p, icoms2i1d3_err(se, 0)); @@ -2695,6 +2697,8 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ if (!p->inited) return inst_no_init; + a1logd(p->log, 1, "i1d3: i1d3_read_sample called\n"); + if (p->trig == inst_opt_trig_user) { if (p->uicallback == NULL) { @@ -2944,6 +2948,7 @@ static inst_code i1d3_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { i1d3 *p = (i1d3 *)pp; @@ -2955,6 +2960,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = i1d3_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -3816,8 +3822,7 @@ int *cbid) { * error if it hasn't been initialised. */ static inst_code -i1d3_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +i1d3_get_set_opt(inst *pp, inst_opt_type m, ...) { i1d3 *p = (i1d3 *)pp; inst_code ev; @@ -4018,7 +4023,18 @@ i1d3_get_set_opt(inst *pp, inst_opt_type m, ...) if (trans_time_prop != NULL) *trans_time_prop = p->led_trans_time_prop; return inst_ok; } - return inst_unsupported; + + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/i1d3.h b/spectro/i1d3.h index bb239df..a7c3b24 100644 --- a/spectro/i1d3.h +++ b/spectro/i1d3.h @@ -37,6 +37,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Note: update huey_interp_error() and huey_interp_code() in huey.c */ /* if anything of these #defines are added or subtracted */ @@ -79,7 +83,7 @@ typedef enum { i1d3_disppro = 0, /* i1 DisplayPro */ i1d3_munkdisp = 1, /* ColorMunki Display */ - i1d3_oem = 2, /* OEM */ + i1d3_oem = 2, /* Generic OEM */ i1d3_nec_ssp = 3, /* NEC SpectraSensor Pro */ i1d3_quato_sh3 = 4, /* Quato Silver Haze 3 */ i1d3_hp_dreamc = 5, /* HP DreameColor */ @@ -169,6 +173,9 @@ struct _i1d3 { /* Constructor */ extern i1d3 *new_i1d3(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define I1D3_H #endif /* I1D3_H */ diff --git a/spectro/i1disp.c b/spectro/i1disp.c index 489c6cd..9e4b01f 100644 --- a/spectro/i1disp.c +++ b/spectro/i1disp.c @@ -481,7 +481,7 @@ i1disp_rdexreg_bytes( ) { unsigned char ibuf[16]; unsigned char obuf[16]; - int rsize; + int ooff, rsize; inst_code ev; if (p->dtype != 2) /* Only ColorMunki Smile ? */ @@ -493,7 +493,7 @@ i1disp_rdexreg_bytes( if (len < 0 || (addr + len) > 0x0200) return i1disp_interp_code((inst *)p, I1DISP_BAD_REG_ADDRESS); - for (; len > 0; ) { + for (ooff = 0; len > 0; ) { int rlen = len; if (rlen > 4) rlen = 4; @@ -512,7 +512,8 @@ i1disp_rdexreg_bytes( if (obuf[0] != rlen) /* Number of bytes returned */ return i1disp_interp_code((inst *)p, I1DISP_UNEXPECTED_RET_VAL); - memcpy(outp + addr, obuf + 1, rlen); + memcpy(outp + ooff, obuf + 1, rlen); + ooff += rlen; addr += rlen; len -= rlen; } @@ -643,6 +644,7 @@ i1d1_take_measurement( int i; int edgec[3]; /* Edge count 1..255 for each channel */ inst_code ev; + double edge_aim = p->clk_freq; if (p->inited == 0) return i1disp_interp_code((inst *)p, I1DISP_NOT_INITED); @@ -659,13 +661,18 @@ i1d1_take_measurement( a1logd(p->log, 3, "Initial RGB = %f %f %f\n",rgb[0],rgb[1],rgb[2]); /* Compute adjusted edge count, aiming */ - /* for count values of clk_freq = 1 second (~1e6). */ + /* for count values of clk_freq = 1 second (~1e6), */ + /* or 2 seconds if an older instrument */ + if (p->stype == i1d1_sencoreIV + || p->stype == i1d1_sencoreIII) + edge_aim = 2.0 * p->clk_freq;; + for (i = 0; i < 3; i++) { double ns; - if (p->clk_freq > ((255.0 - 0.5) * rgb[i])) + if (edge_aim > ((255.0 - 0.5) * rgb[i])) ns = 255.0; else { - ns = floor(p->clk_freq/rgb[i]) + 0.5; + ns = floor(edge_aim/rgb[i]) + 0.5; if (ns < 1.0) ns = 1.0; } @@ -1254,12 +1261,13 @@ i1disp_take_XYZ_measurement( for (j = 0; j < 3; j++) { XYZ[i] += mat[i * 3 + j] * rgb[j]; } - XYZ[i] *= CALFACTOR; /* Times magic scale factor */ -#ifdef NEVER - if (p->chroma4) - XYZ[i] *= 4.0/3.0; /* (Not sure about this factor!) */ -#endif + + /* Magic factors for other devices ?? */ + if (p->stype == i1d1_sencoreIV) + XYZ[i] *= CALFACTOR; /* (Not sure about this factor!) */ + else + XYZ[i] *= CALFACTOR; /* Times magic scale factor */ } if (!IMODETST(p->mode, inst_mode_emis_ambient)) { @@ -1402,25 +1410,26 @@ i1disp_check_unlock( struct { unsigned char code[4]; - int *flag; + i1d2_dtype stype; } codes[] = { - { { 'G','r','M','b' }, NULL }, /* "GrMb" i1 Display */ - { { 'L','i','t','e' }, &p->lite }, /* "Lite" i1 Display LT */ - { { 'M','u','n','k' }, &p->munki }, /* "Munk" ColorMunki Create */ - { { 'O','b','i','W' }, &p->hpdream }, /* "ObiW" HP DreamColor */ - { { 'O','b','i','w' }, &p->hpdream }, /* "Obiw" HP DreamColor */ - { { 'C','M','X','2' }, &p->calmanx2 }, /* "CMX2" Calman X2 */ - { { 0x24,0xb6,0xb5,0x13 }, NULL }, /* ColorMunki Smile */ - { { 'S','p','C','3' }, NULL }, /* SpectraCal C3 (Based on Smile) */ - { { 'R','G','B','c' }, NULL }, /* */ - { { 'C','E','C','5' }, NULL }, /* */ - { { 'C','M','C','5' }, NULL }, /* */ - { { 'C','M','G','5' }, NULL }, /* */ - { { 0x00,0x00,0x01,0x00 }, NULL }, /* */ - { { 0x09,0x0b,0x0c,0x0d }, NULL }, /* */ - { { 0x0e,0x0e,0x0e,0x0e }, NULL }, /* */ - { { 0x11,0x02,0xde,0xf0 }, NULL }, /* Barco Chroma 5 ? */ - { { ' ',' ',' ',' ' }, (int *)-1 } + { { 'G','r','M','b' }, i1d2_norm }, /* "GrMb" i1 Display */ + { { 'L','i','t','e' }, i1d2_lite }, /* "Lite" i1 Display LT */ + { { 'M','u','n','k' }, i1d2_munki }, /* "Munk" ColorMunki Create */ + { { 'O','b','i','W' }, i1d2_hpdream }, /* "ObiW" HP DreamColor */ + { { 'O','b','i','w' }, i1d2_hpdream }, /* "Obiw" HP DreamColor */ + { { 'C','M','X','2' }, i1d1_calmanx2 }, /* "CMX2" Calman X2 */ + { { 0x24,0xb6,0xb5,0x13 }, i1d2_norm }, /* ColorMunki Smile */ + { { 'S','p','C','3' }, i1d2_norm }, /* SpectraCal C3 (Based on Smile) */ + { { 'R','G','B','c' }, i1d2_norm }, /* */ + { { 'C','E','C','5' }, i1d2_norm }, /* */ + { { 'C','M','C','5' }, i1d2_norm }, /* */ + { { 'C','M','G','5' }, i1d2_norm }, /* */ + { { 0x00,0x00,0x01,0x00 }, i1d2_norm }, /* */ + { { 0x09,0x0b,0x0c,0x0d }, i1d2_norm }, /* */ + { { 0x0e,0x0e,0x0e,0x0e }, i1d2_norm }, /* */ + { { 0x11,0x02,0xde,0xf0 }, i1d2_norm }, /* Barco Chroma 5 ? */ +// { { 0xff,0xff,0xff,0xff }, i1d2_norm }, /* Chroma 5 isn't locked ? */ + { { ' ',' ',' ',' ' }, -1 } }; a1logd(p->log, 3, "i1disp: about to check response and unlock instrument if needed\n"); @@ -1433,17 +1442,9 @@ i1disp_check_unlock( /* Try and unlock it if it is locked */ if ((ev & inst_imask) == I1DISP_LOCKED) { - /* Reset any flags */ - for (i = 0; ;i++) { - if (codes[i].flag == (int *)-1) - break; - if (codes[i].flag != NULL) - *codes[i].flag = 0; - } - /* Try each code in turn */ for (i = 0; ;i++) { - if (codes[i].flag == (int *)-1) { + if (codes[i].stype == -1) { a1logd(p->log, 3, "Failed to find correct unlock code\n"); return i1disp_interp_code((inst *)p, I1DISP_UNKNOWN_MODEL); } @@ -1461,8 +1462,9 @@ i1disp_check_unlock( return ev; /* An error other than being locked */ if (ev == inst_ok) { /* Correct code */ - if (codes[i].flag != NULL) - *codes[i].flag = 1; + p->stype = codes[i].stype; + a1logd(p->log, 3, "Unlocked with code '%c%c%c%c'\n", + codes[i].code[0], codes[i].code[1], codes[i].code[2], codes[i].code[3]); break; } } @@ -1485,15 +1487,30 @@ i1disp_check_unlock( a1logd(p->log, 3, "Version character = 0x%02x = '%c'\n",vv,vv); - /* Sequel Chroma 4 with vv == 0xff ? */ - /* Barco Chroma 5 with ver = 5.01 and vv = '5' */ - if (ver >= 4.0 && ver < 5.1 - && (vv == 0xff || vv == 0x35)) { + /* Barco Chroma 5 with ver = 5.01 vv = '5' */ + if (ver >= 4.0 && ver < 5.1 && vv == '5') { p->dtype = 0; /* Sequel Chroma 4 ?? */ - p->chroma4 = 1; /* Treat like an Eye-One Display 1 */ - /* !!! Not fully tested !!! */ + p->stype = i1d1_chroma4; /* Treat like an Eye-One Display 1 */ + + /* Sequel Chroma 4 with vv == 0xff ???? */ + /* Sencore ColorPro III with ver = 5.01 and vv = 0xff */ + } else if (ver >= 4.0 && ver < 5.1 && vv == 0xff) { + p->dtype = 0; /* Eye-One Display 1 */ + p->stype = i1d1_sencoreIII; + + /* Sencore ColorPro IV with ver = 5.01 and vv = 'L' */ + } else if (ver >= 4.0 && ver < 5.1 && vv == 'L') { + p->dtype = 0; /* Eye-One Display 1 */ + p->stype = i1d1_sencoreIV; /* Treat like an Eye-One Display 1 */ + + /* Sencore ColorPro V with ver = 5.01 and vv = 'B' */ + } else if (ver >= 4.0 && ver < 5.1 && vv == 'B') { + p->dtype = 0; /* Eye-One Display 1 */ + p->stype = i1d1_sencoreV; /* Treat like an Eye-One Display 1 */ + } else if (ver >= 5.1 && ver <= 5.3 && vv == 'L') { p->dtype = 0; /* Eye-One Display 1 */ + } else if (ver >= 6.0 && ver <= 6.29 && vv == 'L') { p->dtype = 1; /* Eye-One Display 2 */ @@ -1506,7 +1523,7 @@ i1disp_check_unlock( } else { /* Reject any version or model we don't know about */ - a1logv(p->log, 1, "Version string = %3.1f\nID character = 0x%02x = '%c'\n",ver,vv,vv); + a1logd(p->log, 1, "Version string = %5.3f\nID character = 0x%02x = '%c'\n",ver,vv,vv); return i1disp_interp_code((inst *)p, I1DISP_UNKNOWN_VERS_ID); } @@ -1998,6 +2015,7 @@ inst_code i1disp_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { i1disp *p = (i1disp *)pp; @@ -2009,6 +2027,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = i1disp_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -2588,8 +2607,7 @@ int *cbid) { * was assume that all of these can be done before initialisation. */ static inst_code -i1disp_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +i1disp_get_set_opt(inst *pp, inst_opt_type m, ...) { i1disp *p = (i1disp *)pp; inst_code ev; @@ -2600,7 +2618,17 @@ i1disp_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } - return inst_unsupported; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/i1disp.h b/spectro/i1disp.h index 45e21db..eada827 100644 --- a/spectro/i1disp.h +++ b/spectro/i1disp.h @@ -35,6 +35,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Note: update i1disp_interp_error() and i1disp_interp_code() in i1disp.c */ /* if anything of these #defines are added or subtracted */ @@ -72,17 +76,28 @@ #define I1DISP_WRONG_DEVICE 0x26 #define I1DISP_LOCKED 0x27 +/* Sub-type of instrument (i.e. based on vers, char code, unlock code) */ +typedef enum { + i1d2_norm = 0, /* Normal (i1d1, i1d2, Smile) */ + i1d2_lite = 1, /* "Lite" */ + i1d2_munki = 2, /* "Munk" */ + i1d2_hpdream = 3, /* "ObiW" */ + i1d1_calmanx2 = 4, /* "CMX2" */ + i1d1_chroma4 = 5, /* Chroma 4 */ + i1d1_chroma5 = 6, /* Chroma 5 */ + i1d1_sencoreIII = 7, /* Sencore ColorPro III */ + i1d1_sencoreIV = 8, /* Sencore ColorPro IV */ + i1d1_sencoreV = 9 /* Sencore ColorPro V */ +} i1d2_dtype; + /* I1DISP communication object */ struct _i1disp { INST_OBJ_BASE - int dtype; /* Device type: 0 = i1D1, 1 = i1D2, 2 = Smile */ - int lite; /* i1D2: 0 = normal, 1 = "Lite" */ - int munki; /* i1D2: 0 = normal, 1 = "Munk" */ - int hpdream; /* i1D2: 0 = normal, 1 = "ObiW" */ - int calmanx2; /* i1D2: 0 = normal, 1 = "CMX2" */ - int chroma4; /* 0 = other, 1 = Sequel Chroma 4 (i1D1 based) */ + int dtype; /* Device type: 0 = i1D1, 1 = i1D2, 2 = Smile */ + i1d2_dtype stype; /* Sub type */ + inst_mode mode; /* Currently selected mode */ inst_opt_type trig; /* Reading trigger mode */ @@ -169,6 +184,9 @@ struct _i1disp { /* Constructor */ extern i1disp *new_i1disp(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define I1DISP_H #endif /* I1DISP_H */ diff --git a/spectro/i1pro.c b/spectro/i1pro.c index 816aee5..44614d2 100644 --- a/spectro/i1pro.c +++ b/spectro/i1pro.c @@ -3,7 +3,9 @@ * Argyll Color Correction System * * Gretag i1Monitor & i1Pro related functions - * + */ + +/* * Author: Graeme W. Gill * Date: 24/11/2006 * @@ -85,7 +87,7 @@ i1pro_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { i1pro *p = (i1pro *) pp; int rsize, se; icomuflags usbflags = icomuf_none; -#ifdef __APPLE__ +#ifdef UNIX_APPLE /* If the X-Rite software has been installed, then there may */ /* be a daemon process that has the device open. Kill that process off */ /* so that we can open it here, before it re-spawns. */ @@ -95,10 +97,10 @@ i1pro_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { NULL }; int retries = 20; -#else /* !__APPLE__ */ +#else /* !UNIX_APPLE */ char **pnames = NULL; int retries = 0; -#endif /* !__APPLE__ */ +#endif /* !UNIX_APPLE */ a1logd(p->log, 2, "i1pro_init_coms: called\n"); @@ -238,7 +240,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ if (!p->inited) return inst_no_init; - rv = i1pro_imp_measure(p, vals, npatch, 1); + rv = i1pro_imp_measure(p, vals, npatch, instClamp); return i1pro_interp_code(p, rv); } @@ -328,6 +330,7 @@ static inst_code i1pro_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { i1pro *p = (i1pro *)pp; @@ -338,7 +341,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; - rv = i1pro_imp_calibrate(p, calt, calc, id); + rv = i1pro_imp_calibrate(p, calt, calc, idtype, id); return i1pro_interp_code(p, rv); } @@ -385,8 +388,10 @@ i1pro_interp_error(inst *pp, i1pro_code ec) { return "EEProm key is the wrong type"; case I1PRO_DATA_KEY_CORRUPT: return "EEProm key table seems to be corrupted"; - case I1PRO_DATA_KEY_COUNT: - return "EEProm key table count is too big or small"; + case I1PRO_DATA_KEY_COUNT_SMALL: + return "EEProm key table size is too small for expected number of keys"; + case I1PRO_DATA_KEY_COUNT_LARGE: + return "EEProm key table size is too large for expected number of keys"; case I1PRO_DATA_KEY_UNKNOWN: return "EEProm unknown key type"; case I1PRO_DATA_KEY_MEMRANGE: @@ -448,7 +453,7 @@ i1pro_interp_error(inst *pp, i1pro_code ec) { case I1PRO_RD_TOOMANYPATCHES: return "Too many patches"; case I1PRO_RD_NOTENOUGHSAMPLES: - return "Not enough samples per patch"; + return "Not enough samples per patch - Slow Down!"; case I1PRO_RD_NOFLASHES: return "No flashes recognized"; case I1PRO_RD_NOAMBB4FLASHES: @@ -557,11 +562,32 @@ i1pro_interp_code(i1pro *p, i1pro_code ec) { case I1PRO_CAL_SETUP: return inst_cal_setup | ec; + case I1PRO_DATA_COUNT: + case I1PRO_DATA_BUFSIZE: + case I1PRO_DATA_MAKE_KEY: + case I1PRO_DATA_MEMORY: + case I1PRO_DATA_KEYNOTFOUND: + case I1PRO_DATA_WRONGTYPE: + case I1PRO_DATA_KEY_CORRUPT: + case I1PRO_DATA_KEY_COUNT_SMALL: + case I1PRO_DATA_KEY_COUNT_LARGE: + case I1PRO_DATA_KEY_UNKNOWN: + case I1PRO_DATA_KEY_MEMRANGE: + case I1PRO_DATA_KEY_ENDMARK: + case I1PRO_HW_HIGHPOWERFAIL: + case I1PRO_HW_EE_SIZE: case I1PRO_HW_EE_SHORTREAD: + case I1PRO_HW_EE_SHORTWRITE: case I1PRO_HW_ME_SHORTREAD: case I1PRO_HW_ME_ODDREAD: + case I1PRO_HW_SW_SHORTREAD: + case I1PRO_HW_LED_SHORTWRITE: + case I1PRO_HW_UNEX_SPECPARMS: case I1PRO_HW_CALIBINFO: + case I1PRO_WL_TOOLOW: + case I1PRO_WL_SHAPE: + case I1PRO_WL_ERR2BIG: return inst_hardware_fail | ec; case I1PRO_RD_DARKREADINCONS: @@ -718,8 +744,7 @@ static inst_code i1pro_set_mode(inst *pp, inst_mode m) { * error if it hasn't been initialised. */ static inst_code -i1pro_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +i1pro_get_set_opt(inst *pp, inst_opt_type m, ...) { i1pro *p = (i1pro *)pp; if (m == inst_opt_initcalib) { /* default */ @@ -785,6 +810,81 @@ i1pro_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } + /* Set xcalstd */ + if (m == inst_opt_set_xcalstd) { + i1proimp *imp = (i1proimp *)p->m; + xcalstd standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd); + va_end(args); + + imp->target_calstd = standard; + + return inst_ok; + } + + /* Get the current effective xcalstd */ + if (m == inst_opt_get_xcalstd) { + i1proimp *imp = (i1proimp *)p->m; + xcalstd *standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd *); + va_end(args); + + if (imp->target_calstd == xcalstd_native) + *standard = imp->native_calstd; /* If not overridden */ + else + *standard = imp->target_calstd; /* Overidden std. */ + + return inst_ok; + } + + /* Return the white calibration tile spectrum */ + /* (We always return the normal rez. reference values) */ + if (m == inst_opt_get_cal_tile_sp) { + i1proimp *imp = (i1proimp *)p->m; + xspect *sp; + inst_code rv; + va_list args; + int i; + + va_start(args, m); + sp = va_arg(args, xspect *); + va_end(args); + + if (imp->white_ref[0] == NULL) + return inst_no_init; + + sp->spec_n = imp->nwav[0]; + sp->spec_wl_short = imp->wl_short[0]; + sp->spec_wl_long = imp->wl_long[0]; + sp->norm = 100.0; + + for (i = 0; i < sp->spec_n; i++) + sp->spec[i] = imp->white_ref[0][i] * 100.0; + + return inst_ok; + } + + /* Lamp drift remediation */ + if (m == inst_opt_lamp_remediate) { + i1pro_code ev; + va_list args; + double seconds; + + va_start(args, m); + seconds = va_arg(args, double); + va_end(args); + + ev = i1pro_imp_lamp_fix(p, seconds); + + return i1pro_interp_code(p, ev); + } + /* Use default implementation of other inst_opt_type's */ { inst_code rv; diff --git a/spectro/i1pro.h b/spectro/i1pro.h index 4366e4f..e449f2c 100644 --- a/spectro/i1pro.h +++ b/spectro/i1pro.h @@ -4,7 +4,9 @@ * Argyll Color Correction System * * Gretag i1Pro related defines - * + */ + +/* * Author: Graeme W. Gill * Date: 24/11/2006 * @@ -15,6 +17,10 @@ * see the License2.txt file for licencing details. */ +#ifdef __cplusplus + extern "C" { +#endif + /* If you make use of the instrument driver code here, please note that it is the author(s) of the code who take responsibility @@ -52,5 +58,9 @@ struct _i1pro { /* Constructor */ extern i1pro *new_i1pro(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif + #define I1PRO_H #endif /* I1PRO_H */ diff --git a/spectro/i1pro_imp.c b/spectro/i1pro_imp.c index 8577f8c..a8cb9e0 100644 --- a/spectro/i1pro_imp.c +++ b/spectro/i1pro_imp.c @@ -3,7 +3,9 @@ * Argyll Color Correction System * * Gretag i1Pro implementation functions - * + */ + +/* * Author: Graeme W. Gill * Date: 24/11/2006 * @@ -34,6 +36,10 @@ /* TTBD: + Would be nice to have option to save raw scan data to .ti3 file, + and then have a utility/option to replay it through scan + recognition, to be able to help remote diagnose scan problems. + Some things probably aren't quite correct: The way the sensor saturation and optimal target is computed probably doesn't account for the dark level @@ -46,9 +52,6 @@ (see i1d3.c). Whether this will noticably improve repeatibility remains to be seen. - Would be nice to have option to save raw scan data to .ti3 file, - and then have a utility/option to replay it through scan - recognition, to be able to help remote diagnose scan problems. */ /* @@ -112,7 +115,7 @@ #define DCALTOUT2 ( 1 * 60 * 60) /* [1 Hr] i1pro2 Dark Calibration timeout in seconds */ #define WCALTOUT ( 1 * 60 * 60) /* [1 Hr] White Calibration timeout in seconds */ -#define MAXSCANTIME 20.0 /* [20] Maximum scan time in seconds */ +#define MAXSCANTIME 30.0 /* [30] Maximum scan time in seconds */ #define SW_THREAD_TIMEOUT (10 * 60.0) /* [10 Min] Switch read thread timeout */ #define SINGLE_READ /* [Def] Use a single USB read for scan to eliminate latency issues. */ @@ -131,6 +134,7 @@ #undef TEST_DARK_INTERP /* Test out the dark interpolation (need DEBUG for plot) */ #undef PATREC_DEBUG /* Print & Plot patch/flash recognition information */ #undef PATREC_ALLBANDS /* Plot all bands of scan */ +#undef PATREC_SAVETRIMMED /* Saved trimmed raw to file "i1pro_raw_trimed_N.csv */ #undef IGNORE_WHITE_INCONS /* Ignore define reference reading inconsistency */ #undef HIGH_RES_DEBUG #undef HIGH_RES_PLOT @@ -138,8 +142,8 @@ #undef PLOT_BLACK_SUBTRACT /* Plot temperature corrected black subtraction */ #undef FAKE_AMBIENT /* Fake the ambient mode for a Rev A */ -#define MATCH_SPOT_OMD /* [Def] Match Original Manufacturers Driver. Reduce */ - /* integration time and lamp turn on time */ +#undef USE_SPOT_OMD /* [Und] Use Original Manufacturers Driver timing. Reduce */ + /* integration time and lamp turn on time. */ #define DISP_INTT 2.0 /* Seconds per reading in display spot mode */ /* More improves repeatability in dark colors, but limits */ @@ -171,6 +175,7 @@ #include "i1pro.h" #include "i1pro_imp.h" +#include "xrga.h" /* - - - - - - - - - - - - - - - - - - */ #define LAMP_OFF_TIME 1500 /* msec to make sure lamp is dark for dark measurement */ @@ -448,9 +453,26 @@ i1pro_code i1pro_imp_init(i1pro *p) { i1pro_code ev = I1PRO_OK; unsigned char *eeprom; /* EEProm contents, i1pro = half, i1pro2 = full */ int len = 8192; + char *envv; a1logd(p->log,5,"i1pro_init:\n"); + m->native_calstd = xcalstd_gmdi; /* Rev A-D */ + if (p->itype == instI1Pro2) { + m->native_calstd = xcalstd_xrga; /* Rev E */ + } + m->target_calstd = xcalstd_native; /* Default to native calibration */ + + /* Honor Environment override */ + if ((envv = getenv("ARGYLL_XCALSTD")) != NULL) { + if (strcmp(envv, "XRGA") == 0) + m->target_calstd = xcalstd_xrga; + else if (strcmp(envv, "XRDI") == 0) + m->target_calstd = xcalstd_xrdi; + else if (strcmp(envv, "GMDI") == 0) + m->target_calstd = xcalstd_gmdi; + } + /* Revert to i1pro if i1pro2 driver is not enabled */ if (p->itype == instI1Pro2 #ifdef ENABLE_2 @@ -1006,22 +1028,27 @@ i1pro_code i1pro_imp_init(i1pro *p) { s->dadaptime = 0.10; s->wadaptime = 0.10; -#ifdef MATCH_SPOT_OMD - s->lamptime = 0.18; /* Lamp turn on time, close to OMD */ - /* (Not ideal, but partly compensated by calib.) */ - /* (The actual value the OMD uses is 0.20332) */ - s->dcaltime = 0.05; /* Longer than the original driver for lower */ - /* noise, and lamptime has been reduces to */ - /* compensate. (OMD uses 0.014552) */ - s->wcaltime = 0.05; - s->dreadtime = 0.05; - s->wreadtime = 0.05; +#ifdef USE_SPOT_OMD + s->lamptime = 0.20332; /* (Lamp doesn't stabilize with this) */ + s->dcaltime = 0.02366; + s->wcaltime = 0.02366; + s->dreadtime = 0.02366; + s->wreadtime = 0.02366; #else - s->lamptime = 0.5; /* This should give better accuracy, and better */ - s->dcaltime = 0.5; /* match the scan readings. Difference is about */ - s->wcaltime = 0.5; /* 0.1 DE though, but would be lost in the */ - s->dreadtime = 0.5; /* repeatability noise... */ - s->wreadtime = 0.5; +#ifndef NEVER + s->lamptime = 0.25; /* This should give better accuracy */ + s->dcaltime = 0.05; /* without increasing lamp usage much. */ + s->wcaltime = 0.05; /* Make it too large (ie. 1.0 sec total) */ + s->dreadtime = 0.05; /* and it will dirty the i1pro2 lamp quickly */ + s->wreadtime = 0.05; /* though. */ +#else +# pragma message("######### i1pro_imp.c Dirty Lamp timing !!!!! ########") + s->lamptime = 0.5; /* Dirty up the lamp. */ + s->dcaltime = 2.0; + s->wcaltime = 2.0; + s->dreadtime = 2.0; + s->wreadtime = 2.0; +#endif #endif s->maxscantime = 0.0; s->min_wl = HIGHRES_REF_MIN;/* Too much stray light below this */ @@ -1037,13 +1064,12 @@ i1pro_code i1pro_imp_init(i1pro *p) { s->targoscale = 0.25; else s->targoscale = 0.5; - - s->lamptime = 0.5; /* Lamp turn on time - lots to match scan */ + s->lamptime = 0.5; /* Lamp turn on time - lots to match scan */ s->dadaptime = 0.10; s->wadaptime = 0.10; s->dcaltime = 0.5; - s->wcaltime = 0.5; - s->dreadtime = 0.10; + s->wcaltime = 2.5; /* Lots to get lamp up to temp */ + s->dreadtime = 0.10; /* and to match OMD scan cal. on time */ s->wreadtime = 0.10; s->maxscantime = MAXSCANTIME; s->min_wl = HIGHRES_REF_MIN; /* Too much stray light below this */ @@ -1058,7 +1084,7 @@ i1pro_code i1pro_imp_init(i1pro *p) { s->adaptive = 0; s->inttime = DISP_INTT; /* Default disp integration time (ie. 2.0 sec) */ - s->lamptime = 0.20; /* ???? */ + s->lamptime = 0.0; s->dark_int_time = s->inttime; s->dark_int_time2 = DISP_INTT2; /* Alternate disp integration time (ie. 0.8) */ s->dark_int_time3 = DISP_INTT3; /* Alternate disp integration time (ie. 0.3) */ @@ -1083,7 +1109,7 @@ i1pro_code i1pro_imp_init(i1pro *p) { s->emiss = 1; s->adaptive = 1; - s->lamptime = 0.20; /* ???? */ + s->lamptime = 0.0; s->dadaptime = 0.0; s->wadaptime = 0.10; s->dcaltime = 1.0; @@ -1100,7 +1126,7 @@ i1pro_code i1pro_imp_init(i1pro *p) { s->scan = 1; s->adaptive = 1; /* ???? */ s->inttime = m->min_int_time; /* Maximize scan rate */ - s->lamptime = 0.20; /* ???? */ + s->lamptime = 0.0; s->dark_int_time = s->inttime; if (m->fwrev >= 301) s->targoscale = 0.25; /* (We're not using scan targoscale though) */ @@ -1132,7 +1158,7 @@ i1pro_code i1pro_imp_init(i1pro *p) { s->ambient = 1; s->adaptive = 1; - s->lamptime = 0.20; /* ???? */ + s->lamptime = 0.0; s->dadaptime = 0.0; s->wadaptime = 0.10; s->dcaltime = 1.0; @@ -1161,7 +1187,7 @@ i1pro_code i1pro_imp_init(i1pro *p) { s->flash = 1; s->inttime = m->min_int_time; /* Maximize scan rate */ - s->lamptime = 0.20; /* ???? */ + s->lamptime = 0.0; s->dark_int_time = s->inttime; if (m->fwrev >= 301) s->targoscale = 0.25; /* (We're not using scan targoscale though) */ @@ -1181,7 +1207,7 @@ i1pro_code i1pro_imp_init(i1pro *p) { s->trans = 1; s->adaptive = 1; - s->lamptime = 0.20; /* ???? */ + s->lamptime = 0.0; s->dadaptime = 0.10; s->wadaptime = 0.10; s->dcaltime = 1.0; @@ -1202,7 +1228,7 @@ i1pro_code i1pro_imp_init(i1pro *p) { else s->targoscale = 0.5; - s->lamptime = 0.20; /* ???? */ + s->lamptime = 0.0; s->dadaptime = 0.10; s->wadaptime = 0.10; s->dcaltime = 1.0; @@ -1483,6 +1509,7 @@ i1pro_code i1pro_imp_calibrate( i1pro *p, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ + inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { i1pro_code ev = I1PRO_OK; @@ -2250,6 +2277,16 @@ i1pro_code i1pro_imp_calibrate( s->cal_factor[0], m->white_ref[0], s->cal_factor[0], s->cal_factor[1], m->white_ref[1], s->cal_factor[1], !s->scan); /* Use this for emis hires fine tune if not scan */ + + /* Print white lamp magnitude to track lamp darkening */ + if (p->log != NULL && p->log->debug >= 1) { + double sum = 0.0; + for (i = 0; i < m->nwav[0]; i++) + sum += 1.0/s->cal_factor[0][i]; + + a1logd(p->log,1,"Refl. lamp magnitude = %e\n",sum); + } + if (ev == I1PRO_RD_TRANSWHITEWARN) /* Shouldn't happen ? */ ev = I1PRO_OK; if (ev != I1PRO_OK) { @@ -2365,7 +2402,8 @@ i1pro_code i1pro_imp_calibrate( /* Do ref_white first in case we are doing a high res fine tune. */ if (*calt & (inst_calt_ref_dark | inst_calt_ref_white)) { - sprintf(id, "Serial no. %d",m->serno); + *idtype = inst_calc_id_ref_sn; + sprintf(id, "%d",m->serno); if ((*calc & inst_calc_cond_mask) != inst_calc_man_ref_white) { /* Calibrate using white tile */ *calc = inst_calc_man_ref_white; @@ -2373,6 +2411,7 @@ i1pro_code i1pro_imp_calibrate( } } else if (*calt & inst_calt_wavelength) { /* Wavelength calibration */ if (cs->emiss && cs->ambient) { + *idtype = inst_calc_id_none; id[0] = '\000'; if ((*calc & inst_calc_cond_mask) != inst_calc_man_am_dark) { /* Calibrate using ambient adapter */ @@ -2380,7 +2419,8 @@ i1pro_code i1pro_imp_calibrate( return I1PRO_CAL_SETUP; } } else { - sprintf(id, "Serial no. %d",m->serno); + *idtype = inst_calc_id_ref_sn; + sprintf(id, "%d",m->serno); if ((*calc & inst_calc_cond_mask) != inst_calc_man_ref_white) { /* Calibrate using white tile */ *calc = inst_calc_man_ref_white; @@ -2388,6 +2428,7 @@ i1pro_code i1pro_imp_calibrate( } } } else if (*calt & inst_calt_em_dark) { /* Emissive Dark calib */ + *idtype = inst_calc_id_none; id[0] = '\000'; if ((*calc & inst_calc_cond_mask) != inst_calc_man_em_dark) { /* Any sort of dark reference */ @@ -2395,18 +2436,21 @@ i1pro_code i1pro_imp_calibrate( return I1PRO_CAL_SETUP; } } else if (*calt & inst_calt_trans_dark) { /* Transmissvice dark */ + *idtype = inst_calc_id_none; id[0] = '\000'; if ((*calc & inst_calc_cond_mask) != inst_calc_man_trans_dark) { *calc = inst_calc_man_trans_dark; return I1PRO_CAL_SETUP; } } else if (*calt & inst_calt_trans_vwhite) {/* Transmissvice white for emulated transmission */ + *idtype = inst_calc_id_none; id[0] = '\000'; if ((*calc & inst_calc_cond_mask) != inst_calc_man_trans_white) { *calc = inst_calc_man_trans_white; return I1PRO_CAL_SETUP; } } else if (*calt & inst_calt_emis_int_time) { + *idtype = inst_calc_id_none; id[0] = '\000'; if ((*calc & inst_calc_cond_mask) != inst_calc_emis_white) { *calc = inst_calc_emis_white; @@ -2438,10 +2482,13 @@ i1pro_code i1pro_imp_calibrate( if (m->transwarn) { *calc = inst_calc_message; - if (m->transwarn & 2) + if (m->transwarn & 2) { + *idtype = inst_calc_id_trans_low; strcpy(id, "Warning: Transmission light source is too low for accuracy!"); - else + } else { + *idtype = inst_calc_id_trans_wl; strcpy(id, "Warning: Transmission light source is low at some wavelengths!"); + } m->transwarn = 0; } @@ -2458,6 +2505,51 @@ int icoms2i1pro_err(int se) { } /* - - - - - - - - - - - - - - - - */ +/* Do a dummy reflective read, to fix Lamp Drift. */ + +i1pro_code i1pro_imp_lamp_fix( +i1pro *p, +double seconds) { /* Number of seconds to turn lamp on for */ + i1pro_code ev = I1PRO_OK; + i1proimp *m = (i1proimp *)p->m; + int nummeas; + double inttime; + unsigned char *buf; /* Raw USB reading buffer */ + unsigned int bsize; + i1p_mode cmode = m->mmode; /* Remember current mode */ + + if (seconds > (5 * 60.0)) { + a1loge(p->log, inst_internal_error, "i1pro_imp_lamp_fix %f sec is too long\n",seconds); + return I1PRO_INT_ASSERT; + } + + m->mmode = i1p_refl_spot; /* Override current mode */ + inttime = 0.2; /* Constrain buffer size */ + nummeas = (int)(seconds/inttime + 0.5); + bsize = m->nsen * 2 * nummeas; /* 16 bit raw values */ + + if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) { + m->mmode = cmode; + a1logd(p->log,1,"i1pro_read_patches malloc %d bytes failed (11)\n",bsize); + return I1PRO_INT_MALLOC; + } + + /* Trigger measure and gather raw readings */ + a1logd(p->log, 1, "i1pro_imp_lamp_fix %f seconds\n",seconds); + if ((ev = i1pro_read_patches_1(p, nummeas, nummeas, &inttime, 0, + NULL, buf, bsize)) != I1PRO_OK) { + m->mmode = cmode; + free(buf); + return ev; + } + + m->mmode = cmode; + free(buf); + + return I1PRO_OK; +} + +/* - - - - - - - - - - - - - - - - */ /* Measure a display update delay. It is assumed that */ /* white_stamp(init) has been called, and then a */ /* white to black change has been made to the displayed color, */ @@ -2519,7 +2611,7 @@ int *pinstmsec) { /* Return instrument latency in msec */ } if (m->whitestamp < 0.0) { - a1logd(p->log, 1, "i1d3_meas_delay: White transition wasn't timestamped\n"); + a1logd(p->log, 1, "i1pro_meas_delay: White transition wasn't timestamped\n"); return inst_internal_error; } @@ -2940,8 +3032,13 @@ i1pro_code i1pro_imp_measure( /* Indicate to the user that they can now scan the instrument, */ /* after a little delay that allows for the instrument reaction time. */ if (s->scan) { - /* 500msec delay, 1KHz for 200 msec */ - msec_beep(200 + (int)(s->lamptime * 1000.0 + 0.5), 1000, 200); + int delay = 200 + (int)(s->lamptime * 1000.0 + 0.5); + if (p->eventcallback != NULL) { + issue_scan_ready((inst *)p, delay); + } else { + /* delay then 1KHz for 200 msec */ + msec_beep(delay, 1000, 200); + } } /* Retry loop for certaing cases */ @@ -4121,6 +4218,7 @@ i1pro_code i1pro_save_calibration(i1pro *p) { i1pnonv x; int ss; int argyllversion = ARGYLL_VERSION; + int isRevE = p->itype == instI1Pro2 ? 1 : 0; strcpy(nmode, "w"); #if !defined(O_CREAT) && !defined(_O_CREAT) @@ -4157,6 +4255,7 @@ i1pro_code i1pro_save_calibration(i1pro *p) { write_ints(&x, fp, &argyllversion, 1); write_ints(&x, fp, &ss, 1); write_ints(&x, fp, &m->serno, 1); + write_ints(&x, fp, &isRevE, 1); write_ints(&x, fp, (int *)&m->nraw, 1); write_ints(&x, fp, (int *)&m->nwav[0], 1); write_ints(&x, fp, (int *)&m->nwav[1], 1); @@ -4242,6 +4341,7 @@ i1pro_code i1pro_restore_calibration(i1pro *p) { FILE *fp; i1pnonv x; int argyllversion; + int isRevE; int ss, serno, nraw, nwav0, nwav1, nbytes, chsum1, chsum2; strcpy(nmode, "r"); @@ -4286,6 +4386,7 @@ i1pro_code i1pro_restore_calibration(i1pro *p) { read_ints(&x, fp, &argyllversion, 1); read_ints(&x, fp, &ss, 1); read_ints(&x, fp, &serno, 1); + read_ints(&x, fp, &isRevE, 1); read_ints(&x, fp, &nraw, 1); read_ints(&x, fp, &nwav0, 1); read_ints(&x, fp, &nwav1, 1); @@ -4293,6 +4394,7 @@ i1pro_code i1pro_restore_calibration(i1pro *p) { || argyllversion != ARGYLL_VERSION || ss != (sizeof(i1pro_state) + sizeof(i1proimp)) || serno != m->serno + || isRevE != (p->itype == instI1Pro2 ? 1 : 0) || nraw != m->nraw || nwav0 != m->nwav[0] || nwav1 != m->nwav[1]) { @@ -4398,6 +4500,7 @@ i1pro_code i1pro_restore_calibration(i1pro *p) { read_ints(&x, fp, &argyllversion, 1); read_ints(&x, fp, &ss, 1); read_ints(&x, fp, &m->serno, 1); + read_ints(&x, fp, &isRevE, 1); read_ints(&x, fp, (int *)&m->nraw, 1); read_ints(&x, fp, (int *)&m->nwav[0], 1); read_ints(&x, fp, (int *)&m->nwav[1], 1); @@ -5323,6 +5426,7 @@ i1pro_code i1pro_read_patches_2( } else { a1logd(p->log,3,"Number of patches measured = %d\n",nmeasuered); +{ /* Recognise the required number of ref/trans patch locations, */ /* and average the measurements within each patch. */ if ((ev = i1pro_extract_patches_multimeas(p, &rv, absraw, numpatches, multimes, @@ -5332,6 +5436,7 @@ i1pro_code i1pro_read_patches_2( a1logd(p->log,2,"i1pro_read_patches_2 spot read failed at i1pro_extract_patches_multimeas\n"); return ev; } +} } } free_dmatrix(multimes, 0, nmeasuered-1, -1, m->nraw-1); @@ -5532,7 +5637,7 @@ i1pro_code i1pro_read_patches_all( unsigned int bsize; int rv = 0; - bsize = m->nsen * 2 * numpatches; + bsize = m->nsen * 2 * numpatches; /* 16 bit raw values */ if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) { a1logd(p->log,1,"i1pro_read_patches malloc %d bytes failed (11)\n",bsize); return I1PRO_INT_MALLOC; @@ -5711,6 +5816,11 @@ i1pro_trigger_one_measure( int measmodeflags; /* Measurement mode command flags */ int measmodeflags2; /* Rev E Measurement mode command flags */ + /* Sanity check in case bad value was restored, due to switch */ + /* from Rev A-D to Rev E mode. */ + if (*inttime < m->min_int_time) + *inttime = m->min_int_time; + /* The Rev E measure mode has it's own settings */ if (p->itype == instI1Pro2) { m->intclkp = m->intclkp2; /* From i1pro2_getmeaschar() ? */ @@ -6378,7 +6488,7 @@ i1pro_code i1pro_extract_patches_multimeas( #ifdef PATREC_DEBUG /* Plot out 6 lots of 8 values each */ - plot = dmatrixz(0, 8, 0, nummeas-1); + plot = dmatrixz(0, 11, 0, nummeas-1); // for (j = 45; j <= (m->nraw-8); j += 100) /* Do just one band */ #ifdef PATREC_ALLBANDS for (j = 0; j <= (m->nraw-8); j += 8) /* Plot all the bands */ @@ -6500,7 +6610,6 @@ i1pro_code i1pro_extract_patches_multimeas( /* to skew slightly towards the center */ for (k = 0; k < FW; k++) { double wt; - wt = fabs(2.0 * k - (FW -1.0))/(FW-1.0); dev[k] += wt * bdev; } @@ -6689,6 +6798,7 @@ i1pro_code i1pro_extract_patches_multimeas( break; pat[npat].no++; } + avglegth += (double) pat[npat].no; npat++; } @@ -6812,20 +6922,80 @@ i1pro_code i1pro_extract_patches_multimeas( a1logd(p->log,7,"Patch %d, start %d, length %d:\n",i, pat[i].ss, pat[i].no, pat[i].use); } - /* Now trim the patches simply by shrinking their windows */ +#ifdef NEVER /* [Und] - doesn't seem as good for normal patch sizes. */ + /* Now trim the patches by expanding the spacers/transitions */ for (k = 1; k < (npat-1); k++) { - int nno, trim; + int sw, xsw, trim; if (pat[k].use == 0) continue; + +printf("Patch %d @ %d len %d ->",k,pat[k].ss,pat[k].no); + /* Figure the previous spacer width */ + sw = pat[k].ss - (pat[k-1].ss + pat[k-1].no); + xsw = (sw * 170 + 85)/100; /* Expand spacer by 170% */ + trim = (xsw - sw + 1)/2; /* Move start of patch half that expansion */ + pat[k].ss += trim; + pat[k].no -= trim; + + /* Figure the next spacer width */ + sw = pat[k+1].ss - (pat[k].ss + pat[k].no); + xsw = (sw * 170 + 85)/100; /* Expand spacer by 170% */ + trim = (xsw - sw + 1)/2; /* Move end of patch half that expansion */ + pat[k].no -= trim; + + if (pat[k].no < 0) + pat[k].no = 0; +printf(" @ %d len %d\n",pat[k].ss,pat[k].no); + } +#else + /* Now trim the patches by shrinking their windows */ + for (k = 1; k < (npat-1); k++) { + int nno, trim; + if (pat[k].use == 0) + continue; - nno = (pat[k].no * 3)/4; - trim = (pat[k].no - nno)/2; +// nno = (pat[k].no * 3 + 0)/4; /* Trim to 75% & round down */ + nno = (pat[k].no * 2 + 0)/3; /* Trim to 66% & round down [def] */ +// nno = (pat[k].no * 2 + 0)/4; /* Trim to 50% & round down */ + trim = (pat[k].no - nno + 1)/2; pat[k].ss += trim; pat[k].no = nno; } +#endif + +#ifdef PATREC_SAVETRIMMED /* Save debugging file */ + { + static int filen = 0; /* Debug file index */ + char fname[100]; + FILE *fp; + + sprintf(fname, "i1pro_raw_trimed_%d.csv",filen++); + + if ((fp = fopen(fname, "w")) == NULL) + error("Unable to open debug output file '%'",fname); + + /* Create fake "slope" value that marks patches */ + for (i = 0; i < nummeas; i++) + slope[i] = 1.0; + for (k = 1; k < (npat-1); k++) { + if (pat[k].use == 0) + continue; + for (i = pat[k].ss; i < (pat[k].ss + pat[k].no); i++) + slope[i] = 0.0; + } + + for (i = 0; i < nummeas; i++) { + fprintf(fp, "%f\t",slope[i]); + for (j = 0; j < m->nraw; j++) + fprintf(fp, "%f\t", multimeas[i][j]/maxval[j]); + fprintf(fp, "\n"); + } + fclose(fp); + } +#endif #ifdef PATREC_DEBUG a1logd(p->log,7,"After trimming got:\n"); @@ -6846,6 +7016,17 @@ i1pro_code i1pro_extract_patches_multimeas( } printf("Trimmed output:\n"); +#ifdef PATREC_ALLBANDS + for (j = 0; j <= (m->nraw-9); j += 9) { /* Plot all the bands, 9 at a time */ + for (i = 0; i < nummeas; i++) { + for (k = 0; k < 9; k++) + plot[k][i] = multimeas[i][j+k]/maxval[j+k]; + plot[10][i] = (double)i; + } + printf("Bands %d - %d\n",j,j+9); + do_plot10(plot[10], slope, plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], plot[6], plot[7], plot[8], nummeas, 0); + } +#else for (i = 0; i < nummeas; i++) { int jj; for (jj = 0, j = b_lo; jj < 6 && j < b_hi; jj++, j += ((b_hi-b_lo)/6)) { @@ -6856,8 +7037,9 @@ i1pro_code i1pro_extract_patches_multimeas( } } for (i = 0; i < nummeas; i++) - plot[6][i] = (double)i; - do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas); + plot[10][i] = (double)i; + do_plot6(plot[10], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas); +#endif #endif /* PATREC_DEBUG */ #ifdef PATREC_DEBUG @@ -7884,6 +8066,8 @@ i1pro_code i1pro2_match_wl_meas(i1pro *p, double *pled_off, double *wlraw) { /* given the current wl_led_off, and set them as current, */ /* using triangular filters of the lagrange interpolation of the */ /* CCD values (i.e. the same type of filter used by the OEM driver) */ +/* [ Interestingly, the resulting filter shape is a bit like lanczos2, */ +/* but not identical. ] */ i1pro_code i1pro_compute_wav_filters(i1pro *p, int hr, int refl) { i1proimp *m = (i1proimp *)p->m; i1pro_state *s = &m->ms[m->mmode]; @@ -7979,6 +8163,12 @@ i1pro_code i1pro_compute_wav_filters(i1pro *p, int hr, int refl) { wlcop[i] = 0.0; /* for each Lagrange interpolation position (adjacent CCD locations) */ + /* create the Lagrange and then accuumulate the integral of the convolution */ + /* of the overlap of the central region, with the triangle of our */ + /* underlying re-sampling filter. */ + /* (If we were to run out of enough source points for the Lagrange to */ + /* encompas the region, then in theory we could use the Lagrange to */ + /* extrapolate beyond the end from points within.) */ for (lip = six; (lip + 3) < eix; lip++) { double rwav[4]; /* Relative wavelength of these Lagrange points */ double den[4]; /* Denominator values for points */ @@ -8032,6 +8222,7 @@ i1pro_code i1pro_compute_wav_filters(i1pro *p, int hr, int refl) { ihigh = rwav[1]; ilow = rwav[2]; + /* Over just the central portion, if it overlaps the triangle. */ if ((k == 0 && ilow <= twidth && ihigh >= 0.0) /* Portion is +ve side */ || (k == 1 && ilow <= 0.0 && ihigh >= -twidth)) { /* Portion is -ve side */ @@ -8186,6 +8377,8 @@ i1pro_code i1pro_compute_wav_filters(i1pro *p, int hr, int refl) { #define DO_CCDNORMAVG /* [und ???] Normalise averages rather than per CCD bin */ /* (We relly on fine cal & white cal to fix it) */ +#define BOX_INTEGRATE /* [und] Integrate raw samples as if they were +/-0.5 boxes */ + /* (This improves coeficient consistency a bit ?) */ #undef COMPUTE_DISPERSION /* Compute slit & optics dispersion from red laser data */ #ifdef NEVER @@ -8219,7 +8412,7 @@ static void i1pro_debug_plot_mtx_coef(i1pro *p) { do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw); free_dvector(xx, -1, m->nraw-1); - free_dmatrix(yy, 0, 2, -1, m->nraw-1); + free_dmatrix(yy, 0, 5, -1, m->nraw-1); } #endif /* NEVER */ @@ -9540,7 +9733,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { w1 = i1pro_raw2wav(p, refl, (double)i - 0.5); w2 = i1pro_raw2wav(p, refl, (double)i + 0.5); -// printf("~1 CCD %d, w1 %f, wl %f, w2 %f\n",i,w1,wl,w2); +// printf("~1 CCD %d, w1 %f, wl %f, w2 %f\n",i,w1,wl,w2); /* For each filter */ for (j = 0; j < m->nwav[1]; j++) { @@ -9554,6 +9747,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { if (fabs(w1 - cwl) > fshmax && fabs(w2 - cwl) > fshmax) continue; /* Doesn't fall into this filter */ +#ifdef BOX_INTEGRATE /* Integrate in 0.05 nm increments from filter shape */ /* using triangular integration. */ { @@ -9582,6 +9776,9 @@ i1pro_code i1pro_create_hr(i1pro *p) { lw = cw; } } +#else + we = fabs(w2 - w1) * lanczos2(twidth, rwl); +#endif if (m->mtx_c[1][refl].nocoef[j] >= MXNOFC) { a1logw(p->log, "i1pro: run out of high res filter space\n"); @@ -9943,7 +10140,7 @@ i1pro_code i1pro_set_scan_toll(i1pro *p, double toll_ratio) { } -/* Optics adjustment weights */ +/* Optical adjustment weights */ static double opt_adj_weights[21] = { 1.4944496665144658e-282, 2.0036175483913455e-070, 1.2554893022685038e+232, 2.3898157055642966e+190, 1.5697625128432372e-076, 6.6912978722191457e+281, @@ -9954,7 +10151,8 @@ static double opt_adj_weights[21] = { 9.2709981544886391e+122, 3.7958270103353899e-153, 7.1366083837501666e-154 }; -/* Convert from spectral to XYZ, and transfer to the ipatch array */ +/* Convert from spectral to XYZ, and transfer to the ipatch array. */ +/* Apply XRGA conversion if needed */ i1pro_code i1pro_conv2XYZ( i1pro *p, ipatch *vals, /* Values to return */ @@ -10062,6 +10260,10 @@ i1pro_code i1pro_conv2XYZ( } conv->del(conv); + + /* Apply any XRGA conversion */ + ipatch_convert_xrga(vals, nvals, xcalstd_nonpol, m->target_calstd, m->native_calstd, clamp); + return I1PRO_OK; } @@ -11383,6 +11585,7 @@ i1pro_setmcmode( } /* Hmm. Give the instrument a little time to reconfigure itself. */ + /* (Probably needs about 1msec, but err on the safe side) */ msec_sleep(10); a1logd(p->log,2,"i1pro_setmcmode: done, ICOM err 0x%x (%d msec)\n", @@ -11727,7 +11930,7 @@ i1pro2_triggermeasure(i1pro *p, int delay) { i1proimp *m = (i1proimp *)p->m; int rv = I1PRO_OK; - a1logd(p->log,2,"i1pro2_triggermeasure: triggering Rev Emeasurement after %dmsec " + a1logd(p->log,2,"i1pro2_triggermeasure: triggering Rev E measurement after %dmsec " "delay @ %d msec\n", delay, msec_time() - m->msec); /* NOTE := would be better here to create thread once, and then trigger it */ @@ -12512,6 +12715,8 @@ static i1pro_code i1data_parse_eeprom(i1data *d, unsigned char *buf, unsigned in i1pro *p = d->p; int rv = I1PRO_OK; int dir = 0x1000; /* Location of key directory */ + int minkeys = 300; /* Expected minumum number of bytes for keys */ + int maxkeys = 512; /* Expected maxumum number of bytes for keys */ int block_id; /* Block id */ int nokeys; i1key key, off, nkey = 0, noff = 0; @@ -12519,14 +12724,16 @@ static i1pro_code i1data_parse_eeprom(i1data *d, unsigned char *buf, unsigned in unsigned char *bp; int i; - if (extra) + if (extra) { dir = 0x2000; /* Directory is at half way in i1pro2 2nd table */ + minkeys = 200; /* Hmm. Had a report that the i1Pro2 failed to parse */ + } - a1logd(p->log,3,"i1pro_parse_eeprom called with %d bytes\n",len); + a1logd(p->log,3,"i1pro_parse_eeprom called with %d bytes, table %d\n",len,extra); /* Room for minimum number of keys ? */ - if ((dir + 300) > len) - return I1PRO_DATA_KEY_COUNT; + if ((dir + minkeys) > len) + return I1PRO_DATA_KEY_COUNT_SMALL; block_id = buf2ushort(buf + dir); if ((extra == 0 && block_id != 1) /* Must be 1 for base data */ @@ -12534,12 +12741,15 @@ static i1pro_code i1data_parse_eeprom(i1data *d, unsigned char *buf, unsigned in return I1PRO_DATA_KEY_CORRUPT; nokeys = buf2ushort(buf + dir + 2); /* Bytes in key table */ - if (nokeys < 300 || nokeys > 512) - return I1PRO_DATA_KEY_COUNT; + a1logd(p->log,3,"%d bytes for keys in EEProm table %d\n",nokeys, extra); + if (nokeys < minkeys) + return I1PRO_DATA_KEY_COUNT_SMALL; + if (nokeys > maxkeys) + return I1PRO_DATA_KEY_COUNT_LARGE; nokeys = (nokeys - 4)/6; /* Number of 6 byte entries */ - a1logd(p->log,3,"%d key/values in EEProm table %d\n",nokeys, extra); + a1logd(p->log,3,"%d keys & values in EEProm table %d\n",nokeys, extra); /* We need current and next value to figure data size out */ bp = buf + dir + 4; diff --git a/spectro/i1pro_imp.h b/spectro/i1pro_imp.h index 5a2cef2..582cc98 100644 --- a/spectro/i1pro_imp.h +++ b/spectro/i1pro_imp.h @@ -4,7 +4,9 @@ * Argyll Color Correction System * * Gretag i1Pro implementation defines - * + */ + +/* * Author: Graeme W. Gill * Date: 20/12/2006 * @@ -15,6 +17,10 @@ * see the License2.txt file for licencing details. */ +#ifdef __cplusplus + extern "C" { +#endif + /* If you make use of the instrument driver code here, please note that it is the author(s) of the code who take responsibility @@ -182,6 +188,9 @@ struct _i1proimp { int uv_en; /* NZ to do UV reflective measurement */ /* ~~ change this to uv_mode of none, uv, strip1, 2pass */ + xcalstd native_calstd; /* Instrument native calibration standard */ + xcalstd target_calstd; /* Returned calibration standard */ + double intclkp; /* Integration clock period (typically 68 usec) */ int subclkdiv; /* Sub clock divider ratio */ int subtmode; /* Reading 127 subtract mode (version 301 or greater) */ @@ -363,10 +372,11 @@ void del_i1proimp(i1pro *p); #define I1PRO_DATA_KEYNOTFOUND 0x05 /* a key value wasn't found */ #define I1PRO_DATA_WRONGTYPE 0x06 /* a key is the wrong type */ #define I1PRO_DATA_KEY_CORRUPT 0x07 /* key table seems to be corrupted */ -#define I1PRO_DATA_KEY_COUNT 0x08 /* key table count is too big or small */ -#define I1PRO_DATA_KEY_UNKNOWN 0x09 /* unknown key type */ -#define I1PRO_DATA_KEY_MEMRANGE 0x0a /* key data is out of range of EEProm */ -#define I1PRO_DATA_KEY_ENDMARK 0x0b /* And end section marker was missing */ +#define I1PRO_DATA_KEY_COUNT_SMALL 0x08 /* key table count is too small */ +#define I1PRO_DATA_KEY_COUNT_LARGE 0x09 /* key table count is too big */ +#define I1PRO_DATA_KEY_UNKNOWN 0x0a /* unknown key type */ +#define I1PRO_DATA_KEY_MEMRANGE 0x0b /* key data is out of range of EEProm */ +#define I1PRO_DATA_KEY_ENDMARK 0x0c /* And end section marker was missing */ /* HW errors */ #define I1PRO_HW_HIGHPOWERFAIL 0x10 /* Switch to high power mode failed */ @@ -464,6 +474,7 @@ i1pro_code i1pro_imp_calibrate( i1pro *p, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ + inst_calc_id_type *idtype, /* Condition identifier type */ char id[100] /* Condition identifier (ie. white reference ID) */ ); @@ -475,6 +486,12 @@ i1pro_code i1pro_imp_measure( instClamping clamp /* Clamp XYZ/Lab to be +ve */ ); +/* Do a dummy reflective read, to fix Lamp Drift. */ +i1pro_code i1pro_imp_lamp_fix( + i1pro *p, + double seconds /* Number of seconds to turn lamp on for */ +); + /* Measure the emissive refresh rate */ i1pro_code i1pro_imp_meas_refrate( i1pro *p, @@ -1052,6 +1069,7 @@ i1pro2_getmeaschar( #define I1PRO2_MMF_LAMP 0x0100 /* Use the Incandescent Lamp as the illuminant */ #define I1PRO2_MMF_UV_LED 0x0200 /* Use the Ultra Violet LED as the illuminant */ #define I1PRO2_MMF_WL_LED 0x0300 /* Use the Wavelength Reference LED as the illuminant */ + //#define I1PRO2_MMF_HIGHGAIN 0x0000 /* Rev E mode has no high gain mode ? */ #define I1PRO2_MMF_SCAN 0x0001 /* Scan mode bit, else spot mode */ #define I1PRO2_MMF_RULER_START 0x0004 /* Start ruler tracking in scan mode */ @@ -1404,5 +1422,9 @@ struct _i1data { /* Constructor. Construct from the EEprom contents */ extern i1data *new_i1data(i1proimp *m); +#ifdef __cplusplus + } +#endif + #define I1PRO_IMP #endif /* I1PRO_IMP */ diff --git a/spectro/icoms.c b/spectro/icoms.c index b69b72a..eadecc9 100644 --- a/spectro/icoms.c +++ b/spectro/icoms.c @@ -1,5 +1,5 @@ - /* General instrument + serial I/O support */ +/* General instrument + serial I/O support */ /* * Argyll Color Correction System @@ -71,29 +71,35 @@ static void icompath_del(icompath *p) { free(p); } -/* Delete the last path */ +/* Delete the last combined path */ +/* (Assume this is before icompaths_make_dslists() has been called) */ static void icompaths_del_last_path( icompaths *p ) { - if (p->npaths == 0) + icom_dtix ix = dtix_combined; + + if (p->ndpaths[ix] == 0) return; - icompath_del(p->paths[p->npaths-1]); - p->paths[p->npaths-1] = NULL; - p->npaths--; + icompath_del(p->dpaths[ix][p->ndpaths[ix]-1]); + p->dpaths[ix][p->ndpaths[ix]-1] = NULL; + p->ndpaths[ix]--; } -/* Return the last path */ +/* Return the last combined path */ +/* (Assume this is before icompaths_make_dslists() has been called) */ static icompath *icompaths_get_last_path( icompaths *p ) { - if (p->npaths == 0) + icom_dtix ix = dtix_combined; + + if (p->ndpaths[ix] == 0) return NULL; - return p->paths[p->npaths-1]; + return p->dpaths[ix][p->ndpaths[ix]-1]; } -/* Return the path corresponding to the port number, or NULL if out of range */ +/* Return the instrument path corresponding to the port number, or NULL if out of range */ static icompath *icompaths_get_path( icompaths *p, int port /* Enumerated port number, 1..n */ @@ -103,80 +109,188 @@ static icompath *icompaths_get_path( - if (port <= 0 || port > p->npaths) + if (port <= 0 || port > p->ndpaths[dtix_inst]) + return NULL; + + return p->dpaths[dtix_inst][port-1]; +} + +/* Return the device path corresponding to the port number, or NULL if out of range */ +static icompath *icompaths_get_path_sel( + icompaths *p, + icom_type dctype, /* Device type list */ + int port /* Enumerated port number, 1..n */ +) { + /* Check it is an enumeration */ + if (dctype != dtix_combined + && dctype != dtix_inst + && dctype != dtix_3dlut + && dctype != dtix_vtpg + && dctype != dtix_printer) + return NULL; + + if (dctype == dtix_inst) + return icompaths_get_path(p, port); + + if (port <= 0 || port > p->ndpaths[dctype]) return NULL; - return p->paths[port-1]; + return p->dpaths[dctype][port-1]; } -static void icompaths_clear(icompaths *p) { +/* Clear a single device paths list */ +static void icompaths_clear(icompaths *p, icom_dtix ix) { - /* Free any old list */ - if (p != NULL && p->paths != NULL) { + if (p->dpaths[ix] != NULL) { int i; - for (i = 0; i < p->npaths; i++) { - icompath_del(p->paths[i]); + /* If underlying owner of icompaths */ + if (ix == dtix_combined) { + for (i = 0; i < p->ndpaths[ix]; i++) { + icompath_del(p->dpaths[ix][i]); + } + } + free(p->dpaths[ix]); + p->dpaths[ix] = NULL; + p->ndpaths[ix] = 0; + + /* Clear instrument alias */ + if (ix == dtix_inst) { + p->npaths = 0; + p->paths = NULL; } - free(p->paths); - p->npaths = 0; - p->paths = NULL; } } -/* Add an empty new path. */ +/* Clear all device paths */ +static void icompaths_clear_all(icompaths *p) { + + if (p == NULL) + return; + + /* Free any old instrument list */ + icompaths_clear(p, dtix_inst); + icompaths_clear(p, dtix_3dlut); + icompaths_clear(p, dtix_vtpg); + icompaths_clear(p, dtix_printer); + icompaths_clear(p, dtix_combined); +} + +/* Add the give path to the given list. */ +/* If the path is NULL, allocat an empty */ +/* one and add it to the combined list */ /* Return icom error */ -static int icompaths_add_path(icompaths *p) { - if (p->paths == NULL) { - if ((p->paths = (icompath **)calloc(sizeof(icompath *), 1 + 1)) == NULL) { +static int icompaths_add_path(icompaths *p, int ix, icompath *xp) { + if (xp == NULL) + ix = dtix_combined; + if (p->dpaths[ix] == NULL) { + if ((p->dpaths[ix] = (icompath **)calloc(sizeof(icompath *), 1 + 1)) == NULL) { a1loge(p->log, ICOM_SYS, "icompaths: calloc failed!\n"); return ICOM_SYS; } } else { icompath **npaths; - if ((npaths = (icompath **)realloc(p->paths, - sizeof(icompath *) * (p->npaths + 2))) == NULL) { + if ((npaths = (icompath **)realloc(p->dpaths[ix], + sizeof(icompath *) * (p->ndpaths[ix] + 2))) == NULL) { a1loge(p->log, ICOM_SYS, "icompaths: realloc failed!\n"); return ICOM_SYS; } - p->paths = npaths; - p->paths[p->npaths+1] = NULL; + p->dpaths[ix] = npaths; + p->dpaths[ix][p->ndpaths[ix]+1] = NULL; } - if ((p->paths[p->npaths] = calloc(sizeof(icompath), 1)) == NULL) { - a1loge(p->log, ICOM_SYS, "icompaths: malloc failed!\n"); - return ICOM_SYS; + if (xp == NULL) { + if ((xp = calloc(sizeof(icompath), 1)) == NULL) { + a1loge(p->log, ICOM_SYS, "icompaths: malloc failed!\n"); + return ICOM_SYS; + } + } + p->dpaths[ix][p->ndpaths[ix]] = xp; + p->ndpaths[ix]++; + p->dpaths[ix][p->ndpaths[ix]] = NULL; + return ICOM_OK; +} + +/* Based on each icompath's dctype, create aliase lists for everything */ +/* on the combined path based on each device type. */ +/* (We are only called after clearing all lists) */ +int icompaths_make_dslists(icompaths *p) { + int rv, i; + + for (i = 0; i < p->ndpaths[dtix_combined]; i++) { + icompath *xp = p->dpaths[dtix_combined][i]; + + if (xp == NULL) + break; + + a1logd(g_log, 8, "icompaths_make_dslists '%s' dctype 0x%x\n",xp->name,xp->dctype); + + if (xp->dctype & icomt_instrument) { + if ((rv = icompaths_add_path(p, dtix_inst, xp)) != ICOM_OK) + return rv; + } + if (xp->dctype & icomt_3dlut) { + if ((rv = icompaths_add_path(p, dtix_3dlut, xp)) != ICOM_OK) + return rv; + } + if (xp->dctype & icomt_vtpg) { + if ((rv = icompaths_add_path(p, dtix_vtpg, xp)) != ICOM_OK) + return rv; + } + if (xp->dctype & icomt_printer) { + if ((rv = icompaths_add_path(p, dtix_printer, xp)) != ICOM_OK) + return rv; + } } - p->npaths++; - p->paths[p->npaths] = NULL; + + /* Maintain backwards compatible instrument list alias */ + p->npaths = p->ndpaths[dtix_inst]; + p->paths = p->dpaths[dtix_inst]; + return ICOM_OK; } #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) -/* Add a serial path */ +/* Add a serial path. */ /* return icom error */ -static int icompaths_add_serial(icompaths *p, char *name, char *spath, icom_ser_attr sattr) { +static int icompaths_add_serial(icompaths *p, char *name, char *spath, icom_type dctype) { + icom_dtix ix = dtix_combined; + icompath *xp; int rv; - if ((rv = icompaths_add_path(p)) != ICOM_OK) + if ((rv = icompaths_add_path(p, ix, NULL)) != ICOM_OK) return rv; - - if ((p->paths[p->npaths-1]->name = strdup(name)) == NULL) { + + xp = p->dpaths[ix][p->ndpaths[ix]-1]; + + a1logd(g_log, 8, "icompaths_add_serial got '%s' dctype 0x%x\n",name,dctype); + + /* Type of port, port attributes, device category */ + xp->dctype |= icomt_cat_any; /* Assume any for now */ + xp->dctype |= icomt_serial; + xp->dctype |= icomt_seriallike; + xp->dctype |= dctype; + + if ((xp->name = strdup(name)) == NULL) { a1loge(p->log, ICOM_SYS, "icompaths: strdup failed!\n"); return ICOM_SYS; } - if ((p->paths[p->npaths-1]->spath = strdup(spath)) == NULL) { + if ((xp->spath = strdup(spath)) == NULL) { a1loge(p->log, ICOM_SYS, "icompaths: strdup failed!\n"); return ICOM_SYS; } - p->paths[p->npaths-1]->sattr = sattr; + + a1logd(g_log, 8, "icompaths_add_serial returning '%s' dctype 0x%x\n",xp->name,xp->dctype); return ICOM_OK; } -/* Modify a serial path to add the instrument type */ -int icompaths_set_serial_itype(icompath *p, instType itype) { +/* Modify a serial path to add the device type */ +int icompaths_set_serial_itype(icompath *p, devType itype) { char pname[400], *cp; + /* Convert device type to category */ + p->dctype = (p->dctype & ~icomt_cat_mask) | inst_category(itype); + p->itype = itype; /* Strip any existing description in brackets */ @@ -190,6 +304,9 @@ int icompaths_set_serial_itype(icompath *p, instType itype) { return ICOM_SYS; } free(cp); + + a1logd(g_log, 8, "icompaths_set_serial_itype '%s' returning dctype 0x%x\n",p->name,p->dctype); + return ICOM_OK; } @@ -197,60 +314,81 @@ int icompaths_set_serial_itype(icompath *p, instType itype) { #ifdef ENABLE_USB -/* Set an icompath details */ +/* Set an icompath details. */ /* return icom error */ static int icompath_set_usb(a1log *log, icompath *p, char *name, unsigned int vid, unsigned int pid, - int nep, struct usb_idevice *usbd, instType itype) { + int nep, struct usb_idevice *usbd, devType itype) { int rv; + if ((p->name = strdup(name)) == NULL) { a1loge(log, ICOM_SYS, "icompath: strdup failed!\n"); return ICOM_SYS; } + a1logd(g_log, 8, "icompath_set_usb '%s' got dctype 0x%x\n",p->name,p->dctype); + + p->dctype |= icomt_usb; + p->dctype = (p->dctype & ~icomt_cat_mask) | inst_category(itype); + p->nep = nep; p->vid = vid; p->pid = pid; p->usbd = usbd; p->itype = itype; + a1logd(g_log, 8, "icompath_set_usb '%s' returning dctype 0x%x\n",p->name,p->dctype); + return ICOM_OK; } /* Add a usb path. usbd is taken, others are copied. */ /* return icom error */ static int icompaths_add_usb(icompaths *p, char *name, unsigned int vid, unsigned int pid, - int nep, struct usb_idevice *usbd, instType itype) { + int nep, struct usb_idevice *usbd, devType itype) { + icom_dtix ix = dtix_combined; + icompath *xp; int rv; - if ((rv = icompaths_add_path(p)) != ICOM_OK) + if ((rv = icompaths_add_path(p, 0, NULL)) != ICOM_OK) return rv; - return icompath_set_usb(p->log, p->paths[p->npaths-1], name, vid, pid, nep, usbd, itype); + xp = p->dpaths[ix][p->ndpaths[ix]-1]; - return ICOM_OK; + return icompath_set_usb(p->log, xp, name, vid, pid, nep, usbd, itype); } /* Add an hid path. hidd is taken, others are copied. */ /* return icom error */ static int icompaths_add_hid(icompaths *p, char *name, unsigned int vid, unsigned int pid, - int nep, struct hid_idevice *hidd, instType itype) { + int nep, struct hid_idevice *hidd, devType itype) { + icom_dtix ix = dtix_combined; + icompath *xp; int rv; - if ((rv = icompaths_add_path(p)) != ICOM_OK) + if ((rv = icompaths_add_path(p, 0, NULL)) != ICOM_OK) return rv; - if ((p->paths[p->npaths-1]->name = strdup(name)) == NULL) { + xp = p->dpaths[ix][p->ndpaths[ix]-1]; + + a1logd(g_log, 8, "icompaths_add_hid '%s' got dctype 0x%x\n",xp->name,xp->dctype); + + xp->dctype |= icomt_hid; + xp->dctype = (xp->dctype & ~icomt_cat_mask) | inst_category(itype); + + if ((xp->name = strdup(name)) == NULL) { a1loge(p->log, ICOM_SYS, "icompaths: strdup failed!\n"); return ICOM_SYS; } - p->paths[p->npaths-1]->nep = nep; - p->paths[p->npaths-1]->vid = vid; - p->paths[p->npaths-1]->pid = pid; - p->paths[p->npaths-1]->hidd = hidd; - p->paths[p->npaths-1]->itype = itype; + xp->nep = nep; + xp->vid = vid; + xp->pid = pid; + xp->hidd = hidd; + xp->itype = itype; + + a1logd(g_log, 8, "icompath_set_usb '%s' returning dctype 0x%x\n",xp->name,xp->dctype); return ICOM_OK; } @@ -258,18 +396,115 @@ static int icompaths_add_hid(icompaths *p, char *name, unsigned int vid, unsigne static void icompaths_del(icompaths *p) { if (p != NULL) { - icompaths_clear(p); + icompaths_clear_all(p); p->log = del_a1log(p->log); /* Unreference it and set to NULL */ free(p); } } -int icompaths_refresh_paths(icompaths *p); /* Defined in platform specific */ +/* Create and return a list of available serial ports or USB devices for this system. */ +/* We look at the registry key "HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM" */ +/* to determine serial ports, and use libusb to discover USB devices. */ +/* return icom error */ +int icompaths_refresh_paths_sel(icompaths *p, icom_type mask) { + int rv, usbend = 0; + int i, j; + + a1logd(p->log, 7, "icoms_refresh_paths: called with mask = %d\n",mask); + + /* Clear any existing device paths */ + p->clear(p); + +#ifdef ENABLE_USB + if (mask & icomt_hid) { + a1logd(p->log, 6, "icoms_refresh_paths: looking for HID device\n"); + if ((rv = hid_get_paths(p)) != ICOM_OK) + return rv; + } + if (mask & icomt_usb) { + a1logd(p->log, 6, "icoms_refresh_paths: looking for USB device\n"); + if ((rv = usb_get_paths(p)) != ICOM_OK) + return rv; + } + usbend = p->ndpaths[dtix_combined]; + a1logd(p->log, 6, "icoms_refresh_paths: now got %d paths\n",usbend); +#endif /* ENABLE_USB */ + +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + + if (mask & (icomt_serial | icomt_fastserial | icomt_btserial)) { + a1logd(p->log, 6, "icoms_refresh_paths: looking for serial ports\n"); + if ((rv = serial_get_paths(p, mask)) != ICOM_OK) + return rv; + + } +#endif /* ENABLE_SERIAL || ENABLE_FAST_SERIAL */ + + /* Sort the COM keys so people don't get confused... */ + /* Sort identified instruments ahead of unknown serial ports */ + a1logd(p->log, 6, "icoms_refresh_paths: we now have %d devices and are about to sort them\n",p->ndpaths[dtix_combined]); + + { + icompath **pl = p->dpaths[dtix_combined]; + int np = p->ndpaths[dtix_combined]; + + for (i = usbend; i < (np-1); i++) { + for (j = i+1; j < np; j++) { + if ((pl[i]->itype == instUnknown && pl[j]->itype != instUnknown) + || (((pl[i]->itype == instUnknown && pl[j]->itype == instUnknown) + || (pl[i]->itype != instUnknown && pl[j]->itype != instUnknown)) + && strcmp(pl[i]->name, pl[j]->name) > 0)) { + icompath *tt = pl[i]; + pl[i] = pl[j]; + pl[j] = tt; + } + } + } + } + + /* Create the device specific lists */ + if ((rv = icompaths_make_dslists(p)) != ICOM_OK) { + a1logd(p->log, 1, "icoms_refresh_paths: icompaths_make_dslists failed with %d\n",rv); + return rv; + } + + a1logd(p->log, 8, "icoms_refresh_paths: returning %d paths and ICOM_OK\n",p->ndpaths[dtix_combined]); + + return ICOM_OK; +} + +/* (In implementation specific) */ +int serial_is_open(icoms *p); +void serial_close_port(icoms *p); + +/* Close the port */ +static void icoms_close_port(icoms *p) { + if (p->is_open) { +#ifdef ENABLE_USB + if (p->usbd) { + usb_close_port(p); + } else if (p->hidd) { + hid_close_port(p); + } +#endif +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + if (serial_is_open(p)) { + serial_close_port(p); + } +#endif /* ENABLE_SERIAL */ + p->is_open = 0; + } +} + +int icompaths_refresh_paths(icompaths *p) { + return icompaths_refresh_paths_sel(p, icomt_instrument | icomt_portattr_all); +} /* Allocate an icom paths and set it to the list of available devices */ +/* that match the icom_type mask. */ /* Return NULL on error */ -icompaths *new_icompaths(a1log *log) { +icompaths *new_icompaths_sel(a1log *log, icom_type mask) { icompaths *p; if ((p = (icompaths *)calloc(sizeof(icompaths), 1)) == NULL) { a1loge(log, ICOM_SYS, "new_icompath: calloc failed!\n"); @@ -278,11 +513,14 @@ icompaths *new_icompaths(a1log *log) { p->log = new_a1log_d(log); - p->clear = icompaths_clear; + p->clear = icompaths_clear_all; p->refresh = icompaths_refresh_paths; - p->del_last_path = icompaths_del_last_path; - p->get_last_path = icompaths_get_last_path; + p->refresh_sel = icompaths_refresh_paths_sel; p->get_path = icompaths_get_path; + p->get_path_sel = icompaths_get_path_sel; + p->del = icompaths_del; + + /* ====== internal implementation ======= */ #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) p->add_serial = icompaths_add_serial; #endif /* ENABLE_SERIAL */ @@ -290,9 +528,11 @@ icompaths *new_icompaths(a1log *log) { p->add_usb = icompaths_add_usb; p->add_hid = icompaths_add_hid; #endif /* ENABLE_USB */ - p->del = icompaths_del; + p->del_last_path = icompaths_del_last_path; + p->get_last_path = icompaths_get_last_path; + /* ====================================== */ - if (icompaths_refresh_paths(p)) { + if (icompaths_refresh_paths_sel(p, mask)) { a1loge(log, ICOM_SYS, "new_icompaths: icompaths_refresh_paths failed!\n"); free(p); return NULL; @@ -301,6 +541,12 @@ icompaths *new_icompaths(a1log *log) { return p; } +/* Allocate an icom paths and set it to the list of available instruments */ +/* Return NULL on error */ +icompaths *new_icompaths(a1log *log) { + return new_icompaths_sel(log, icomt_instrument | icomt_portattr_all); +} + /* ----------------------------------------------------- */ @@ -321,7 +567,6 @@ static int icom_copy_path_to_icom(icoms *p, icompath *pp) { a1loge(p->log, ICOM_SYS, "copy_path_to_icom: malloc spath failed\n"); return ICOM_SYS; } - p->sattr = pp->sattr; } else { p->spath = NULL; } @@ -335,24 +580,36 @@ static int icom_copy_path_to_icom(icoms *p, icompath *pp) { if ((rv = hid_copy_hid_idevice(p, pp)) != ICOM_OK) return rv; #endif + p->dctype = pp->dctype; p->itype = pp->itype; + a1logd(g_log, 8, "icom_copy_path_to_icom '%s' returning dctype 0x%x\n",p->name,p->dctype); + return ICOM_OK; } -/* Return the port type */ -static icom_type icoms_port_type( +/* Return the device category */ +/* (Returns bit flags) */ +static icom_type icoms_cat_type( icoms *p ) { + return p->dctype & icomt_cat_mask; +} -#ifdef ENABLE_USB - if (p->hidd != NULL) - return icomt_hid; - if (p->usbd != NULL) - return icomt_usb; -#endif +/* Return the communication port type */ +/* (Can use equality tests on return value) */ +static icom_type icoms_port_type( +icoms *p +) { + return p->dctype & icomt_port_mask; +} - return icomt_serial; +/* Return the communication port attributes */ +/* (Returns bit flags) */ +static icom_type icoms_port_attr( +icoms *p +) { + return p->dctype & icomt_attr_mask; } /* ----------------------------------------------------- */ @@ -365,10 +622,10 @@ icoms *p # include "icoms_ux.c" #endif -/* write and read convenience function */ +/* write and read convenience function with read flush */ /* return icom error */ static int -icoms_write_read( +icoms_write_read_ex( icoms *p, char *wbuf, /* Write puffer */ int nwch, /* if > 0, number of characters to write, else nul terminated */ @@ -377,27 +634,33 @@ int bsize, /* Buffer size */ int *bread, /* Bytes read (not including forced '\000') */ char *tc, /* Terminating characers, NULL for none or char count mode */ int ntc, /* Number of terminating characters needed, or char count needed */ -double tout /* Timeout for write and then read (i.e. max = 2 x tout) */ +double tout, /* Timeout for write and then read (i.e. max = 2 x tout) */ +int frbw /* nz to Flush Read Before Write */ ) { int rv = ICOM_OK; a1logd(p->log, 8, "icoms_write_read: called\n"); if (p->write == NULL || p->read == NULL) { /* Neither serial nor USB ? */ - a1loge(p->log, ICOM_NOTS, "icoms_write_read: Neither serial nor USB device!\n"); + a1loge(p->log, ICOM_NOTS, "icoms_write_read: No write and read functions set!\n"); return ICOM_NOTS; } #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) - /* Flush any stray chars if serial ?? */ - if (0 && p->usbd == NULL && p->hidd == NULL) { - char tbuf[100]; + /* Flush any stray chars if serial */ + if (frbw && (p->dctype & icomt_serial)) { + char tbuf[500]; int debug = p->log->debug; + int bread; if (debug < 8) p->log->debug = 0; - for (; rv == ICOM_OK;) /* Until we get a timeout */ - rv = p->read(p, tbuf, 100, NULL, NULL, 100, 0.01); + for (;;) { + bread = 0; + p->read(p, tbuf, 500, &bread, NULL, 500, 0.02); + if (bread == 0) + break; + } p->log->debug = debug; rv = ICOM_OK; } @@ -420,6 +683,23 @@ double tout /* Timeout for write and then read (i.e. max = 2 x tout) */ return rv; } +/* write and read convenience function */ +/* return icom error */ +static int +icoms_write_read( +icoms *p, +char *wbuf, /* Write puffer */ +int nwch, /* if > 0, number of characters to write, else nul terminated */ +char *rbuf, /* Read buffer */ +int bsize, /* Buffer size */ +int *bread, /* Bytes read (not including forced '\000') */ +char *tc, /* Terminating characers, NULL for none or char count mode */ +int ntc, /* Number of terminating characters needed, or char count needed */ +double tout /* Timeout for write and then read (i.e. max = 2 x tout) */ +) { + return icoms_write_read_ex(p, wbuf, nwch, rbuf, bsize, bread, tc, ntc, tout, 0); +} + /* Optional callback to client from device */ /* Default implementation is a NOOP */ static int icoms_interrupt(icoms *p, @@ -428,6 +708,29 @@ static int icoms_interrupt(icoms *p, return ICOM_OK; } +/* Destroy ourselves */ +static void +icoms_del(icoms *p) { + a1logd(p->log, 8, "icoms_del: called\n"); + if (p->is_open) { + a1logd(p->log, 8, "icoms_del: closing port\n"); + p->close_port(p); + } +#ifdef ENABLE_USB + usb_del_usb(p); + hid_del_hid(p); +#endif +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + if (p->spath != NULL) + free(p->spath); +#endif + p->log = del_a1log(p->log); + if (p->name != NULL) + free(p->name); + p->log = del_a1log(p->log); /* unref */ + free (p); +} + /* icoms Constructor */ /* Return NULL on error */ icoms *new_icoms( @@ -435,6 +738,9 @@ icoms *new_icoms( a1log *log /* log to take reference from, NULL for default */ ) { icoms *p; + + a1logd(log, 2, "new_icoms '%s' itype '%s' dctype 0x%x\n",ipath->name,inst_sname(ipath->itype),ipath->dctype); + if ((p = (icoms *)calloc(sizeof(icoms), 1)) == NULL) { a1loge(log, ICOM_SYS, "new_icoms: calloc failed!\n"); return NULL; @@ -475,14 +781,19 @@ icoms *new_icoms( p->close_port = icoms_close_port; + p->dev_cat = icoms_cat_type; p->port_type = icoms_port_type; + p->port_attr = icoms_port_attr; + #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + p->set_ser_port_ex = icoms_set_ser_port_ex; p->set_ser_port = icoms_set_ser_port; #endif /* ENABLE_SERIAL */ p->write = NULL; /* Serial open or set_methods will set */ p->read = NULL; p->write_read = icoms_write_read; + p->write_read_ex = icoms_write_read_ex; p->interrupt = icoms_interrupt; p->del = icoms_del; @@ -512,9 +823,9 @@ void good_beep() { /* Emit a "bad" double beep */ void bad_beep() { - /* 0msec delay, 800Hz for 200 msec */ + /* 0 msec delay, 800Hz for 200 msec */ msec_beep(0, 800, 200); - /* 500msec delay, 800Hz for 200 msec */ + /* 350 msec delay, 800Hz for 200 msec */ msec_beep(350, 800, 200); } diff --git a/spectro/icoms.h b/spectro/icoms.h index 2a282f0..be36cac 100644 --- a/spectro/icoms.h +++ b/spectro/icoms.h @@ -1,7 +1,7 @@ #ifndef ICOMS_H - /* An abstracted instrument serial and USB communication class. */ +/* An abstracted instrument serial and USB communication class. */ /* * Argyll Color Correction System @@ -30,7 +30,7 @@ #include <windows.h> #endif -#if defined(__APPLE__) +#if defined(UNIX_APPLE) #include <IOKit/usb/IOUSBLib.h> #endif @@ -40,6 +40,76 @@ #undef QUIET_MEMCHECKERS /* #define to memset coms read buffers before reading */ +/* USB open/handling/buf workaround flags */ +typedef enum { + icomuf_none = 0x0000, + icomuf_detach = 0x0001, /* Attempt to detach from system driver */ + icomuf_no_open_clear = 0x0002, /* Don't send a clear_halt after opening the port */ + icomuf_reset_before_close = 0x0004, /* Reset port before closing it */ + /* ??? Could change to reset before open ??? */ + icomuf_resetep_before_read = 0x0008 /* Do a usb_resetep before each ep read */ +} icomuflags; + +typedef enum { + icomt_unknown = 0x0000, + + /* Category of device */ + icomt_instrument = 0x010000, /* Color measurement instrument (default) */ + icomt_3dlut = 0x020000, /* A 3D cLUT box */ + icomt_vtpg = 0x040000, /* A video test patern generator box */ + icomt_printer = 0x080000, /* A printing device */ + + icomt_cat_any = 0x0f0000, /* Could be any device category */ + icomt_cat_mask = 0xff0000, /* Mask for device category */ + + /* Type of underlying communication port */ + /* (fastserio driver will have icomt_serial and icomt_usb set) */ + icomt_serial = 0x000001, /* Serial port (i.e. has baud rate etc.) */ + icomt_usb = 0x000002, /* USB port */ + icomt_hid = 0x000004, /* HID USB port */ + icomt_bt = 0x000008, /* Bluetooth (non-serial) */ + + icomt_port_mask = 0x0000ff, /* Mask for port type */ + + /* Attributes */ + icomt_fastserial = 0x000100, /* Fast Serial (i.e. USB, fastserio, Bluetooth) */ + icomt_btserial = 0x000200, /* Bluetooth serial port */ + icomt_seriallike = 0x000400, /* Uses serial message methods, but */ + /* may not be actual icomt_serial. */ + + icomt_attr_mask = 0x00ff00, /* Mask for port attributes */ + + icomt_portattr_mask = icomt_port_mask | icomt_attr_mask, + + icomt_portattr_all = icomt_portattr_mask /* Scan for all port types */ + + +} icom_type; + +/* Status bits/return values */ +#define ICOM_OK 0x000000 /* No error */ + +#define ICOM_NOTS 0x001000 /* Not supported */ +#define ICOM_SIZE 0x002000 /* Request/response size exceeded limits */ +#define ICOM_TO 0x004000 /* Timed out, but there may be bytes read/written */ +#define ICOM_SHORT 0x008000 /* No timeout but number of bytes wasn't read/written */ +#define ICOM_CANC 0x010000 /* Was cancelled */ +#define ICOM_SYS 0x020000 /* System error (ie. malloc, system call fail) */ +#define ICOM_VER 0x040000 /* Version error - need up to date kernel driver */ + +#define ICOM_USBR 0x000100 /* Unspecified USB read error */ +#define ICOM_USBW 0x000200 /* Unspecified USB write error */ +#define ICOM_SERR 0x000400 /* Unspecified Serial read error */ +#define ICOM_SERW 0x000800 /* Unspecified Serial write error */ + +#define ICOM_XRE 0x000040 /* Xmit shift reg empty */ +#define ICOM_XHE 0x000020 /* Xmit hold reg empty */ +#define ICOM_BRK 0x000010 /* Break detected */ +#define ICOM_FER 0x000008 /* Framing error */ +#define ICOM_PER 0x000004 /* Parity error */ +#define ICOM_OER 0x000002 /* Overun error */ +#define ICOM_DRY 0x000001 /* Recv data ready */ + typedef struct _icompath icompath; typedef struct _icompaths icompaths; typedef struct _icoms icoms; @@ -56,7 +126,7 @@ typedef struct { int packetsize; /* The max packet size */ int type; /* 2 = bulk, 3 = interrupt */ int interface; /* interface number */ -#if defined(__APPLE__) +#if defined(UNIX_APPLE) int pipe; /* pipe number (1..N, OS X only) */ #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) @@ -72,27 +142,17 @@ typedef struct { #endif /* ENABLE_USB */ -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) - -/* Attributes of the serial port */ -typedef enum { - icom_normal = 0x0000, /* Normal serial port */ - icom_fast = 0x0001, /* Fast port */ - icom_bt = 0x0002 /* Bluetooth serial port */ -} icom_ser_attr; - -#endif - /* - - - - - - - - - - - - - - - - - - - - */ /* Store information about a possible instrument communication path */ /* (Note a path doesn't have a reference to icompaths or its' log) */ -struct _icompath{ - instType itype; /* Type of instrument if known */ +struct _icompath { + devType itype; /* Type of device if known */ char *name; /* instance description */ + + icom_type dctype; /* Device and com. type */ #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) char *spath; /* Serial device path */ - icom_ser_attr sattr; /* Virtual serial port that can be identified quickly */ #endif #ifdef ENABLE_USB int nep; /* Number of end points */ @@ -104,56 +164,93 @@ struct _icompath{ extern icompath icomFakeDevice; /* Declare fake device */ +/* Device type specific list index */ +typedef enum { + dtix_combined = 0, /* Combined list */ + dtix_inst, /* Instrument */ + dtix_3dlut, + dtix_vtpg, + dtix_printer, + + dtix_number /* Number of entries */ +} icom_dtix; + /* The available instrument communication paths */ struct _icompaths { - int npaths; /* Number of paths */ - icompath **paths; /* Paths if any */ a1log *log; /* Verbose, debuge & error logging */ - /* Re-populate the available instrument list */ + /* Combined and device category specific path lists (read only). */ + /* Paths may appear on more than one, if they are multi-function, */ + /* or the category can't be determined without opening them. */ + /* (dpaths[dtix_combined] is the owner of the icompath, */ + /* and all the others are only populated after discovery.) */ + icompath **dpaths[dtix_number]; /* Device paths if any */ + int ndpaths[dtix_number]; /* Number of device paths */ + + /* Alias of dpaths[dtix_inst] for backwards compatibility, */ + /* only set after discovery. */ + icompath **paths; /* Device instrument if any */ + int npaths; /* Number of instrument paths */ + + /* Re-discover and populate the available instrument list */ /* return icom error */ int (*refresh)(struct _icompaths *p); + /* Same as above, but just scan for selected types of devices and/or port types */ + int (*refresh_sel)(struct _icompaths *p, icom_type mask); + + /* Return the instrument path corresponding to the port number, or NULL if out of range */ + icompath *(*get_path)( + struct _icompaths *p, + int port); /* Enumerated port number, 1..n */ +#define FAKE_DEVICE_PORT -98 /* Fake display & instrument */ +#define DEMO_DEVICE_PORT -99 /* Fake Demo instrument */ + + /* Return the device path corresponding to the port number, or NULL if out of range */ + icompath *(*get_path_sel)( + struct _icompaths *p, + icom_type dctype, /* Device type list */ + int port); /* Enumerated port number, 1..n */ + + /* Clear all the device paths */ + void (*clear)(struct _icompaths *p); + + /* We're done */ + void (*del)(struct _icompaths *p); + + /* ====== internal implementation ======= */ + #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) - /* Add a serial path. path is copied. Return icom error */ - int (*add_serial)(struct _icompaths *p, char *name, char *spath, icom_ser_attr sattr); + /* Add a serial path to combined. path is copied. Return icom error */ + int (*add_serial)(struct _icompaths *p, char *name, char *spath, icom_type dctype); #endif /* ENABLE_SERIAL */ #ifdef ENABLE_USB - /* Add a usb path. usbd is taken, others are copied. Return icom error */ + /* Add a usb path to combined. usbd is taken, others are copied. Return icom error */ int (*add_usb)(struct _icompaths *p, char *name, unsigned int vid, unsigned int pid, - int nep, struct usb_idevice *usbd, instType itype); + int nep, struct usb_idevice *usbd, devType itype); - /* Add an hid path. hidd is taken, others are copied. Return icom error */ + /* Add an hid path to combined. hidd is taken, others are copied. Return icom error */ int (*add_hid)(struct _icompaths *p, char *name, unsigned int vid, unsigned int pid, - int nep, struct hid_idevice *hidd, instType itype); + int nep, struct hid_idevice *hidd, devType itype); #endif /* ENABLE_USB */ - /* Delete the last path */ + /* Delete the last combined path */ void (*del_last_path)(struct _icompaths *p); - /* Return the last path */ + /* Return the last combined path */ icompath *(*get_last_path)(struct _icompaths *p); - /* Return the path corresponding to the port number, or NULL if out of range */ - icompath *(*get_path)( - struct _icompaths *p, - int port); /* Enumerated port number, 1..n */ -#define FAKE_DEVICE_PORT -98 /* Fake display & instrument */ -#define DEMO_DEVICE_PORT -99 /* Fake Demo instrument */ - - /* Clear all the paths */ - void (*clear)(struct _icompaths *p); - - /* We're done */ - void (*del)(struct _icompaths *p); - }; /* Allocate an icom paths and set it to the list of available devices */ +/* Use g_log as argument for default logging. */ /* Return NULL on error */ icompaths *new_icompaths(a1log *log); +/* Same as above, but just scan for selected types of devices and/or port types */ +icompaths *new_icompaths_sel(a1log *log, icom_type mask); + /* - - - - - - - - - - - */ /* Serial related stuff */ @@ -161,7 +258,7 @@ icompaths *new_icompaths(a1log *log); /* Flow control */ typedef enum { fc_nc = 0, /* not configured/default */ - fc_none, + fc_None, fc_XonXOff, fc_Hardware, /* RTS CTS flow control */ fc_HardwareDTR /* DTR DSR flow control */ @@ -211,47 +308,6 @@ typedef enum { length_8 } word_length; -/* USB open/handling/buf workaround flags */ -typedef enum { - icomuf_none = 0x0000, - icomuf_detach = 0x0001, /* Attempt to detach from system driver */ - icomuf_no_open_clear = 0x0001, /* Don't send a clear_halt after opening the port */ - icomuf_reset_before_close = 0x0004, /* Reset port before closing it */ - icomuf_resetep_before_read = 0x0008 /* Do a usb_resetep before each ep read */ -} icomuflags; - -/* Type of port driver */ -typedef enum { - icomt_serial, /* Serial port */ - icomt_usbserial, /* Serial port using fastserio.c driver */ - icomt_usb, /* USB port */ - icomt_hid /* HID (USB) port */ -} icom_type; - -/* Status bits/return values */ -#define ICOM_OK 0x000000 /* No error */ - -#define ICOM_NOTS 0x001000 /* Not supported */ -#define ICOM_SIZE 0x002000 /* Request/response size exceeded limits */ -#define ICOM_TO 0x004000 /* Timed out, but there may be bytes read/written */ -#define ICOM_SHORT 0x008000 /* No timeout but number of bytes wasn't read/written */ -#define ICOM_CANC 0x010000 /* Was cancelled */ -#define ICOM_SYS 0x020000 /* System error (ie. malloc, system call fail) */ -#define ICOM_VER 0x040000 /* Version error - need up to date kernel driver */ - -#define ICOM_USBR 0x000100 /* Unspecified USB read error */ -#define ICOM_USBW 0x000200 /* Unspecified USB write error */ -#define ICOM_SERR 0x000400 /* Unspecified Serial read error */ -#define ICOM_SERW 0x000800 /* Unspecified Serial write error */ - -#define ICOM_XRE 0x000040 /* Xmit shift reg empty */ -#define ICOM_XHE 0x000020 /* Xmit hold reg empty */ -#define ICOM_BRK 0x000010 /* Break detected */ -#define ICOM_FER 0x000008 /* Framing error */ -#define ICOM_PER 0x000004 /* Parity error */ -#define ICOM_OER 0x000002 /* Overun error */ -#define ICOM_DRY 0x000001 /* Recv data ready */ - /* Interrupt callback type */ typedef enum { icomi_data_available /* Data is available to be read */ @@ -270,8 +326,10 @@ struct _icoms { /* Private: */ /* Copy of some of icompath contents: */ + icom_type dctype; /* Device cat. and com. type */ + devType itype; /* Type of device if known */ + char *name; /* Device description */ - instType itype; /* Type of instrument if known */ int is_open; /* Flag, NZ if this port is open */ @@ -287,11 +345,9 @@ struct _icoms { #if defined (NT) HANDLE phandle; /* NT handle */ #endif -#if defined (UNIX) || defined(__APPLE__) +#if defined (UNIX) int fd; /* Unix file descriptor */ #endif - icom_ser_attr sattr; /* Serial port attributes, such as being fast */ - flow_control fc; baud_rate br; parity py; @@ -339,10 +395,31 @@ struct _icoms { /* Public: */ - /* Return the port type */ + /* Return the device category */ + /* (Returns bit flags) */ + icom_type (*dev_cat)(struct _icoms *p); + + /* Return the communication port type */ + /* (Can use equality tests on return value for normal ports, */ + /* or bit flag for fastserio USB/serial port) */ icom_type (*port_type)(struct _icoms *p); + /* Return the communication port attributes */ + /* (Returns bit flags) */ + icom_type (*port_attr)(struct _icoms *p); + #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + /* Select the serial communications port and characteristics - extended version */ + /* return icom error */ + int (*set_ser_port_ex)( + struct _icoms *p, + flow_control fc, /* Flow control */ + baud_rate baud, + parity parity, + stop_bits stop_bits, + word_length word_length, + int delayms); /* Delay in ms after open */ + /* Select the serial communications port and characteristics */ /* return icom error */ int (*set_ser_port)( @@ -414,6 +491,20 @@ struct _icoms { int ntc, /* Number of any terminating characters needed, or char count needed */ double tout); /* Timeout in seconds */ + /* "Serial" write and read with read flush */ + /* return icom error */ + int (*write_read_ex)( + struct _icoms *p, + char *wbuf, /* Write puffer */ + int nwch, /* if > 0, number of characters to write, else nul terminated */ + char *rbuf, /* Read buffer */ + int bsize, /* Buffer size. Make this larger than chars required! */ + int *bread, /* Bytes read (not including forced '\000') */ + char *tc, /* Terminating characers, NULL for none or char count mode */ + int ntc, /* Number of any terminating characters needed, or char count needed */ + double tout, /* Timeout in seconds */ + int frbw); /* nz to Flush Read Before Write */ + /* For a USB device, do a control message */ /* return icom error */ int (*usb_control)(struct _icoms *p, @@ -504,6 +595,16 @@ char *icoms_fix(char *s); /* Convert a limited binary buffer to a list of hex */ char *icoms_tohex(unsigned char *s, int len); +/* Implementation declarations */ + +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + +int icoms_ser_write(icoms *p, char *wbuf, int nwch, double tout); +int icoms_ser_read(icoms *p, char *rbuf, int bsize, int *bread, + char *tc, int ntc, double tout); + +#endif + #ifdef __cplusplus } #endif diff --git a/spectro/icoms_nt.c b/spectro/icoms_nt.c index 14258e9..b9e0d0c 100644 --- a/spectro/icoms_nt.c +++ b/spectro/icoms_nt.c @@ -1,5 +1,5 @@ - - /* Windows NT serial I/O class */ + +/* Windows NT serial I/O class */ /* * Argyll Color Correction System @@ -14,195 +14,171 @@ * see the License2.txt file for licencing details. */ +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + #include <conio.h> -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) instType fast_ser_inst_type(icoms *p, int tryhard, void *, void *); -#endif /* ENABLE_SERIAL */ - -/* Create and return a list of available serial ports or USB instruments for this system. */ -/* We look at the registry key "HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM" */ -/* to determine serial ports, and use libusb to discover USB instruments. */ -/* Create and return a list of available serial ports or USB instruments for this system */ -/* return icom error */ -int icompaths_refresh_paths(icompaths *p) { - int rv, usbend = 0; - int i, j; + +/* Add paths to serial connected device. */ +/* Return an icom error */ +int serial_get_paths(icompaths *p, icom_type mask) { + int i; LONG stat; HKEY sch; /* Serial coms handle */ - a1logd(p->log, 8, "icoms_get_paths: called\n"); + a1logd(p->log, 7, "serial_get_paths: called with mask = %d\n",mask); - /* Clear any existing paths */ - p->clear(p); - -#ifdef ENABLE_USB - if ((rv = hid_get_paths(p)) != ICOM_OK) - return rv; - if ((rv = usb_get_paths(p)) != ICOM_OK) - return rv; -#endif /* ENABLE_USB */ - usbend = p->npaths; - - a1logd(p->log, 6, "icoms_get_paths: got %d paths, looking up the registry for serial ports\n",p->npaths); - -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) // (Beware KEY_WOW64_64KEY ?) #define MXKSIZE 500 #define MXVSIZE 300 - /* Look in the registry for serial ports */ - if ((stat = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", - 0, KEY_READ, &sch)) != ERROR_SUCCESS) { - a1logd(p->log, 1, "icoms_get_paths: There don't appear to be any serial ports\n"); - return ICOM_OK; /* Maybe they have USB ports */ - } + if (mask & (icomt_serial | icomt_fastserial | icomt_btserial)) { - /* Look at all the values in this key */ - a1logd(p->log, 8, "icoms_get_paths: looking through all the values in the SERIALCOMM key\n"); - - for (i = 0; ; i++) { - char valname[MXKSIZE], *vp; - DWORD vnsize = MXKSIZE; - DWORD vtype; - char value[MXVSIZE]; - DWORD vsize = MXVSIZE; - icom_ser_attr sattr = icom_normal; - - stat = RegEnumValue( - sch, /* handle to key to enumerate */ - i, /* index of subkey to enumerate */ - valname, /* address of buffer for value name */ - &vnsize, /* address for size of value name buffer */ - NULL, /* reserved */ - &vtype, /* Address of value type */ - value, /* Address of value buffer */ - &vsize /* Address of value buffer size */ - ); - if (stat == ERROR_NO_MORE_ITEMS) { - a1logd(p->log, 8, "icoms_get_paths: got ERROR_NO_MORE_ITEMS\n"); - break; - } - if (stat == ERROR_MORE_DATA /* Hmm. Should expand buffer size */ - || stat != ERROR_SUCCESS) { - a1logw(p->log, "icoms_get_paths: RegEnumValue failed with %d\n",stat); - break; + a1logd(p->log, 6, "serial_get_paths: looking up the registry for serial ports\n"); + /* Look in the registry for serial ports */ + if ((stat = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", + 0, KEY_READ, &sch)) != ERROR_SUCCESS) { + a1logd(p->log, 1, "serial_get_paths: There don't appear to be any serial ports\n"); + return ICOM_OK; } - valname[MXKSIZE-1] = '\000'; - value[MXVSIZE-1] = '\000'; - if (vtype != REG_SZ) { - a1logw(p->log, "icoms_get_paths: RegEnumValue didn't return stringz type\n"); - continue; - } + /* Look at all the values in this key */ + a1logd(p->log, 8, "serial_get_paths: looking through all the values in the SERIALCOMM key\n"); + + for (i = 0; ; i++) { + char valname[MXKSIZE], *vp; + DWORD vnsize = MXKSIZE; + DWORD vtype; + char value[MXVSIZE]; + DWORD vsize = MXVSIZE; + icom_type dctype = icomt_unknown; + + stat = RegEnumValue( + sch, /* handle to key to enumerate */ + i, /* index of subkey to enumerate */ + valname, /* address of buffer for value name */ + &vnsize, /* address for size of value name buffer */ + NULL, /* reserved */ + &vtype, /* Address of value type */ + value, /* Address of value buffer */ + &vsize /* Address of value buffer size */ + ); + if (stat == ERROR_NO_MORE_ITEMS) { + a1logd(p->log, 8, "serial_get_paths: got ERROR_NO_MORE_ITEMS\n"); + break; + } + if (stat == ERROR_MORE_DATA /* Hmm. Should expand buffer size */ + || stat != ERROR_SUCCESS) { + a1logw(p->log, "serial_get_paths: RegEnumValue failed with %d\n",stat); + break; + } + valname[MXKSIZE-1] = '\000'; + value[MXVSIZE-1] = '\000'; - if ((vp = strrchr(valname, '\\')) == NULL) - vp = valname; - else - vp++; + if (vtype != REG_SZ) { + a1logw(p->log, "serial_get_paths: RegEnumValue didn't return stringz type\n"); + continue; + } - /* See if it looks like a fast port */ - if (strncmp(vp, "VCP", 3) == 0) { /* Virtual */ - sattr |= icom_fast; - } + if ((vp = strrchr(valname, '\\')) == NULL) + vp = valname; + else + vp++; - if (strncmp(vp, "BtPort", 6) == 0) { /* Blue tooth */ - sattr |= icom_fast; - sattr |= icom_bt; - } + a1logd(p->log, 8, "serial_get_paths: checking '%s'\n",vp); + + /* See if it looks like a fast port */ + if (strncmp(vp, "VCP", 3) == 0) { /* Virtual */ + dctype |= icomt_fastserial; + } + + if (strncmp(vp, "USBSER", 6) == 0) { /* USB Serial port */ + dctype |= icomt_fastserial; + } + + if (strncmp(vp, "BtPort", 6) == 0 /* Blue tooth */ + || strncmp(vp, "BthModem", 8) == 0) { + dctype |= icomt_fastserial; + dctype |= icomt_btserial; + } #ifndef ENABLE_SERIAL - if (sattr & icom_fast) { /* Only add fast ports if !ENABLE_SERIAL */ + if (dctype & icomt_fastserial) { /* Only add fast ports if !ENABLE_SERIAL */ #endif - /* Add the port to the list */ - p->add_serial(p, value, value, sattr); - a1logd(p->log, 8, "icoms_get_paths: Added path '%s' sattr 0x%x\n",value,sattr); + if (((mask & icomt_serial) && !(dctype & icomt_fastserial)) + || ((mask & icomt_fastserial) && (dctype & icomt_fastserial) + && !(dctype & icomt_btserial)) + || ((mask & icomt_btserial) && (dctype & icomt_btserial))) { + + // ~~ would be nice to add better description, similar + // to that of device manager, i.e. "Prolific USB-to-SerialBridge (COM6)" + /* Add the port to the list */ + p->add_serial(p, value, value, dctype); + a1logd(p->log, 8, "serial_get_paths: Added '%s' path '%s' dctype 0x%x\n",vp, value,dctype); + } #ifndef ENABLE_SERIAL - } + } #endif - /* If fast, try and identify it */ - if (sattr & icom_fast) { - icompath *path; - icoms *icom; - if ((path = p->get_last_path(p)) != NULL - && (icom = new_icoms(path, p->log)) != NULL) { - instType itype = fast_ser_inst_type(icom, 0, NULL, NULL); - if (itype != instUnknown) - icompaths_set_serial_itype(path, itype); - icom->del(icom); + /* If fast, try and identify it */ + if (dctype & icomt_fastserial) { + icompath *path; + icoms *icom; + if ((path = p->get_last_path(p)) != NULL + && (icom = new_icoms(path, p->log)) != NULL) { + devType itype = fast_ser_inst_type(icom, 0, NULL, NULL); + if (itype != instUnknown) + icompaths_set_serial_itype(path, itype); /* And set category */ + icom->del(icom); + } + a1logd(p->log, 8, "serial_get_paths: Identified '%s' dctype 0x%x\n",inst_sname(path->itype),path->dctype); } } - } - if ((stat = RegCloseKey(sch)) != ERROR_SUCCESS) { - a1logw(p->log, "icoms_get_paths: RegCloseKey failed with %d\n",stat); - } -#endif /* ENABLE_SERIAL || ENABLE_FAST_SERIAL */ - - /* Sort the COM keys so people don't get confused... */ - /* Sort identified instruments ahead of unknown serial ports */ - a1logd(p->log, 6, "icoms_get_paths: we now have %d entries and are about to sort them\n",p->npaths); - for (i = usbend; i < (p->npaths-1); i++) { - for (j = i+1; j < p->npaths; j++) { - if ((p->paths[i]->itype == instUnknown && p->paths[j]->itype != instUnknown) - || (((p->paths[i]->itype == instUnknown && p->paths[j]->itype == instUnknown) - || (p->paths[i]->itype != instUnknown && p->paths[j]->itype != instUnknown)) - && strcmp(p->paths[i]->name, p->paths[j]->name) > 0)) { - icompath *tt = p->paths[i]; - p->paths[i] = p->paths[j]; - p->paths[j] = tt; - } + if ((stat = RegCloseKey(sch)) != ERROR_SUCCESS) { + a1logw(p->log, "serial_get_paths: RegCloseKey failed with %d\n",stat); } } - a1logd(p->log, 8, "icoms_get_paths: returning %d paths and ICOM_OK\n",p->npaths); - return ICOM_OK; } +/* -------------------------------------------------------------------- */ -/* Close the port */ -static void icoms_close_port(icoms *p) { - if (p->is_open) { -#ifdef ENABLE_USB - if (p->usbd) { - usb_close_port(p); - } else if (p->hidd) { - hid_close_port(p); - } -#endif -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) - if (p->phandle != NULL) { - CloseHandle(p->phandle); - } -#endif /* ENABLE_SERIAL */ - p->is_open = 0; - } +/* Is the serial port actually open ? */ +int serial_is_open(icoms *p) { + return p->phandle != NULL; } -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) +/* Close the serial port */ +void serial_close_port(icoms *p) { + + if (p->is_open && p->phandle != NULL) { + CloseHandle(p->phandle); + p->phandle = NULL; + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ + } +} -static int icoms_ser_write(icoms *p, char *wbuf, int nwch, double tout); -static int icoms_ser_read(icoms *p, char *rbuf, int bsize, int *bread, - char *tc, int ntc, double tout); +/* -------------------------------------------------------------------- */ #ifndef CBR_921600 # define CBR_921600 921600 #endif -/* Set the serial port characteristics */ +/* Set the serial port characteristics - extended */ /* This always re-opens the port */ /* return an icom error */ static int -icoms_set_ser_port( +icoms_set_ser_port_ex( icoms *p, flow_control fc, baud_rate baud, parity parity, stop_bits stop, -word_length word) -{ +word_length word, +int delayms) { /* Delay after open in msec */ a1logd(p->log, 8, "icoms_set_ser_port: About to set port characteristics:\n" " Port name = %s\n" @@ -211,10 +187,13 @@ word_length word) " Parity = %d\n" " Stop bits = %d\n" " Word length = %d\n" - ,p->name ,fc ,baud_rate_to_str(baud) ,parity ,stop ,word); + " Open delay = %d ms\n" + ,p->name ,fc ,baud_rate_to_str(baud) ,parity ,stop ,word, delayms); - if (p->is_open) + if (p->is_open) { + a1logd(p->log, 8, "icoms_set_ser_port: closing port '%s'\n",p->name); p->close_port(p); + } if (p->port_type(p) == icomt_serial) { DCB dcb; @@ -234,7 +213,7 @@ word_length word) /* Make sure the port is open */ if (!p->is_open) { - char buf[50]; /* Temporary for COM device path */ + char buf[100]; /* Temporary for COM device path */ a1logd(p->log, 8, "icoms_set_ser_port: about to open serial port '%s'\n",p->spath); @@ -253,11 +232,18 @@ word_length word) a1logd(p->log, 1, "icoms_set_ser_port: open port '%s' failed with LastError %d\n",buf,GetLastError()); return ICOM_SYS; } + + if (delayms < 160) /* Seems to need at least 80 msec with many drivers */ + delayms = 160; + + msec_sleep(delayms); /* For Bluetooth */ + p->is_open = 1; } if (GetCommState(p->phandle, &dcb) == FALSE) { CloseHandle(p->phandle); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: reading state '%s' failed with LastError %d\n",p->spath,GetLastError()); return ICOM_SYS; } @@ -274,14 +260,17 @@ word_length word) dcb.fErrorChar = FALSE; dcb.fNull = FALSE; dcb.fRtsControl = RTS_CONTROL_ENABLE; /* Turn RTS on during connection */ -// dcb.fAbortOnError = TRUE; // Hmm. Stuffs up FTDI. Is it needed ? - dcb.fAbortOnError = FALSE; + dcb.fAbortOnError = FALSE; /* Hmm. TRUE Stuffs up FTDI. Is it needed ? */ switch (p->fc) { case fc_nc: CloseHandle(p->phandle); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal flow control %d\n",p->fc); return ICOM_SYS; + case fc_None: + /* Use no flow control */ + break; case fc_XonXOff: /* Use Xon/Xoff bi-directional flow control */ dcb.fOutX = TRUE; @@ -307,6 +296,7 @@ word_length word) switch (p->py) { case parity_nc: CloseHandle(p->phandle); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal parity setting %d\n",p->py); return ICOM_SYS; case parity_none: @@ -328,6 +318,7 @@ word_length word) switch (p->sb) { case stop_nc: CloseHandle(p->phandle); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal stop bits %d\n",p->sb); return ICOM_SYS; case stop_1: @@ -341,6 +332,7 @@ word_length word) switch (p->wl) { case length_nc: CloseHandle(p->phandle); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal word length %d\n",p->wl); return ICOM_SYS; case length_5: @@ -399,20 +391,24 @@ word_length word) break; default: CloseHandle(p->phandle); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal baud rate! (0x%x)\n",p->br); return ICOM_SYS; } - PurgeComm(p->phandle, PURGE_TXCLEAR | PURGE_RXCLEAR); + PurgeComm(p->phandle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); if (!SetCommState(p->phandle, &dcb)) { CloseHandle(p->phandle); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: SetCommState failed with LastError %d\n", GetLastError()); return ICOM_SYS; } - PurgeComm(p->phandle, PURGE_TXCLEAR | PURGE_RXCLEAR); + PurgeComm(p->phandle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); + + msec_sleep(50); /* Improves reliability of USB<->Serial converters */ p->write = icoms_ser_write; p->read = icoms_ser_read; @@ -423,6 +419,22 @@ word_length word) return ICOM_OK; } +/* Set the serial port characteristics */ +/* This always re-opens the port */ +/* return an icom error */ +static int +icoms_set_ser_port( +icoms *p, +flow_control fc, +baud_rate baud, +parity parity, +stop_bits stop, +word_length word) +{ + return icoms_set_ser_port_ex(p, fc, baud, parity, stop, word, 0); +} + + /* ---------------------------------------------------------------------------------*/ /* Serial write, read */ @@ -430,7 +442,7 @@ word_length word) /* Data will be written up to the terminating nul */ /* Return relevant error status bits */ /* Set the icoms lserr value */ -static int +int icoms_ser_write( icoms *p, char *wbuf, /* null terminated unless nwch > 0 */ @@ -519,7 +531,7 @@ double tout) /* Read characters into the buffer */ /* Return string will be terminated with a nul */ -static int +int icoms_ser_read( icoms *p, char *rbuf, /* Buffer to store characters read */ @@ -645,30 +657,5 @@ double tout /* Time out in seconds */ return p->lserr; } -#endif /* ENABLE_SERIAL */ -/* ---------------------------------------------------------------------------------*/ - - -/* Destroy ourselves */ -static void -icoms_del(icoms *p) { - a1logd(p->log, 8, "icoms_del: called\n"); - if (p->is_open) { - a1logd(p->log, 8, "icoms_del: closing port\n"); - p->close_port(p); - } -#ifdef ENABLE_USB - usb_del_usb(p); - hid_del_hid(p); -#endif -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) - if (p->spath != NULL) - free(p->spath); -#endif - p->log = del_a1log(p->log); - if (p->name != NULL) - free(p->name); - p->log = del_a1log(p->log); /* unref */ - free (p); -} +#endif /* ENABLE_SERIAL || ENABLE_FAST_SERIAL*/ diff --git a/spectro/icoms_ux.c b/spectro/icoms_ux.c index 7fb7359..9e79d7f 100644 --- a/spectro/icoms_ux.c +++ b/spectro/icoms_ux.c @@ -1,5 +1,5 @@ - /* Unix icoms and serial I/O class */ +/* Unix icoms and serial I/O class */ /* * Argyll Color Correction System @@ -18,6 +18,8 @@ TTBD: */ +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + #include <sys/types.h> /* Include sys/select.h ? */ #include <sys/stat.h> #include <fcntl.h> @@ -27,62 +29,50 @@ /* select() defined, but not poll(), so emulate poll() */ #if defined(FD_CLR) && !defined(POLLIN) -#include "pollem.h" -#define poll_x pollem +# include "pollem.h" +# define poll_x pollem #else -#include <sys/poll.h> /* Else assume poll() is native */ -#define poll_x poll +# include <sys/poll.h> /* Else assume poll() is native */ +# define poll_x poll #endif -#ifdef __APPLE__ +#ifdef UNIX_APPLE //#include <stdbool.h> -#include <sys/sysctl.h> -#include <sys/param.h> -#include <CoreFoundation/CoreFoundation.h> -#include <IOKit/IOKitLib.h> -#include <IOKit/serial/IOSerialKeys.h> -#include <IOKit/IOBSD.h> -#include <mach/mach_init.h> -#include <mach/task_policy.h> -#endif /* __APPLE__ */ - -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) -instType fast_ser_inst_type(icoms *p, int tryhard, void *, void *); -#endif /* ENABLE_SERIAL */ +# include <sys/sysctl.h> +# include <sys/param.h> +# include <CoreFoundation/CoreFoundation.h> +# include <IOKit/IOKitLib.h> +# include <IOKit/serial/IOSerialKeys.h> +# include <IOKit/IOBSD.h> +# include <mach/mach_init.h> +# include <mach/task_policy.h> +#endif /* UNIX_APPLE */ -/* Create and return a list of available serial ports or USB instruments for this system */ -/* return icom error */ -int icompaths_refresh_paths(icompaths *p) { - int rv, usbend = 0; - int i,j; - a1logd(p->log, 8, "icoms_get_paths: called\n"); +instType fast_ser_inst_type(icoms *p, int tryhard, void *, void *); - /* Clear any existing paths */ - p->clear(p); +/* Add paths to serial connected device. */ +/* Return an icom error */ +int serial_get_paths(icompaths *p, icom_type mask) { + int rv; -#ifdef ENABLE_USB - if ((rv = hid_get_paths(p)) != ICOM_OK) - return rv; - if ((rv = usb_get_paths(p)) != ICOM_OK) - return rv; -#endif /* ENABLE_USB */ - usbend = p->npaths; + a1logd(p->log, 7, "serial_get_paths: called with mask %d\n",mask); -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) -#ifdef __APPLE__ +#ifdef UNIX_APPLE /* Search the OSX registry for serial ports */ - { + if (mask & (icomt_serial | icomt_fastserial | icomt_btserial)) { kern_return_t kstat; mach_port_t mp; /* Master IO port */ CFMutableDictionaryRef sdict; /* Serial Port dictionary */ io_iterator_t mit; /* Matching itterator */ io_object_t ioob; /* Serial object found */ + a1logd(p->log, 6, "serial_get_paths: looking up serial ports services\n"); + /* Get dictionary of serial ports */ if ((sdict = IOServiceMatching(kIOSerialBSDServiceValue)) == NULL) { a1loge(p->log, ICOM_SYS, "IOServiceMatching returned a NULL dictionary\n"); - return ICOM_OK; + return ICOM_OK; /* Hmm. There are no serial ports ? */ } /* Set value to match to RS232 type serial */ @@ -98,7 +88,7 @@ int icompaths_refresh_paths(icompaths *p) { /* Find all the matching serial ports */ for (;;) { char pname[200]; - icom_ser_attr sattr = icom_normal; + icom_type dctype = icomt_unknown; CFTypeRef dfp; /* Device file path */ @@ -114,36 +104,50 @@ int icompaths_refresh_paths(icompaths *p) { if (!CFStringGetCString(dfp, pname, 100, kCFStringEncodingASCII)) goto continue2; - /* Ignore infra red port or Bluetooth, or any other noise */ + a1logd(p->log, 8, "serial_get_paths: checking '%s'\n",pname); + + /* Ignore infra red port or any other noise */ if (strstr(pname, "IrDA") != NULL || strstr(pname, "Dialup") != NULL - || strstr(pname, "Bluetooth") != NULL) + || strstr(pname, "PDA-Sync") != NULL) goto continue2; /* Would be nice to identify FTDI serial ports more specifically ? */ if (strstr(pname, "usbserial") != NULL) - sattr |= icom_fast; + dctype |= icomt_fastserial; + + if (strstr(pname, "Bluetooth") != NULL + || strstr(pname, "JETI") != NULL) { + dctype |= icomt_fastserial; + dctype |= icomt_btserial; + } #ifndef ENABLE_SERIAL - if (sattr & icom_fast) { /* Only add fast ports if !ENABLE_SERIAL */ + if (dctype & icomt_fastserial) { /* Only add fast ports if !ENABLE_SERIAL */ #endif - /* Add the port to the list */ - p->add_serial(p, pname, pname, sattr); - a1logd(p->log, 8, "icoms_get_paths: Added path '%s' attr 0x%x\n",pname, sattr); + if (((mask & icomt_serial) && !(dctype & icomt_fastserial)) + || ((mask & icomt_fastserial) && (dctype & icomt_fastserial) + && !(dctype & icomt_btserial)) + || ((mask & icomt_btserial) && (dctype & icomt_btserial))) { + /* Add the port to the list */ + p->add_serial(p, pname, pname, dctype); + a1logd(p->log, 8, "serial_get_paths: Added path '%s' dctype 0x%x\n",pname, dctype); + } #ifndef ENABLE_SERIAL } #endif /* If fast, try and identify it */ - if (sattr & icom_fast) { + if (dctype & icomt_fastserial) { icompath *path; icoms *icom; if ((path = p->get_last_path(p)) != NULL && (icom = new_icoms(path, p->log)) != NULL) { instType itype = fast_ser_inst_type(icom, 0, NULL, NULL); if (itype != instUnknown) - icompaths_set_serial_itype(path, itype); + icompaths_set_serial_itype(path, itype); /* And set category */ icom->del(icom); } + a1logd(p->log, 8, "serial_get_paths: Identified '%s' dctype 0x%x\n",inst_sname(path->itype),path->dctype); } continue2: CFRelease(dfp); @@ -152,8 +156,9 @@ int icompaths_refresh_paths(icompaths *p) { } IOObjectRelease(mit); /* Release the itterator */ } -#else - /* Other UNIX like systems */ + +#else /* Other UNIX like systems */ + /* Many are crude and list every available device name, whether */ /* it's usable or not. Do any UNIX systems have a mechanism for listing */ /* serial ports ?? */ @@ -203,12 +208,16 @@ int icompaths_refresh_paths(icompaths *p) { */ /* Search for devices that match the pattern /dev/ttyS[0-9]* and /dev/ttyUSB* */ - /* We will assume that ttyUSB* ports includes FTDI ports */ - { + /* We will assume that ttyUSB* ports includes FTDI ports. */ + /* Bluetooth ports are named ttyHS* or rfcomm* ?? */ + + if (mask & (icomt_serial | icomt_fastserial | icomt_bt)) { DIR *dd; struct dirent *de; char *dirn = "/dev/"; + a1logd(p->log, 6, "serial_get_paths: looking up serial port devices\n"); + if ((dd = opendir(dirn)) == NULL) { a1loge(p->log, ICOM_SYS, "failed to open directory \"%s\"\n",dirn); return ICOM_OK; @@ -217,10 +226,13 @@ int icompaths_refresh_paths(icompaths *p) { for (;;) { int fd; char *dpath; - icom_ser_attr sattr = icom_normal; + icom_type dctype = icomt_unknown; - if ((de = readdir(dd)) == NULL) + if ((de = readdir(dd)) == NULL) { break; + } + + a1logd(p->log, 8, "serial_get_paths: checking '%s'\n",de->d_name); if (!( #if defined(__FreeBSD__) || defined(__OpenBSD__) @@ -231,27 +243,32 @@ int icompaths_refresh_paths(icompaths *p) { /* Presumably Linux.. */ ( strncmp(de->d_name, "ttyS", 4) == 0 && de->d_name[4] >= '0' && de->d_name[4] <= '9') - || ( strncmp(de->d_name, "ttyUSB", 5) == 0) + || ( strncmp(de->d_name, "ttyUSB", 6) == 0) + || ( strncmp(de->d_name, "ttyHS", 5) == 0) + || ( strncmp(de->d_name, "rfcomm", 6) == 0) #endif )) continue; if ((dpath = (char *)malloc(strlen(dirn) + strlen(de->d_name) + 1)) == NULL) { closedir(dd); - a1loge(p->log, ICOM_SYS, "icompaths_refresh_paths() malloc failed!\n"); + a1loge(p->log, ICOM_SYS, "icompaths_refresh_paths_sel() malloc failed!\n"); return ICOM_SYS; } strcpy(dpath, dirn); strcat(dpath, de->d_name); /* See if the serial port is real */ - if (strncmp(de->d_name, "ttyUSB", 5) != 0) { + if (strncmp(de->d_name, "ttyUSB", 6) != 0 + && strncmp(de->d_name, "ttyHS", 5) != 0 + && strncmp(de->d_name, "rfcomm", 6) != 0) { /* Hmm. This is probably a bad idea - it can upset other */ /* programs that use the serial ports ? */ if ((fd = open(dpath, O_RDONLY | O_NOCTTY | O_NONBLOCK)) < 0) { - a1logd(p->log, 8, "icoms_get_paths: failed to open serial \"%s\" - not real\n",dpath); + a1logd(p->log, 8, "serial_get_paths: failed to open serial \"%s\" r/o - not real\n",dpath); free(dpath); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ continue; } /* On linux we could do a @@ -267,94 +284,83 @@ int icompaths_refresh_paths(icompaths *p) { */ close(fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ + a1logd(p->log, 8, "serial_get_paths: open'd serial \"%s\" r/o - assume real\n",dpath); } - a1logd(p->log, 8, "icoms_get_paths: open'd serial \"%s\" - assume real\n",dpath); - if (strncmp(de->d_name, "ttyUSB", 5) == 0) - sattr |= icom_fast; + if (strncmp(de->d_name, "ttyUSB", 6) == 0 + || strncmp(de->d_name, "ttyHS", 5) == 0 + || strncmp(de->d_name, "rfcomm", 6) == 0) + dctype |= icomt_fastserial; + + if (strncmp(de->d_name, "rfcomm", 6) == 0) { + dctype |= icomt_fastserial; + dctype |= icomt_btserial; + } #ifndef ENABLE_SERIAL - if (sattr & icom_fast) { /* Only add fast ports if !ENABLE_SERIAL */ + if (dctype & icomt_fastserial) { /* Only add fast ports if !ENABLE_SERIAL */ #endif - /* Add the path to the list */ - p->add_serial(p, dpath, dpath, 0); - a1logd(p->log, 8, "icoms_get_paths: Added path '%s' attr 0x%x\n",dpath,sattr); + if (((mask & icomt_serial) && !(dctype & icomt_fastserial)) + || ((mask & icomt_fastserial) && (dctype & icomt_fastserial) + && !(dctype & icomt_btserial)) + || ((mask & icomt_btserial) && (dctype & icomt_btserial))) { + /* Add the port to the list */ + p->add_serial(p, dpath, dpath, dctype); + a1logd(p->log, 8, "serial_get_paths: Added path '%s' dctype 0x%x\n",dpath, dctype); + } #ifndef ENABLE_SERIAL } #endif free(dpath); /* If fast, try and identify it */ - if (sattr & icom_fast) { + if (dctype & icomt_fastserial) { icompath *path; icoms *icom; if ((path = p->get_last_path(p)) != NULL && (icom = new_icoms(path, p->log)) != NULL) { instType itype = fast_ser_inst_type(icom, 0, NULL, NULL); if (itype != instUnknown) - icompaths_set_serial_itype(path, itype); + icompaths_set_serial_itype(path, itype); /* And set category */ icom->del(icom); } + a1logd(p->log, 8, "serial_get_paths: Identified '%s' dctype 0x%x\n",inst_sname(path->itype),path->dctype); } } closedir(dd); } -#endif /* ! __APPLE__ */ -#endif /* ENABLE_SERIAL */ - - /* Sort the serial /dev keys so people don't get confused... */ - /* Sort identified instruments ahead of unknown serial ports */ - for (i = usbend; i < (p->npaths-1); i++) { - for (j = i+1; j < p->npaths; j++) { - if ((p->paths[i]->itype == instUnknown && p->paths[j]->itype != instUnknown) - || (((p->paths[i]->itype == instUnknown && p->paths[j]->itype == instUnknown) - || (p->paths[i]->itype != instUnknown && p->paths[j]->itype != instUnknown)) - && strcmp(p->paths[i]->name, p->paths[j]->name) > 0)) { - icompath *tt = p->paths[i]; - p->paths[i] = p->paths[j]; - p->paths[j] = tt; - } - } - } +#endif /* ! UNIX_APPLE */ + return ICOM_OK; } /* -------------------------------------------------------------------- */ -/* Close the port */ -static void icoms_close_port(icoms *p) { - if (p->is_open) { -#ifdef ENABLE_USB - if (p->usbd) { - usb_close_port(p); - } else if (p->hidd) { - hid_close_port(p); - } -#endif -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) - if (p->fd != -1) { - close(p->fd); - p->fd = -1; - } -#endif /* ENABLE_SERIAL */ - p->is_open = 0; - } +/* Is the serial port actually open ? */ +int serial_is_open(icoms *p) { + return p->fd != -1; } -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) +/* Close the serial port */ +void serial_close_port(icoms *p) { -static int icoms_ser_write(icoms *p, char *wbuf, int nwch, double tout); -static int icoms_ser_read(icoms *p, char *rbuf, int bsize, int *bread, - char *tc, int ntc, double tout); + if (p->is_open && p->fd != -1) { + close(p->fd); + p->fd = -1; + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ + } +} +/* -------------------------------------------------------------------- */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE # ifndef IOSSIOSPEED # define IOSSIOSPEED _IOW('T', 2, speed_t) # endif #endif -#if defined(__APPLE__) || defined(__OpenBSD__) +#if defined(UNIX_APPLE) || defined(__OpenBSD__) # ifndef B921600 # define B921600 921600 # endif @@ -364,14 +370,14 @@ static int icoms_ser_read(icoms *p, char *rbuf, int bsize, int *bread, /* This always re-opens the port */ /* return icom error */ static int -icoms_set_ser_port( +icoms_set_ser_port_ex( icoms *p, flow_control fc, baud_rate baud, parity parity, stop_bits stop, -word_length word -) { +word_length word, +int delayms) { /* Delay after open in msec */ int rv; struct termios tio; speed_t speed = 0; @@ -379,15 +385,18 @@ word_length word a1logd(p->log, 8, "icoms_set_ser_port: About to set port characteristics:\n" " Port name = %s\n" " Flow control = %d\n" - " Baud Rate = %d\n" + " Baud Rate = %s\n" " Parity = %d\n" " Stop bits = %d\n" " Word length = %d\n" - ,p->name ,fc ,baud ,parity ,stop ,word); + " Open delay = %d ms\n" + ,p->name ,fc ,baud_rate_to_str(baud) ,parity ,stop ,word, delayms); - if (p->is_open) /* Close it and re-open it */ + if (p->is_open) { /* Close it and re-open it */ + a1logd(p->log, 8, "icoms_set_ser_port: closing port\n"); p->close_port(p); + } if (p->port_type(p) == icomt_serial) { @@ -410,10 +419,52 @@ word_length word a1logd(p->log, 8, "icoms_set_ser_port: about to open serial port '%s'\n",p->spath); if ((p->fd = open(p->spath, O_RDWR | O_NOCTTY )) < 0) { - a1logd(p->log, 1, "icoms_set_ser_port: open port '%s' failed with %d (%s)\n",p->spath,p->fd,strerror(errno)); + a1logd(p->log, 1, "icoms_set_ser_port: open port '%s' r/w failed with %d (%s)\n",p->spath,p->fd,strerror(errno)); + + if (delayms < 160) /* Seems to need at least 80 msec for many drivers */ + delayms = 160; + + msec_sleep(delayms); /* For Bluetooth */ + +#ifdef NEVER /* See what supplementary groups we are a member of */ + { + int j, ngroups = 16; + gid_t *groups = (gid_t *)malloc (ngroups * sizeof(gid_t)); + struct passwd *pw = getpwuid(getuid()); + + if (groups == NULL) { + a1logd(p->log, 0, "icoms_set_ser_port: malloc of sgroups failed\n"); + goto fail; + } + if (pw == NULL) { + a1logd(p->log, 0, "icoms_set_ser_port: getpwuid failed\n"); + goto fail; + } + + if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups) < 0) { + groups = realloc(groups, ngroups * sizeof(gid_t)); + if (groups == NULL) { + a1logd(p->log, 0, "icoms_set_ser_port: realloc of sgroups failed\n"); + goto fail; + } + getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); + } + a1logd(p->log, 0, "icoms_set_ser_port: ngroups = %d\n", ngroups); + for (j = 0; j < ngroups; j++) { + struct group *gr = getgrgid(groups[j]); + if (gr != NULL) + a1logd(p->log, 0, "icoms_set_ser_port: %d: gid %d\n", j,groups[j]); + else + a1logd(p->log, 0, "icoms_set_ser_port: %d: gid %d (%s)\n", j,groups[j],gr->gr_name); + } + fail:; + } +#endif return ICOM_SYS; } + /* O_NONBLOCK O_SYNC */ + p->is_open = 1; } @@ -448,6 +499,7 @@ word_length word switch (p->fc) { case fc_nc: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal flow control %d\n",p->fc); return ICOM_SYS; case fc_XonXOff: @@ -459,7 +511,7 @@ word_length word break; case fc_Hardware: /* Use RTS/CTS bi-directional flow control */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE tio.c_cflag |= CCTS_OFLOW; tio.c_cflag |= CRTS_IFLOW; #else @@ -468,7 +520,7 @@ word_length word break; case fc_HardwareDTR: /* Use DTR/DSR bi-directional flow control */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE tio.c_cflag |= CDSR_OFLOW; tio.c_cflag |= CDTR_IFLOW; #else @@ -484,6 +536,7 @@ word_length word switch (p->py) { case parity_nc: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal parity setting %d\n",p->py); return ICOM_SYS; break; @@ -504,6 +557,7 @@ word_length word switch (p->sb) { case stop_nc: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal stop bits %d\n",p->sb); return ICOM_SYS; case stop_1: @@ -516,6 +570,7 @@ word_length word switch (p->wl) { case length_nc: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal word length %d\n",p->wl); return ICOM_SYS; case length_5: @@ -572,6 +627,7 @@ word_length word break; default: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal baud rate! (0x%x)\n",p->br); return ICOM_SYS; } @@ -589,11 +645,13 @@ word_length word #endif if ((rv = cfsetispeed(&tio, speed)) < 0) { close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: cfsetispeed failed with '%s'\n", strerror(errno)); return ICOM_SYS; } if ((rv = cfsetospeed(&tio, speed)) < 0) { close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: cfsetospeed failed with '%s'\n", strerror(errno)); return ICOM_SYS; } @@ -601,11 +659,13 @@ word_length word /* Make change immediately */ if ((rv = tcsetattr(p->fd, TCSANOW, &tio)) < 0) { close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: tcsetattr failed with '%s'\n", strerror(errno)); return ICOM_SYS; } tcflush(p->fd, TCIOFLUSH); /* Discard any current in/out data */ + msec_sleep(50); /* Improves reliability of USB<->Serial converters */ p->write = icoms_ser_write; p->read = icoms_ser_read; @@ -616,6 +676,29 @@ word_length word return ICOM_OK; } +/* Set the serial port characteristics */ +/* This always re-opens the port */ +/* return an icom error */ +static int +icoms_set_ser_port( +icoms *p, +flow_control fc, +baud_rate baud, +parity parity, +stop_bits stop, +word_length word) +{ + return icoms_set_ser_port_ex(p, fc, baud, parity, stop, word, 0); +} + +/* ------------------------------ */ +/* Could add read flush function, to discard all read data using + + tcflush(fd, TCIFLUSH); + +*/ + + /* ---------------------------------------------------------------------------------*/ /* Serial write/read */ @@ -623,7 +706,7 @@ word_length word /* Data will be written up to the terminating nul */ /* Return relevant error status bits */ /* Set the icoms lserr value */ -static int +int icoms_ser_write( icoms *p, char *wbuf, /* null terminated unless nwch > 0 */ @@ -699,6 +782,8 @@ double tout retrv |= ICOM_TO; } +// tcdrain(p->fd); + a1logd(p->log, 8, "icoms_ser_write: took %d msec, returning ICOM err 0x%x\n",etime - stime,retrv); p->lserr = retrv; return p->lserr; @@ -707,7 +792,7 @@ double tout /* Read characters into the buffer */ /* Return string will be terminated with a nul */ /* return icom error */ -static int +int icoms_ser_read( icoms *p, char *rbuf, /* Buffer to store characters read */ @@ -826,30 +911,5 @@ double tout /* Time out in seconds */ return p->lserr; } -#endif /* ENABLE_SERIAL */ - -/* ---------------------------------------------------------------------------------*/ - -/* Destroy ourselves */ -static void -icoms_del(icoms *p) { - a1logd(p->log, 8, "icoms_del: called\n"); - if (p->is_open) { - a1logd(p->log, 8, "icoms_del: closing port\n"); - p->close_port(p); - } -#ifdef ENABLE_USB - usb_del_usb(p); - hid_del_hid(p); -#endif -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) - if (p->spath != NULL) - free(p->spath); -#endif - p->log = del_a1log(p->log); - if (p->name != NULL) - free(p->name); - p->log = del_a1log(p->log); - free (p); -} +#endif /* ENABLE_SERIAL || ENABLE_FAST_SERIAL*/ diff --git a/spectro/illumread.c b/spectro/illumread.c index 381f6b6..58569e3 100644 --- a/spectro/illumread.c +++ b/spectro/illumread.c @@ -393,7 +393,7 @@ int main(int argc, char *argv[]) if (na == NULL) usage(NULL); if (na[0] == 'n' || na[0] == 'N') - fc = fc_none; + fc = fc_None; else if (na[0] == 'h' || na[0] == 'H') fc = fc_Hardware; else if (na[0] == 'x' || na[0] == 'X') @@ -431,17 +431,17 @@ int main(int argc, char *argv[]) /* Special debug */ strcpy(tnp, "_i.sp"); - if (read_xspect(&i_sp, tname) == 0) { + if (read_xspect(&i_sp, NULL, tname) == 0) { rd_i = 1; printf("(Found '%s' file and loaded it)\n",tname); } strcpy(tnp, "_r.sp"); - if (read_xspect(&r_sp, tname) == 0) { + if (read_xspect(&r_sp, NULL, tname) == 0) { rd_r = 1; printf("(Found '%s' file and loaded it)\n",tname); } strcpy(tnp, "_p.sp"); - if (read_xspect(&p_sp, tname) == 0) { + if (read_xspect(&p_sp, NULL, tname) == 0) { rd_p = 1; /* Should read instrument type from debug_p.sp !! */ if (inst_illuminant(&insp, instI1Pro) != 0) /* Hack !! */ @@ -548,8 +548,8 @@ int main(int argc, char *argv[]) printf("Instrument lacks spectral measurement capability"); } - if (refrmode >= 0 && !IMODETST(cap, inst_mode_emis_refresh_ovd) - && !IMODETST(cap, inst_mode_emis_norefresh_ovd)) { + if (refrmode >= 0 && it->check_mode(it, inst_mode_emis_refresh_ovd) != inst_ok + && it->check_mode(it, inst_mode_emis_norefresh_ovd) != inst_ok) { if (verb) { printf("Requested refresh mode override and instrument doesn't support it (ignored)\n"); refrmode = -1; @@ -570,9 +570,9 @@ int main(int argc, char *argv[]) it->capabilities(it, &cap, &cap2, &cap3); if (c == '1') { - if (IMODETST(cap, inst_mode_emis_ambient)) { + if (it->check_mode(it, inst_mode_emis_ambient) == inst_ok) { mode = inst_mode_emis_ambient; - } else if (IMODETST(cap, inst_mode_emis_spot)) { + } else if (it->check_mode(it, inst_mode_emis_spot) == inst_ok) { mode = inst_mode_emis_spot; } else { printf("!!! Instrument doesn't have ambient or emissive capability !!!\n"); @@ -580,9 +580,9 @@ int main(int argc, char *argv[]) } } if (c == '2') { - if (IMODETST(cap, inst_mode_emis_tele)) { + if (it->check_mode(it, inst_mode_emis_tele) == inst_ok) { mode = inst_mode_emis_tele; - } else if (IMODETST(cap, inst_mode_emis_spot)) { + } else if (it->check_mode(it, inst_mode_emis_spot) == inst_ok) { mode = inst_mode_emis_spot; } else { printf("!!! Instrument doesn't have telephoto or emissive capability !!!\n"); @@ -592,7 +592,7 @@ int main(int argc, char *argv[]) if (c == '3') { inst_opt_filter filt; - if (IMODETST(cap, inst_mode_ref_spot)) { + if (it->check_mode(it, inst_mode_ref_spot) == inst_ok) { mode = inst_mode_ref_spot; } else { printf("!!! Instrument lacks reflective spot measurement capability !!!\n"); @@ -769,7 +769,7 @@ int main(int argc, char *argv[]) } else { if (c == '1') { - if (IMODETST(cap, inst_mode_emis_ambient)) { + if (it->check_mode(it, inst_mode_emis_ambient) == inst_ok) { printf("\n(If applicable) set instrument to ambient measurenent mode, or place\n"); printf("ambient adapter on it, and position it so as to measure the illuminant directly.\n"); } else { @@ -777,7 +777,7 @@ int main(int argc, char *argv[]) printf("and position it so as to measure the illuminant directly.\n"); } } else if (c == '2') { - if (IMODETST(cap, inst_mode_emis_tele)) { + if (it->check_mode(it, inst_mode_emis_tele) == inst_ok) { printf("\n(If applicable) set instrument to telephoto measurenent mode,\n"); printf("position it so as to measure the illuminant reflected from the paper.\n"); } else { @@ -921,19 +921,19 @@ int main(int argc, char *argv[]) continue; } - if (c == '1') { + if (c == '1') { /* Illuminant */ i_sp = val.sp; if (tmode && rd_i == 0) { strcpy(tnp, "_i.sp"); - write_xspect(tname,&i_sp); + write_xspect(tname, inst_mrt_emission, &i_sp); } - } else if (c == '2') { + } else if (c == '2') { /* Illuminant reflected on paper */ r_sp = val.sp; if (tmode && rd_r == 0) { strcpy(tnp, "_r.sp"); - write_xspect(tname,&r_sp); + write_xspect(tname, inst_mrt_emission, &r_sp); } - } else if (c == '3') { + } else if (c == '3') { /* Paper reflectance */ p_sp = val.sp; /* Get the illuminant spectrum too */ @@ -943,7 +943,7 @@ int main(int argc, char *argv[]) if (tmode && rd_p == 0) { /* Should save instrument type/instrument illuminant spectrum !!! */ strcpy(tnp, "_p.sp"); - write_xspect(tname,&p_sp); + write_xspect(tname, inst_mrt_reflective, &p_sp); } } @@ -1125,16 +1125,16 @@ int main(int argc, char *argv[]) for (i = 0; i < ill.spec_n; i++) ill.spec[i] = aill.spec[i]/nacc; - if(write_xspect(outname, &ill)) + if(write_xspect(outname, inst_mrt_ambient, &ill)) printf("\nWriting file '%s' failed\n",outname); else printf("\nWriting file '%s' succeeded\n",outname); if (tmode) { strcpy(tnp, "_mpir.sp"); // Measured paper under illuminant spectrum - write_xspect(tname,&bf.srop); + write_xspect(tname, inst_mrt_reflective, &bf.srop); strcpy(tnp, "_cpir.sp"); // Computed paper under illuminant spectrum - write_xspect(tname,&bf.cpsp); + write_xspect(tname, inst_mrt_reflective, &bf.cpsp); } } @@ -1188,9 +1188,9 @@ int main(int argc, char *argv[]) for (j = 0; j < bf.ill.spec_n; j++) { GCC_BUGFIX(j) xx[j] = XSPECT_XWL(&bf.ill, j); - y1[j] = value_xspect(bf.i_sp, xx[j]); /* Measured (black) */ - y2[j] = value_xspect(&bf.ill, xx[j]); /* Computed (red)*/ - y3[j] = value_xspect(&cf.dxx, xx[j]); /* Daylight match (green)*/ + y1[j] = value_xspect(bf.i_sp, xx[j]); /* Measured (black) */ + y2[j] = value_xspect(&bf.ill, xx[j]); /* Computed (red)*/ + y3[j] = value_xspect(&cf.dxx, xx[j]); /* Daylight match (green)*/ } xmax = bf.ill.spec_wl_long; @@ -1212,10 +1212,10 @@ int main(int argc, char *argv[]) for (j = 0; j < bf.cpsp.spec_n; j++) { GCC_BUGFIX(j) xx[j] = XSPECT_XWL(&bf.cpsp, j); - y1[j] = value_xspect(&bf.srop, xx[j]); /* Measured reflectance (black) */ - y2[j] = value_xspect(&cpisp, xx[j]); /* Computed initial reflectance (red) */ - y3[j] = value_xspect(&bf.cpsp, xx[j]); /* Computed final reflectance (green) */ -// y3[j] = value_xspect(&cpdsp, xx[j]); /* Computed daylight reflectance (green) */ + y1[j] = value_xspect(&bf.srop, xx[j]); /* Measured reflectance (black) */ + y2[j] = value_xspect(&cpisp, xx[j]); /* Computed initial reflectance (red) */ + y3[j] = value_xspect(&bf.cpsp, xx[j]); /* Computed final reflectance (green) */ +// y3[j] = value_xspect(&cpdsp, xx[j]); /* Computed daylight reflectance (green) */ } xmax = bf.cpsp.spec_wl_long; @@ -1233,8 +1233,8 @@ int main(int argc, char *argv[]) for (j = 0; j < bf.ill.spec_n; j++) { GCC_BUGFIX(j) xx[j] = XSPECT_XWL(&bf.ill, j); - y1[j] = value_xspect(bf.i_sp, xx[j]); /* Measured (black) */ - y2[j] = value_xspect(&bf.ill, xx[j]); /* Computed (red)*/ + y1[j] = value_xspect(bf.i_sp, xx[j]); /* Measured (black) */ + y2[j] = value_xspect(&bf.ill, xx[j]); /* Computed (red)*/ } xmax = bf.ill.spec_wl_long; @@ -1248,9 +1248,9 @@ int main(int argc, char *argv[]) for (j = 0; j < bf.cpsp.spec_n; j++) { GCC_BUGFIX(j) xx[j] = XSPECT_XWL(&bf.cpsp, j); - y1[j] = value_xspect(&bf.srop, xx[j]); /* Measured reflectance (black) */ - y2[j] = value_xspect(&cpisp, xx[j]); /* Computed initial reflectance (red) */ - y3[j] = value_xspect(&bf.cpsp, xx[j]); /* Computed final reflectance (green) */ + y1[j] = value_xspect(&bf.srop, xx[j]); /* Measured reflectance (black) */ + y2[j] = value_xspect(&cpisp, xx[j]); /* Computed initial reflectance (red) */ + y3[j] = value_xspect(&bf.cpsp, xx[j]); /* Computed final reflectance (green) */ } xmax = bf.cpsp.spec_wl_long; diff --git a/spectro/inst.c b/spectro/inst.c index 8976669..681ef0a 100644 --- a/spectro/inst.c +++ b/spectro/inst.c @@ -1,5 +1,5 @@ - /* Abstract instrument class implemenation */ +/* Abstract instrument class implemenation */ /* * Argyll Color Correction System @@ -373,6 +373,7 @@ static inst_code calibrate( inst *p, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN]) { /* Condition identifier (ie. white reference ID, filter ID) */ return inst_unsupported; } @@ -585,7 +586,7 @@ static inst_config config_enum(inst *p, int ec) { /* Delete things set/done by new_inst() */ static inst_code virtual_del(inst *p) { -#if defined(__APPLE__) +#if defined(UNIX_APPLE) osx_latencycritical_end(); #endif @@ -612,7 +613,7 @@ void *cntx /* Context for callback */ return NULL; } - a1logd(log, 2, "new_inst: called with path '%s'\n",path->name); + a1logd(log, 2, "new_inst: called with path '%s' type '%s'\n",path->name,inst_sname(path->itype)); if ((icom = new_icoms(path, log)) == NULL) { a1logd(log, 2, "new_inst: new_icoms failed to open it\n"); @@ -625,14 +626,19 @@ void *cntx /* Context for callback */ itype = icom->itype; /* Instrument type if its known from usb/hid */ #if defined(ENABLE_FAST_SERIAL) - if (itype == instUnknown && !nocoms && (icom->sattr & icom_fast)) { + if (itype == instUnknown && !nocoms && (icom->dctype & icomt_fastserial)) { itype = fast_ser_inst_type(icom, 1, uicallback, cntx); /* Else type from serial */ + icom->dctype = (icom->dctype & ~icomt_cat_mask) | inst_category(itype); + a1logd(log, 8, "new_inst: fast set '%s' dctype 0x%x\n",icom->name,icom->dctype); } #endif /* ENABLE_FAST_SERIAL */ #if defined(ENABLE_SERIAL) - if (itype == instUnknown && !nocoms) + if (itype == instUnknown && !nocoms) { itype = ser_inst_type(icom, uicallback, cntx); /* Else type from serial */ + icom->dctype = (icom->dctype & ~icomt_cat_mask) | inst_category(itype); + a1logd(log, 8, "new_inst: set '%s' dctype 0x%x\n",icom->name,icom->dctype); + } #endif /* ENABLE_SERIAL */ @@ -657,7 +663,8 @@ void *cntx /* Context for callback */ #ifdef ENABLE_FAST_SERIAL if (itype == instSpecbos1201 - || itype == instSpecbos) + || itype == instSpecbos + || itype == instSpectraval) p = (inst *)new_specbos(icom, itype); if (itype == instKleinK10) p = (inst *)new_kleink10(icom, itype); @@ -696,8 +703,10 @@ void *cntx /* Context for callback */ p = (inst *)new_huey(icom, itype); else if (itype == instSmile) p = (inst *)new_i1disp(icom, itype); +#ifdef ENABLE_FAST_SERIAL else if (itype == instSMCube) p = (inst *)new_smcube(icom, itype); +#endif else if (itype == instHCFR) p = (inst *)new_hcfr(icom, itype); else if (itype == instColorHug @@ -799,7 +808,7 @@ void *cntx /* Context for callback */ /* Set the provided user interaction callback */ p->set_uicallback(p, uicallback, cntx); -#if defined(__APPLE__) +#if defined(UNIX_APPLE) osx_latencycritical_start(); #endif @@ -917,8 +926,10 @@ int doccmx /* Add matching installed ccmx files */ for (i = 0; ss_list[i].path != NULL; i++) { - if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) + if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) { + free_iccss(ss_list); return inst_internal_error; + } list[nlist-1].flags = inst_dtflags_ccss | inst_dtflags_ld | inst_dtflags_wr; if (!ss_list[i].oem) @@ -938,6 +949,7 @@ int doccmx /* Add matching installed ccmx files */ list[nlist-1].sets = ss_list[i].sets; ss_list[i].sets = NULL; list[nlist-1].no_sets = ss_list[i].no_sets; ss_list[i].no_sets = 0; } + free_iccss(ss_list); } /* Add any OEM and custom ccmx's */ @@ -963,8 +975,10 @@ int doccmx /* Add matching installed ccmx files */ continue; } - if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) + if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) { + free_iccmx(ss_list); return inst_internal_error; + } list[nlist-1].flags = inst_dtflags_ccmx | inst_dtflags_ld | inst_dtflags_wr; if (!ss_list[i].oem) @@ -984,6 +998,7 @@ int doccmx /* Add matching installed ccmx files */ list[nlist-1].cc_cbid = ss_list[i].cc_cbid; icmCpy3x3(list[nlist-1].mat, ss_list[i].mat); } + free_iccmx(ss_list); } /* Copy candidate selectors to private isel[] list */ @@ -1047,6 +1062,39 @@ int doccmx /* Add matching installed ccmx files */ return inst_ok; } +/* --------------------------------------------------- */ + +/* Delayed scan-ready prompt handler */ +static int delayed_scan_ready(void *pp) { + inst *p = (inst *)pp; + + msec_sleep(p->scan_ready_delay); + a1logd(g_log,8, "delayed scan_ready activate\n"); + + if (p->eventcallback != NULL) + p->eventcallback(p->event_cntx, inst_event_scan_ready); + return 0; +} + +/* Issue an inst_event_scan_ready event/prompt after a delay */ +void issue_scan_ready(inst *p, int delay) { + a1logd(g_log,8, "msec_scan_ready %d msec\n",delay); + + if (p->eventcallback == NULL) /* Hmm. */ + return; + + if (delay > 0) { + if (p->scan_ready_thread != NULL) + p->scan_ready_thread->del(p->scan_ready_thread); + p->scan_ready_delay = delay; + if ((p->scan_ready_thread = new_athread(delayed_scan_ready, (void *)p)) == NULL) + a1logw(g_log, "msec_scan_ready: Delayed scan_ready failed to create thread\n"); + } else { + a1logd(g_log,8, "msec_scan_ready activate\n"); + p->eventcallback(p->event_cntx, inst_event_scan_ready); + } +} + /* ============================================================= */ /* CCMX location support */ @@ -1089,6 +1137,7 @@ iccmx *list_iccmx(instType itype, int *no) { free(rv[j].desc); } xdg_free(paths, npaths); + free(rv); if (no != NULL) *no = -1; return NULL; } @@ -1098,8 +1147,10 @@ iccmx *list_iccmx(instType itype, int *no) { } /* Skip any that don't match */ - if (itype != instUnknown && cs->inst != NULL && inst_enum(cs->inst) != itype) + if (itype != instUnknown && cs->inst != NULL && inst_enum(cs->inst) != itype) { + cs->del(cs); continue; + } a1logd(g_log, 5, "Reading '%s'\n",paths[i]); if ((tech = cs->tech) == NULL) @@ -1336,21 +1387,38 @@ instType fast_ser_inst_type( void *cntx /* Context for callback */ ) { instType rv = instUnknown; -#define BUFSZ (128 + 10) +#define BUFSZ (2048 + 10) char buf[BUFSZ]; - baud_rate brt[] = { baud_921600, baud_115200, baud_38400, baud_9600, baud_nc }; + baud_rate brt1[] = { baud_9600, baud_921600, baud_115200, + baud_38400, baud_nc }; /* HS - do K10/Spectrolino first */ + baud_rate brt2[] = { baud_115200, baud_nc }; /* Bluetooth */ + + baud_rate *brt = brt1; unsigned int etime; unsigned int i; + int delayms = 0; int se, len; + double tryto = 0.1; /* [0.1] Communication timout */ +// double tryto = 0.9; /* Communication timout (test) */ - if (p->port_type(p) != icomt_serial - && p->port_type(p) != icomt_usbserial) + a1logd(p->log, 8, "fast_ser_inst_type: on '%s' dctype 0x%x\n",p->name,p->dctype); + + if (!(p->dctype & icomt_seriallike) + && !(p->dctype & icomt_fastserial)) { return p->itype; + } /* The tick to give up on */ etime = msec_time() + (long)(2000.0 + 0.5); +// etime = msec_time() + (long)(20000.0 + 0.5); /* (test) */ - a1logd(p->log, 1, "fser_inst_type: Trying different baud rates (%u msec to go)\n",etime - msec_time()); + a1logd(p->log, 1, "fser_inst_type: Trying different baud rates (%u msec to go) Path %s%s\n", + etime - msec_time(),p->spath,(p->dctype & icomt_btserial) ? " [Bluetooth]" : ""); + + if (p->dctype & icomt_btserial) { + brt = brt2; /* Only try BT relevant baud rates. */ + delayms = 600; /* Spectraval locks up otherwise. */ + } /* Until we time out, find the correct baud rate */ for (i = 0; msec_time() < etime; i++) { @@ -1361,40 +1429,97 @@ instType fast_ser_inst_type( } a1logd(p->log, 5, "Trying %s baud\n",baud_rate_to_str(brt[i])); - if ((se = p->set_ser_port(p, fc_none, brt[i], parity_none, - stop_1, length_8)) != ICOM_OK) { + if ((se = p->set_ser_port_ex(p, fc_None, brt[i], parity_none, + stop_1, length_8, delayms)) != ICOM_OK) { a1logd(p->log, 5, "fser_inst_type: set_ser_port failed with 0x%x\n",se); - return instUnknown; /* Give up */ + return instUnknown; /* Give up on port */ } - if (brt[i] == baud_9600) { - /* See if it's a Klein K10 */ + /* Assume Klein K10 only uses 9600. */ + /* We need to also assume that we might be talking to a Spectrolino, */ + /* and avoid upsetting it. */ + if ((p->dctype & icomt_btserial) == 0 && brt[i] == baud_9600) { + double sto = 0.2; /* Give 9600 a little more time */ + int bread, len; + + /* Try a spectrolino/spectroscan command first */ + if (tryto > sto) + sto = tryto; + p->write_read_ex(p, ";D024\r\n", 0, buf, BUFSZ-1, &bread, "\r", 1, sto, 1); + + if (bread == 0) { + a1logd(p->log, 5, "fser_inst_type: Spectroino command returned nothing\n"); + goto not_k10; + } + buf[bread] = '\000'; + len = strlen(buf); + + a1logd(p->log, 5, "fser_inst_type: got %d bytes\n",len); - if ((se = p->write_read(p, "P0\r", 0, buf, BUFSZ, NULL, ">", 1, 0.100)) != inst_ok) { - /* Check for user abort */ - if (uicallback != NULL) { - inst_code ev; - if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) { + if (len < 4) { + a1logd(p->log, 5, "fser_inst_type: Reply was too short\n"); + goto not_k10; /* Not K10, X-Rite or Spectrolino */ + } + + /* Is this a Spectrolino or Spectroscan error resonse ? */ + if (len >= 5 && strncmp(buf, ":26", 3) == 0 + || len >= 7 && strncmp(buf, ":D183", 5) == 0) { + a1logd(p->log, 5, "fser_inst_type: Ignore Spectrolino\n"); + return instUnknown; /* Not doing Spectrolino detection here */ + } + + /* Is this an X-Rite error value such as "<01>" ? */ + if (buf[0] == '<' && isdigit(buf[1]) && isdigit(buf[2]) && buf[3] == '>') { + a1logd(p->log, 5, "fser_inst_type: Ignore X-Rite\n"); + return instUnknown; /* Not doing X-Rite detection here */ + } + + /* The Klein K10 seems to respond with it's calibration list, preceeded by "D4" ? */ + if (buf[0] != 'D' || buf[1] != '4') { + a1logd(p->log, 5, "fser_inst_type: Not Klein response\n"); + goto not_k10;; + } + + a1logd(p->log, 5, "fser_inst_type: Looks like it may be a Klein\n"); + + /* Confirm this is a Klein instrument */ + + /* The first response is the calibration list, and it may need flushing. */ + /* (write_read_ex doesn't cope with time it takes to dump this.) */ + for (;;) { + bread = 0; + p->read(p, buf, BUFSZ, &bread, NULL, BUFSZ, 0.1); + if (bread == 0) + break; + } + + if ((se = p->write_read_ex(p, "P0\r", 0, buf, BUFSZ, NULL, ">", 1, tryto, 1)) == inst_ok) { + + /* Is this a Klein K1/K8/K10 response ? */ + if (strncmp(buf, "P0K-1 ", 6) == 0 + || strncmp(buf, "P0K-8 ", 6) == 0 + || strncmp(buf, "P0K-10", 6) == 0 + || strncmp(buf, "P0KV-10", 7) == 0) { + a1logd(p->log, 5, "fser_inst_type: found Klein K1/K8/K10\n"); + rv = instKleinK10; + break; + } + } + + not_k10:; + + /* Check for user abort */ + if (uicallback != NULL) { + inst_code ev; + if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) { a1logd(p->log, 5, "fser_inst_type: User aborted\n"); return instUnknown; - } } - continue; - } - len = strlen(buf); - - /* Is this a Klein K1/K8/K10 response ? */ - if (strncmp(buf, "P0K-1 ", 6) == 0 - || strncmp(buf, "P0K-8 ", 6) == 0 - || strncmp(buf, "P0K-10", 6) == 0 - || strncmp(buf, "P0KV-10", 7) == 0) { - a1logd(p->log, 5, "fser_inst_type: found Klein K1/K8/K10\n"); - rv = instKleinK10; - break; } } - if (brt[i] == baud_38400) - { + + /* SwatchMate Cube only uses 38400 */ + if ((p->dctype & icomt_btserial) == 0 && brt[i] == baud_38400) { int bread; /* See if it's a SwatchMate Cube. */ @@ -1403,56 +1528,81 @@ instType fast_ser_inst_type( buf[1] = 0x00; buf[2] = 0x02; /* Ping command */ buf[3] = 0x00; - if ((se = p->write_read(p, buf, 4, buf, BUFSZ, &bread, NULL, 4, 0.100)) != inst_ok) { - /* Check for user abort */ - if (uicallback != NULL) { - inst_code ev; - if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) { - a1logd(p->log, 5, "fser_inst_type: User aborted\n"); - return instUnknown; + if ((se = p->write_read_ex(p, buf, 4, buf, BUFSZ, &bread, NULL, 4, tryto, 1)) == inst_ok) { + if (bread == 4) { + if (buf[0] == 0x7e && buf[1] == 0x20 && buf[2] == 0x02 && buf[3] == 0x00) { + a1logd(p->log, 5, "fser_inst_type: found SwatchMate Cube\n"); + rv = instSMCube; + break; } } - continue; } - if (bread == 4) { - if (buf[0] == 0x7e && buf[1] == 0x20 && buf[2] == 0x02 && buf[3] == 0x00) { - a1logd(p->log, 5, "fser_inst_type: found SwatchMate Cube\n"); - rv = instSMCube; - break; + + /* Check for user abort */ + if (uicallback != NULL) { + inst_code ev; + if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) { + a1logd(p->log, 5, "fser_inst_type: User aborted\n"); + return instUnknown; } } } - /* Bluetooth only uses baud_115200 */ - if ((p->sattr & icom_bt) == 0 || brt[i] == baud_115200) { + + /* JETI specbos or spectraval. */ + /* We are fudging the baud rate selection here - */ + /* the 1211 RS and BT can't handle 921600, */ + /* while the 15x1 can handle 230400 & 3000000, which we don't test for... */ +// if ((p->dctype & icomt_btserial) == 0 || brt[i] == baud_115200) + if (brt[i] == baud_38400 || brt[i] == baud_115200 || brt[i] == baud_921600) + { + int bread; /* See if it's a JETI specbos */ - if ((se = p->write_read(p, "*idn?\r", 0, buf, BUFSZ, NULL, "\r", 1, 0.100)) != inst_ok) { - /* Check for user abort */ - if (uicallback != NULL) { - inst_code ev; - if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) { - a1logd(p->log, 5, "fser_inst_type: User aborted\n"); - return instUnknown; - } + p->write_read_ex(p, "*idn?\r", 0, buf, BUFSZ, &bread, "\r", 1, tryto, 1); + if (bread > 0) { + len = strlen(buf); + + /* JETI specbos returns "JETI_SBXXXX", where XXXX is the instrument type, */ + /* except for the 1201 which returns "SB05" */ + /* The spectraval 1501 returns JETI_SDCM3 NNNNNNN */ + + /* Over Bluetooth, we get an erronious string "AT+JSCR\r\n" mixed in our output. */ + /* This would appear to be from the eBMU Bluetooth adapter AT command set. */ + if (len > 9 && strncmp(buf, "AT+JSCR\r\n", 9) == 0) { + memmove(buf, buf+9, len-9); + len -= 9; + } + + /* Is this a JETI specbos 1201 response ? */ + if (strncmp(buf, "SB05", 4) == 0) { + a1logd(p->log, 5, "fser_inst_type: found JETI specbos 1201\n"); + rv = instSpecbos1201; + break; + } + /* Is this a JETI specbos XXXX response ? */ + if (len >= 11 && strncmp(buf, "JETI_SB", 7) == 0) { + a1logd(p->log, 5, "fser_inst_type: found JETI specbos\n"); + rv = instSpecbos; + break; + } + /* Is this a JETI spectraval response ? */ + /* (Bluetooth returns "DCM3_JETI ... and other rubbish for some reason.) */ + if ((len >= 10 && strncmp(buf, "JETI_SDCM3", 10) == 0) + || (len >= 9 && strncmp(buf, "DCM3_JETI", 9) == 0) + || (len >= 17 && strncmp(buf, "PECFIRM_JETI_1501", 17) == 0) + || (len >= 18 && strncmp(buf, "SPECFIRM_JETI_1501", 18) == 0)) { + a1logd(p->log, 5, "fser_inst_type: found JETI spectraval\n"); + rv = instSpectraval; + break; } - continue; - } - len = strlen(buf); - - /* JETI specbos returns "JETI_SBXXXX", where XXXX is the instrument type, */ - /* except for the 1201 which returns "SB05" */ - - /* Is this a JETI specbos 1201 response ? */ - if (strncmp(buf, "SB05", 4) == 0) { - a1logd(p->log, 5, "fser_inst_type: found JETI specbos 1201\n"); - rv = instSpecbos1201; - break; } - /* Is this a JETI specbos XXXX response ? */ - if (len >= 11 && strncmp(buf, "JETI_SB", 7) == 0) { - a1logd(p->log, 5, "fser_inst_type: found JETI specbos\n"); - rv = instSpecbos; - break; + /* Check for user abort */ + if (uicallback != NULL) { + inst_code ev; + if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) { + a1logd(p->log, 5, "fser_inst_type: User aborted\n"); + return instUnknown; + } } } } @@ -1492,11 +1642,12 @@ static instType ser_inst_type( #define BUFSZ (128 + 10) char buf[BUFSZ]; baud_rate brt[] = { baud_9600, baud_19200, baud_4800, baud_2400, - baud_1200, baud_38400, baud_57600, baud_115200, - baud_600, baud_300, baud_110, baud_nc }; + baud_1200, baud_38400, baud_57600, baud_115200, + baud_600, baud_300, baud_110, baud_nc }; unsigned int etime; unsigned int bi, i; int se, len, bread; + int klein = 0; int xrite = 0; int ss = 0; int so = 0; @@ -1517,7 +1668,7 @@ static instType ser_inst_type( for (i = bi; msec_time() < etime; i++) { if (brt[i] == baud_nc) i = 0; - if ((se = p->set_ser_port(p, fc_none, brt[i], parity_none, + if ((se = p->set_ser_port(p, fc_None, brt[i], parity_none, stop_1, length_8)) != ICOM_OK) { a1logd(p->log, 5, "ser_inst_type: set_ser_port failed with 0x%x\n",se); return instUnknown; /* Give up */ @@ -1525,7 +1676,11 @@ static instType ser_inst_type( a1logd(p->log, 5, "Trying %s baud\n",baud_rate_to_str(brt[i])); bread = 0; - if ((se = p->write_read(p, ";D024\r\n", 0, buf, BUFSZ, &bread, "\r", 1, 0.5)) != inst_ok) { + + /* Try a spectrolino/spectroscan command first */ + p->write_read_ex(p, ";D024\r\n", 0, buf, BUFSZ-1, &bread, "\r", 1, 0.5, 1); + + if (bread == 0) { /* Check for user abort */ if (uicallback != NULL) { inst_code ev; @@ -1534,15 +1689,24 @@ static instType ser_inst_type( return instUnknown; } } - if (bread == 0) - continue; + continue; } + buf[bread] = '\000'; len = strlen(buf); // a1logd(p->log, 5, "len = %d\n",len); if (len < 4) continue; + /* The Klein K10 seems to respond with it's calibration list, preceeded by "D4" ? */ + /* - don't know how reliable this is though. Another way may be to look for a */ + /* response len > 100 characters ?? */ + if (buf[0] == 'D' && buf[1] == '4') { +// a1logd(p->log, 5, "klein\n"); + klein = 1; + break; + } + /* Is this an X-Rite error value such as "<01>" ? */ if (buf[0] == '<' && isdigit(buf[1]) && isdigit(buf[2]) && buf[3] == '>') { // a1logd(p->log, 5, "xrite\n"); @@ -1580,7 +1744,7 @@ static instType ser_inst_type( /* SpectroScan */ if (ss) { rv = instSpectroScan; - if ((se = p->write_read(p, ";D030\r\n", 0, buf, BUFSZ, NULL, "\n", 1, 1.5)) == 0) { + if ((se = p->write_read_ex(p, ";D030\r\n", 0, buf, BUFSZ, NULL, "\n", 1, 1.5, 1)) == 0) { if (strlen(buf) >= 41) { hex2bin(&buf[5], 12); // a1logd(p->log, 5, "spectroscan type = '%s'\n",buf); @@ -1592,7 +1756,7 @@ static instType ser_inst_type( if (xrite) { /* Get the X-Rite model and version number */ - if ((se = p->write_read(p, "SV\r\n", 0, buf, BUFSZ, NULL, ">", 1, 2.5)) != 0) + if ((se = p->write_read_ex(p, "SV\r\n", 0, buf, BUFSZ, NULL, ">", 1, 2.5, 1)) != 0) return instUnknown; if (strlen(buf) >= 12) { @@ -1613,6 +1777,31 @@ static instType ser_inst_type( } } + if (klein) { + + /* The first response is the calibration list, and it may need flushing. */ + /* (write_read_ex doesn't cope with time it takes to dump this.) */ + for (;;) { + bread = 0; + p->read(p, buf, BUFSZ, &bread, NULL, BUFSZ, 0.1); + if (bread == 0) + break; + } + + /* See if this is a Klein K10 or similar */ + if ((se = p->write_read_ex(p, "P0\r", 0, buf, BUFSZ, NULL, ">", 1, 1.5, 1)) != inst_ok) + return instUnknown; + + /* Is this a Klein K1/K8/K10 response ? */ + if (strncmp(buf, "P0K-1 ", 6) == 0 + || strncmp(buf, "P0K-8 ", 6) == 0 + || strncmp(buf, "P0K-10", 6) == 0 + || strncmp(buf, "P0KV-10", 7) == 0) { + a1logd(p->log, 5, "fser_inst_type: found Klein K1/K8/K10\n"); + rv = instKleinK10; + } + } + a1logd(p->log, 5, "ser_inst_type: Instrument type is '%s'\n", inst_name(rv)); p->close_port(p); /* Or should we leave it open ?? */ @@ -1732,6 +1921,25 @@ int sym_to_inst_mode(inst_mode *mode, const char *sym) { return rv; } +/* ============================================================= */ +/* Utilities */ + +/* Return a string for the xcalstd enum */ +char *xcalstd2str(xcalstd std) { + switch(std) { + case xcalstd_native: + return "NATIVE"; + case xcalstd_xrdi: + return "XRDI"; + case xcalstd_gmdi: + return "GMDI"; + case xcalstd_xrga: + return "XRGA"; + default: + break; + } + return "None"; +} diff --git a/spectro/inst.h b/spectro/inst.h index 736a879..01f579d 100644 --- a/spectro/inst.h +++ b/spectro/inst.h @@ -1,13 +1,15 @@ #ifndef INST_H - /* instlib API definition. */ - - /* See spotread.c, chartread.c, illumread.c & ccxxmake.c for examples of */ - /* the API usage. */ - - /* Abstract base class for common color instrument interface */ - /* and other common instrument stuff. */ +/* + * instlib API definition. + * + * See spotread.c, chartread.c, illumread.c & ccxxmake.c for examples of + * the API usage. + * + * Abstract base class for common color instrument interface + * and other common instrument stuff. + */ /* * Argyll Color Correction System @@ -64,7 +66,7 @@ /* ------------------------------------------------- */ /* Structure for holding an instrument patch reading */ -#define ICOM_MAX_LOC_LEN 10 +#ifdef NEVER /* Declared in xicc/xspect.h */ /* Type of measurement result */ typedef enum { /* XYZ units, Spectral units */ @@ -78,6 +80,10 @@ typedef enum { /* XYZ units, Spectral units */ inst_mrt_frequency = 7 /* Hz */ } inst_meas_type; +#endif // NEVER + +#define ICOM_MAX_LOC_LEN 10 + struct _ipatch { char loc[ICOM_MAX_LOC_LEN]; /* patch location */ @@ -95,11 +101,25 @@ struct _ipatch { }; typedef struct _ipatch ipatch; +/* ------------------------------------------------------ */ +/* Gretag/X-Rite specific reflective measurement standard */ + +typedef enum { + xcalstd_none = -2, /* Not set */ + xcalstd_native = -1, /* No conversion */ + xcalstd_xrdi = 0, /* Historical X-Rite */ + xcalstd_gmdi = 1, /* Historical Gretag-Macbeth */ + xcalstd_xrga = 2, /* Current X-Rite */ +} xcalstd; + +/* Return a string for the xcalstd enum */ +char *xcalstd2str(xcalstd std); + /* ---------------------------------------- */ /* Instrument interface abstract base class */ -/* Abstract return codes in ms byte. */ -/* Instrument dependant codes in ls byte. */ +/* Abstract return codes in ms 8bits. */ +/* Instrument dependant codes in ls 16 bits. */ /* Note :- update inst_interp_error() in inst.c if anything here is changed. */ /* and also check all the instrument specific XXX_interp_code() routines too. */ typedef enum { @@ -163,7 +183,7 @@ typedef enum { typedef enum { inst_mode_none = 0x00000000, /* No capability or mode */ - /* Mode of light measurement */ + /* Light measurement mode */ inst_mode_reflection = 0x00000001, /* General reflection mode */ # define inst_mode_reflection_sym "REFL" inst_mode_s_reflection = 0x00000002, /* General saved reflection mode */ @@ -174,7 +194,7 @@ typedef enum { # define inst_mode_emission_sym "EMIS" inst_mode_illum_mask = 0x0000000f, /* Mask of sample illumination sub mode */ - /* Access mode of measurement */ + /* Action of measurement */ inst_mode_spot = 0x00000010, /* General spot measurement mode */ # define inst_mode_spot_sym "SPOT" inst_mode_strip = 0x00000020, /* General strip measurement mode */ @@ -241,7 +261,9 @@ typedef enum { | inst_mode_s_reflection, inst_mode_trans_spot = inst_mode_spot /* Transmission spot measurement mode */ - | inst_mode_transmission, + | inst_mode_transmission, /* Normal Diffuse/90 */ + inst_mode_trans_spot_a = inst_mode_ambient /* Transmission spot measurement mode */ + | inst_mode_transmission, /* Alternate 90/diffuse */ inst_mode_trans_strip = inst_mode_strip /* Transmission strip measurement mode */ | inst_mode_transmission, inst_mode_trans_xy = inst_mode_xy /* Transmission X-Y measurement mode */ @@ -267,6 +289,8 @@ typedef enum { } inst_mode; /* Test for a specific mode */ +/* (This isn't foolproof - it->check_mode() is better for modes */ +/* composed of more than one bit) */ #define IMODETST(mbits, mode) (((mbits) & (mode)) == (mode)) /* Test for a specific mode in capability and mode */ @@ -299,8 +323,11 @@ typedef enum { inst2_emis_refr_meas = 0x00000080, /* Has an emissive refresh rate measurement func. */ inst2_prog_trig = 0x00000100, /* Progromatic trigger measure capability */ - inst2_user_trig = 0x00000200, /* User trigger measure capability */ - inst2_switch_trig = 0x00000400, /* Inst. switch trigger measure capability */ + inst2_user_trig = 0x00000200, /* User trigger measure capability, */ + /* i.e. via user button and uicallback. */ + inst2_switch_trig = 0x00000400, /* Inst. switch trigger measure capability, */ + /* i.e. instrument directly starts measurement */ + /* via mechanical switch. */ inst2_user_switch_trig = 0x00000800, /* User or switch trigger measure capability */ inst2_bidi_scan = 0x00001000, /* Try and recognise patches scanned from either dir. */ @@ -356,6 +383,11 @@ typedef enum { optional password configuration specific calibrations. How are they marked ? + + + Also for ccss capable instruments: + + write corresponding ccmx for given ccss. */ typedef enum { @@ -446,7 +478,7 @@ typedef enum { inst_opt_trig_prog = 0x000E, /* Trigger progromatically [No args] */ inst_opt_trig_user = 0x000F, /* Trigger from user via uicallback [No args] */ - inst_opt_trig_switch = 0x0010, /* Trigger using instrument switch [No args] */ + inst_opt_trig_switch = 0x0010, /* Trigger directly using instrument switch [No args] */ inst_opt_trig_user_switch = 0x0011, /* Trigger from user via uicallback or switch (def) [No args] */ inst_opt_highres = 0x0012, /* Enable high res spectral mode indep. of set_mode() */ @@ -468,12 +500,24 @@ typedef enum { inst_opt_get_min_int_time = 0x001D, /* Get the minimum integration time [*double time] */ inst_opt_set_min_int_time = 0x001E, /* Set the minimum integration time [double time] */ - inst_opt_opt_calibs_valid = 0x001F, /* Are optional calibrations valid [*int valid] */ - inst_opt_clear_opt_calibs = 0x0020 /* Clear all optional calibrations. */ + inst_opt_opt_calibs_valid = 0x001F, /* Are optional (white/black/gloss etc.) calibrations */ + /* valid? [*int valid] */ + inst_opt_clear_opt_calibs = 0x0020, /* Clear all optional calibrations. */ + + inst_opt_get_cal_tile_sp = 0x0021, /* Return refl. white tile reference spectrum. */ + /* for current filter. [*xspect tile] */ + + inst_opt_set_xcalstd = 0x0022, /* Set the X-Rite reflective calibration standard */ + /* [xcalstd standard] */ + inst_opt_get_xcalstd = 0x0023, /* Get the effective X-Rite ref. cal. standard */ + /* [xcalstd *standard] */ + inst_opt_lamp_remediate = 0x0024 /* Remediate i1Pro lamp [double seconds] */ + } inst_opt_type; /* Optional filter fitted to instrument (for inst_opt_set_filter) */ +/* Set this before calling init_coms() */ typedef enum { inst_opt_filter_unknown = 0xffff, /* Unspecified filter */ inst_opt_filter_none = 0x0000, /* No filters fitted */ @@ -588,24 +632,42 @@ typedef enum { } inst_cal_cond; +/* Condition identifier message type. This can be useful in internationalizing the */ +/* string returned in id[] from calibrate() */ +typedef enum { + inst_calc_id_none = 0x00000000, /* No id */ + inst_calc_id_ref_sn = 0x00000001, /* Calibration reference (ie. tile) serial number */ + + inst_calc_id_trans_low = 0x00010000, /* Trans. Ref. light is too low for accuracy warning */ + inst_calc_id_trans_wl = 0x00020000, /* Trans. Ref. light is low at some wavelengths warning */ + inst_calc_id_filt_unkn = 0x00100000, /* Request unknown filter */ + inst_calc_id_filt_none = 0x00200000, /* Request no filter */ + inst_calc_id_filt_pol = 0x00300000, /* Request polarizing filter */ + inst_calc_id_filt_D65 = 0x00400000, /* Request D65 filter */ + inst_calc_id_filt_UV = 0x00500000, /* Request UV cut filter */ + inst_calc_id_filt_cust = 0x00600000 /* Request custom filter */ +} inst_calc_id_type; + /* Clamping state */ typedef enum { instNoClamp = 0, /* Don't clamp XYZ/Lab to +ve */ instClamp = 1, /* Clamp XYZ/Lab to +ve */ } instClamping; -/* User interaction callback function purpose */ +/* User interaction callback (uicallback()) function purpose */ typedef enum { - inst_negcoms, /* Negotiating communications */ - inst_triggered, /* Measurement has been triggered by switch or user (not progromatic) */ - inst_armed, /* Armed and waiting for a measurement trigger */ - inst_measuring /* Busy measuring */ + inst_negcoms, /* Negotiating communications - can abort */ + inst_armed, /* Armed and waiting for a measurement trigger - can wait, trigger, abort */ + inst_triggered, /* Measurement triggered by switch or user (not progromatic) - notice */ + /* (Also triggered by switch driven calibration,i.e. DTP41) */ + inst_measuring /* Busy measuring - can abort */ } inst_ui_purp; /* Asynchronous event callback type */ typedef enum { - inst_event_switch, /* Instrument measure/calibrate switch pressed */ - inst_event_mconf /* Change in measurement configuration (ie. sensor position) */ + inst_event_switch, /* Instrument measure/calibrate switch pressed. */ + inst_event_mconf, /* Change in measurement configuration (ie. sensor position) */ + inst_event_scan_ready /* Ready for manual strip scan (i.e. issue audible prompt) */ } inst_event_type; /* Instrument configuration/sensor position*/ @@ -641,7 +703,9 @@ typedef enum { inst_code (*uicallback)(void *cntx, inst_ui_purp purp); \ void *uic_cntx; /* User interaction callback function */ \ void (*eventcallback)(void *cntx, inst_event_type event); \ - void *event_cntx; /* Event callback function */ \ + void *event_cntx; /* Asynchronous event callback function */ \ + athread *scan_ready_thread; /* msec_scan_ready() support */ \ + int scan_ready_delay; /* msec_scan_ready() support */ \ \ /* Virtual delete. Cleans up things done by new_inst(). */ \ inst_code (*vdel)( \ @@ -819,15 +883,15 @@ typedef enum { \ \ /* Read a set of strips (applicable to strip reader) */ \ - /* Obeys the trigger mode set, and may return user trigger code */ \ - /* (to hint that a user command may be available) */ \ + /* Obeys the trigger mode set, and may return user trigger code, */ \ + /* to hint that a user command may be available. */ \ /* Return the inst error code */ \ inst_code (*read_strip)( \ struct _inst *p, \ - char *name, /* Strip name (up to 7 chars) */ \ + char *name, /* Strip name (up to 7 chars, for DTP51) */ \ int npatch, /* Number of patches in each pass */ \ - char *pname, /* Pass name (3 chars) */ \ - int sguide, /* Guide number (decrements by 5) */ \ + char *pname, /* Pass name (3 chars, for DTP51) */ \ + int sguide, /* Guide number (for DTP51, decrements by 5) */ \ double pwid, /* Patch width in mm (For DTP20/DTP41) */ \ double gwid, /* Gap width in mm (For DTP20/DTP41) */ \ double twid, /* Trailer width in mm (For DTP41T) */ \ @@ -836,9 +900,9 @@ typedef enum { \ /* Read a single sample (applicable to spot instruments) */ \ /* Obeys the trigger mode set, and may return user trigger code */ \ - /* Values are in XYZ 0..100 for reflective transmissive, */ \ - /* aXYZ in cd/m^2 for emissive, amd Lux/3.1415926 for ambient. */ \ - /* Spectral will be analogous to the XYZ. */ \ + /* Values are in XYZ % (0..100) for reflective & transmissive, */ \ + /* cd/m^2 for emissive, and Lux for ambient. */ \ + /* Spectral will be analogous to the XYZ (see inst_meas_type). */ \ /* By default values may be -ve due to noise (depending on instrument) */ \ /* Return the inst error code */ \ inst_code (*read_sample)( \ @@ -916,8 +980,10 @@ typedef enum { struct _inst *p, \ inst_cal_type *calt, /* Calibration type to do/remaining */ \ inst_cal_cond *calc, /* Current condition/desired condition */ \ + inst_calc_id_type *idtype, /* Condition identifier type */ \ char id[CALIDLEN]); /* Condition identifier (ie. white */ \ - /* reference ID, filter ID) */ \ + /* reference ID, filter ID, message string, */ \ + /* etc.) */ \ \ /* Measure a display update, and instrument reaction time. It is */ \ /* assumed that a white to black change will be made to the */ \ @@ -1006,12 +1072,12 @@ typedef enum { * For inst_negcoms, the return value of inst_user_abort \ * will abort the communication negotiation. \ * \ - * For inst_triggered, the return value of the callback is ignored. \ - * \ * For inst_armed return value should be one of: \ * inst_ok to do nothing, inst_user_abort to abort the measurement, \ * or inst_user_trig to trigger the measurement. \ * \ + * For inst_triggered, the return value of the callback is ignored. \ + * \ * For inst_measuring the return value should be one of: \ * inst_ok to do nothing, inst_user_abort to abort the measurement. \ * \ @@ -1022,10 +1088,11 @@ typedef enum { void *cntx); \ \ /* Supply an aynchronous event callback function. \ - * This is called from a thread with the following possible events: \ + * This is called from a different thread with the following possible events: \ * \ * inst_event_switch: Instrument measure/calibrate switch pressed \ * inst_event_mconf: The measurement configuration has changed (ie. sensor position) \ + * inst_event_scan_ready: Ready for manual strip scan (i.e. issue audible prompt) \ * \ * NULL can be set to disable the callback. \ */ \ @@ -1068,7 +1135,8 @@ extern inst *new_inst( ); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* Implementation functions used by drivers */ +/* Implementation functions used by instrument drivers */ +/* (Client code should not call these) */ /* Get a status or set or get an option (default implementation) */ inst_code inst_get_set_opt_def( @@ -1090,6 +1158,10 @@ int doccmx /* Add matching installed ccmx files */ /* Free a display type list */ void inst_del_disptype_list(inst_disptypesel *list, int no); +/* - - - - - - - - - - - - - - - - - - -- */ + +/* Issue an inst_event_scan_ready event after a delay */ +void issue_scan_ready(inst *p, int delay); /* - - - - - - - - - - - - - - - - - - -- */ /* CCMX support - ccmx instrument proxy */ diff --git a/spectro/instappsup.c b/spectro/instappsup.c index d2ce7ff..fc550b4 100644 --- a/spectro/instappsup.c +++ b/spectro/instappsup.c @@ -1,5 +1,5 @@ - /* Instrument command line application support functions */ +/* Instrument command line application support functions */ /* * Argyll Color Correction System @@ -72,7 +72,6 @@ static inst_code def_uicallback(void *cntx, inst_ui_purp purp) { return inst_user_trig; } - /* Change in measurement configuration */ } else if (purp == inst_measuring) { return inst_ok; } @@ -173,8 +172,9 @@ inst_code inst_handle_calibrate( int doimmediately /* If nz, don't wait for user, calibrate immediatley */ ) { inst_code rv = inst_ok, ev; - int usermes = 0; /* User was given a message */ - char id[200]; /* Condition identifier */ + int usermes = 0; /* User was given a message */ + inst_calc_id_type idtype; /* Condition identifier type */ + char id[200]; /* Condition identifier */ int ch; a1logd(p->log,1,"inst_handle_calibrate called\n"); @@ -183,13 +183,15 @@ inst_code inst_handle_calibrate( for (;;) { a1logd(p->log,1,"About to call calibrate at top of loop\n"); - ev = p->calibrate(p, &calt, &calc, id); + ev = p->calibrate(p, &calt, &calc, &idtype, id); a1logd(p->log,1,"Calibrate returned calt 0x%x, calc 0x%x, ev 0x%x\n",calt,calc,ev); /* We're done */ if ((ev & inst_mask) == inst_ok) { - if ((calc & inst_calc_cond_mask) == inst_calc_message) + if ((calc & inst_calc_cond_mask) == inst_calc_message) { + /* (Or could create our own message text based on value of idtype) */ printf("%s\n",id); + } if (usermes) printf("Calibration complete\n"); fflush(stdout); @@ -249,7 +251,7 @@ inst_code inst_handle_calibrate( break; case inst_calc_man_ref_white: - printf("Place the instrument on its reflective white reference %s,\n",id); + printf("Place the instrument on its reflective white reference S/N %s,\n",id); printf(" and then hit any key to continue,\n"); break; @@ -448,12 +450,12 @@ inst2_capability inst_show_disptype_options(FILE *fp, char *oline, icompaths *ic olen = strlen(oline); /* lenth of option part of line */ - for (i = 0; icmps != NULL && i < icmps->npaths; i++) { + for (i = 0; icmps != NULL && i < icmps->ndpaths[dtix_inst]; i++) { inst *it; inst2_capability cap; int k; - if ((it = new_inst(icmps->paths[i], 1, g_log, NULL, NULL)) == NULL) { + if ((it = new_inst(icmps->dpaths[dtix_inst][i], 1, g_log, NULL, NULL)) == NULL) { notall = 1; continue; } diff --git a/spectro/instlib.ksh b/spectro/instlib.ksh index 88796f8..8b0303f 100644 --- a/spectro/instlib.ksh +++ b/spectro/instlib.ksh @@ -42,6 +42,7 @@ RSPL_FILES=" SPECTRO_FILES=" License2.txt + spotread.c Makefile.OSX Makefile.UNIX Makefile.WNT @@ -49,6 +50,8 @@ SPECTRO_FILES=" pollem.c conv.h conv.c + sa_conv.h + sa_conv.c aglob.c aglob.h hidio.h @@ -56,13 +59,13 @@ SPECTRO_FILES=" icoms.h inst.h inst.c - disptechs.h - disptechs.c insttypes.c insttypes.h insttypeinst.h instappsup.c instappsup.h + disptechs.h + disptechs.c dtp20.c dtp20.h dtp22.c @@ -101,6 +104,11 @@ SPECTRO_FILES=" specbos.c kleink10.h kleink10.c + ex1.c + ex1.h + smcube.h + smcube.c + cubecal.h oemarch.c oemarch.h oeminst.c @@ -118,10 +126,14 @@ SPECTRO_FILES=" usbio_nt.c usbio_ox.c usbio_lx.c - usbio_bsd.c + rspec.h + rspec.c xdg_bds.c xdg_bds.h - spotread.c + base64.h + base64.c + xrga.h + xrga.c " FILES=" $H_FILES $CGATS_FILES $NUMLIB_FILES $RSPL_FILES $XICC_FILES $SPECTRO_FILES " @@ -138,7 +150,7 @@ for j in $FILES do if [ ! -e ${j} ] ; then echo "!!!!!!!!!!!!!!!!!!!!!!!!!!! Can't find file ${j} !!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - NOTFOUND="$NOTFOUND ${tt}" + NOTFOUND="$NOTFOUND ${j}" else cp ${j} _zipdir/instlib/${j##*/} echo instlib/${j##*/} >> _ziplist diff --git a/spectro/insttypeinst.h b/spectro/insttypeinst.h index ad8047d..081d130 100644 --- a/spectro/insttypeinst.h +++ b/spectro/insttypeinst.h @@ -9,6 +9,10 @@ * see the License2.txt file for licencing details. */ +#ifdef __cplusplus + extern "C" { +#endif + #ifdef ENABLE_SERIAL # include "dtp22.h" diff --git a/spectro/insttypes.c b/spectro/insttypes.c index 5a660a8..760d678 100644 --- a/spectro/insttypes.c +++ b/spectro/insttypes.c @@ -1,5 +1,5 @@ - /* Instrument supported types utilities */ +/* Instrument supported types utilities */ /* * Argyll Color Correction System @@ -37,6 +37,65 @@ /* Utility functions */ +/* Given a device type, return the corrsponding */ +/* category */ +extern icom_type inst_category(instType itype) { + switch (itype) { + + /* Color measurement instruments */ + case instDTP22: + case instDTP41: + case instDTP51: + case instSpectrolino: + case instSpectroScan: + case instSpectroScanT: + case instSpectrocam: + case instSpecbos1201: + case instSpecbos: + case instSpectraval: + case instKleinK10: + case instSMCube: + case instDTP20: + case instDTP92: + case instDTP94: + case instI1Disp1: + case instI1Disp2: + case instI1Disp3: + case instI1Monitor: + case instI1Pro: + case instI1Pro2: + case instColorMunki: + case instHCFR: + case instSpyder1: + case instSpyder2: + case instSpyder3: + case instSpyder4: + case instSpyder5: + case instHuey: + case instSmile: + case instEX1: + case instColorHug: + case instColorHug2: + + + return icomt_instrument; + + /* 3D cLUT box */ + + /* Video test patern generator box */ + + /* Printers */ + case devEpsonR1800: + return icomt_printer; + + case instFakeDisp: + return icomt_unknown; // ??? + + } + + return icomt_cat_any; +} + /* Return the short instrument identification name (static string) */ char *inst_sname(instType itype) { switch (itype) { @@ -94,6 +153,8 @@ char *inst_sname(instType itype) { return "specbos 1201"; case instSpecbos: return "specbos"; + case instSpectraval: + return "spectraval"; case instKleinK10: return "K-10"; case instEX1: @@ -104,6 +165,7 @@ char *inst_sname(instType itype) { return "ColorHug"; case instColorHug2: return "ColorHug2"; + default: break; } @@ -167,6 +229,8 @@ char *inst_name(instType itype) { return "JETI specbos 1201"; case instSpecbos: return "JETI specbos"; + case instSpectraval: + return "JETI spectraval"; case instKleinK10: return "Klein K-10"; case instEX1: @@ -177,6 +241,7 @@ char *inst_name(instType itype) { return "Hughski ColorHug"; case instColorHug2: return "Hughski ColorHug2"; + default: break; } @@ -258,6 +323,8 @@ instType inst_enum(char *name) { return instSpecbos1201; else if (strcmp(name, "JETI specbos") == 0) return instSpecbos; + else if (strcmp(name, "JETI spectraval") == 0) + return instSpectraval; else if (strcmp(name, "Klein K-10") == 0) return instKleinK10; else if (strcmp(name, "Image Engineering EX1") == 0) @@ -445,6 +512,7 @@ int inst_illuminant(xspect *sp, instType itype) { case instSpecbos1201: case instSpecbos: + case instSpectraval: return 1; /* Not applicable */ case instKleinK10: @@ -456,6 +524,7 @@ int inst_illuminant(xspect *sp, instType itype) { case instSMCube: return 1; /* Not applicable */ + case instColorHug: case instColorHug2: return 1; /* Not applicable */ diff --git a/spectro/insttypes.h b/spectro/insttypes.h index ab80cd2..8306bc8 100644 --- a/spectro/insttypes.h +++ b/spectro/insttypes.h @@ -1,7 +1,7 @@ #ifndef INSTTYPES_H - /* Instrument suported types utilities. */ +/* Device and Instrument suported types definitions. */ /* * Argyll Color Correction System @@ -23,9 +23,11 @@ /* ----------------------------- */ -/* Possible types of instruments */ +/* Possible types of devices and instruments */ typedef enum { - instUnknown = 0, /* Undefined Instrument */ + devUnknown = 0, /* Undefined Device */ + + /* Color measurement instruments */ instDTP22, /* Xrite DTP22 (Digital Swatchbook) */ instDTP41, /* Xrite DTP41 */ instDTP51, /* Xrite DTP51 */ @@ -35,6 +37,7 @@ typedef enum { instSpectrocam, /* Avantes Spectrocam */ instSpecbos1201, /* JETI specbos 1201 */ instSpecbos, /* JETI specbos XXXX */ + instSpectraval, /* JETI spectraval 1501, 1511 */ instKleinK10, /* Klein K10-A */ instSMCube, /* SwatchMate Cube */ instDTP20, /* Xrite DTP20 (Pulse) */ @@ -62,12 +65,33 @@ typedef enum { instFakeDisp = 9998, /* Fake display & instrument device id */ -} instType; + /* 3D cLUT box */ + // 20000 + + /* Video test patern generator box */ + // 30000 + + /* Printers */ + devEpsonR1800 = 40000 /* Epson R1800 printer */ -struct _icoms; /* Forward declaration */ +} devType; + +/* Aliases for backwards compatibility */ +#define instUnknown devUnknown +typedef devType instType; +typedef devType cLUTType; +typedef devType vtpgType; +typedef devType printerType; + +struct _icoms; /* Forward declarations */ +enum _icom_type; /* Utility functions in libinsttypes */ +/* Given a device type, return the corrsponding */ +/* category */ +//extern _icom_type inst_category(instType itype); + /* Given its instrument type, return the matching */ /* short instrument name (static string), */ extern char *inst_sname(instType itype); diff --git a/spectro/iusb.h b/spectro/iusb.h index a254ec0..94047cf 100644 --- a/spectro/iusb.h +++ b/spectro/iusb.h @@ -10,6 +10,10 @@ * see the License2.txt file for licencing details. */ +#ifdef __cplusplus + extern "C" { +#endif + /* Device and/or Interface Class codes */ #define IUSB_CLASS_PER_INTERFACE 0x00 /* for DeviceClass */ #define IUSB_CLASS_AUDIO 0x01 @@ -127,5 +131,9 @@ #define IUSB_DEVICE_STATUS_REMOTE_WAKEUP 0x0002 #define IUSB_ENDPOINT_STATUS_HALT 0x0001 +#ifdef __cplusplus + } +#endif + #define _IUSB_H_ #endif /* _IUSB_H_ */ diff --git a/spectro/kleink10.c b/spectro/kleink10.c index 9599972..3d67b6c 100644 --- a/spectro/kleink10.c +++ b/spectro/kleink10.c @@ -369,8 +369,7 @@ k10_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { amutex_lock(p->lock); - if (p->icom->port_type(p->icom) != icomt_serial - && p->icom->port_type(p->icom) != icomt_usbserial) { + if (!(p->icom->port_type(p->icom) & icomt_serial)) { amutex_unlock(p->lock); a1logd(p->log, 1, "k10_init_coms: wrong communications type for device!\n"); return inst_coms_fail; @@ -386,7 +385,8 @@ k10_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { if (brt[i] == baud_nc) { i = 0; } - a1logd(p->log, 5, "k10_init_coms: trying baud ix %d\n",brt[i]); + a1logd(p->log, 5, "k10_init_coms: Trying %s baud, %d msec to go\n", + baud_rate_to_str(brt[i]), etime- msec_time()); if ((se = p->icom->set_ser_port(p->icom, fc_HardwareDTR, brt[i], parity_none, stop_1, length_8)) != ICOM_OK) { amutex_unlock(p->lock); @@ -397,7 +397,7 @@ k10_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* Check instrument is responding */ if (((ev = k10_command(p, "P0\r", buf, MAX_MES_SIZE, NULL, 21, ec_ec, 0.5)) & inst_mask) != inst_coms_fail) { - break; /* We've got coms or user abort */ + goto got_coms; /* We've got coms or user abort */ } /* Check for user abort */ @@ -411,11 +411,12 @@ k10_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } } - if (msec_time() >= etime) { /* We haven't established comms */ - amutex_unlock(p->lock); - a1logd(p->log, 2, "k10_init_coms: failed to establish coms\n"); - return inst_coms_fail; - } + /* We haven't established comms */ + amutex_unlock(p->lock); + a1logd(p->log, 2, "k10_init_coms: failed to establish coms\n"); + return inst_coms_fail; + + got_coms:; /* Check the response */ if (ev != inst_ok) { @@ -2255,6 +2256,7 @@ inst_code k10_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { kleink10 *p = (kleink10 *)pp; @@ -2267,6 +2269,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = k10_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -2738,8 +2741,7 @@ double mtx[3][3] * error if it hasn't been initialised. */ static inst_code -k10_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +k10_get_set_opt(inst *pp, inst_opt_type m, ...) { kleink10 *p = (kleink10 *)pp; char buf[MAX_MES_SIZE]; int se; @@ -2753,6 +2755,11 @@ k10_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + /* Get target light state */ if (m == inst_opt_get_target_state) { va_list args; @@ -2821,12 +2828,17 @@ k10_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } - if (!p->gotcoms) - return inst_no_coms; - if (!p->inited) - return inst_no_init; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; - return inst_unsupported; + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/kleink10.h b/spectro/kleink10.h index 4dcc334..136476a 100644 --- a/spectro/kleink10.h +++ b/spectro/kleink10.h @@ -37,6 +37,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Fake Error codes */ #define K10_INTERNAL_ERROR 0xff01 /* Internal software error */ #define K10_TIMEOUT 0xff02 /* Communication timeout */ @@ -116,6 +120,9 @@ struct _kleink10 { /* Constructor */ extern kleink10 *new_kleink10(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define KLEINK10_H #endif /* KLEINK10_H */ diff --git a/spectro/linear.cal b/spectro/linear.cal index 9c88605..b5e06c0 100644 --- a/spectro/linear.cal +++ b/spectro/linear.cal @@ -2,7 +2,7 @@ CAL DESCRIPTOR "Argyll Device Calibration Curves" ORIGINATOR "Argyll synthcal" -CREATED "Mon Oct 26 01:10:48 2015" +CREATED "Wed Sep 28 02:35:54 2016" DEVICE_CLASS "DISPLAY" COLOR_REP "RGB" diff --git a/spectro/madvrwin.c b/spectro/madvrwin.c index e131e5e..305a240 100644 --- a/spectro/madvrwin.c +++ b/spectro/madvrwin.c @@ -215,8 +215,11 @@ static ramdac *madvrwin_get_ramdac(dispwin *p) { debugr("madvrwin_get_ramdac failed on malloc()\n"); return NULL; } - r->pdepth = p->pdepth; - r->nent = (1 << p->pdepth); + r->fdepth = p->fdepth; + r->rdepth = p->rdepth; + r->ndepth = p->ndepth; + r->nent = p->nent; + r->clone = dispwin_clone_ramdac; r->setlin = dispwin_setlin_ramdac; r->del = dispwin_del_ramdac; @@ -506,8 +509,15 @@ int ddebug /* >0 to print debug statements to stderr */ dispwin_set_default_delays(p); - p->pdepth = 8; /* Assume this */ - p->edepth = 16; + p->fdepth = 8; /* Assume this */ + p->rdepth = p->fdepth; /* Assumed */ + p->ndepth = p->rdepth; /* Assumed */ +#ifdef ENABLE_RAMDAC + p->nent = (1 << p->ndepth); +#else + p->nent = 0; /* No ramdac */ +#endif + p->edepth = 16; /* Assumed */ if (initMadVR(p)) { debugr2((errout,"Failed to locate MadVR .dll or functions\n")); @@ -515,7 +525,7 @@ int ddebug /* >0 to print debug statements to stderr */ return NULL; } - if (!madVR_BlindConnect(0, 1000)) { + if (!madVR_BlindConnect(1, 1000)) { debugr2((errout,"Failed to connect to MadVR\n")); free(p); return NULL; diff --git a/spectro/munki.c b/spectro/munki.c index df54cd9..16cd863 100644 --- a/spectro/munki.c +++ b/spectro/munki.c @@ -71,8 +71,15 @@ static inst_code munki_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { munki *p = (munki *) pp; int se; +#if defined(UNIX_X11) + /* Some Linux drivers fail to start every second time the device is opened */ + /* if no clear halt is done on open, and some do the opposite. So */ + /* reset after close to avoid the problem in all cases. */ + icomuflags usbflags = icomuf_no_open_clear | icomuf_reset_before_close; +#else icomuflags usbflags = icomuf_none; -#ifdef __APPLE__ +#endif +#ifdef UNIX_APPLE /* If the ColorMunki software has been installed, then there will */ /* be a daemon process that has the device open. Kill that process off */ /* so that we can open it here, before it re-spawns. */ @@ -82,10 +89,10 @@ munki_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { NULL }; int retries = 20; -#else /* !__APPLE__ */ +#else /* !UNIX_APPLE */ char **pnames = NULL; int retries = 0; -#endif /* !__APPLE__ */ +#endif /* !UNIX_APPLE */ a1logd(p->log, 2, "munki_init_coms: called\n"); @@ -98,6 +105,8 @@ munki_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* Set config, interface, write end point, read end point, read quanta */ /* ("serial" end points aren't used - the Munki uses USB control messages) */ + /* (The ColorMunki on Linux only starts every second time if we use the */ + /* icomuf_no_open_clear flag.) */ if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, usbflags, retries, pnames)) != ICOM_OK) { a1logd(p->log, 1, "munki_init_coms: failed ICOM err 0x%x\n",se); @@ -368,6 +377,7 @@ static inst_code munki_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { munki *p = (munki *)pp; @@ -378,7 +388,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; - rv = munki_imp_calibrate(p, calt, calc, id); + rv = munki_imp_calibrate(p, calt, calc, idtype, id); return munki_interp_code(p, rv); } @@ -455,7 +465,7 @@ munki_interp_error(inst *pp, munki_code ec) { case MUNKI_RD_TOOMANYPATCHES: return "Too many patches"; case MUNKI_RD_NOTENOUGHSAMPLES: - return "Not enough samples per patch"; + return "Not enough samples per patch - Slow Down!"; case MUNKI_RD_NOFLASHES: return "No flashes recognized"; case MUNKI_RD_NOAMBB4FLASHES: @@ -797,6 +807,39 @@ munki_get_set_opt(inst *pp, inst_opt_type m, ...) { return munki_interp_code(p, munki_set_scan_toll(p, toll_ratio)); } + /* Set xcalstd */ + if (m == inst_opt_set_xcalstd) { + munkiimp *imp = (munkiimp *)p->m; + xcalstd standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd); + va_end(args); + + imp->target_calstd = standard; + + return inst_ok; + } + + /* Get the current effective xcalstd */ + if (m == inst_opt_get_xcalstd) { + munkiimp *imp = (munkiimp *)p->m; + xcalstd *standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd *); + va_end(args); + + if (imp->target_calstd == xcalstd_native) + *standard = imp->native_calstd; /* If not overridden */ + else + *standard = imp->target_calstd; /* Overidden std. */ + + return inst_ok; + } + if (!p->gotcoms) return inst_no_coms; if (!p->inited) @@ -915,6 +958,33 @@ munki_get_set_opt(inst *pp, inst_opt_type m, ...) { return inst_ok; } + /* Return the white calibration tile spectrum */ + /* (We always return the normal rez. reference values) */ + if (m == inst_opt_get_cal_tile_sp) { + munkiimp *imp = (munkiimp *)p->m; + xspect *sp; + inst_code rv; + va_list args; + int i; + + va_start(args, m); + sp = va_arg(args, xspect *); + va_end(args); + + if (imp->white_ref1 == NULL) + return inst_no_init; + + sp->spec_n = imp->nwav1; + sp->spec_wl_short = imp->wl_short1; + sp->spec_wl_long = imp->wl_long1; + sp->norm = 100.0; + + for (i = 0; i < sp->spec_n; i++) + sp->spec[i] = imp->white_ref1[i] * 100.0; + + return inst_ok; + } + /* Use default implementation of other inst_opt_type's */ { inst_code rv; diff --git a/spectro/munki_imp.c b/spectro/munki_imp.c index 6f49d0d..eae2837 100644 --- a/spectro/munki_imp.c +++ b/spectro/munki_imp.c @@ -146,6 +146,7 @@ #include "munki.h" #include "munki_imp.h" +#include "xrga.h" /* - - - - - - - - - - - - - - - - - - */ @@ -478,12 +479,27 @@ munki_code munki_imp_init(munki *p) { unsigned char buf[4]; int calsize = 0, rucalsize; unsigned char *calbuf; /* EEProm contents */ + char *envv; a1logd(p->log,2,"munki_init:\n"); if (p->itype != instColorMunki) return MUNKI_UNKNOWN_MODEL; + + m->native_calstd = xcalstd_xrga; + m->target_calstd = xcalstd_native; /* Default to native calibration standard*/ + + /* Honour Environment override */ + if ((envv = getenv("ARGYLL_XCALSTD")) != NULL) { + if (strcmp(envv, "XRGA") == 0) + m->target_calstd = xcalstd_xrga; + else if (strcmp(envv, "XRDI") == 0) + m->target_calstd = xcalstd_xrdi; + else if (strcmp(envv, "GMDI") == 0) + m->target_calstd = xcalstd_gmdi; + } + #ifdef ENABLE_SPOS_CHECK m->nosposcheck = 0; #else @@ -523,7 +539,7 @@ munki_code munki_imp_init(munki *p) { return ev; /* Dump the eeprom contents as a block */ - if (p->log->debug >= 7) { + if (p->log->debug >= 9) { int base, size; a1logd(p->log,7, "EEPROM contents:\n"); @@ -960,6 +976,7 @@ munki_code munki_imp_calibrate( munki *p, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ + inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { munki_code ev = MUNKI_OK; @@ -1670,24 +1687,28 @@ if (ss->idark_int_time[j] != s->idark_int_time[j]) return MUNKI_CAL_SETUP; } } else if (*calt & inst_calt_em_dark) { /* Emissive Dark calib */ + *idtype = inst_calc_id_none; id[0] = '\000'; if ((*calc & inst_calc_cond_mask) != inst_calc_man_cal_smode) { *calc = inst_calc_man_cal_smode; return MUNKI_CAL_SETUP; } } else if (*calt & inst_calt_trans_dark) { /* Transmissive dark */ + *idtype = inst_calc_id_none; id[0] = '\000'; if ((*calc & inst_calc_cond_mask) != inst_calc_man_cal_smode) { *calc = inst_calc_man_cal_smode; return MUNKI_CAL_SETUP; } } else if (*calt & inst_calt_trans_vwhite) { /* Transmissive white for emulated trans. */ + *idtype = inst_calc_id_none; id[0] = '\000'; if ((*calc & inst_calc_cond_mask) != inst_calc_man_trans_white) { *calc = inst_calc_man_trans_white; return MUNKI_CAL_SETUP; } } else if (*calt & inst_calt_emis_int_time) { + *idtype = inst_calc_id_none; id[0] = '\000'; if ((*calc & inst_calc_cond_mask) != inst_calc_emis_white) { *calc = inst_calc_emis_white; @@ -1709,10 +1730,13 @@ if (ss->idark_int_time[j] != s->idark_int_time[j]) if (m->transwarn) { *calc = inst_calc_message; - if (m->transwarn & 2) + if (m->transwarn & 2) { + *idtype = inst_calc_id_trans_low; strcpy(id, "Warning: Transmission light source is too low for accuracy!"); - else + } else { + *idtype = inst_calc_id_trans_wl; strcpy(id, "Warning: Transmission light source is low at some wavelengths!"); + } m->transwarn = 0; return MUNKI_OK; } @@ -2259,8 +2283,13 @@ munki_code munki_imp_measure( /* Indicate to the user that they can now scan the instrument, */ /* after a little delay that allows for the instrument reaction time. */ if (s->scan) { - /* 100msec delay, 1KHz for 200 msec */ - msec_beep(0 + (int)(invsampt * 1000.0 + 0.9), 1000, 200); + int delay = 100 + (int)(invsampt * 1000.0 + 0.9); + if (p->eventcallback != NULL) { + issue_scan_ready((inst *)p, delay); + } else { + /* delay then 1KHz for 200 msec */ + msec_beep(delay, 1000, 200); + } } /* Retry loop in case a display read is saturated */ @@ -5716,9 +5745,10 @@ munki_code munki_extract_patches_multimeas( if (pat[k].use == 0) continue; - nno = (pat[k].no * 3)/4; -// nno = (pat[k].no * 2)/3; // experimental tighter trim - trim = (pat[k].no - nno)/2; +// nno = (pat[k].no * 3 + 0)/4; /* Trim to 75% & round down */ + nno = (pat[k].no * 2 + 0)/3; /* Trim to 66% & round down [def] */ +// nno = (pat[k].no * 2 + 0)/4; /* Trim to 50% & round down */ + trim = (pat[k].no - nno + 1)/2; pat[k].ss += trim; pat[k].no = nno; @@ -6257,6 +6287,8 @@ void munki_scale_specrd( #define DO_CCDNORM /* [def] Normalise CCD values to original */ #define DO_CCDNORMAVG /* [und???] Normalise averages rather than per CCD bin */ +#define BOX_INTEGRATE /* [und] Integrate raw samples as if they were +/-0.5 boxes */ + /* (This improves coeficient consistency a bit ?) */ #ifdef NEVER /* Plot the matrix coefficients */ @@ -6918,7 +6950,7 @@ munki_code munki_create_hr(munki *p, int ref) { if (fabs(w1 - cwl) > fshmax && fabs(w2 - cwl) > fshmax) continue; /* Doesn't fall into this filter */ -#ifndef NEVER +#ifdef BOX_INTEGRATE /* Integrate in 0.05 nm increments from filter shape */ { int nn; @@ -7924,6 +7956,11 @@ munki_code munki_conv2XYZ( } conv->del(conv); + + /* Apply any XRGA conversion */ + ipatch_convert_xrga(vals, nvals, xcalstd_nonpol, m->target_calstd, m->native_calstd, clamp); + + return MUNKI_OK; } diff --git a/spectro/munki_imp.h b/spectro/munki_imp.h index ea9451f..f639395 100644 --- a/spectro/munki_imp.h +++ b/spectro/munki_imp.h @@ -177,6 +177,9 @@ struct _munkiimp { double intclkp; /* Integration clock period (computed from tickdur) */ + xcalstd native_calstd; /* Instrument native calibration standard */ + xcalstd target_calstd; /* Returned calibration standard */ + /* Current state of hardware (~~99 are all these used ??) */ double c_inttime; /* Integration period (=inttime + deadtime) */ int c_measmodeflags; /* Measurement mode flags (set by trigger() */ @@ -418,6 +421,7 @@ munki_code munki_imp_calibrate( munki *p, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[100] /* Condition identifier (ie. white reference ID) */ ); diff --git a/spectro/oemarch.c b/spectro/oemarch.c index a9bbda1..be9abf3 100644 --- a/spectro/oemarch.c +++ b/spectro/oemarch.c @@ -38,6 +38,8 @@ #include <sys/param.h> #include <sys/mount.h> #include <ctype.h> +#include <sys/types.h> +#include <pwd.h> #endif /* UNIX */ #ifndef SALONEINSTLIB #include "copyright.h" @@ -96,7 +98,7 @@ oem_target oemtargs = { { NULL } } #endif /* NT */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE { /* Installed files */ { "/Applications/Spyder2express 2.2/Spyder2express.app/Contents/MacOSClassic/Spyder.lib", targ_spyd2_pld }, { "/Applications/Spyder2pro 2.2/Spyder2pro.app/Contents/MacOSClassic/Spyder.lib", targ_spyd2_pld }, @@ -122,20 +124,29 @@ oem_target oemtargs = { { "/Installer/ColorMunkiDisplaySetup.exe", targ_i1d3_edr }, { NULL } } -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #ifdef UNIX_X11 { /* Installed files */ { NULL } }, - { /* Volume names the CDROM may have */ + { /* Volume names the CDROM may have. */ + /* (It's a pity the linux developers have no idea what a stable API looks like...) */ + { "/run/media/$USER/ColorVision", targ_spyd2_pld | targ_spyd_cal }, + { "/run/media/$USER/Datacolor", targ_spyd2_pld | targ_spyd_cal }, + { "/run/media/$USER/i1Profiler", targ_i1d3_edr }, + { "/run/media/$USER/ColorMunki Displ", targ_i1d3_edr }, + { "/media/ColorVision", targ_spyd2_pld | targ_spyd_cal }, { "/media/Datacolor", targ_spyd2_pld | targ_spyd_cal }, { "/media/i1Profiler", targ_i1d3_edr }, { "/media/ColorMunki Displ", targ_i1d3_edr }, + { "/mnt/cdrom", targ_spyd2_pld | targ_spyd_cal | targ_i1d3_edr }, { "/mnt/cdrecorder", targ_spyd2_pld | targ_spyd_cal | targ_i1d3_edr }, + { "/media/cdrom", targ_spyd2_pld | targ_spyd_cal | targ_i1d3_edr }, { "/media/cdrecorder", targ_spyd2_pld | targ_spyd_cal | targ_i1d3_edr }, + { "/cdrom", targ_spyd2_pld | targ_spyd_cal | targ_i1d3_edr }, { "/cdrecorder", targ_spyd2_pld | targ_spyd_cal | targ_i1d3_edr }, { NULL } @@ -152,14 +163,14 @@ oem_target oemtargs = { #endif /* UNIX_X11 */ }; -#if defined(__APPLE__) +#if defined(UNIX_APPLE) /* Global: */ char *oemamount_path = NULL; #endif /* Cleanup function for transfer on Apple OS X */ void oem_umiso() { -#if defined(__APPLE__) +#if defined(UNIX_APPLE) if (oemamount_path != NULL) { char sbuf[MAXNAMEL+1 + 100]; sprintf(sbuf, "umount \"%s\"",oemamount_path); @@ -167,7 +178,7 @@ void oem_umiso() { sprintf(sbuf, "rmdir \"%s\"",oemamount_path); system(sbuf); } -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ } static xfile *locate_volume(int verb); @@ -561,7 +572,7 @@ static xfile *locate_volume(int verb) { } #endif /* NT */ -#if defined(__APPLE__) +#if defined(UNIX_APPLE) { int j; char tname[MAXNAMEL+1] = { '\000' }; @@ -626,7 +637,7 @@ static xfile *locate_volume(int verb) { } } } -#endif /* __APPLE__ */ +#endif /* UNIX_APPLE */ #if defined(UNIX_X11) { @@ -635,15 +646,49 @@ static xfile *locate_volume(int verb) { /* See if we can see what we're looking for on one of the volumes */ /* It would be nice to be able to read the volume name ! */ for (j = 0;;j++) { - if (oemtargs.volnames[j].path == NULL) + char *vol, *cp; + + vol = oemtargs.volnames[j].path; + + if (vol == NULL) break; - if (access(oemtargs.volnames[j].path, 0) == 0) { - if (verb) printf("found '%s'\n",oemtargs.volnames[j].path); - new_add_xf(&xf, oemtargs.volnames[j].path, NULL, 0, - file_vol, oemtargs.volnames[j].ttype); - break; + /* Some linux paths include the real user name */ + if ((cp = strstr(vol, "$USER")) != NULL) { + char *ivol = vol; + char *usr; + int len; + + /* Media gets mounted as console user, */ + /* so we need to know what that is. */ + /* (Hmm. this solves access problem when + running as root, but not saving resulting + file to $HOME/.local/etc */ + if ((usr = getenv("SUDO_USER")) == NULL) { + if ((usr = getenv("USER")) == NULL) + error("$USER is empty"); + } + + len = strlen(ivol) - 5 + strlen(usr) + 1; + + if ((vol = malloc(len)) == NULL) + error("Malloc of volume path length %d failed",len); + + strncpy(vol, ivol, cp-ivol); + vol[cp-ivol]= '\000'; + strcat(vol, usr); + strcat(vol, cp + 5); + } + + if (access(vol, 0) == 0) { + if (verb) printf("found '%s'\n",vol); + new_add_xf(&xf, vol, NULL, 0, file_vol, oemtargs.volnames[j].ttype); + if (vol != oemtargs.volnames[j].path) + free(vol); + break; } + if (vol != oemtargs.volnames[j].path) + free(vol); } } #endif /* UNIX */ @@ -1407,7 +1452,7 @@ static xfile *edr_convert(xfile **pxf, xfile *xi, int verb) { char *ccssname; char *edrname; unsigned char *buf; - int len; + size_t len; if (c->buf_write_ccss(c, &buf, &len)) { error("Failed to create ccss for '%s' error '%s'",xi->name,c->err); } @@ -2622,7 +2667,8 @@ static xfile *ai_extract_cab(xfile **pxf, xfile *xi, char *tname, int verb) { if (verb) printf("Extracted '%s' length %ld\n",xf->name,xf->len); -save_xfile(xf, "temp.cab", NULL, verb); +// /* Save diagnostic file */ +// save_xfile(xf, "temp.cab", NULL, verb); return xf; } diff --git a/spectro/oeminst.c b/spectro/oeminst.c index bce7a67..726a54c 100644 --- a/spectro/oeminst.c +++ b/spectro/oeminst.c @@ -20,6 +20,7 @@ #include "copyright.h" #include "aconfig.h" #include "numlib.h" +#include "ui.h" #else /* SALONEINSTLIB */ #include <fcntl.h> #include "sa_config.h" @@ -33,7 +34,6 @@ #include "disptechs.h" #include "ccmx.h" #include "ccss.h" -#include "ui.h" void usage(void) { fprintf(stderr,"Install OEM data files, Version %s\n",ARGYLL_VERSION_STR); @@ -43,7 +43,7 @@ void usage(void) { fprintf(stderr," -n Don't install, show where files would be installed\n"); fprintf(stderr," -c Don't install, save files to current directory\n"); fprintf(stderr," -S d Specify the install scope u = user (def.), l = local system]\n"); - fprintf(stderr," infile setup.exe CD install file(s) or .dll(s) containing install files\n"); + fprintf(stderr," infile Manufacturers setup.exe install file(s) or .dll(s) containing install files\n"); fprintf(stderr," infile.[edr|ccss|ccmx] EDR file(s) to translate and install or CCSS or CCMX files to install\n"); fprintf(stderr," If no file is provided, oeminst will look for the install CD.\n"); exit(1); diff --git a/spectro/pollem.c b/spectro/pollem.c index f7578c8..855702b 100644 --- a/spectro/pollem.c +++ b/spectro/pollem.c @@ -1,5 +1,5 @@ - /* Unix serial I/O class poll() emulation. */ +/* Unix serial I/O class poll() emulation. */ /* * Argyll Color Correction System diff --git a/spectro/pollem.h b/spectro/pollem.h index f8a86ee..bd879e1 100644 --- a/spectro/pollem.h +++ b/spectro/pollem.h @@ -1,7 +1,7 @@ #ifndef POLLEN_H - /* Unix serial I/O class poll() emulation. */ +/* Unix serial I/O class poll() emulation. */ /* * Argyll Color Correction System diff --git a/spectro/rspec.c b/spectro/rspec.c index e0e0194..6a7f8c7 100644 --- a/spectro/rspec.c +++ b/spectro/rspec.c @@ -28,6 +28,7 @@ #include <ctype.h> #include <string.h> #include <time.h> +#include <fcntl.h> #if defined(UNIX) # include <utime.h> #else @@ -43,7 +44,9 @@ #include "sa_config.h" #include "numsup.h" #endif /* !SALONEINSTLIB */ -#include "plot.h" +#ifndef SALONEINSTLIB +# include "plot.h" +#endif #include "xspect.h" #include "insttypes.h" #include "conv.h" @@ -51,6 +54,9 @@ #include "inst.h" #include "rspec.h" +#define BOX_INTEGRATE /* [und] Integrate raw samples as if they were +/-0.5 boxes */ + /* (This improves coeficient consistency a bit ?) */ + /* -------------------------------------------------- */ #if defined(__APPLE__) && defined(__POWERPC__) @@ -250,6 +256,7 @@ void del_rspec(rspec *p) { /* Plot the first rspec */ void plot_rspec1(rspec *p) { +#ifndef SALONEINSTLIB int i, no; double xx[RSPEC_MAXSAMP]; double yy[RSPEC_MAXSAMP]; @@ -264,10 +271,12 @@ void plot_rspec1(rspec *p) { yy[i] = p->samp[0][i]; } do_plot(xx, yy, NULL, NULL, no); +#endif } /* Plot the first rspec of 2 */ void plot_rspec2(rspec *p1, rspec *p2) { +#ifndef SALONEINSTLIB int i, no; double xx[RSPEC_MAXSAMP]; double y1[RSPEC_MAXSAMP]; @@ -286,9 +295,11 @@ void plot_rspec2(rspec *p1, rspec *p2) { y2[i] = p2->samp[0][i]; } do_plot(xx, y1, y2, NULL, no); +#endif } void plot_ecal(rspec_inf *inf) { +#ifndef SALONEINSTLIB int i, no; double xx[RSPEC_MAXSAMP]; double yy[RSPEC_MAXSAMP]; @@ -303,6 +314,7 @@ void plot_ecal(rspec_inf *inf) { yy[i] = inf->ecal[i]; } do_plot(xx, yy, NULL, NULL, no); +#endif } @@ -710,7 +722,7 @@ void rspec_make_resample_filters(rspec_inf *inf) { a1logd(inf->log, 4,"rspec_make_resample_filters: maxcoeffs = %d\n",maxcoeffs); - /* Figure out integration step size */ + /* Figure out box integration step size */ #ifdef FAST_HIGH_RES_SETUP finc = twidth/50.0; if (rawspace/finc < 10.0) @@ -754,6 +766,7 @@ void rspec_make_resample_filters(rspec_inf *inf) { if (fabs(w1 - cwl) > fshmax && fabs(w2 - cwl) > fshmax) continue; /* Doesn't fall into this filter */ +#ifdef BOX_INTEGRATE /* Integrate in finc nm increments from filter shape */ /* using triangular integration. */ { @@ -778,6 +791,9 @@ void rspec_make_resample_filters(rspec_inf *inf) { lw = cw; } } +#else + we = fabs(w2 - w1) * kernel(twidth, rwl); +#endif if (inf->fnocoef[j] >= maxcoeffs) error("rspec_make_resample_filters: run out of high res filter space\n"); @@ -846,6 +862,7 @@ void rspec_make_resample_filters(rspec_inf *inf) { /* Plot the wave resampling filters */ void plot_resample_filters(rspec_inf *inf) { +#ifndef SALONEINSTLIB double *xx, *ss; double **yy; int i, j, k, sx; @@ -877,6 +894,7 @@ void plot_resample_filters(rspec_inf *inf) { do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], inf->nraw); free_dvector(xx, 0, inf->nraw-1); free_dmatrix(yy, 0, 2, 0, inf->nraw-1); +#endif } /* ================================================== */ diff --git a/spectro/sa_conv.c b/spectro/sa_conv.c new file mode 100644 index 0000000..84286ba --- /dev/null +++ b/spectro/sa_conv.c @@ -0,0 +1,865 @@ + +#ifdef SALONEINSTLIB + +/* + * A very small subset of icclib, copied to here. + * This is just enough to support the standalone instruments + */ + +/* + * Argyll Color Correction System + * + * Author: Graeme W. Gill + * Date: 28/9/97 + * + * Copyright 1997 - 2013 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- + * see the License2.txt file for licencing details. + */ + +#include "sa_config.h" +#include "numsup.h" +#include "sa_conv.h" + +#include <stdio.h> +#include <stdlib.h> + +sa_XYZNumber sa_D50 = { + 0.9642, 1.0000, 0.8249 +}; + +sa_XYZNumber sa_D65 = { + 0.9505, 1.0000, 1.0890 +}; + +sa_XYZNumber sa_D50_100 = { + 96.42, 100.00, 82.49 +}; + +sa_XYZNumber sa_D65_100 = { + 95.05, 100.00, 108.90 +}; + +unsigned int sa_CSSig2nchan(icColorSpaceSignature sig) { + switch(sig) { + case icSigXYZData: + return 3; + case icSigLabData: + return 3; + case icSigLuvData: + return 3; + case icSigYCbCrData: + return 3; + case icSigYxyData: + return 3; + case icSigRgbData: + return 3; + case icSigGrayData: + return 1; + case icSigHsvData: + return 3; + case icSigHlsData: + return 3; + case icSigCmykData: + return 4; + case icSigCmyData: + return 3; + case icSig2colorData: + return 2; + case icSig3colorData: + return 3; + case icSig4colorData: + return 4; + case icSig5colorData: + case icSigMch5Data: + return 5; + case icSig6colorData: + case icSigMch6Data: + return 6; + case icSig7colorData: + case icSigMch7Data: + return 7; + case icSig8colorData: + case icSigMch8Data: + return 8; + case icSig9colorData: + return 9; + case icSig10colorData: + return 10; + case icSig11colorData: + return 11; + case icSig12colorData: + return 12; + case icSig13colorData: + return 13; + case icSig14colorData: + return 14; + case icSig15colorData: + return 15; + +#ifdef NEVER + /* Non-standard and Pseudo spaces */ + case icmSigYData: + return 1; + case icmSigLData: + return 1; + case icmSigL8Data: + return 1; + case icmSigLV2Data: + return 1; + case icmSigLV4Data: + return 1; + case icmSigPCSData: + return 3; + case icmSigLab8Data: + return 3; + case icmSigLabV2Data: + return 3; + case icmSigLabV4Data: + return 3; +#endif /* NEVER */ + + default: + break; + } + return 0; +} + +void sa_SetUnity3x3(double mat[3][3]) { + int i, j; + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) { + if (i == j) + mat[j][i] = 1.0; + else + mat[j][i] = 0.0; + } + } + +} + +void sa_Cpy3x3(double dst[3][3], double src[3][3]) { + int i, j; + + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) + dst[j][i] = src[j][i]; + } +} + +void sa_MulBy3x3(double out[3], double mat[3][3], double in[3]) { + double tt[3]; + + tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1] + mat[0][2] * in[2]; + tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1] + mat[1][2] * in[2]; + tt[2] = mat[2][0] * in[0] + mat[2][1] * in[1] + mat[2][2] * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + +void sa_Mul3x3_2(double dst[3][3], double src1[3][3], double src2[3][3]) { + int i, j, k; + double td[3][3]; /* Temporary dest */ + + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) { + double tt = 0.0; + for (k = 0; k < 3; k++) + tt += src1[j][k] * src2[k][i]; + td[j][i] = tt; + } + } + + /* Copy result out */ + for (j = 0; j < 3; j++) + for (i = 0; i < 3; i++) + dst[j][i] = td[j][i]; +} + + +/* Matrix Inversion by Richard Carling from "Graphics Gems", Academic Press, 1990 */ +#define det2x2(a, b, c, d) (a * d - b * c) + +static void adjoint( +double out[3][3], +double in[3][3] +) { + double a1, a2, a3, b1, b2, b3, c1, c2, c3; + + /* assign to individual variable names to aid */ + /* selecting correct values */ + + a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2]; + a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2]; + a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2]; + + /* row column labeling reversed since we transpose rows & columns */ + + out[0][0] = det2x2(b2, b3, c2, c3); + out[1][0] = - det2x2(a2, a3, c2, c3); + out[2][0] = det2x2(a2, a3, b2, b3); + + out[0][1] = - det2x2(b1, b3, c1, c3); + out[1][1] = det2x2(a1, a3, c1, c3); + out[2][1] = - det2x2(a1, a3, b1, b3); + + out[0][2] = det2x2(b1, b2, c1, c2); + out[1][2] = - det2x2(a1, a2, c1, c2); + out[2][2] = det2x2(a1, a2, b1, b2); +} + +static double sa_Det3x3(double in[3][3]) { + double a1, a2, a3, b1, b2, b3, c1, c2, c3; + double ans; + + a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2]; + a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2]; + a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2]; + + ans = a1 * det2x2(b2, b3, c2, c3) + - b1 * det2x2(a2, a3, c2, c3) + + c1 * det2x2(a2, a3, b2, b3); + return ans; +} + +#define SA__SMALL_NUMBER 1.e-8 + +int sa_Inverse3x3(double out[3][3], double in[3][3]) { + int i, j; + double det; + + /* calculate the 3x3 determinant + * if the determinant is zero, + * then the inverse matrix is not unique. + */ + det = sa_Det3x3(in); + + if ( fabs(det) < SA__SMALL_NUMBER) + return 1; + + /* calculate the adjoint matrix */ + adjoint(out, in); + + /* scale the adjoint matrix to get the inverse */ + for (i = 0; i < 3; i++) + for(j = 0; j < 3; j++) + out[i][j] /= det; + return 0; +} + +#undef SA__SMALL_NUMBER +#undef det2x2 + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Transpose a 3x3 matrix */ +void sa_Transpose3x3(double out[3][3], double in[3][3]) { + int i, j; + if (out != in) { + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + out[i][j] = in[j][i]; + } else { + double tt[3][3]; + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + tt[i][j] = in[j][i]; + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + out[i][j] = tt[i][j]; + } +} + +/* Scale a 3 vector by the given ratio */ +void sa_Scale3(double out[3], double in[3], double rat) { + out[0] = in[0] * rat; + out[1] = in[1] * rat; + out[2] = in[2] * rat; +} + +/* Clamp a 3 vector to be +ve */ +void sa_Clamp3(double out[3], double in[3]) { + int i; + for (i = 0; i < 3; i++) + out[i] = in[i] < 0.0 ? 0.0 : in[i]; +} + +/* Return the normal Delta E given two Lab values */ +double sa_LabDE(double *Lab0, double *Lab1) { + double rv = 0.0, tt; + + tt = Lab0[0] - Lab1[0]; + rv += tt * tt; + tt = Lab0[1] - Lab1[1]; + rv += tt * tt; + tt = Lab0[2] - Lab1[2]; + rv += tt * tt; + + return sqrt(rv); +} + +/* Return the CIE94 Delta E color difference measure, squared */ +double sa_CIE94sq(double Lab0[3], double Lab1[3]) { + double desq, dhsq; + double dlsq, dcsq; + double c12; + + { + double dl, da, db; + dl = Lab0[0] - Lab1[0]; + dlsq = dl * dl; /* dl squared */ + da = Lab0[1] - Lab1[1]; + db = Lab0[2] - Lab1[2]; + + /* Compute normal Lab delta E squared */ + desq = dlsq + da * da + db * db; + } + + { + double c1, c2, dc; + + /* Compute chromanance for the two colors */ + c1 = sqrt(Lab0[1] * Lab0[1] + Lab0[2] * Lab0[2]); + c2 = sqrt(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]); + c12 = sqrt(c1 * c2); /* Symetric chromanance */ + + /* delta chromanance squared */ + dc = c1 - c2; + dcsq = dc * dc; + } + + /* Compute delta hue squared */ + if ((dhsq = desq - dlsq - dcsq) < 0.0) + dhsq = 0.0; + { + double sc, sh; + + /* Weighting factors for delta chromanance & delta hue */ + sc = 1.0 + 0.045 * c12; + sh = 1.0 + 0.015 * c12; + return dlsq + dcsq/(sc * sc) + dhsq/(sh * sh); + } +} + +/* Return the CIE94 Delta E color difference measure */ +double sa_CIE94(double Lab0[3], double Lab1[3]) { + return sqrt(sa_CIE94sq(Lab0, Lab1)); +} + +/* Return the CIE94 Delta E color difference measure for two XYZ values */ +double sa_XYZCIE94(sa_XYZNumber *w, double *in0, double *in1) { + double lab0[3], lab1[3]; + + sa_XYZ2Lab(w, lab0, in0); + sa_XYZ2Lab(w, lab1, in1); + return sqrt(sa_CIE94sq(lab0, lab1)); +} + +/* CIE XYZ to perceptual CIE 1976 L*a*b* */ +void +sa_XYZ2Lab(sa_XYZNumber *w, double *out, double *in) { + double X = in[0], Y = in[1], Z = in[2]; + double x,y,z,fx,fy,fz; + + x = X/w->X; + y = Y/w->Y; + z = Z/w->Z; + + if (x > 0.008856451586) + fx = pow(x,1.0/3.0); + else + fx = 7.787036979 * x + 16.0/116.0; + + if (y > 0.008856451586) + fy = pow(y,1.0/3.0); + else + fy = 7.787036979 * y + 16.0/116.0; + + if (z > 0.008856451586) + fz = pow(z,1.0/3.0); + else + fz = 7.787036979 * z + 16.0/116.0; + + out[0] = 116.0 * fy - 16.0; + out[1] = 500.0 * (fx - fy); + out[2] = 200.0 * (fy - fz); +} + +void sa_Lab2XYZ(sa_XYZNumber *w, double *out, double *in) { + double L = in[0], a = in[1], b = in[2]; + double x,y,z,fx,fy,fz; + + fy = (L + 16.0)/116.0; + fx = a/500.0 + fy; + fz = fy - b/200.0; + + if (fy > 24.0/116.0) + y = pow(fy,3.0); + else + y = (fy - 16.0/116.0)/7.787036979; + + if (fx > 24.0/116.0) + x = pow(fx,3.0); + else + x = (fx - 16.0/116.0)/7.787036979; + + if (fz > 24.0/116.0) + z = pow(fz,3.0); + else + z = (fz - 16.0/116.0)/7.787036979; + + out[0] = x * w->X; + out[1] = y * w->Y; + out[2] = z * w->Z; +} + + +void sa_Yxy2XYZ(double *out, double *in) { + double Y = in[0]; + double x = in[1]; + double y = in[2]; + double z = 1.0 - x - y; + double sum; + if (y < 1e-9) { + out[0] = out[1] = out[2] = 0.0; + } else { + sum = Y/y; + out[0] = x * sum; + out[1] = Y; + out[2] = z * sum; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Object for computing RFC 1321 MD5 checksums. */ +/* Derived from Colin Plumb's 1993 public domain code. */ + +/* Reset the checksum */ +static void sa_MD5_reset(sa_MD5 *p) { + p->tlen = 0; + + p->sum[0] = 0x67452301; + p->sum[1] = 0xefcdab89; + p->sum[2] = 0x98badcfe; + p->sum[3] = 0x10325476; + + p->fin = 0; +} + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, pp, xtra, s) \ + data = (pp)[0] + ((pp)[3] << 24) + ((pp)[2] << 16) + ((pp)[1] << 8); \ + w += f(x, y, z) + data + xtra; \ + w = (w << s) | (w >> (32-s)); \ + w += x; + +/* Add another 64 bytes to the checksum */ +static void sa_MD5_accume(sa_MD5 *p, ORD8 *in) { + ORD32 data, a, b, c, d; + + a = p->sum[0]; + b = p->sum[1]; + c = p->sum[2]; + d = p->sum[3]; + + MD5STEP(F1, a, b, c, d, in + (4 * 0), 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in + (4 * 1), 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in + (4 * 2), 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in + (4 * 3), 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in + (4 * 4), 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in + (4 * 5), 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in + (4 * 6), 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in + (4 * 7), 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in + (4 * 8), 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in + (4 * 9), 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in + (4 * 10), 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in + (4 * 11), 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in + (4 * 12), 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in + (4 * 13), 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in + (4 * 14), 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in + (4 * 15), 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in + (4 * 1), 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in + (4 * 6), 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in + (4 * 11), 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in + (4 * 0), 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in + (4 * 5), 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in + (4 * 10), 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in + (4 * 15), 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in + (4 * 4), 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in + (4 * 9), 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in + (4 * 14), 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in + (4 * 3), 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in + (4 * 8), 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in + (4 * 13), 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in + (4 * 2), 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in + (4 * 7), 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in + (4 * 12), 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in + (4 * 5), 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in + (4 * 8), 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in + (4 * 11), 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in + (4 * 14), 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in + (4 * 1), 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in + (4 * 4), 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in + (4 * 7), 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in + (4 * 10), 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in + (4 * 13), 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in + (4 * 0), 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in + (4 * 3), 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in + (4 * 6), 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in + (4 * 9), 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in + (4 * 12), 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in + (4 * 15), 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in + (4 * 2), 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in + (4 * 0), 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in + (4 * 7), 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in + (4 * 14), 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in + (4 * 5), 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in + (4 * 12), 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in + (4 * 3), 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in + (4 * 10), 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in + (4 * 1), 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in + (4 * 8), 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in + (4 * 15), 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in + (4 * 6), 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in + (4 * 13), 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in + (4 * 4), 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in + (4 * 11), 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in + (4 * 2), 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in + (4 * 9), 0xeb86d391, 21); + + p->sum[0] += a; + p->sum[1] += b; + p->sum[2] += c; + p->sum[3] += d; +} + +#undef F1 +#undef F2 +#undef F3 +#undef F4 +#undef MD5STEP + +/* Add some bytes */ +static void sa_MD5_add(sa_MD5 *p, ORD8 *ibuf, unsigned int len) { + unsigned int bs; + + if (p->fin) + return; /* This is actually an error */ + + bs = p->tlen; /* Current bytes added */ + p->tlen = bs + len; /* Update length after adding this buffer */ + bs &= 0x3f; /* Bytes already in buffer */ + + /* Deal with any existing partial bytes in p->buf */ + if (bs) { + ORD8 *np = (ORD8 *)p->buf + bs; /* Next free location in partial buffer */ + + bs = 64 - bs; /* Free space in partial buffer */ + + if (len < bs) { /* Not enought new to make a full buffer */ + memmove(np, ibuf, len); + return; + } + + memmove(np, ibuf, bs); /* Now got one full buffer */ + sa_MD5_accume(p, np); + ibuf += bs; + len -= bs; + } + + /* Deal with input data 64 bytes at a time */ + while (len >= 64) { + sa_MD5_accume(p, ibuf); + ibuf += 64; + len -= 64; + } + + /* Deal with any remaining bytes */ + memmove(p->buf, ibuf, len); +} + +/* Finalise the checksum and return the result. */ +static void sa_MD5_get(sa_MD5 *p, ORD8 chsum[16]) { + int i; + unsigned count; + ORD32 bits1, bits0; + ORD8 *pp; + + if (p->fin == 0) { + + /* Compute number of bytes processed mod 64 */ + count = p->tlen & 0x3f; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + pp = p->buf + count; + *pp++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64, allowing 8 bytes for length in bits. */ + if (count < 8) { /* Not enough space for padding and length */ + + memset(pp, 0, count); + sa_MD5_accume(p, p->buf); + + /* Now fill the next block with 56 bytes */ + memset(p->buf, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(pp, 0, count - 8); + } + + /* Compute number of bits */ + bits1 = 0x7 & (p->tlen >> (32 - 3)); + bits0 = p->tlen << 3; + + /* Append number of bits */ + p->buf[64 - 8] = bits0 & 0xff; + p->buf[64 - 7] = (bits0 >> 8) & 0xff; + p->buf[64 - 6] = (bits0 >> 16) & 0xff; + p->buf[64 - 5] = (bits0 >> 24) & 0xff; + p->buf[64 - 4] = bits1 & 0xff; + p->buf[64 - 3] = (bits1 >> 8) & 0xff; + p->buf[64 - 2] = (bits1 >> 16) & 0xff; + p->buf[64 - 1] = (bits1 >> 24) & 0xff; + + sa_MD5_accume(p, p->buf); + + p->fin = 1; + } + + /* Return the result, lsb to msb */ + pp = chsum; + for (i = 0; i < 4; i++) { + *pp++ = p->sum[i] & 0xff; + *pp++ = (p->sum[i] >> 8) & 0xff; + *pp++ = (p->sum[i] >> 16) & 0xff; + *pp++ = (p->sum[i] >> 24) & 0xff; + } +} + + +/* Delete the instance */ +static void sa_MD5_del(sa_MD5 *p) { + + /* This object */ + if (p != NULL) + free(p); +} + +/* Create a new MD5 checksumming object, with a reset checksum value */ +/* Return it or NULL if there is an error */ +sa_MD5 *new_sa_MD5() { + sa_MD5 *p; + + if ((p = (sa_MD5 *)calloc(1,sizeof(sa_MD5))) == NULL) + return NULL; + + p->reset = sa_MD5_reset; + p->add = sa_MD5_add; + p->get = sa_MD5_get; + p->del = sa_MD5_del; + + p->reset(p); + + return p; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* A sub-set of ludecomp code from numlib */ + +int sa_lu_decomp(double **a, int n, int *pivx, double *rip) { + int i, j; + double *rscale, RSCALE[10]; + + if (n <= 10) + rscale = RSCALE; + else + rscale = dvector(0, n-1); + + for (i = 0; i < n; i++) { + double big; + for (big = 0.0, j=0; j < n; j++) { + double temp; + temp = fabs(a[i][j]); + if (temp > big) + big = temp; + } + if (fabs(big) <= DBL_MIN) { + if (rscale != RSCALE) + free_dvector(rscale, 0, n-1); + return 1; + } + rscale[i] = 1.0/big; + } + + for (*rip = 1.0, j = 0; j < n; j++) { + double big; + int k, bigi = 0; + + for (i = 0; i < j; i++) { + double sum; + sum = a[i][j]; + for (k = 0; k < i; k++) + sum -= a[i][k] * a[k][j]; + a[i][j] = sum; + } + + for (big = 0.0, i = j; i < n; i++) { + double sum, temp; + sum = a[i][j]; + for (k = 0; k < j; k++) + sum -= a[i][k] * a[k][j]; + a[i][j] = sum; + temp = rscale[i] * fabs(sum); + if (temp >= big) { + big = temp; + bigi = i; + } + } + + if (j != bigi) { + { + double *temp; + temp = a[bigi]; + a[bigi] = a[j]; + a[j] = temp; + } + *rip = -(*rip); + rscale[bigi] = rscale[j]; + } + + pivx[j] = bigi; + if (fabs(a[j][j]) <= DBL_MIN) { + if (rscale != RSCALE) + free_dvector(rscale, 0, n-1); + return 1; + } + + if (j != (n-1)) { + double temp; + temp = 1.0/a[j][j]; + for (i = j+1; i < n; i++) + a[i][j] *= temp; + } + } + if (rscale != RSCALE) + free_dvector(rscale, 0, n-1); + return 0; +} + +void sa_lu_backsub(double **a, int n, int *pivx, double *b) { + int i, j; + int nvi; + + for (nvi = -1, i = 0; i < n; i++) { + int px; + double sum; + + px = pivx[i]; + sum = b[px]; + b[px] = b[i]; + if (nvi >= 0) { + for (j = nvi; j < i; j++) + sum -= a[i][j] * b[j]; + } else { + if (sum != 0.0) + nvi = i; + } + b[i] = sum; + } + + for (i = (n-1); i >= 0; i--) { + double sum; + sum = b[i]; + for (j = i+1; j < n; j++) + sum -= a[i][j] * b[j]; + b[i] = sum/a[i][i]; + } +} + +int sa_lu_invert(double **a, int n) { + int i, j; + double rip; + int *pivx, PIVX[10]; + double **y; + + if (n <= 10) + pivx = PIVX; + else + pivx = ivector(0, n-1); + + if (sa_lu_decomp(a, n, pivx, &rip)) { + if (pivx != PIVX) + free_ivector(pivx, 0, n-1); + return 1; + } + + y = dmatrix(0, n-1, 0, n-1); + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + y[i][j] = a[i][j]; + } + } + + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) + a[i][j] = 0.0; + a[i][i] = 1.0; + sa_lu_backsub(y, n, pivx, a[i]); + } + + free_dmatrix(y, 0, n-1, 0, n-1); + if (pivx != PIVX) + free_ivector(pivx, 0, n-1); + + return 0; +} + +int sa_lu_psinvert(double **out, double **in, int m, int n) { + int rv = 0; + double **tr; + double **sq; + + tr = dmatrix(0, n-1, 0, m-1); + matrix_trans(tr, in, m, n); + + if (m > n) { + sq = dmatrix(0, n-1, 0, n-1); + if ((rv = matrix_mult(sq, n, n, tr, n, m, in, m, n)) == 0) { + if ((rv = sa_lu_invert(sq, n)) == 0) { + rv = matrix_mult(out, n, m, sq, n, n, tr, n, m); + } + } + free_dmatrix(sq, 0, n-1, 0, n-1); + } else { + sq = dmatrix(0, m-1, 0, m-1); + if ((rv = matrix_mult(sq, m, m, in, m, n, tr, n, m)) == 0) { + if ((rv = sa_lu_invert(sq, m)) == 0) { + rv = matrix_mult(out, n, m, tr, n, m, sq, m, m); + } + } + free_dmatrix(sq, 0, m-1, 0, m-1); + } + + free_dmatrix(tr, 0, n-1, 0, m-1); + return rv; +} + + +#endif /* SALONEINSTLIB */ + + diff --git a/spectro/sa_conv.h b/spectro/sa_conv.h new file mode 100644 index 0000000..0f7e635 --- /dev/null +++ b/spectro/sa_conv.h @@ -0,0 +1,233 @@ +#ifndef SA_CONV_H + +#ifdef SALONEINSTLIB +/* + * A very small subset of icclib and numlib for the standalone instrument lib. + */ + +/* + * Argyll Color Correction System + * + * Author: Graeme W. Gill + * Date: 2008/2/9 + * + * Copyright 1996 - 2013 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- + * see the License2.txt file for licencing details. + * + * Derived from icoms.h + */ + +#if defined (NT) +# if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0501 +# if defined _WIN32_WINNT +# undef _WIN32_WINNT +# endif +# define _WIN32_WINNT 0x0501 +# endif +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# include <io.h> +#endif + +#if defined (UNIX) +# include <unistd.h> +# include <glob.h> +# include <pthread.h> +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* A subset of icclib */ + +#ifndef MAX_CHAN +# define MAX_CHAN 15 +#endif + +typedef enum { + sa_SigXYZData = 0x58595A20L, /* 'XYZ ' */ + sa_SigLabData = 0x4C616220L, /* 'Lab ' */ + sa_SigLuvData = 0x4C757620L, /* 'Luv ' */ + sa_SigYCbCrData = 0x59436272L, /* 'YCbr' */ + sa_SigYxyData = 0x59787920L, /* 'Yxy ' */ + sa_SigRgbData = 0x52474220L, /* 'RGB ' */ + sa_SigGrayData = 0x47524159L, /* 'GRAY' */ + sa_SigHsvData = 0x48535620L, /* 'HSV ' */ + sa_SigHlsData = 0x484C5320L, /* 'HLS ' */ + sa_SigCmykData = 0x434D594BL, /* 'CMYK' */ + sa_SigCmyData = 0x434D5920L, /* 'CMY ' */ + + sa_Sig2colorData = 0x32434C52L, /* '2CLR' */ + sa_Sig3colorData = 0x33434C52L, /* '3CLR' */ + sa_Sig4colorData = 0x34434C52L, /* '4CLR' */ + sa_Sig5colorData = 0x35434C52L, /* '5CLR' */ + sa_Sig6colorData = 0x36434C52L, /* '6CLR' */ + sa_Sig7colorData = 0x37434C52L, /* '7CLR' */ + sa_Sig8colorData = 0x38434C52L, /* '8CLR' */ + sa_Sig9colorData = 0x39434C52L, /* '9CLR' */ + sa_Sig10colorData = 0x41434C52L, /* 'ACLR' */ + sa_Sig11colorData = 0x42434C52L, /* 'BCLR' */ + sa_Sig12colorData = 0x43434C52L, /* 'CCLR' */ + sa_Sig13colorData = 0x44434C52L, /* 'DCLR' */ + sa_Sig14colorData = 0x45434C52L, /* 'ECLR' */ + sa_Sig15colorData = 0x46434C52L, /* 'FCLR' */ + + sa_SigMch5Data = 0x4D434835L, /* 'MCH5' Colorsync ? */ + sa_SigMch6Data = 0x4D434836L, /* 'MCH6' Hexachrome: CMYKOG */ + sa_SigMch7Data = 0x4D434837L, /* 'MCH7' Colorsync ? */ + sa_SigMch8Data = 0x4D434838L, /* 'MCH8' Colorsync ? */ + sa_SigNamedData = 0x6e6d636cL, /* 'nmcl' ??? */ + + sa_MaxEnumData = -1 +} sa_ColorSpaceSignature; + +typedef enum { + sa_SigInputClass = 0x73636E72L, /* 'scnr' */ + sa_SigDisplayClass = 0x6D6E7472L, /* 'mntr' */ + sa_SigOutputClass = 0x70727472L, /* 'prtr' */ + sa_SigLinkClass = 0x6C696E6BL, /* 'link' */ + sa_SigAbstractClass = 0x61627374L, /* 'abst' */ + sa_SigColorSpaceClass = 0x73706163L, /* 'spac' */ + sa_SigNamedColorClass = 0x6e6d636cL, /* 'nmcl' */ + sa_MaxEnumClass = -1 +} sa_ProfileClassSignature; + +typedef struct { + double X; + double Y; + double Z; +} sa_XYZNumber; + +unsigned int sa_CSSig2nchan(sa_ColorSpaceSignature sig); +extern sa_XYZNumber sa_D50; +extern sa_XYZNumber sa_D65; +extern sa_XYZNumber sa_D50_100; +extern sa_XYZNumber sa_D65_100; +void sa_SetUnity3x3(double mat[3][3]); +void sa_Cpy3x3(double out[3][3], double mat[3][3]); +void sa_MulBy3x3(double out[3], double mat[3][3], double in[3]); +void sa_Mul3x3_2(double dst[3][3], double src1[3][3], double src2[3][3]); +int sa_Inverse3x3(double out[3][3], double in[3][3]); +void sa_Transpose3x3(double out[3][3], double in[3][3]); +void sa_Scale3(double out[3], double in[3], double rat); +double sa_LabDE(double *in0, double *in1); +double sa_CIE94sq(double *in0, double *in1); +void sa_Lab2XYZ(sa_XYZNumber *w, double *out, double *in); +void sa_XYZ2Lab(sa_XYZNumber *w, double *out, double *in); +void sa_Yxy2XYZ(double *out, double *in); + +#define icColorSpaceSignature sa_ColorSpaceSignature +#define icSigXYZData sa_SigXYZData +#define icSigLabData sa_SigLabData +#define icSigLuvData sa_SigLuvData +#define icSigYCbCrData sa_SigYCbCrData +#define icSigYxyData sa_SigYxyData +#define icSigRgbData sa_SigRgbData +#define icSigGrayData sa_SigGrayData +#define icSigHsvData sa_SigHsvData +#define icSigHlsData sa_SigHlsData +#define icSigCmykData sa_SigCmykData +#define icSigCmyData sa_SigCmyData +#define icSig2colorData sa_Sig2colorData +#define icSig3colorData sa_Sig3colorData +#define icSig4colorData sa_Sig4colorData +#define icSig5colorData sa_Sig5colorData +#define icSig6colorData sa_Sig6colorData +#define icSig7colorData sa_Sig7colorData +#define icSig8colorData sa_Sig8colorData +#define icSig9colorData sa_Sig9colorData +#define icSig10colorData sa_Sig10colorData +#define icSig11colorData sa_Sig11colorData +#define icSig12colorData sa_Sig12colorData +#define icSig13colorData sa_Sig13colorData +#define icSig14colorData sa_Sig14colorData +#define icSig15colorData sa_Sig15colorData +#define icSigMch5Data sa_SigMch5Data +#define icSigMch6Data sa_SigMch6Data +#define icSigMch7Data sa_SigMch7Data +#define icSigMch8Data sa_SigMch8Data +#define icSigNamedData sa_SigNamedData +#define icMaxEnumData sa_MaxEnumData + +#define icProfileClassSignature sa_ProfileClassSignature +#define icSigInputClass sa_SigInputClass +#define icSigDisplayClass sa_SigDisplayClass +#define icSigOutputClass sa_SigOutputClass +#define icSigLinkClass sa_SigLinkClass +#define icSigAbstractClass sa_SigAbstractClass +#define icSigColorSpaceClass sa_SigColorSpaceClass +#define icSigNamedColorClass sa_SigNamedColorClass + +#define icmCSSig2nchan sa_CSSig2nchan + +#define icmXYZNumber sa_XYZNumber +#define icmD50 sa_D50 +#define icmD65 sa_D65 +#define icmD50_100 sa_D50_100 +#define icmD65_100 sa_D65_100 + +#define icmSetUnity3x3 sa_SetUnity3x3 +#define icmCpy3x3 sa_Cpy3x3 +#define icmMulBy3x3 sa_MulBy3x3 +#define icmMul3x3_2 sa_Mul3x3_2 +#define icmInverse3x3 sa_Inverse3x3 +#define icmTranspose3x3 sa_Transpose3x3 + +#define icmCpy3(d_ary, s_ary) ((d_ary)[0] = (s_ary)[0], (d_ary)[1] = (s_ary)[1], \ + (d_ary)[2] = (s_ary)[2]) +#define icmScale3 sa_Scale3 +#define icmClamp3 sa_Clamp3 + +#define icmAry2XYZ(xyz, ary) ((xyz).X = (ary)[0], (xyz).Y = (ary)[1], (xyz).Z = (ary)[2]) + +#define icmLabDE sa_LabDE +#define icmCIE94sq sa_CIE94sq +#define icmXYZ2Lab sa_XYZ2Lab +#define icmLab2XYZ sa_Lab2XYZ +#define icmYxy2XYZ sa_Yxy2XYZ + +/* A helper object that computes MD5 checksums */ +struct _sa_MD5 { + /* Private: */ + int fin; /* Flag, nz if final has been called */ + ORD32 sum[4]; /* Current/final checksum */ + unsigned int tlen; /* Total length added in bytes */ + ORD8 buf[64]; /* Partial buffer */ + + /* Public: */ + void (*reset)(struct _sa_MD5 *p); /* Reset the checksum */ + void (*add)(struct _sa_MD5 *p, ORD8 *buf, unsigned int len); /* Add some bytes */ + void (*get)(struct _sa_MD5 *p, ORD8 chsum[16]); /* Finalise and get the checksum */ + void (*del)(struct _sa_MD5 *p); /* We're done with the object */ + +}; typedef struct _sa_MD5 sa_MD5; + +/* Create a new MD5 checksumming object, with a reset checksum value */ +/* Return it or NULL if there is an error. */ +extern sa_MD5 *new_sa_MD5(void); + +#define icmMD5 sa_MD5 +#define new_icmMD5 new_sa_MD5 + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* A subset of numlib */ + +int sa_lu_psinvert(double **out, double **in, int m, int n); + +#define lu_psinvert sa_lu_psinvert + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef __cplusplus + } +#endif + +#endif /* SALONEINSTLIB */ + +#define SA_CONV_H +#endif /* SA_CONV_H */ diff --git a/spectro/smcube.c b/spectro/smcube.c index 8a788ec..c450d48 100644 --- a/spectro/smcube.c +++ b/spectro/smcube.c @@ -68,9 +68,10 @@ #include "copyright.h" #include "aconfig.h" #include "numlib.h" -#else /* !SALONEINSTLIB */ +#else /* SALONEINSTLIB */ #include "sa_config.h" #include "numsup.h" +#include "sa_conv.h" #endif /* !SALONEINSTLIB */ #include "xspect.h" #include "insttypes.h" @@ -210,12 +211,13 @@ smcube_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { a1logd(p->log, 2, "smcube_init_coms: About to init Serial I/O\n"); - if (p->icom->port_type(p->icom) != icomt_serial - && p->icom->port_type(p->icom) != icomt_usbserial) { - a1logd(p->log, 1, "smcube_init_coms: wrong communications type for device!\n"); + if (!(p->icom->dctype & icomt_seriallike) + && !(p->icom->dctype & icomt_fastserial)) { + a1logd(p->log, 1, "smcube_init_coms: wrong communications type for device! (dctype 0x%x)\n",p->icom->dctype); return inst_coms_fail; } + /* Communications has already been established */ if (p->bt) { amutex_lock(p->lock); @@ -234,8 +236,8 @@ smcube_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } amutex_unlock(p->lock); + /* We need to setup communications */ } else { - amutex_lock(p->lock); /* The tick to give up on */ @@ -248,7 +250,8 @@ smcube_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { if (brt[i] == baud_nc) { i = 0; } - a1logd(p->log, 5, "smcube_init_coms: trying %s baud\n",baud_rate_to_str(brt[i])); + a1logd(p->log, 4, "smcube_init_coms: Trying %s baud, %d msec to go\n", + baud_rate_to_str(brt[i]), etime- msec_time()); if ((se = p->icom->set_ser_port(p->icom, fc_Hardware, brt[i], parity_none, stop_1, length_8)) != ICOM_OK) { amutex_unlock(p->lock); @@ -260,7 +263,7 @@ smcube_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { buf[0] = 0x7e, buf[1] = 0x00, buf[2] = 0x02, buf[3] = 0x00; /* Ping command */ if (((ev = smcube_command(p, buf, 4, buf, 4, DEFTO)) & inst_mask) != inst_coms_fail) { - break; /* We've got coms or user abort */ + goto got_coms; /* We've got coms or user abort */ } /* Check for user abort */ @@ -274,11 +277,12 @@ smcube_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } } - if (msec_time() >= etime) { /* We haven't established comms */ - amutex_unlock(p->lock); - a1logd(p->log, 2, "smcube_init_coms: failed to establish coms\n"); - return inst_coms_fail; - } + /* We haven't established comms */ + amutex_unlock(p->lock); + a1logd(p->log, 2, "smcube_init_coms: failed to establish coms\n"); + return inst_coms_fail; + + got_coms:; /* Check the response */ if (buf[0] != 0x7e || buf[1] != 0x20 || buf[2] != 0x02 || buf[3] != 0x00) { @@ -609,6 +613,7 @@ inst_code smcube_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { smcube *p = (smcube *)pp; @@ -621,6 +626,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *id = inst_calc_id_none; id[0] = '\000'; if ((ev = smcube_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -1056,8 +1062,7 @@ static void set_optcalibs_default(smcube *p) { * error if it hasn't been initialised. */ static inst_code -smcube_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +smcube_get_set_opt(inst *pp, inst_opt_type m, ...) { smcube *p = (smcube *)pp; inst_code ev = inst_ok; @@ -1114,13 +1119,17 @@ smcube_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } - /* Get/Sets that require instrument coms. */ - if (!p->gotcoms) - return inst_no_coms; - if (!p->inited) - return inst_no_init; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; - return inst_unsupported; + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/smcube.h b/spectro/smcube.h index 7283635..06aee9d 100644 --- a/spectro/smcube.h +++ b/spectro/smcube.h @@ -37,6 +37,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Fake Error codes */ #define SMCUBE_INTERNAL_ERROR 0xff01 /* Internal software error */ #define SMCUBE_TIMEOUT 0xff02 /* Communication timeout */ @@ -67,7 +71,7 @@ struct _smcube { INST_OBJ_BASE - int bt; /* Bluetooth coms rather than USB/serial */ + int bt; /* Bluetooth coms rather than USB/serial flag */ amutex lock; /* Command lock */ @@ -114,6 +118,9 @@ struct _smcube { /* Constructor */ extern smcube *new_smcube(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define SMCUBE_H #endif /* SMCUBE_H */ diff --git a/spectro/spec2cie.c b/spectro/spec2cie.c index 322ab05..d77c334 100644 --- a/spectro/spec2cie.c +++ b/spectro/spec2cie.c @@ -1,7 +1,7 @@ /* * Argyll Color Correction System - * Spectral .ti3 file converter + * Spectral .ti3 or .sp file converter * * Copyright 2005 Gerhard Fuernkranz * All rights reserved. @@ -26,17 +26,17 @@ * under the GNU GENERAL PUBLIC LICENSE Version 3 :- * see the License.txt file for licencing details. * - * This program takes the spectral data in a .ti3 file, converts them + * This program takes the spectral data in a .ti3 or .sp file, converts them * to XYZ and Lab and fills the XYZ_[XYZ] and LAB_[LAB] columns in the - * output .ti3 file with the computed XYZ and Lab values. If the columns + * output .ti3 or .sp file with the computed XYZ and Lab values. If the columns * XYZ_[XYZ] and/or LAB_[LAB] are missing in the input file, they are * added to the output file. * - * All other colums are copied from the input to the output .ti3 file. + * All other colums are copied from the input to the output .ti3 or .sp file. * * If the -f option is used, the FWA corrected spectral reflectances - * are written to the output .ti3 file, instead of simply copying the - * spectral reflectances from the input .ti3 file. In this case, the + * are written to the output .ti3 or .sp file, instead of simply copying the + * spectral reflectances from the input .ti3 or .sp file. In this case, the * XYZ_[XYZ] and D50 LAB_[LAB] values are computed from the FWA corrected * reflectances as well. */ @@ -50,10 +50,11 @@ Calibration tables aren't being passed through either ?? - L*a*b* is always D50. - This is intended for conversion of reflective measurements to XYZ - there is no illuminant for emissive values. + + L*a*b* is always D50. + */ #define ALLOW_PLOT @@ -74,33 +75,33 @@ #include "inst.h" #ifdef ALLOW_PLOT #include "plot.h" -#endif #include "ui.h" +#endif void usage (void) { - fprintf (stderr, "Convert spectral .ti3 file, Version %s\n", ARGYLL_VERSION_STR); + fprintf (stderr, "Convert spectral .ti3 or .sp file, Version %s\n", ARGYLL_VERSION_STR); fprintf (stderr, "Author: Gerhard Fuernkranz, licensed under the AGPL Version 3\n"); fprintf (stderr, "\n"); - fprintf (stderr, "Usage: spec2cie [options] input.ti3 output.ti3\n"); - fprintf (stderr, " -v Verbose mode\n"); - fprintf (stderr, " -I illum Override actual instrument illuminant in .ti3 file:\n"); - fprintf (stderr, " A, C, D50, D50M2, D65, F5, F8, F10 or file.sp\n"); - fprintf (stderr, " (only used in conjunction with -f)\n"); - fprintf (stderr, " -f [illum] Use Fluorescent Whitening Agent compensation [simulated inst. illum.:\n"); - fprintf (stderr, " M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]\n"); - fprintf (stderr, " -i illum Choose illuminant for computation of CIE XYZ from spectral data & FWA:\n"); - fprintf (stderr, " A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp\n"); - fprintf (stderr, " -o observ Choose CIE Observer for spectral data:\n"); - fprintf (stderr, " 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2 or file.cmf\n"); - fprintf (stderr, " -n Don't output spectral values\n"); + fprintf (stderr, "Usage: spec2cie [options] input.[ti3|sp] output.[ti3|sp]\n"); + fprintf (stderr, " -v Verbose mode\n"); + fprintf (stderr, " -I illum Override actual instrument illuminant in .ti3 or .sp file:\n"); + fprintf (stderr, " A, C, D50, D50M2, D65, F5, F8, F10 or file.sp\n"); + fprintf (stderr, " (only used in conjunction with -f)\n"); + fprintf (stderr, " -f [illum] Use Fluorescent Whitening Agent compensation [simulated inst. illum.:\n"); + fprintf (stderr, " M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]\n"); + fprintf (stderr, " -i illum Choose illuminant for computation of CIE XYZ from spectral data & FWA:\n"); + fprintf (stderr, " A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp\n"); + fprintf (stderr, " -o observ Choose CIE Observer for spectral data:\n"); + fprintf (stderr, " 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2 or file.cmf\n"); + fprintf (stderr, " -n Don't output spectral values\n"); #ifdef ALLOW_PLOT - fprintf (stderr, " -p Plot each values spectrum\n"); + fprintf (stderr, " -p Plot each values spectrum\n"); #endif - fprintf (stderr, " input.ti3 Measurement file\n"); - fprintf (stderr, " output.ti3 Converted measurement file\n"); + fprintf (stderr, " input.[ti3|sp] Measurement file\n"); + fprintf (stderr, " output.[ti3|sp] Converted measurement file\n"); exit (1); } @@ -116,6 +117,7 @@ main(int argc, char *argv[]) cgats *ocg; /* output cgats structure */ cgats_set_elem *elems; + int isspect = 0; /* nz if SPECT file rathe than TI3 */ int isemis = 0; /* nz if this is an emissive reference */ int isdisp = 0; /* nz if this is a display device */ int isdnormed = 0; /* Has display data been normalised to 100 ? */ @@ -126,12 +128,18 @@ main(int argc, char *argv[]) int fwacomp = 0; /* FWA compensation */ int doplot = 0; /* Plot each patches spectrum */ char* illum_str = "D50"; - icxIllumeType tillum = icxIT_none; /* Target/simulated instrument illuminant */ + icxIllumeType tillum = icxIT_none; /* Target/simulated instrument illuminant, if set. */ xspect cust_tillum, *tillump = NULL; /* Custom target/simulated illumination spectrum */ - icxIllumeType illum = icxIT_none; /* Spectral defaults */ - xspect cust_illum; /* Custom CIE illumination spectrum */ - icxIllumeType inst_illum = icxIT_none; /* Spectral defaults */ + /* if tillum == icxIT_custom */ + icxIllumeType illum = icxIT_none; /* CIE calc. illuminant spectrum, and FWA inst. */ + /* illuminant if tillum not set. */ + xspect cust_illum; /* Custom CIE illumination spectrum if illum == icxIT_custom */ + double *ill_wp = NULL; /* If illum is not D50, illum white point XYZ */ + double _ill_wp[3]; /* (What ill_wp points at if it is not NULL) */ + icxIllumeType inst_illum = icxIT_none; /* Actual instrument illumination */ xspect inst_cust_illum; /* Custom actual instrument illumination spectrum */ + /* if inst_illum == icxIT_custom */ + icxObserverType observ = icxOT_none; xspect cust_observ[3]; /* Custom observer CMF's */ @@ -211,9 +219,18 @@ main(int argc, char *argv[]) inst_illum = icxIT_F10; } else { /* Assume it's a filename */ + inst_meas_type mt; + inst_illum = icxIT_custom; - if (read_xspect (&inst_cust_illum, na) != 0) + if (read_xspect (&inst_cust_illum, &mt,na) != 0) usage (); + + if (mt != inst_mrt_none + && mt != inst_mrt_emission + && mt != inst_mrt_ambient + && mt != inst_mrt_emission_flash + && mt != inst_mrt_ambient_flash) + error("Instrument illuminant '%s' is wrong measurement type",na); } } @@ -243,14 +260,24 @@ main(int argc, char *argv[]) } else if (strcmp(na, "F10") == 0) { tillum = icxIT_F10; } else { /* Assume it's a filename */ + inst_meas_type mt; + tillum = icxIT_custom; - if (read_xspect(&cust_tillum, na) != 0) + if (read_xspect(&cust_tillum, &mt, na) != 0) usage(); + + if (mt != inst_mrt_none + && mt != inst_mrt_emission + && mt != inst_mrt_ambient + && mt != inst_mrt_emission_flash + && mt != inst_mrt_ambient_flash) + error("Target illuminant '%s' is wrong measurement type",na); } } } /* CIE tristimulous spectral Illuminant type */ + /* (and FWA simulated instrument illuminant if tillum == icxIT_none) */ else if (argv[fa][1] == 'i') { fa = nfa; if (na == NULL) @@ -281,9 +308,18 @@ main(int argc, char *argv[]) illum = icxIT_F10; } else { /* Assume it's a filename */ + inst_meas_type mt; + illum = icxIT_custom; - if (read_xspect (&cust_illum, na) != 0) + if (read_xspect (&cust_illum, &mt, na) != 0) usage (); + + if (mt != inst_mrt_none + && mt != inst_mrt_emission + && mt != inst_mrt_ambient + && mt != inst_mrt_emission_flash + && mt != inst_mrt_ambient_flash) + error("CIE illuminant '%s' is wrong measurement type",na); } } @@ -334,23 +370,32 @@ main(int argc, char *argv[]) /* Open and look at the .ti3 profile patches file */ - icg = new_cgats (); /* Create a CGATS structure */ + icg = new_cgats (); /* Create a CGATS structure */ icg->add_other (icg, "CTI3"); /* Calibration Target Information 3 */ + icg->add_other (icg, "SPECT"); /* Spectral file */ - ocg = new_cgats (); /* Create a CGATS structure */ + ocg = new_cgats (); /* Create a CGATS structure */ ocg->add_other (ocg, "CTI3"); /* Calibration Target Information 3 */ + icg->add_other (ocg, "SPECT"); /* Spectral file */ if (icg->read_name (icg, in_ti3_name)) error ("CGATS file read error: %s", icg->err); - if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0) - error ("Input file isn't a CTI3 format file"); + if (icg->ntables == 0 || icg->t[0].tt != tt_other + || (icg->t[0].oi != 0 && icg->t[0].oi != 1)) + error ("Input file isn't a CTI3 or SPECT format file"); + + if (icg->t[0].oi == 1) + isspect = 1; + if (icg->ntables < 1) error ("Input file doesn't contain at least one table"); /* add table to output file */ - - ocg->add_table(ocg, tt_other, 0); + if (isspect) + ocg->add_table(ocg, tt_other, 1); + else + ocg->add_table(ocg, tt_other, 0); /* copy keywords */ @@ -370,19 +415,39 @@ main(int argc, char *argv[]) } } - if ((dti = icg->find_kword (icg, 0, "DEVICE_CLASS")) < 0) - error ("Input file doesn't contain keyword DEVICE_CLASS"); - - /* Reflective options when not a reflective profile type */ - if (strcmp(icg->t[0].kdata[dti],"DISPLAY") == 0 - || strcmp(icg->t[0].kdata[dti],"EMISINPUT") == 0) { - isemis = 1; - if (illum != icxIT_none) - error("-i illuminant can't be used for emissive reference type"); - if (fwacomp) - error("-f FWA compensation can't be used for emissive reference type"); - fwacomp = 0; - tillum = icxIT_none; + if (isspect) { + if ((dti = icg->find_kword (icg, 0, "MEAS_TYPE")) < 0) + error ("Input file doesn't contain keyword MEAS_TYPE"); + + /* Reflective options when not a reflective profile type */ + if (strcmp(icg->t[0].kdata[dti],"EMISSION") == 0 + || strcmp(icg->t[0].kdata[dti],"AMBIENT") == 0 + || strcmp(icg->t[0].kdata[dti],"EMISSION_FLASH") == 0 + || strcmp(icg->t[0].kdata[dti],"AMBIENT_FLASH") == 0) { + isemis = 1; + if (illum != icxIT_none) + error("-i illuminant can't be used for emissive reference type"); + if (fwacomp) + error("-f FWA compensation can't be used for emissive reference type"); + fwacomp = 0; + tillum = icxIT_none; + } + + } else { + if ((dti = icg->find_kword (icg, 0, "DEVICE_CLASS")) < 0) + error ("Input file doesn't contain keyword DEVICE_CLASS"); + + /* Reflective options when not a reflective profile type */ + if (strcmp(icg->t[0].kdata[dti],"DISPLAY") == 0 + || strcmp(icg->t[0].kdata[dti],"EMISINPUT") == 0) { + isemis = 1; + if (illum != icxIT_none) + error("-i illuminant can't be used for emissive reference type"); + if (fwacomp) + error("-f FWA compensation can't be used for emissive reference type"); + fwacomp = 0; + tillum = icxIT_none; + } } /* Set defaults */ @@ -392,26 +457,30 @@ main(int argc, char *argv[]) if (observ == icxOT_none) observ = icxOT_CIE_1931_2; - /* Figure out what sort of device it is */ + /* See if the display CIE data has been normalised to Y = 100 */ { int ti; - char *tos; - - if (strcmp (icg->t[0].kdata[dti], "DISPLAY") == 0) { - isdisp = 1; - } - /* See if the display CIE data has been normalised to Y = 100 */ if ((ti = icg->find_kword(icg, 0, "NORMALIZED_TO_Y_100")) < 0 || strcmp(icg->t[0].kdata[ti],"NO") == 0) { isdnormed = 0; } else { isdnormed = 1; } + } + + /* Figure out what sort of device it is */ + if (icg->find_kword(icg, 0, "COLOR_REP") >= 0) { + int ti; + char *tos; if ((ti = icg->find_kword(icg, 0, "COLOR_REP")) < 0) error("Input file doesn't contain keyword COLOR_REP"); + if (strcmp (icg->t[0].kdata[dti], "DISPLAY") == 0) { + isdisp = 1; + } + if ((tos = strchr(icg->t[0].kdata[ti], '_')) == NULL) tos = icg->t[0].kdata[ti]; @@ -513,7 +582,7 @@ main(int argc, char *argv[]) /* Read in the CGATs fields */ { - int sidx; /* Sample ID index */ +// int sidx; /* Sample ID index */ int ti, ii; int Xi, Yi, Zi, Li, ai, bi; /* CGATS indexes for each field */ int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */ @@ -528,10 +597,12 @@ main(int argc, char *argv[]) xspect mwsp; /* FWA compensated medium white spectrum */ double mwXYZ[3]; /* Media white XYZ */ +#ifdef NEVER if ((sidx = icg->find_field (icg, 0, "SAMPLE_ID")) < 0) error ("Input file doesn't contain field SAMPLE_ID"); if (icg->t[0].ftype[sidx] != nqcs_t) error ("Field SAMPLE_ID is wrong type"); +#endif /* Using spectral data */ @@ -628,14 +699,28 @@ main(int argc, char *argv[]) error("Out of memory"); } + /* If CIE calculation illuminant is not standard, compute it's white point */ + if (illum != icxIT_D50) { + ill_wp = _ill_wp; + + /* Compute XYZ of illuminant */ + if (icx_ill_sp2XYZ(ill_wp, observ, cust_observ, illum, 0.0, &cust_illum) != 0) + error("icx_ill_sp2XYZ returned error"); + } + /* Create a spectral conversion object */ - if ((sp2cie = new_xsp2cie (illum, - illum == icxIT_none ? NULL : &cust_illum, - observ, cust_observ, icSigXYZData, icxClamp)) == NULL) + if ((sp2cie = new_xsp2cie(illum, &cust_illum, observ, cust_observ, + icSigXYZData, icxClamp)) == NULL) { error ("Creation of spectral conversion object failed"); } + if (fwacomp && devspace == icmSigDefaultData) { + // In theory could fake white spectra by accumulating max of + // all values as an alternative. + error ("No device values, so can't locate white patch for FWA compensation"); + } + if (fwacomp) { double nw = 0.0; /* Number of media white patches */ @@ -741,6 +826,8 @@ main(int argc, char *argv[]) } } + /* Enable FWA, and use tillump as instrument illuminant if */ + /* it is set, else use observer illuminant set by new_xsp2cie(). */ /* (Note that sp and mwsp.norm is set to 100.0) */ if (sp2cie->set_fwa(sp2cie, &insp, tillump, &mwsp)) error ("Set FWA on sp2cie failed"); @@ -755,6 +842,15 @@ main(int argc, char *argv[]) sp2cie->sconvert (sp2cie, &rmwsp, mwXYZ, &mwsp); } + /* If CIE conversion illuminant is non-standard, add it to the output */ + if (ill_wp != NULL) { + char buf[100]; + + sprintf(buf,"%f %f %f", ill_wp[0], ill_wp[1], ill_wp[2]); + ocg->add_kword(ocg, 0, "ILLUMINANT_WHITE_POINT_XYZ",buf, NULL); + } + + /* Transform patches from spectral to CIE */ for (i = 0; i < npat; i++) { xspect corr_sp; diff --git a/spectro/specbos.c b/spectro/specbos.c index 491fe97..4c5e4ba 100644 --- a/spectro/specbos.c +++ b/spectro/specbos.c @@ -2,7 +2,7 @@ /* * Argyll Color Correction System * - * JETI specbos 1211/1201 related functions + * JETI specbos & spectraval related functions * * Author: Graeme W. Gill * Date: 13/3/2013 @@ -38,9 +38,15 @@ TTBD: - Should add a reflective and transmissive modes, + Should add a reflective mode, by doing a white calibration and dividing the measurement. + Should check transmissive white spectral quality + + Should save transmissive white cal. into file, and restore it on startup. + + Should time transmissive white cal out. + */ #include <stdio.h> @@ -78,6 +84,14 @@ static int icoms2specbos_err(int se) { return SPECBOS_OK; } +/* Type of reply terminator expected */ +typedef enum { + tnorm = 0, /* Normal terminators */ + tmeas = 1, /* Measurement command */ + trefr = 2, /* Refresh measurement */ + tspec = 3 /* Spectraval spectral read */ +} spterm; + /* Do a full command/response echange with the specbos */ /* (This level is not multi-thread safe) */ /* Return the specbos error code. */ @@ -89,26 +103,28 @@ char *out, /* Out string buffer */ int bsize, /* Out buffer size */ double to, /* Timeout in seconds */ int ntc, /* Number or termination chars */ -int ctype, /* 0 = normal, 1 = *init, 2 = refr reading */ +spterm ctype, /* Exected reply terminator type */ int nd /* nz to disable debug messages */ ) { int se; int bread = 0; char *cp, *tc = "", *dp; - if (ctype == 0) + if (ctype == tnorm) tc = "\r\006\025"; /* Return, Ack or Nak */ - else if (ctype == 1) + else if (ctype == tmeas) tc = "\007\025"; /* Bell or Nak */ - else if (ctype == 2) + else if (ctype == trefr) tc = "\r\025"; /* Return or Nak */ + else if (ctype == tspec) + tc = "\003\025"; /* Atx or Nak */ - se = p->icom->write_read(p->icom, in, 0, out, bsize, &bread, tc, ntc, to); + se = p->icom->write_read_ex(p->icom, in, 0, out, bsize, &bread, tc, ntc, to, 1); /* Because we are sometimes waiting for 3 x \r characters to terminate the read, */ - /* we will instead time out on getting a single NAK (\025), so convert timout */ - /* with bytes to non-timeout, so that we can process the error. */ - if (se == ICOM_TO && bread > 0) + /* we will instead time out on getting a single NAK (\025), so ignore timout */ + /* if we got a NAK. */ + if (se == ICOM_TO && bread > 0 && out[0] == '\025') se = ICOM_OK; if (se != 0) { @@ -116,6 +132,14 @@ int nd /* nz to disable debug messages */ return icoms2specbos_err(se); } + /* Over Bluetooth, we get an erronious string "AT+JSCR\r\n" mixed in our output. */ + /* This would appear to be from the eBMU Bluetooth adapter AT command set. */ + if (bread > 9 && strncmp(out, "AT+JSCR\r\n", 9) == 0) { + a1logd(p->log, 8, "specbos: ignored 'AT+JSCR\\r\\n' response\n"); + memmove(out, out+9, bsize-9); + bread -= 9; + } + /* See if there was an error, and remove any enquire codes */ for (dp = cp = out; *cp != '\000' && (dp - out) < bsize; cp++) { if (*cp == '\025') { /* Got a NAK */ @@ -125,9 +149,16 @@ int nd /* nz to disable debug messages */ if (!nd) a1logd(p->log, 1, "specbos_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in)); return icoms2specbos_err(se);; } - if (sscanf(buf, "Error Code: %d ",&se) != 1) { - if (!nd) a1logd(p->log, 1, "specbos_fcommand: failed to parse error code '%s'\n",icoms_fix(buf)); - return SPECBOS_DATA_PARSE_ERROR; + if (p->model == 1501 || p->model == 1511) { + if (sscanf(buf, "%d ",&se) != 1) { + if (!nd) a1logd(p->log, 1, "specbos_fcommand: failed to parse error code '%s'\n",icoms_fix(buf)); + return SPECBOS_DATA_PARSE_ERROR; + } + } else { + if (sscanf(buf, "Error Code: %d ",&se) != 1) { + if (!nd) a1logd(p->log, 1, "specbos_fcommand: failed to parse error code '%s'\n",icoms_fix(buf)); + return SPECBOS_DATA_PARSE_ERROR; + } } if (!nd) a1logd(p->log, 1, "Got specbos error code %d\n",se); @@ -155,7 +186,7 @@ char *in, /* In string */ char *out, /* Out string buffer */ int bsize, /* Out buffer size */ double to) { /* Timout in seconds */ - int rv = specbos_fcommand(p, in, out, bsize, to, 1, 0, 0); + int rv = specbos_fcommand(p, in, out, bsize, to, 1, tnorm, 0); return specbos_interp_code((inst *)p, rv); } @@ -186,8 +217,10 @@ specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { specbos *p = (specbos *) pp; char buf[MAX_MES_SIZE]; baud_rate brt[] = { baud_921600, baud_115200, baud_38400, baud_nc }; +// spectraval 38400, 115200, 230400, 921600, 3000000 + unsigned int etime; - unsigned int i; + unsigned int len, i; instType itype = pp->itype; int se; @@ -195,64 +228,104 @@ specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { a1logd(p->log, 2, "specbos_init_coms: About to init Serial I/O\n"); - amutex_lock(p->lock); - if (p->icom->port_type(p->icom) != icomt_serial - && p->icom->port_type(p->icom) != icomt_usbserial) { - amutex_unlock(p->lock); - a1logd(p->log, 1, "specbos_init_coms: wrong communications type for device!\n"); + if (!(p->icom->dctype & icomt_serial) + && !(p->icom->dctype & icomt_fastserial)) { + a1logd(p->log, 1, "specbos_init_coms: wrong communications type for device! (dctype 0x%x)\n",p->icom->dctype); return inst_coms_fail; } - /* The tick to give up on */ - etime = msec_time() + (long)(1500.0 + 0.5); + /* Communications has already been established over BlueTooth serial */ + if (p->bt) { - a1logd(p->log, 1, "specbos_init_coms: Trying different baud rates (%u msec to go)\n",etime - msec_time()); + amutex_lock(p->lock); - /* Until we time out, find the correct baud rate */ - for (i = 0; msec_time() < etime; i++) { - if (brt[i] == baud_nc) { - i = 0; - } - a1logd(p->log, 5, "specbos_init_coms: trying baud ix %d\n",brt[i]); - if ((se = p->icom->set_ser_port(p->icom, fc_none, brt[i], parity_none, - stop_1, length_8)) != ICOM_OK) { + /* Let instrument get its act together */ + msec_sleep(600); + + /* Get the instrument identification */ + if ((ev = specbos_command(p, "*idn?\r", buf, MAX_MES_SIZE, 0.5)) != inst_ok) { amutex_unlock(p->lock); - a1logd(p->log, 5, "specbos_init_coms: set_ser_port failed with 0x%x\n",se); - return specbos_interp_code((inst *)p, icoms2specbos_err(se));; /* Give up */ + a1logd(p->log, 2, "specbos_init_coms: idn didn't return\n"); + return ev; } + /* We need to setup communications */ + } else { + int delayms = 0; + amutex_lock(p->lock); - /* Check instrument is responding */ - if (((ev = specbos_command(p, "*idn?\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) - != inst_coms_fail) { - break; /* We've got coms or user abort */ - } + /* The tick to give up on */ + etime = msec_time() + (long)(1500.0 + 0.5); - /* Check for user abort */ - if (p->uicallback != NULL) { - inst_code ev; - if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { + a1logd(p->log, 1, "specbos_init_coms: Trying different baud rates (%u msec to go)\n",etime - msec_time()); + + /* Spectraval Bluetooth serial doesn't seem to actuall function */ + /* until 600msec after it is opened. We get arroneos "AT+JSCR\r\n" reply */ + /* within that time, and it won't re-open after closing. */ + if (p->icom->dctype & icomt_btserial) + delayms = 600; + + /* Until we time out, find the correct baud rate */ + for (i = 0; msec_time() < etime; i++) { + if (brt[i] == baud_nc) { + i = 0; + } + + /* Only connect at 115200 if bluetooth */ + if ((p->icom->dctype & icomt_btserial) != 0 && brt[i] != baud_115200) + continue; + + a1logd(p->log, 5, "specbos_init_coms: Trying %s baud, %d msec to go\n", + baud_rate_to_str(brt[i]), etime- msec_time()); + if ((se = p->icom->set_ser_port_ex(p->icom, fc_None, brt[i], parity_none, + stop_1, length_8, delayms)) != ICOM_OK) { amutex_unlock(p->lock); - a1logd(p->log, 1, "specbos_init_coms: user aborted\n"); - return inst_user_abort; + a1logd(p->log, 5, "specbos_init_coms: set_ser_port failed with 0x%x\n",se); + return specbos_interp_code((inst *)p, icoms2specbos_err(se));; /* Give up */ + } + +// if ((p->icom->sattr & icom_bt) != 0) +// specbos_command(p, "\r", buf, MAX_MES_SIZE, 0.5); + + /* Check instrument is responding */ + if (((ev = specbos_command(p, "*idn?\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) + != inst_coms_fail) { + goto got_coms; /* We've got coms or user abort */ + } + + /* Check for user abort */ + if (p->uicallback != NULL) { + inst_code ev; + if ((ev = p->uicallback(p->uic_cntx, inst_negcoms)) == inst_user_abort) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_init_coms: user aborted\n"); + return inst_user_abort; + } } } - } - if (msec_time() >= etime) { /* We haven't established comms */ + /* We haven't established comms */ amutex_unlock(p->lock); a1logd(p->log, 2, "specbos_init_coms: failed to establish coms\n"); return inst_coms_fail; + + got_coms:; } /* Check the response */ + len = strlen(buf); p->model = 0; - if (strncmp (buf, "SB05", 4) == 0) /* Special case */ + if (len >= 4 && strncmp(buf, "SB05", 4) == 0) { p->model = 1201; - else { - if (strlen(buf) < 11 + } else if ((len >= 10 && strncmp(buf, "JETI_SDCM3", 10) == 0) + || (len >= 9 && strncmp(buf, "DCM3_JETI", 9) == 0) + || (len >= 17 && strncmp(buf, "PECFIRM_JETI_1501", 18) == 0) + || (len >= 18 && strncmp(buf, "SPECFIRM_JETI_1501", 17) == 0)) { + p->model = 1501; + } else { + if (len < 11 || sscanf(buf, "JETI_SB%d\r", &p->model) != 1) { amutex_unlock(p->lock); a1logd(p->log, 2, "specbos_init_coms: unrecognised ident string '%s'\n",icoms_fix(buf)); @@ -260,7 +333,9 @@ specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } } - if (p->model != 1201 && p->model != 1211) { + if (p->model != 1201 + && p->model != 1211 + && p->model != 1501) { amutex_unlock(p->lock); a1logd(p->log, 2, "specbos_init_coms: unrecognised model %04d\n",p->model); return inst_unknown_model; @@ -269,6 +344,40 @@ specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { p->gotcoms = 1; + /* See if it's a 1501 or 1511 */ + if (p->model == 1501) { + int dispen = 0; + + if ((ev = specbos_command(p, "*conf:dispen?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 2, "specbos_init_coms: failed to get display status\n"); + return inst_protocol_error; + } + if (sscanf(buf, "%d ",&dispen) != 1) { + amutex_unlock(p->lock); + a1loge(p->log, 1, "specbos_init_inst: failed to parse display status\n"); + return ev; + } + a1logd(p->log, 1, " spectraval %s display\n",dispen ? "has" : "doesn't have"); + if (dispen) { + p->model = 1511; + + /* Set remote mode */ + if ((ev = specbos_command(p, "*REMOTE 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 2, "specbos_init_coms: failed to set remote mode\n"); + return inst_protocol_error; + } + } + + /* Check Bluetooth status */ + if ((ev = specbos_command(p, "*conf:bten?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 2, "specbos_init_coms: failed to get Bluetooth status\n"); + return inst_protocol_error; + } + } + #ifdef NEVER /* Test a change in baud rate on next plug in */ a1logd(p->log, 2, "specbos_init_coms: changing baudrate\n"); // if ((ev = specbos_command(p, "*para:baud 384\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) @@ -285,12 +394,11 @@ specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { #endif amutex_unlock(p->lock); - return inst_ok; } /* - Notes on commands + Notes on commands for 1201: *conf is temporary, *para can be saved to instrument Since we set the instrument up every time, use *conf ? @@ -378,7 +486,8 @@ int specbos_diff_thread(void *pp) { int pos; amutex_lock(p->lock); - rv1 = specbos_get_diffpos(p, &pos, 1); + if (p->model != 1501 && p->model != 1511) + rv1 = specbos_get_diffpos(p, &pos, 1); rv2 = specbos_get_target_laser(p, &p->laser, 1); amutex_unlock(p->lock); @@ -419,9 +528,11 @@ specbos_init_inst(inst *pp) { amutex_lock(p->lock); - /* Restore the instrument to it's default settings */ - if ((ev = specbos_command(p, "*conf:default\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) - return ev; + if (p->model != 1501 && p->model != 1511) { + /* Restore the instrument to it's default settings */ + if ((ev = specbos_command(p, "*conf:default\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) + return ev; + } /* Set calibration type to auto on ambient cap */ if ((ev = specbos_command(p, "*para:calibn 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { @@ -429,78 +540,177 @@ specbos_init_inst(inst *pp) { return ev; } - /* Set auto exposure/integration time */ - /* Set calibration type to auto on ambient cap */ - if ((ev = specbos_command(p, "*para:expo 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok - || (ev = specbos_command(p, "*para:adapt 2\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; + if (p->model == 1501 || p->model == 1511) { + /* Set auto exposure/integration time */ + if ((ev = specbos_command(p, "*para:tint 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + } else { + /* Set auto exposure/integration time */ + if ((ev = specbos_command(p, "*para:expo 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok +// || (ev = specbos_command(p, "*para:adapt 2\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok + ) { + amutex_unlock(p->lock); + return ev; + } } - p->measto = 20.0; /* default */ + p->measto = 20.0; /* Set default. Specbos default is 60.0 */ if (p->model == 1211) - p->measto = 5.0; /* Same overall time as i1d3 ?? */ + p->measto = 6.0; /* Same overall time as i1d3 ?? */ else if (p->model == 1201) - p->measto = 15.0; + p->measto = 16.0; + else if (p->model == 1501 || p->model == 1511) + p->measto = 6.0; - /* Set maximum integration time to speed up display measurement */ - sprintf(mes, "*conf:maxtin %d\r", (int)(p->measto * 1000.0+0.5)); - if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; - } + /* Implement max auto int. time, to speed up display measurement */ + if (p->model == 1501 || p->model == 1511) { + int maxaver = 2; /* Maximum averages for auto int time */ + double dmaxtint; + int maxtint; - /* Set the measurement function to be Radiometric spectrum */ - if ((ev = specbos_command(p, "*conf:func 6\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; - } + /* Actual time taken depends on maxtint, autoavc & fudge factor. */ + dmaxtint = p->measto/(maxaver + 3.5); - /* Set the measurement format to ASCII */ - if ((ev = specbos_command(p, "*conf:form 4\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; - } + maxtint = (int)(dmaxtint * 1000.0+0.5); - if ((ev = specbos_command(p, "*para:wavbeg?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; - } - if (sscanf(buf, "Predefined start wave: %lf ",&p->wl_short) != 1) { - amutex_unlock(p->lock); - a1loge(p->log, 1, "specbos_init_inst: failed to parse start wave\n"); - return ev; - } - a1logd(p->log, 1, " Short wl range %f\n",p->wl_short); + if (maxtint < 1000 || maxtint > 64999) + error("specbos: assert, maxtint %d out of range",maxtint); - if ((ev = specbos_command(p, "*para:wavend?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; - } - if (sscanf(buf, "Predefined end wave: %lf ",&p->wl_long) != 1) { - amutex_unlock(p->lock); - a1loge(p->log, 1, "specbos_init_inst: failed to parse end wave\n"); - return ev; - } - if (p->wl_long > 830.0) /* Could go to 1000 with 1211 */ - p->wl_long = 830.0; /* Limit it to useful visual range */ + /* Set maximum integration time */ + sprintf(mes, "*para:maxtint %d\r", maxtint); + if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } - a1logd(p->log, 1, " Long wl range %f\n",p->wl_long); + /* Set maximum number of auto averages. Min value is 2 */ + sprintf(mes, "*para:maxaver %d\r", maxaver); + if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } - p->nbands = (int)((p->wl_long - p->wl_short + 1.0)/1.0 + 0.5); + /* spectraval doesn't support *fetch:XYZ command */ + p->noXYZ = 1; - /* Set the wavelength range and resolution */ - sprintf(mes, "*conf:wran %d %d 1\r", (int)(p->wl_short+0.5), (int)(p->wl_long+0.5)); - if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; + } else { + double dmaxtin; + int maxtin; + + dmaxtin = p->measto/2.5; /* Fudge factor */ + maxtin = (int)(dmaxtin * 1000.0+0.5); + + if (maxtin < 1000 || maxtin > 64999) + error("specbos: assert, maxtin %d out of range",maxtin); + + /* Set maximum integration time */ + sprintf(mes, "*para:maxtint %d\r", maxtin); + if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } } - /* Set to average just 1 reading */ - if ((ev = specbos_command(p, "*conf:aver 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; + if (p->model == 1501 || p->model == 1511) { + int wstart, wend, wstep; + + /* Set the measurement format to None. We will read measurement manually. */ + /* (0 = None, 1 = Binary, 2 = ASCII) */ + if ((ev = specbos_command(p, "*para:form 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + if ((ev = specbos_command(p, "*para:wran?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + if (sscanf(buf, "%d %d %d",&wstart,&wend,&wstep) != 3) { + amutex_unlock(p->lock); + a1loge(p->log, 1, "specbos_init_inst: failed to parse wavelength range\n"); + return ev; + } + + p->wl_short = (double)wstart; + p->wl_long = (double)wend; + + a1logd(p->log, 1, " Short wl range %f\n",p->wl_short); + + if (p->wl_long > 830.0) + p->wl_long = 830.0; /* Limit it to useful visual range */ + + a1logd(p->log, 1, " Long wl range %f\n",p->wl_long); + + p->nbands = (int)((p->wl_long - p->wl_short + 1.0)/1.0 + 0.5); + + /* Set the wavelength range and resolution */ + sprintf(mes, "*para:wran %d %d 1\r", (int)(p->wl_short+0.5), (int)(p->wl_long+0.5)); + if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + /* Set to average just 1 reading */ + if ((ev = specbos_command(p, "*para:aver 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + } else { + + /* Set the measurement function to be Radiometric spectrum */ + if ((ev = specbos_command(p, "*conf:func 6\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + /* Set the measurement format to ASCII */ + if ((ev = specbos_command(p, "*conf:form 4\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + if ((ev = specbos_command(p, "*para:wavbeg?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + if (sscanf(buf, "Predefined start wave: %lf ",&p->wl_short) != 1) { + amutex_unlock(p->lock); + a1loge(p->log, 1, "specbos_init_inst: failed to parse start wave\n"); + return ev; + } + a1logd(p->log, 1, " Short wl range %f\n",p->wl_short); + + if ((ev = specbos_command(p, "*para:wavend?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + if (sscanf(buf, "Predefined end wave: %lf ",&p->wl_long) != 1) { + amutex_unlock(p->lock); + a1loge(p->log, 1, "specbos_init_inst: failed to parse end wave\n"); + return ev; + } + if (p->wl_long > 830.0) /* Could go to 1000 with 1211 */ + p->wl_long = 830.0; /* Limit it to useful visual range */ + + a1logd(p->log, 1, " Long wl range %f\n",p->wl_long); + + p->nbands = (int)((p->wl_long - p->wl_short + 1.0)/1.0 + 0.5); + + /* Set the wavelength range and resolution */ + sprintf(mes, "*conf:wran %d %d 1\r", (int)(p->wl_short+0.5), (int)(p->wl_long+0.5)); + if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + /* Set to average just 1 reading */ + if ((ev = specbos_command(p, "*conf:aver 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } } if (p->log->verb) { @@ -558,6 +768,7 @@ static inst_code specbos_imp_measure_set_refresh(specbos *p); static inst_code specbos_imp_set_refresh(specbos *p); /* Get the ambient diffuser position */ +/* (Not valid for 1501 or 1511) */ /* (This is not multithread safe) */ static inst_code specbos_get_diffpos( @@ -569,7 +780,7 @@ specbos_get_diffpos( int ec; /* See if we're in emissive or ambient mode */ - if ((ec = specbos_fcommand(p, "*contr:mhead?\r", buf, MAX_MES_SIZE, 1.0, 1, 0, nd)) != inst_ok) { + if ((ec = specbos_fcommand(p, "*contr:mhead?\r", buf, MAX_MES_SIZE, 1.0, 1, tnorm, nd)) != inst_ok) { return specbos_interp_code((inst *)p, ec); } if (sscanf(buf, "mhead: %d ",pos) != 1) { @@ -591,17 +802,61 @@ specbos_get_target_laser( int ec; int lstate; - if ((ec = specbos_fcommand(p, "*contr:laser?\r", buf, MAX_MES_SIZE, 1.0, 1, 0, nd)) != inst_ok) { + if ((ec = specbos_fcommand(p, "*contr:laser?\r", buf, MAX_MES_SIZE, 1.0, 1, tnorm, nd)) != inst_ok) { return specbos_interp_code((inst *)p, ec); } - if (sscanf(buf, "laser: %d ",&lstate) != 1) { - a1loge(p->log, 2, "specbos_get_target_laser: failed to parse laser state\n"); - return inst_protocol_error; + if (p->model == 1501 || p->model == 1511) { + if (sscanf(buf, "%d ",&lstate) != 1) { + a1loge(p->log, 2, "specbos_get_target_laser: failed to parse laser state\n"); + return inst_protocol_error; + } + } else { + if (sscanf(buf, "laser: %d ",&lstate) != 1) { + a1loge(p->log, 2, "specbos_get_target_laser: failed to parse laser state\n"); + return inst_protocol_error; + } } *laser = lstate; return inst_ok; } +/* Configure for averaging */ +static inst_code set_average(specbos *p, int nav, int lock) { + char mes[100]; + char buf[MAX_MES_SIZE]; + inst_code ev; + + if (lock) amutex_lock(p->lock); + + if (p->model == 1501 || p->model == 1511) { + /* Set number to average */ + sprintf(mes, "*para:aver %d\r", nav); + if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + if (lock) amutex_unlock(p->lock); + return ev; + } + } else { + /* Set number to average */ + sprintf(mes, "*conf:aver %d\r", nav); + if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + if (lock) amutex_unlock(p->lock); + return ev; + } + } + +#ifdef NEVER /* Doesn't seem to make any difference ? */ + /* Only dark average at start of batch */ + sprintf(mes, "*conf:darkm %d\r", nav == 1 ? 0 : 1); + if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + if (lock) amutex_unlock(p->lock); + return ev; + } +#endif + + if (lock) amutex_unlock(p->lock); + return inst_ok; +} + /* Read a single sample */ /* Return the dtp error code */ static inst_code @@ -622,9 +877,15 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ if (!p->inited) return inst_no_init; + /* Request calibration if it is needed */ + if ((p->mode & inst_mode_illum_mask) == inst_mode_transmission + && p->trans_white.spec_n == 0) { + return inst_needs_cal; + } + amutex_lock(p->lock); - if (p->trig == inst_opt_trig_user) { + if (!p->doing_cal && p->trig == inst_opt_trig_user) { amutex_unlock(p->lock); if (p->uicallback == NULL) { @@ -650,7 +911,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ amutex_lock(p->lock); /* Progromatic Trigger */ - } else { + } else if (!p->doing_cal) { /* Check for abort */ if (p->uicallback != NULL && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) { @@ -659,21 +920,23 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } } - /* See if we're in emissive or ambient mode */ - if ((rv = specbos_get_diffpos(p, &pos, 0) ) != inst_ok) { - amutex_unlock(p->lock); - return rv; - } - - if (p->mode & inst_mode_ambient) { - if (pos != 1) { + if (p->model != 1501 && p->model != 1511) { + /* See if we're in emissive or ambient mode */ + if ((rv = specbos_get_diffpos(p, &pos, 0) ) != inst_ok) { amutex_unlock(p->lock); - return specbos_interp_code((inst *)p, SPECBOS_SPOS_AMB); + return rv; } - } else { - if (pos != 0) { - amutex_unlock(p->lock); - return specbos_interp_code((inst *)p, SPECBOS_SPOS_EMIS); + + if (p->mode & inst_mode_ambient) { + if (pos != 1) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, SPECBOS_SPOS_AMB); + } + } else { + if (pos != 0) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, SPECBOS_SPOS_EMIS); + } } } @@ -693,9 +956,26 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } } + /* Set to average 10 readings for transmission */ + if ((p->mode & inst_mode_illum_mask) == inst_mode_transmission) { + if ((rv = set_average(p, 10, 0)) != inst_ok) + return rv; + } + + if (p->model == 1501 || p->model == 1511) { + /* Set the measurement format to None. We will read measurement manually. */ + if ((rv = specbos_command(p, "*para:form 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + } + /* Trigger a measurement */ /* (Note that ESC will abort it) */ - ec = specbos_fcommand(p, "*init\r", buf, MAX_MES_SIZE, 5.0 * p->measto + 10.0 , 1, 1, 0); + if (p->model == 1501 || p->model == 1511) + ec = specbos_fcommand(p, "*meas:refer\r", buf, MAX_MES_SIZE, p->measto + 10.0 , 1, tmeas, 0); + else + ec = specbos_fcommand(p, "*init\r", buf, MAX_MES_SIZE, p->measto + 10.0 , 1, tmeas, 0); // Test out bug workaround // if (!p->badCal) ec = SPECBOS_EXCEED_CAL_WL; @@ -719,15 +999,24 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ /* Re-set the wavelength range and resolution */ sprintf(mes, "*conf:wran %d %d 1\r", (int)(p->wl_short+0.5), (int)(p->wl_long+0.5)); if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + if ((p->mode & inst_mode_illum_mask) == inst_mode_transmission) + set_average(p, 1, 0); amutex_unlock(p->lock); return ev; } p->badCal = 1; /* Try command again */ - ec = specbos_fcommand(p, "*init\r", buf, MAX_MES_SIZE, 5.0 * p->measto + 10.0 , 1, 1, 0); + if (p->model == 1501 || p->model == 1511) + ec = specbos_fcommand(p, "*meas:refer\r", buf, MAX_MES_SIZE, p->measto + 10.0 , 1, tmeas, 0); + else + ec = specbos_fcommand(p, "*init\r", buf, MAX_MES_SIZE, p->measto + 10.0 , 1, tmeas, 0); } + /* Restore single reading if transmission */ + if ((p->mode & inst_mode_illum_mask) == inst_mode_transmission) + set_average(p, 1, 0); + if (ec != SPECBOS_OK) { amutex_unlock(p->lock); return specbos_interp_code((inst *)p, ec); @@ -738,7 +1027,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ ec = SPECBOS_COMMAND; } else { /* Read the XYZ */ - ec = specbos_fcommand(p, "*fetch:XYZ\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0); + ec = specbos_fcommand(p, "*fetch:XYZ\r", buf, MAX_RD_SIZE, 0.5, 3, tnorm, 0); } @@ -758,26 +1047,52 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ p->noXYZ = 1; - if ((ec = specbos_fcommand(p, "*fetch:PHOTOmetric\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0)) - != SPECBOS_OK) { - amutex_unlock(p->lock); - return specbos_interp_code((inst *)p, ec); - } - if (sscanf(buf, "Luminance[cd/m^2]: %lf ", &Yxy[0]) != 1) { - amutex_unlock(p->lock); - a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); - return inst_protocol_error; + if (p->model == 1501 || p->model == 1511) { + if ((ec = specbos_fcommand(p, "*calc:PHOTOmetric\r", buf, MAX_RD_SIZE, 0.5, 1, tnorm, 0)) + != SPECBOS_OK) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, ec); + } + if (sscanf(buf, "%lf ", &Yxy[0]) != 1) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); + return inst_protocol_error; + } + } else { + if ((ec = specbos_fcommand(p, "*fetch:PHOTOmetric\r", buf, MAX_RD_SIZE, 0.5, 1, tnorm, 0)) + != SPECBOS_OK) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, ec); + } + if (sscanf(buf, "Luminance[cd/m^2]: %lf ", &Yxy[0]) != 1) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); + return inst_protocol_error; + } } - if ((ec = specbos_fcommand(p, "*fetch:CHROMXY\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0)) - != SPECBOS_OK) { - amutex_unlock(p->lock); - return specbos_interp_code((inst *)p, ec); - } - if (sscanf(buf, "Chrom_x: %lf Chrom_y: %lf ", &Yxy[1], &Yxy[2]) != 2) { - amutex_unlock(p->lock); - a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); - return inst_protocol_error; + if (p->model == 1501 || p->model == 1511) { + if ((ec = specbos_fcommand(p, "*calc:CHROMXY\r", buf, MAX_RD_SIZE, 0.5, 1, tnorm, 0)) + != SPECBOS_OK) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, ec); + } + if (sscanf(buf, " %lf %lf ", &Yxy[1], &Yxy[2]) != 2) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); + return inst_protocol_error; + } + } else { + if ((ec = specbos_fcommand(p, "*fetch:CHROMXY\r", buf, MAX_RD_SIZE, 0.5, 1, tnorm, 0)) + != SPECBOS_OK) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, ec); + } + if (sscanf(buf, "Chrom_x: %lf Chrom_y: %lf ", &Yxy[1], &Yxy[2]) != 2) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); + return inst_protocol_error; + } } icmYxy2XYZ(val->XYZ, Yxy); @@ -790,9 +1105,10 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ if (clamp) icmClamp3(val->XYZ, val->XYZ); val->loc[0] = '\000'; + /* Ambient or Trans 90/diffuse */ if (p->mode & inst_mode_ambient) { val->mtype = inst_mrt_ambient; - } else + } else /* Emis or Trans diffuse/90 */ val->mtype = inst_mrt_emission; val->XYZ_v = 1; /* These are absolute XYZ readings */ val->sp.spec_n = 0; @@ -800,32 +1116,45 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ rv = inst_ok; - /* spectrum data is returned only if requested */ - if (p->mode & inst_mode_spectral) { + /* spectrum data is returned only if requested, */ + /* or if we are emulating transmission mode */ + if (p->mode & inst_mode_spectral + || (p->mode & inst_mode_illum_mask) == inst_mode_transmission) { int tries, maxtries = 5; - int i, xsize; + int ii,i, xsize; char *cp, *ncp; - /* (Format 12 doesn't seem to work on the 1211) */ - /* (Format 9 reportedly doesn't work on the 1201) */ - /* The folling works on the 1211 and is reported to work on the 1201 */ + if (p->model == 1501 || p->model == 1511) { + /* Turn on spetrum output */ + if ((ec = specbos_command(p, "*para:form 2\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ec; + } + ii = 0; /* Spectrum from the start */ + } else { + ii = -2; /* Skip first two tokens */ + + /* (Format 12 doesn't seem to work on the 1211) */ + /* (Format 9 reportedly doesn't work on the 1201) */ + /* The folling works on the 1211 and is reported to work on the 1201 */ + } /* Because the specbos doesn't use flow control in its */ /* internal serial communications, it may overrun */ /* the FT232R buffer, so retry fetching the spectra if */ /* we get a comm error or parsing error. */ - for (tries = 0;;) { + for (tries = 0; tries < maxtries; tries++) { /* Fetch the spectral readings */ - ec = specbos_fcommand(p, "*fetch:sprad\r", buf, MAX_RD_SIZE, 4.0, 2+p->nbands+1, 0, 0); - tries++; + if (p->model == 1501 || p->model == 1511) + ec = specbos_fcommand(p, "*calc:sprad\r", buf, MAX_RD_SIZE, 4.0, + 1, tspec, 0); + else + ec = specbos_fcommand(p, "*fetch:sprad\r", buf, MAX_RD_SIZE, 4.0, + 2+p->nbands+1, tnorm, 0); if (ec != SPECBOS_OK) { - if (tries > maxtries) { - amutex_unlock(p->lock); - a1logd(p->log, 1, "specbos_fcommand: failed with 0x%x\n",ec); - return specbos_interp_code((inst *)p, ec); - } - continue; /* Retry the fetch */ + a1logd(p->log, 1, "specbos_fcommand: failed with 0x%x\n",ec); + goto try_again; /* Retry the fetch */ } val->sp.spec_n = p->nbands; @@ -835,19 +1164,27 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ /* Spectral data is in W/nm/m^2 */ val->sp.norm = 1.0; cp = buf; - for (i = -2; i < val->sp.spec_n; i++) { + for (i = ii; i < val->sp.spec_n; i++) { if ((ncp = strchr(cp, '\r')) == NULL) { - a1logd(p->log, 1, "specbos_read_sample: failed to parse spectra at %d/%d\n",i+1,val->sp.spec_n); - if (tries > maxtries) { - amutex_unlock(p->lock); - return inst_protocol_error; - } - continue; /* Retry the fetch and parse */ + a1logd(p->log, 1, "specbos_read_sample: failed to parse spectra '%s' at %d/%d\n",cp,i+1,val->sp.spec_n); + goto try_again; /* Retry the fetch and parse */ } *ncp = '\000'; if (i >= 0) { - a1logd(p->log, 6, "sample %d/%d got %f from '%s'\n",i+1,val->sp.spec_n,atof(cp),cp); - val->sp.spec[i] = 1000.0 * atof(cp); /* Convert to mW/m^2/nm */ + double wl, sp = -1e6; + if (p->model == 1501 || p->model == 1511) { + if (sscanf(cp, "%lf %lf", &wl, &sp) != 2) + sp = -1e6; + } else { + if (sscanf(cp, "%lf", &sp) != 1) + sp = -1e6; + } + if (sp == -1e6) { + a1logd(p->log, 1, "specbos_read_sample: failed to parse spectra '%s' at %d/%d\n",cp,i+1,val->sp.spec_n); + goto try_again; /* Retry the fetch and parse */ + } + a1logd(p->log, 6, "sample %d/%d got %f from '%s'\n",i+1,val->sp.spec_n,sp,cp); + val->sp.spec[i] = 1000.0 * sp; /* Convert to mW/m^2/nm */ if (p->mode & inst_mode_ambient) val->mtype = inst_mrt_ambient; } @@ -855,11 +1192,127 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } /* We've parsed correctly, so don't retry */ break; + + try_again:; + } + if (tries >= maxtries) { + a1logd(p->log, 1, "specbos_fcommand: ran out of retries\n"); + amutex_unlock(p->lock); + return inst_protocol_error; } a1logd(p->log, 1, "specbos_read_sample: got total %d samples/%d expected in %d tries\n",i,val->sp.spec_n, tries); + } amutex_unlock(p->lock); + /* Emulate transmission mode */ + if ((p->mode & inst_mode_illum_mask) == inst_mode_transmission) { + int i; + + if (p->trans_white.spec_n != val->sp.spec_n + || p->trans_white.spec_wl_short != val->sp.spec_wl_short + || p->trans_white.spec_wl_long != val->sp.spec_wl_long) { + a1logd(p->log, 1, "specbos_read_sample: Emulated transmission mode got mismatched white ref. !"); + return specbos_interp_code((inst *)p, SPECBOS_INTERNAL_ERROR); + } + + for (i = 0; i < p->trans_white.spec_n; i++) { + double vv; + + if ((p->trans_white.spec[i] * 3.0) < val->sp.spec[i]) { + val->sp.spec[i] = 0.0; + } else { + val->sp.spec[i] = 100.0 * val->sp.spec[i]/p->trans_white.spec[i]; + } + } + val->sp.norm = 100.0; + + /* Do some filtering of the short wavelengths. */ + /* (This is really compensating for lower instrument sensitivity */ + /* and an assumed 'A' type illuminant source.) */ + { + int i, ii, j, k; + double w, ifw, fw, nn; + double wl, we, vv; + xspect ts = val->sp; /* Structure copy */ + double refl; + + /* Do a linear regression to establish and end reflection value */ + { + double s = 0.0, sxx = 0.0, sy = 0.0, sx = 0.0, sxy = 0.0; + + ii = XSPECT_XIX(&val->sp, 400.0); /* End index */ + for (i = 0; i < ii; i++) { + s++; + sxx += i * i; + sx += i; + sy += ts.spec[i]; + sxy += i * ts.spec[i]; + } + refl = (sxx * sy - sx * sxy)/(s * sxx - sx * sx); +//printf("~1 [0] = %f, linear regression = %f\n",ts.spec[0],refl); + } + + ii = XSPECT_XIX(&val->sp, 500.0); /* End index */ + ifw = 5.0; /* Initial +/-width */ + for (i = 0; i < ii; i++) { + wl = XSPECT_XWL(&val->sp, i); + vv = (ii - i)/(double)ii; /* Reduce width as we increase center wl */ + fw = pow(vv, 1.7) * ifw; + w = nn = vv = 0.0; + + /* Scan down */ + for (j = 0; ; j++) { + w = wl - XSPECT_XWL(&val->sp, i - j); /* Offset we're at */ + if (w >= fw) + break; + + we = fw - w; + we = sqrt(we); + nn += we; + + if ((i - j) < 0) { /* Reflect */ + vv += we * (2.0 * refl - ts.spec[i + j]); + } else { + vv += we * ts.spec[i - j]; + } + } + + /* Scan up */ + for (j = 0; ; j++) { + w = XSPECT_XWL(&val->sp, i + j) - wl; /* Offset we're at */ + if (w >= fw) + break; + we = fw - w; + we = sqrt(we); + nn += we; + vv += we * ts.spec[i + j]; + } + vv /= nn; + val->sp.spec[i] = vv; + } + } + + /* Convert to XYZ */ + if (p->conv == NULL) { + p->conv = new_xsp2cie(icxIT_D50, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, + icxNoClamp); + if (p->conv == NULL) { + a1logd(p->log, 1, "specbos_read_sample: Emulated transmission new_xsp2cie() failed"); + return specbos_interp_code((inst *)p, SPECBOS_INTERNAL_ERROR); + } + } + p->conv->convert(p->conv, val->XYZ, &val->sp); + if (clamp) + icmClamp3(val->XYZ, val->XYZ); + val->XYZ_v = 1; + val->XYZ[0] *= 100.0; + val->XYZ[1] *= 100.0; + val->XYZ[2] *= 100.0; + + val->mtype = inst_mrt_transmissive; + } + if (user_trig) return inst_user_trig; @@ -879,17 +1332,36 @@ specbos_imp_set_refresh(specbos *p) { /* Set synchronised read if we should do so */ if (p->refrmode != 0 && p->refrvalid) { char mes[100]; - if ((rv = specbos_command(p, "*conf:cycmod 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - return rv; + if (p->model == 1501 || p->model == 1511) { + if ((rv = specbos_command(p, "*para:syncmod 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + } else { + if ((rv = specbos_command(p, "*conf:cycmod 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } } - sprintf(mes,"*conf:cyctim %f\r",p->refperiod * 1e6); - if ((rv = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - return rv; + if (p->model == 1501 || p->model == 1511) { + sprintf(mes,"*para:syncfreq %f\r",1.0/p->refperiod); + if ((rv = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + } else { + sprintf(mes,"*conf:cyctim %f\r",p->refperiod * 1e6); + if ((rv = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } } a1logd(p->log,5,"specbos_imp_set_refresh set refresh rate to %f Hz\n",1.0/p->refperiod); } else { - if ((rv = specbos_command(p, "*conf:cycmod 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - return rv; + if (p->model == 1501 || p->model == 1511) { + if ((rv = specbos_command(p, "*para:syncmod 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + } else { + if ((rv = specbos_command(p, "*conf:cycmod 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } } a1logd(p->log,5,"specbos_imp_set_refresh set non-refresh mode\n"); } @@ -920,16 +1392,32 @@ double *ref_rate return rv; } - if ((ec = specbos_fcommand(p, "*contr:cyctim 200 4000\r", buf, MAX_MES_SIZE, 5.0, 1, 2, 0)) != SPECBOS_OK) { - return specbos_interp_code((inst *)p, ec); + if (p->model == 1501 || p->model == 1511) { + if ((ec = specbos_fcommand(p, "*meas:flic\r", buf, MAX_MES_SIZE, 30.0, 1, trefr, 0)) != SPECBOS_OK) { + return specbos_interp_code((inst *)p, ec); + } + } else { + if ((ec = specbos_fcommand(p, "*contr:cyctim 200 4000\r", buf, MAX_MES_SIZE, 5.0, 1, trefr, 0)) != SPECBOS_OK) { + return specbos_interp_code((inst *)p, ec); + } } - if ((cp = strchr(buf, 'c')) == NULL) - cp = buf; - if (sscanf(cp, "cyctim[ms]: %lf ", &refperiod) != 1) { - a1logd(p->log, 1, "specbos_imp_measure_refresh rate: failed to parse string '%s'\n",icoms_fix(buf)); - *ref_rate = 0.0; - return inst_misread; + if (p->model == 1501 || p->model == 1511) { + double ffreq; + if (sscanf(buf+1, "%lf ", &ffreq) != 1) { + a1logd(p->log, 1, "specbos_imp_measure_refresh rate: failed to parse string '%s'\n",icoms_fix(buf)); + *ref_rate = 0.0; + return inst_misread; + } + refperiod = 1000.0/ffreq; + } else { + if ((cp = strchr(buf, 'c')) == NULL) + cp = buf; + if (sscanf(cp, "cyctim[ms]: %lf ", &refperiod) != 1) { + a1logd(p->log, 1, "specbos_imp_measure_refresh rate: failed to parse string '%s'\n",icoms_fix(buf)); + *ref_rate = 0.0; + return inst_misread; + } } if (refperiod == 0.0) @@ -1033,6 +1521,12 @@ static inst_code specbos_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal a_cals |= inst_calt_ref_freq; } + if ((p->mode & inst_mode_illum_mask) == inst_mode_transmission) { + if (p->trans_white.spec_n == 0) + n_cals |= inst_calt_trans_vwhite; + a_cals |= inst_calt_trans_vwhite; + } + if (pn_cals != NULL) *pn_cals = n_cals; @@ -1047,6 +1541,7 @@ inst_code specbos_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { specbos *p = (specbos *)pp; @@ -1058,11 +1553,14 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = specbos_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) return ev; + a1logd(p->log,4,"specbos_calibrate: needed 0x%x, avaialble 0x%x\n",needed, available); + /* Translate inst_calt_all/needed into something specific */ if (*calt == inst_calt_all || *calt == inst_calt_needed @@ -1082,6 +1580,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ /* See if it's a calibration we understand */ if (*calt & ~available & inst_calt_all_mask) { + a1logd(p->log,4,"specbos_calibrate: not a calibration we understand\n"); return inst_unsupported; } @@ -1100,6 +1599,65 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ *calt &= ~inst_calt_ref_freq; } + + /* If we are doing a transmission white reference calibrate */ + if ((*calt & inst_calt_trans_vwhite) != 0 + && (*calc & inst_calc_cond_mask) == inst_calc_man_trans_white + && (p->mode & inst_mode_illum_mask) == inst_mode_transmission) { + inst_mode cmode = p->mode; + int i; + ipatch val; + + a1logd(p->log,4,"specbos_calibrate: doing transmission white calib\n"); + + if (p->mode & inst_mode_ambient) + p->mode = inst_mode_emis_ambient | inst_mode_spectral; + else + p->mode = inst_mode_emis_tele | inst_mode_spectral; + p->doing_cal = 1; + + /* Set to average 50 readings */ + if ((ev = set_average(p, 50, 1)) != inst_ok) + return ev; + + if ((ev = specbos_read_sample(pp, "Transmission White", &val, 0)) != inst_ok) { + a1logd(p->log,1,"specbos_calibrate: Transmission white cal failed with 0x%x\n",ev); + p->mode = cmode; + p->doing_cal = 0; + set_average(p, 1, 1); + return ev; + } + p->trans_white = val.sp; /* Struct copy */ + + /* Restore average */ + if ((ev = set_average(p, 1, 1)) != inst_ok) + return ev; + + // ~~~~9999 check white quality + + *calt &= ~inst_calt_trans_vwhite; + + p->mode = cmode; + p->doing_cal = 0; + } + + /* Make sure there's the right condition for any remaining calibrations. */ + if (*calt & inst_calt_trans_vwhite) {/* Transmissvice white for emulated transmission */ + *idtype = inst_calc_id_none; + id[0] = '\000'; + if ((*calc & inst_calc_cond_mask) != inst_calc_man_trans_white) { + *calc = inst_calc_man_trans_white; + a1logd(p->log,4,"specbos_calibrate: needs calc 0x%x\n",*calc); + return inst_cal_setup; + } + } + + /* Go around again if we've still got calibrations to do */ + if (*calt & inst_calt_all_mask) { + a1logd(p->log,4,"specbos_calibrate: more calibrations to do\n"); + return inst_cal_setup; + } + return inst_ok; } @@ -1361,6 +1919,7 @@ specbos_interp_code(inst *pp, int ec) { case SPECBOS_OVEREXPOSED: case SPECBOS_UNDEREXPOSED: + case SPECBOS_ADAPT_INT_TIME: return inst_misread | ec; case SPECBOS_PASSWORD: @@ -1368,7 +1927,6 @@ specbos_interp_code(inst *pp, int ec) { case SPECBOS_USERFILE_CHSUM: case SPECBOS_USERFILE2_CHSUM: case SPECBOS_USERFILE2_ARG: - case SPECBOS_ADAPT_INT_TIME: case SPECBOS_NO_SHUTTER: case SPECBOS_NO_DARK_MEAS: case SPECBOS_NO_REF_MEAS: @@ -1434,6 +1992,8 @@ specbos_del(inst *pp) { if (p->icom != NULL) p->icom->del(p->icom); amutex_del(p->lock); + if (p->conv != NULL) + p->conv->del(p->conv); p->vdel(pp); free(p); } @@ -1449,13 +2009,18 @@ inst3_capability *pcap3) { inst2_capability cap2 = 0; cap1 |= inst_mode_emis_tele - | inst_mode_ambient + | inst_mode_trans_spot /* Emulated transmission mode diffuse/90 */ + | inst_mode_trans_spot_a /* Emulated transmission mode 90/diffuse */ | inst_mode_colorimeter | inst_mode_spectral | inst_mode_emis_refresh_ovd | inst_mode_emis_norefresh_ovd ; + if (p->model != 1501 && p->model != 1511) { + cap1 |= inst_mode_ambient; + } + /* can inst2_has_sensmode, but not report it asynchronously */ cap2 |= inst2_prog_trig | inst2_user_trig @@ -1500,9 +2065,13 @@ int *conf_ix || *conf_ix > 1) { /* Return current configuration measrement modes */ amutex_lock(p->lock); - if ((ev = specbos_get_diffpos(p, &pos, 0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; + if (p->model != 1501 && p->model != 1511) { + if ((ev = specbos_get_diffpos(p, &pos, 0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + } else { + pos = 0; /* 1501 & 1511 only have emssion */ } amutex_unlock(p->lock); } else { @@ -1534,6 +2103,7 @@ int *conf_ix /* Check device measurement mode */ static inst_code specbos_check_mode(inst *pp, inst_mode m) { + specbos *p = (specbos *)pp; inst_mode cap; if (!pp->gotcoms) @@ -1547,9 +2117,17 @@ static inst_code specbos_check_mode(inst *pp, inst_mode m) { if (m & ~cap) return inst_unsupported; - /* Only tele emission mode supported */ + /* 1501 doesn't support ambient */ + if ((p->model == 1501 || p->model == 1511) + && IMODETST(m, inst_mode_emis_ambient)) { + return inst_unsupported; + } + + /* Only tele emission, ambient and (emulated) transmision modes supported */ if (!IMODETST(m, inst_mode_emis_tele) - && !IMODETST(m, inst_mode_emis_ambient)) { + && !IMODETST(m, inst_mode_trans_spot) + && !IMODETST(m, inst_mode_trans_spot_a) + && !IMODETST(m, inst_mode_emis_ambient)) { return inst_unsupported; } @@ -1562,12 +2140,13 @@ static inst_code specbos_set_mode(inst *pp, inst_mode m) { int refrmode; inst_code ev; - if ((ev = specbos_check_mode(pp, m)) != inst_ok) + if ((ev = specbos_check_mode(pp, m)) != inst_ok) { + a1logd(p->log,1,"specbos_set_mode 0x%x invalid\n",m); return ev; + } p->mode = m; - if (p->model != 1201) { /* Can't set refresh mode on 1201 */ /* Effective refresh mode may change */ @@ -1714,8 +2293,7 @@ static inst_code specbos_set_disptype(inst *pp, int ix) { * error if it hasn't been initialised. */ static inst_code -specbos_get_set_opt(inst *pp, inst_opt_type m, ...) -{ +specbos_get_set_opt(inst *pp, inst_opt_type m, ...) { specbos *p = (specbos *)pp; char buf[MAX_MES_SIZE]; inst_code ev = inst_ok; @@ -1790,7 +2368,17 @@ specbos_get_set_opt(inst *pp, inst_opt_type m, ...) if (!p->inited) return inst_no_init; - return inst_unsupported; + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/specbos.h b/spectro/specbos.h index e926c42..5c89fd5 100644 --- a/spectro/specbos.h +++ b/spectro/specbos.h @@ -37,6 +37,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Fake Error codes */ #define SPECBOS_INTERNAL_ERROR 0xff01 /* Internal software error */ #define SPECBOS_TIMEOUT 0xff02 /* Communication timeout */ @@ -122,11 +126,15 @@ struct _specbos { INST_OBJ_BASE + int bt; /* Bluetooth coms rather than USB/serial flag */ + amutex lock; /* Command lock */ - int model; /* JETI specbos model number */ + int model; /* JETI specbos/spectraval model number */ /* 1201 */ /* 1211 */ + /* 1501 */ + /* 1511 - has display */ int noXYZ; /* nz if firmware doesn't support fetch*XYZ */ int badCal; /* nz if its been calibrated with a reduced WL range by 3rd party */ @@ -147,6 +155,10 @@ struct _specbos { double wl_short; double wl_long; + xspect trans_white; /* Synthetic transmission mode white reference */ + xsp2cie *conv; /* transmission spectral to XYZ conversion */ + int doing_cal; /* Flag - doing internal calibration measure */ + /* Other state */ athread *th; /* Diffuser position monitoring thread */ volatile int th_term; /* nz to terminate thread */ @@ -159,6 +171,9 @@ struct _specbos { /* Constructor */ extern specbos *new_specbos(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif #define SPECBOS_H #endif /* SPECBOS_H */ diff --git a/spectro/spotread.c b/spectro/spotread.c index baf1c76..c5ac955 100644 --- a/spectro/spotread.c +++ b/spectro/spotread.c @@ -64,7 +64,7 @@ #include "ccss.h" #include "ccmx.h" #include "instappsup.h" -#ifdef ENABLE_USB +#if !defined(NOT_ALLINSTS) || defined(EN_SPYD2) # include "spyd2.h" #endif @@ -285,10 +285,12 @@ usage(char *diag, ...) { for (i = 0; ; i++) { if (paths[i] == NULL) break; +#if !defined(NOT_ALLINSTS) || defined(EN_SPYD2) if ((paths[i]->itype == instSpyder1 && setup_spyd2(0) == 0) || (paths[i]->itype == instSpyder2 && setup_spyd2(1) == 0)) fprintf(stderr," %d = '%s' !! Disabled - no firmware !!\n",i+1,paths[i]->name); else +#endif fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name); } } else @@ -329,6 +331,7 @@ usage(char *diag, ...) { fprintf(stderr," 6 D65\n"); fprintf(stderr," u U.V. Cut\n"); fprintf(stderr," -E extrafilterfile Apply extra filter compensation file\n"); + fprintf(stderr," -A N|A|X|G XRGA conversion (default N)\n"); fprintf(stderr," -x Display Yxy instead of Lab\n"); fprintf(stderr," -h Display LCh instead of Lab\n"); fprintf(stderr," -V Show running average and std. devation from ref.\n"); @@ -344,10 +347,17 @@ usage(char *diag, ...) { if (cap2 & inst2_ccss) { fprintf(stderr," -X file.ccss Use Colorimeter Calibration Spectral Samples for calibration\n"); } +#ifndef SALONEINSTLIB + fprintf(stderr," -R fname.sp Preset reference to spectrum\n"); +#endif fprintf(stderr," -Y r|n Override refresh, non-refresh display mode\n"); fprintf(stderr," -Y R:rate Override measured refresh rate with rate Hz\n"); fprintf(stderr," -Y A Use non-adaptive integration time mode (if available).\n"); + fprintf(stderr," -Y l|L Test for i1Pro Lamp Drift (l), and remediate it (L)\n"); // fprintf(stderr," -Y U Test i1pro2 UV measurement mode\n"); +#ifndef SALONEINSTLIB + fprintf(stderr," -Y W:fname.sp Save white tile ref. spectrum to file\n"); +#endif /* !SALONEINSTLIB */ fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n"); fprintf(stderr," -D [level] Print debug diagnostics to stderr\n"); fprintf(stderr," logfile Optional file to save reading results as text\n"); @@ -373,7 +383,9 @@ int main(int argc, char *argv[]) { int tele = 0; /* 1 = Use telephoto emissive sub-mode. */ int ambient = 0; /* 1 = Use ambient emissive mode, 2 = ambient flash mode */ int highres = 0; /* Use high res mode if available */ + int lampdrift = 0; /* i1Pro Lamp Drift test (1) & fix (2) */ int uvmode = 0; /* ~~~ i1pro2 test mode ~~~ */ + xcalstd calstd = xcalstd_none; /* X-Rite calibration standard */ int refrmode = -1; /* -1 = default, 0 = non-refresh mode, 1 = refresh mode */ double refrate = 0.0; /* 0.0 = default, > 0.0 = override refresh rate */ int nadaptive = 0; /* Use non-apative mode if available */ @@ -387,6 +399,8 @@ int main(int argc, char *argv[]) { char outname[MAXNAMEL+1] = "\000"; /* Output logfile name */ char ccxxname[MAXNAMEL+1] = "\000"; /* Colorimeter Correction/Colorimeter Calibration name */ char filtername[MAXNAMEL+1] = "\000"; /* Filter compensation */ + char wtilename[MAXNAMEL+1] = "\000"; /* White file spectrum */ + char psetrefname[MAXNAMEL+1] = "\000"; /* Preset reference spectrum */ FILE *fp = NULL; /* Logfile */ icompaths *icmps = NULL; int comport = COMPORT; /* COM port used */ @@ -529,12 +543,21 @@ int main(int argc, char *argv[]) { tillum_set = spec = 1; tillum = icxIT_F10; } else { /* Assume it's a filename */ + inst_meas_type mt; + tillum_set = spec = 1; tillum = icxIT_custom; - if (read_xspect(&cust_tillum, na) != 0) + if (read_xspect(&cust_tillum, &mt, na) != 0) usage("Failed to read custom target illuminant spectrum in file '%s'",na); + + if (mt != inst_mrt_none + && mt != inst_mrt_emission + && mt != inst_mrt_ambient + && mt != inst_mrt_emission_flash + && mt != inst_mrt_ambient_flash) + error("Target illuminant '%s' is wrong measurement type",na); } -#endif /* SALONEINSTLIB */ +#endif /* !SALONEINSTLIB */ /* Spectral Illuminant type for XYZ computation */ } else if (argv[fa][1] == 'i') { @@ -566,10 +589,19 @@ int main(int argc, char *argv[]) { illum_set = spec = 1; illum = icxIT_F10; } else { /* Assume it's a filename */ + inst_meas_type mt; + illum_set = spec = 1; illum = icxIT_custom; - if (read_xspect(&cust_illum, na) != 0) + if (read_xspect(&cust_illum, &mt, na) != 0) usage("Unable to read custom illuminant file '%s'",na); + + if (mt != inst_mrt_none + && mt != inst_mrt_emission + && mt != inst_mrt_ambient + && mt != inst_mrt_emission_flash + && mt != inst_mrt_ambient_flash) + error("Custom illuminant '%s' is wrong measurement type",na); } #else /* SALONEINSTLIB */ } else @@ -600,7 +632,7 @@ int main(int argc, char *argv[]) { emiss = 0; trans = 1; tele = 0; - ambient = 0; + ambient = 0; /* Default normal diffuse/90 geometry trans. */ /* Request emissive measurement */ } else if (argv[fa][1] == 'e' || argv[fa][1] == 'd') { @@ -642,10 +674,14 @@ int main(int argc, char *argv[]) { /* Request ambient measurement */ } else if (argv[fa][1] == 'a') { - emiss = 1; - trans = 0; - tele = 0; - ambient = 1; + if (trans) { + ambient = 1; /* Alternate 90/diffuse geometry */ + } else { + emiss = 1; + trans = 0; + tele = 0; + ambient = 1; + } /* Request ambient flash measurement */ } else if (argv[fa][1] == 'f') { @@ -675,6 +711,21 @@ int main(int argc, char *argv[]) { if (na == NULL) usage("Paramater expected following -E"); strncpy(filtername,na,MAXNAMEL-1); filtername[MAXNAMEL-1] = '\000'; + /* XRGA conversion */ + } else if (argv[fa][1] == 'A') { + fa = nfa; + if (na == NULL) usage("Paramater expected following -A"); + if (na[0] == 'N') + calstd = xcalstd_none; + else if (na[0] == 'A') + calstd = xcalstd_xrga; + else if (na[0] == 'X') + calstd = xcalstd_xrdi; + else if (na[0] == 'G') + calstd = xcalstd_gmdi; + else + usage("Paramater after -A '%c' not recognized",na[0]); + /* Show Yxy */ } else if (argv[fa][1] == 'x') { doYxy = 1; @@ -725,6 +776,15 @@ int main(int argc, char *argv[]) { if (na == NULL) usage("Parameter expected after -K"); strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000'; +#ifndef SALONEINSTLIB + /* Preset reference spectrum */ + } else if (argv[fa][1] == 'R') { + fa = nfa; + if (na == NULL) + usage("-R fname.sp syntax incorrect"); + strncpy(psetrefname,na,MAXNAMEL-1); psetrefname[MAXNAMEL-1] = '\000'; +#endif + /* Extra flags */ } else if (argv[fa][1] == 'Y') { if (na == NULL) @@ -742,9 +802,25 @@ int main(int argc, char *argv[]) { refrate = atof(na+2); if (refrate < 5.0 || refrate > 150.0) usage("-Y R:rate %f Hz not in valid range",refrate); + +#ifndef SALONEINSTLIB + /* Save white tile reference spectrum to a file */ + } else if (na[0] == 'W') { + if (na[1] != ':') + usage("-Y W:fname.sp syntax incorrect"); + strncpy(wtilename,&na[2],MAXNAMEL-1); wtilename[MAXNAMEL-1] = '\000'; +#endif /* !SALONEINSTLIB */ + + /* i1Pro lamp drift test & fix */ + } else if (na[0] == 'l') { + lampdrift = 1; + } else if (na[0] == 'L') { + lampdrift = 2; + /* ~~~ i1pro2 test code ~~~ */ } else if (na[0] == 'U') { uvmode = 1; + } else { usage("-Y parameter '%c' not recognised",na[0]); } @@ -755,7 +831,7 @@ int main(int argc, char *argv[]) { fa = nfa; if (na == NULL) usage("Parameter expected after -W"); if (na[0] == 'n' || na[0] == 'N') - fc = fc_none; + fc = fc_None; else if (na[0] == 'h' || na[0] == 'H') fc = fc_Hardware; else if (na[0] == 'x' || na[0] == 'X') @@ -850,6 +926,135 @@ int main(int argc, char *argv[]) { return -1; } + /* Check i1Pro lamp drift, and remediate if it is too large */ + /* (Hmm. If cooltime is too long, drift appears to be worse + after remediation. It's not clear why, but it returns + to expected values once instrument has truly cooled down (i.e. 2+min ?) + */ + if (lampdrift) { + int pass = 0; + double remtime = 0.0; + int cooltime = 30; + + if (it->itype != instI1Pro + && it->itype != instI1Pro2) { + printf("LampDrift is only applicable to i1Pro instrument"); + } + + /* 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 initial-calibrate not supported\n"); + } + } + + if ((rv = it->get_set_opt(it, inst_opt_trig_prog)) != inst_ok) + error("Setting trigger mode failed with error :'%s' (%s)", + it->inst_interp_error(it, rv), it->interp_error(it, rv)); + + for (pass = 0; pass < 2; pass++) { + ipatch val; + double dl, maxdl = -100.0, de, maxde = -100.0; + int ii; + + printf("\nDoing Lamp Drift check - place instrument on calibration tile\n"); + + /* Do any needed calibration before the user places the instrument on a desired spot */ + if (it->needs_calibration(it) & inst_calt_n_dfrble_mask) { + inst_code ev; + + printf("\nNeed a calibration before continuing\n"); + + ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, doone); + if (ev != inst_ok) { /* Abort or fatal error */ + error("Got abort or error from calibration"); + } + } + +#ifndef NEVER + /* Ensure lamp has cooled down */ + printf("\nAllowing lamp to cool\n"); + for (i = cooltime; i > 0; i--) { + msec_sleep(1000); + printf("\r%d ",i); fflush(stdout); + } + printf("\r0 \n"); +#endif + + printf("\nChecking Lamp Drift\n"); + /* Measure 1 spot and save as ref */ + if ((rv = it->read_sample(it, "SPOT", &val, instNoClamp)) != inst_ok) { + error("Read sample failed with '%s' (%s)", + it->inst_interp_error(it, rv), it->interp_error(it, rv)); + } + if (val.XYZ_v == 0) error("Instrument didn't return XYZ value"); + icmXYZ2Lab(&icmD50_100, rLab, val.XYZ); + + // Until measurement is stable, or 30 measurements + // Read and save max DE + + /* 30 trials, or no new biggest in 4 measurements */ + for (ii = 100, i = 0; i < 40 && (i < 10 || (i - ii) < 6) ; i++) { + if ((rv = it->read_sample(it, "SPOT", &val, instNoClamp)) != inst_ok) { + error("Read sample failed with '%s' (%s)", + it->inst_interp_error(it, rv), it->interp_error(it, rv)); + } + if (val.XYZ_v == 0) error("Instrument didn't return XYZ value"); + icmXYZ2Lab(&icmD50_100, Lab, val.XYZ); + + dl = Lab[0] - rLab[0]; + de = icmLabDE(Lab, rLab); + + /* Keep going while dl is rising */ + if (dl > maxdl) { + maxdl = dl; + ii = i; + } + if (de > maxde) { + maxde = de; + printf("\r%1.3f DE",de); fflush(stdout); + } + } + + // Print DE +// printf("\nLamp Drift Delta E = %f\n",maxde); + + if (lampdrift == 1) { + printf("\nDrift test complete - %s\n", maxde >= 0.09 ? "Needs Fixing!" : "OK"); + break; + } + + if (pass > 0) { + printf("\nDrift test & fix complete\n"); + break; + } + + if (maxde >= 0.09) + remtime = 60.0; + else if (maxde >= 0.12) + remtime = 90.0; + else if (maxde > 0.20) + remtime = 120.0; + + if (remtime > 0.0) { + printf("\nDoing %.0f seconds of remediation\n",remtime); + // Do remediation */ + if ((rv = it->get_set_opt(it, inst_opt_lamp_remediate, remtime)) != inst_ok) { + error("Remediating Lamp Drift failed with error :'%s' (%s)\n", + it->inst_interp_error(it, rv), it->interp_error(it, rv)); + } + cooltime = 45; + } else { + printf("\nDrift is OK\n"); + break; + } + } + + goto done; + } + /* Configure the instrument mode */ { int ccssset = 0; @@ -873,17 +1078,26 @@ int main(int argc, char *argv[]) { } if (trans) { - if (!IMODETST(cap, inst_mode_trans_spot) - || it->check_mode(it, inst_mode_trans_spot) != inst_ok) { - printf("Need transmission spot capability,\n"); - printf("and instrument doesn't support it\n"); - it->del(it); - return -1; + /* Alternate geometry - 90/diffuse */ + if (ambient) { + if (it->check_mode(it, inst_mode_trans_spot_a) != inst_ok) { + printf("Need transmission spot (alt) capability,\n"); + printf("and instrument doesn't support it\n"); + it->del(it); + return -1; + } + /* Normal geometry - diffuce/90 */ + } else { + if (it->check_mode(it, inst_mode_trans_spot) != inst_ok) { + printf("Need transmission spot capability,\n"); + printf("and instrument doesn't support it\n"); + it->del(it); + return -1; + } } } else if (ambient == 1) { - if (!IMODETST(cap, inst_mode_emis_ambient) - || it->check_mode(it, inst_mode_emis_ambient) != inst_ok) { + if (it->check_mode(it, inst_mode_emis_ambient) != inst_ok) { printf("Requested ambient light capability,\n"); printf("and instrument doesn't support it.\n"); return -1; @@ -895,8 +1109,7 @@ int main(int argc, char *argv[]) { } } else if (ambient == 2) { - if (!IMODETST(cap, inst_mode_emis_ambient_flash) - || it->check_mode(it, inst_mode_emis_ambient_flash) != inst_ok) { + if (it->check_mode(it, inst_mode_emis_ambient_flash) != inst_ok) { printf("Requested ambient flash capability,\n"); printf("and instrument doesn't support it.\n"); it->del(it); @@ -912,22 +1125,20 @@ int main(int argc, char *argv[]) { } else if (emiss || tele) { /* If there is a tele mode but no emission, use tele */ - if (!IMODETST(cap, inst_mode_emis_spot) - && IMODETST(cap, inst_mode_emis_tele)) { + if (it->check_mode(it, inst_mode_emis_spot) != inst_ok + && it->check_mode(it, inst_mode_emis_tele) == inst_ok) { tele = 1; } if (tele) { - if (!IMODETST(cap, inst_mode_emis_tele) - || it->check_mode(it, inst_mode_emis_tele) != inst_ok) { + if (it->check_mode(it, inst_mode_emis_tele) != inst_ok) { printf("Need telephoto spot capability\n"); printf("and instrument doesn't support it\n"); it->del(it); return -1; } } else { - if (!IMODETST(cap, inst_mode_emis_spot) - || it->check_mode(it, inst_mode_emis_spot) != inst_ok) { + if (it->check_mode(it, inst_mode_emis_spot) != inst_ok) { printf("Need emissive spot capability\n"); printf("and instrument doesn't support it\n"); it->del(it); @@ -941,16 +1152,15 @@ int main(int argc, char *argv[]) { nadaptive = 0; } } - if (refrmode >= 0 && !IMODETST(cap, inst_mode_emis_refresh_ovd) - && !IMODETST(cap, inst_mode_emis_norefresh_ovd)) { + if (refrmode >= 0 && it->check_mode(it, inst_mode_emis_refresh_ovd) != inst_ok + && it->check_mode(it, inst_mode_emis_norefresh_ovd) != inst_ok) { if (verb) { printf("Requested refresh mode override and instrument doesn't support it (ignored)\n"); refrmode = -1; } } } else { - if (!IMODETST(cap, inst_mode_ref_spot) - || it->check_mode(it, inst_mode_ref_spot) != inst_ok) { + if (it->check_mode(it, inst_mode_ref_spot) != inst_ok) { printf("Need reflection spot reading capability,\n"); printf("and instrument doesn't support it\n"); it->del(it); @@ -1007,12 +1217,15 @@ int main(int argc, char *argv[]) { /* Set it to the appropriate mode */ /* Should look at instrument type & user spec ??? */ - if (trans) - smode = mode = inst_mode_trans_spot; - else if (ambient == 1 && IMODETST(cap, inst_mode_emis_ambient) + if (trans) { + if (ambient) + smode = mode = inst_mode_trans_spot_a; + else + smode = mode = inst_mode_trans_spot; + } else if (ambient == 1 && it->check_mode(it, inst_mode_emis_ambient) == inst_ok) smode = mode = inst_mode_emis_ambient; - else if (ambient == 2 && IMODETST(cap, inst_mode_emis_ambient_flash) + else if (ambient == 2 && it->check_mode(it, inst_mode_emis_ambient_flash) == inst_ok) smode = mode = inst_mode_emis_ambient_flash; else if (tele) // Hmm. What about tele flash ? @@ -1021,8 +1234,7 @@ int main(int argc, char *argv[]) { smode = mode = inst_mode_emis_spot; else { smode = mode = inst_mode_ref_spot; - if (IMODETST(cap, inst_mode_s_ref_spot) - && it->check_mode(it, inst_mode_s_ref_spot) == inst_ok) + if (it->check_mode(it, inst_mode_s_ref_spot) == inst_ok) smode = inst_mode_s_ref_spot; } @@ -1087,6 +1299,15 @@ int main(int argc, char *argv[]) { } } + /* set XRGA conversion */ + if (calstd != xcalstd_none) { + if ((rv = it->get_set_opt(it, inst_opt_set_xcalstd, calstd)) != inst_ok) { + printf("Setting calibration standard not supported by instrument\n"); + it->del(it); + return -1; + } + } + /* Colorimeter Correction Matrix */ if (ccxxname[0] != '\000') { ccss *cs = NULL; @@ -1242,6 +1463,26 @@ int main(int argc, char *argv[]) { inst_set_uih(0x1b, 0x1b, DUIH_ABORT); /* Esc */ } +#ifndef SALONEINSTLIB + /* Save reference white tile reflectance spectrum */ + if (wtilename[0] != '\000') { + xspect sp; + + if ((rv = it->get_set_opt(it, inst_opt_get_cal_tile_sp, &sp)) != inst_ok) { + printf("\nGetting reference white tile spectrum failed with error :'%s' (%s)\n", + it->inst_interp_error(it, rv), it->interp_error(it, rv)); + it->del(it); + return -1; + } + + if (write_xspect(wtilename, inst_mrt_reflective, &sp) != 0) + error("Failed to save spectrum to file '%s'",wtilename); + + if (verb) + printf("Saved reference white tile spectrum to '%s'\n",wtilename); + } +#endif /* !SALONEINSTLIB */ + #ifdef DEBUG printf("About to enter read loop\n"); #endif @@ -1253,9 +1494,9 @@ int main(int argc, char *argv[]) { it->set_uicallback(it, uicallback, NULL); } - if (spec) { + if (spec || psetrefname[0] != '\000') { /* Any non-illuminated mode has no illuminant */ - if (emiss || tele || ambient) + if (emiss || ambient) illum = icxIT_none; /* Create a spectral conversion object */ @@ -1274,6 +1515,43 @@ int main(int argc, char *argv[]) { } } +#ifndef SALONEINSTLIB + /* Load preset reference spectrum */ + if (psetrefname[0] != '\000') { + inst_meas_type mt; + + if (read_xspect(&rsp, &mt, psetrefname) != 0) + error("Failed to read spectrum from file '%s'",psetrefname); + + if (!emiss && !ambient) { + if (mt != inst_mrt_none + && mt != inst_mrt_transmissive + && mt != inst_mrt_reflective) + error("Reference reflectance spectrum '%s' is wrong measurement type",psetrefname); + } else { + if (mt != inst_mrt_none + && mt != inst_mrt_emission + && mt != inst_mrt_ambient + && mt != inst_mrt_emission_flash + && mt != inst_mrt_ambient_flash) + error("Reference reflectance spectrum '%s' is wrong measurement type",psetrefname); + } + + if (verb) + printf("Loaded reference spectrum from '%s'\n",psetrefname); + + sp2cie->convert(sp2cie, rXYZ, &rsp); + + if (!(emiss || tele || ambient)) { + for (j = 0; j < 3; j++) + rXYZ[j] *= 100.0; /* 0..100 scale */ + } + icmXYZ2Lab(&icmD50_100, rLab, rXYZ); + if (verb) + printf("Preset ref. XYZ %f %f %f, Lab %f %f %f\n", rXYZ[0], rXYZ[1], rXYZ[2], rLab[0], rLab[1], rLab[2]); + } +#endif + /* Hold table */ if (cap2 & inst2_xy_holdrel) { for (;;) { /* retry loop */ @@ -1338,8 +1616,7 @@ int main(int argc, char *argv[]) { #endif // NEVER - if (savdrd != -1 && IMODETST(cap, inst_mode_s_ref_spot) - && it->check_mode(it, inst_mode_s_ref_spot) == inst_ok) { + if (savdrd != -1 && it->check_mode(it, inst_mode_s_ref_spot) == inst_ok) { inst_stat_savdrd sv; savdrd = 0; @@ -1670,7 +1947,8 @@ int main(int argc, char *argv[]) { break; } printf("\n"); - if (it->icom->port_type(it->icom) == icomt_serial) { + 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)) { @@ -1792,7 +2070,7 @@ int main(int argc, char *argv[]) { printf("\nEnter filename (ie. xxxx.sp): "); fflush(stdout); if (getns(buf, 500) != NULL && strlen(buf) > 0) { - if(write_xspect(buf, &tsp)) + if(write_xspect(buf, val.mtype, &tsp)) printf("\nWriting file '%s' failed\n",buf); else printf("\nWriting file '%s' succeeded\n",buf); @@ -2079,7 +2357,7 @@ int main(int argc, char *argv[]) { ymin = 0.0; ymax = 120.0; } - do_plot_x(xx, yy, rLab[0] >= -1.0 ? yr : NULL, NULL, nn, 1, + do_plot_x(xx, yy, NULL, rLab[0] >= -1.0 ? yr : NULL, nn, 1, xmin, xmax, ymin, ymax, 2.0); } #endif /* !SALONEINSTLIB */ @@ -2333,12 +2611,18 @@ int main(int argc, char *argv[]) { } #ifndef SALONEINSTLIB if (val.sp.spec_n > 0 && (ambient || doCCT)) { - int invalid = 0; + int i, invalid = 0; double RR[14]; double cri; cri = icx_CIE1995_CRI(&invalid, RR, &sp); printf(" Color Rendering Index (Ra) = %.1f [ R9 = %.1f ]%s\n", cri, RR[9-1], invalid ? " (Invalid)" : ""); + for (i = 0; i < 14; i++) { + printf(" R%d%s = %.1f", i+1, i < 9 ? " " : "", RR[i]); + if (i == 6) + printf("\n"); + } + printf("\n"); } if (val.sp.spec_n > 0 && (ambient || doCCT)) { int invalid = 0; @@ -2380,6 +2664,8 @@ int main(int argc, char *argv[]) { } /* Next reading */ +done:; + /* Release paper */ if (cap2 & inst2_xy_holdrel) { it->xy_clear(it); diff --git a/spectro/spyd2.c b/spectro/spyd2.c index 6833e89..85a2d7f 100644 --- a/spectro/spyd2.c +++ b/spectro/spyd2.c @@ -819,10 +819,12 @@ spyd2_GetReading_ll( msec_sleep(500); a1logd(p->log, 1, "spyd2_GetReading_ll: reading retry with ICOM err 0x%x\n",se); -#ifdef DO_RESETEP /* Do the miscelanous resetep()'s */ - a1logd(p->log, 1, "spyd2_GetReading_ll: resetting end point\n"); - p->icom->usb_resetep(p->icom, 0x81); - msec_sleep(1); /* Let device recover ? */ +#ifdef DO_RESETEP /* Do the miscelanous resetep()'s every second time */ + if ((retr & 1) == 0) { + a1logd(p->log, 1, "spyd2_GetReading_ll: resetting end point\n"); + p->icom->usb_resetep(p->icom, 0x81); + msec_sleep(1); /* Let device recover ? */ + } #endif /* DO_RESETEP */ } /* End of whole command retries */ @@ -2888,7 +2890,7 @@ spyd2_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { #endif /* On OS X the Spyder 2 can't close properly */ -#if defined(__APPLE__) /* OS X*/ +#if defined(UNIX_APPLE) /* OS X*/ if (p->itype == instSpyder1 || p->itype == instSpyder2) { usbflags |= icomuf_reset_before_close; /* The spyder 2 USB is buggy ? */ @@ -3255,6 +3257,7 @@ static inst_code spyd2_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { spyd2 *p = (spyd2 *)pp; @@ -3266,6 +3269,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; + *idtype = inst_calc_id_none; id[0] = '\000'; if ((ev = spyd2_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) @@ -4060,7 +4064,18 @@ spyd2_get_set_opt(inst *pp, inst_opt_type m, ...) { if (trans_time_prop != NULL) *trans_time_prop = p->led_trans_time_prop; return inst_ok; } - return inst_unsupported; + + /* Use default implementation of other inst_opt_type's */ + { + inst_code rv; + va_list args; + + va_start(args, m); + rv = inst_get_set_opt_def(pp, m, args); + va_end(args); + + return rv; + } } /* Constructor */ diff --git a/spectro/spyd2.h b/spectro/spyd2.h index 0588b9b..318ed7c 100644 --- a/spectro/spyd2.h +++ b/spectro/spyd2.h @@ -37,6 +37,10 @@ #include "inst.h" +#ifdef __cplusplus + extern "C" { +#endif + /* Note: update spyd2_interp_error() and spyd2_interp_code() in spyd2.c */ /* if anything of these #defines are added or subtracted */ @@ -178,5 +182,9 @@ extern spyd2 *new_spyd2(icoms *icom, instType itype); /* Return 1 if Spyder firmware is available */ extern int setup_spyd2(int id); +#ifdef __cplusplus + } +#endif + #define SPYD2_H #endif /* SPYD2_H */ diff --git a/spectro/ss.c b/spectro/ss.c index 837e264..260df4a 100644 --- a/spectro/ss.c +++ b/spectro/ss.c @@ -43,17 +43,20 @@ There is a bug or limitation with using -N to skip the calibration when using any of the emissive modes - the readings end up being nearly zero. + When -N is used, doing a manual calibration (i.e. spotread 'k') doesn't + work. + We aren't saving the spectrolino fake tranmission white reference in a calibration file, so -N doesn't work with it. You can't trigger a calibration reading using the instrument switch. - You should be able to do use the table enter key anywhere the user + You should be able to use the table enter key anywhere the user is asked to hit a key. The corner positioning could be smarter. - The SpectroscanT transmission cal. merely reminds the user (vie verbose) + The SpectroscanT transmission cal. merely reminds the user (via verbose) that it is assuming the correct apatture, rather than given them a chance to change it. @@ -78,6 +81,7 @@ #include "conv.h" #include "icoms.h" #include "ss.h" +#include "xrga.h" /* Default flow control */ #define DEFFC fc_Hardware @@ -97,6 +101,17 @@ char* filter_desc[] = { "Custon Filter" }; +/* Filter id */ +inst_calc_id_type filter_id[] = { + inst_calc_id_filt_unkn, + inst_calc_id_filt_none, + inst_calc_id_filt_pol, + inst_calc_id_filt_D65, + inst_calc_id_filt_unkn, + inst_calc_id_filt_UV, + inst_calc_id_filt_cust +}; + #define SS_REF_CAL_COUNT 50 #define SS_TRANS_CAL_COUNT 10 @@ -165,7 +180,7 @@ ss_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { fcc1 = ss_ctt_ProtokolWithHardwareHS; fcc2 = ss_hst_Hardware; } else { - fc = fc_none; + fc = fc_None; fcc1 = ss_ctt_ProtokolWithoutXonXoff; fcc2 = ss_hst_None; } @@ -195,7 +210,7 @@ ss_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* Until we time out, find the correct baud rate */ for (i = ci; clock() < etime;) { a1logd(p->log, 4, "ss_init_coms: trying baud rate %d\n",i); - if ((se = p->icom->set_ser_port(p->icom, fc_none, brt[i], parity_none, + if ((se = p->icom->set_ser_port(p->icom, fc_None, brt[i], parity_none, stop_1, length_8)) != ICOM_OK) { a1logd(p->log, 1, "ss_init_coms: set_ser_port failed ICOM err 0x%x\n",se); p->snerr = icoms2ss_err(se); @@ -258,8 +273,24 @@ ss_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { return ss_inst_err(p); } + /* Make sure the Spectrolino is still talking to us. */ + ss_init_send(p); + ss_add_soreq(p, ss_ParameterRequest); + ss_command(p, SH_TMO); + + if (ss_sub_1(p) != ss_ParameterAnswer) { /* Comms failed */ + a1logd(p->log, 1, "ss_init_coms: spectrolino, instrument isn't communicating after final coms setup"); + return inst_coms_fail; + } + } else { /* Spectroscan */ + /* Reset the Spectroscan, in case Spectrolino got messed up. */ + if ((ev = ss_do_ScanInitializeDevice(p)) != inst_ok) { + a1logd(p->log, 1, "ss_init_coms: Spectroscan reset failed ICOM err 0x%x\n",ev); + return ev; + } + ss_do_SetDeviceOnline(p); /* Put the device online */ /* Make sure other communication parameters are right */ @@ -275,9 +306,19 @@ ss_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { return ss_inst_err(p); } - /* Make sure the Spectrolino is talking to us. */ + /* Make sure the Spectroscan is still talking to us. */ + ss_init_send(p); + ss_add_ssreq(p, ss_OutputStatus); + ss_command(p, SH_TMO); + + if (ss_sub_1(p) != ss_AnsPFX) { /* Comms failed */ + a1logd(p->log, 1, "ss_init_coms: spectroscan, instrument isn't communicating after final coms setup"); + return inst_coms_fail; + } + + /* Make sure the Spectrolino is talking to Spectroscan. */ if ((ev = ss_do_ScanSpectrolino(p)) != inst_ok) { - a1logd(p->log, 1, "ss_init_coms: spectroscan, instrument isn't communicating ICOM err 0x%x\n",se); + a1logd(p->log, 1, "ss_init_coms: Spectrolino isn't communicating with Spectroscan ICOM err 0x%x\n",ev); return ev; } } @@ -322,7 +363,7 @@ ss_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { || strncmp(devn, "Spectrolino",11) != 0) return inst_unknown_model; - if (p->itype == instUnknown) /* No SpectrScan */ + if (p->itype == instUnknown) /* No SpectroScan */ p->itype = instSpectrolino; } } @@ -387,6 +428,7 @@ static void ss_determine_capabilities(ss *p) { /* return non-zero on an error, with dtp error code */ static inst_code ss_init_inst(inst *pp) { + char *envv; ss *p = (ss *)pp; inst_code rv = inst_ok; @@ -395,6 +437,19 @@ ss_init_inst(inst *pp) { if (p->gotcoms == 0) return inst_internal_error; /* Must establish coms before calling init */ + p->native_calstd = xcalstd_gmdi; /* Native is GMDI */ + p->target_calstd = xcalstd_native; /* Default to native calibration */ + + /* Honor Environment override */ + if ((envv = getenv("ARGYLL_XCALSTD")) != NULL) { + if (strcmp(envv, "XRGA") == 0) + p->target_calstd = xcalstd_xrga; + else if (strcmp(envv, "XRDI") == 0) + p->target_calstd = xcalstd_xrdi; + else if (strcmp(envv, "GMDI") == 0) + p->target_calstd = xcalstd_gmdi; + } + /* Reset the instrument to a known state */ if (p->itype != instSpectrolino) { @@ -409,8 +464,9 @@ ss_init_inst(inst *pp) { return rv; if ((rv = ss_do_ReleasePaper(p)) != inst_ok) return rv; - if ((rv = ss_do_InitMotorPosition(p)) != inst_ok) - return rv; + /* Skip this since we did a ss_do_ScanInitializeDevice() */ +// if ((rv = ss_do_InitMotorPosition(p)) != inst_ok) +// return rv; if (p->log->verb) { char dn[19]; /* Device name */ @@ -642,7 +698,7 @@ struct _inst *pp) { return rv; } -static inst_code ss_calibrate_imp(ss *p, inst_cal_type *calt, inst_cal_cond *calc, char id[CALIDLEN]); +static inst_code ss_calibrate_imp(ss *p, inst_cal_type *calt, inst_cal_cond *calc, inst_calc_id_type *idtype, char id[CALIDLEN]); /* Read a sheet full of patches using xy mode */ /* Return the inst error code */ @@ -725,10 +781,11 @@ ipatch *vals) { /* Pointer to array of values */ if ( (p->need_wd_cal || p->need_t_cal) && p->noinitcalib == 0) { inst_cal_type calt = inst_calt_needed; inst_cal_cond calc = inst_calc_none; + inst_calc_id_type idtype; char id[CALIDLEN]; /* We expect this to be automatic, but handle as if it mightn't be */ - if ((rv = ss_calibrate_imp(p, &calt, &calc, id)) != inst_ok) { + if ((rv = ss_calibrate_imp(p, &calt, &calc, &idtype, id)) != inst_ok) { if (rv == inst_cal_setup) return inst_needs_cal; /* Not automatic, needs a manual setup */ if (try < notries) { @@ -819,6 +876,11 @@ ipatch *vals) { /* Pointer to array of values */ } } + /* Apply any XRGA conversion */ + ipatch_convert_xrga(vals, npatch, + p->filt == ss_aft_PolFilter ? xcalstd_pol : xcalstd_nonpol, + p->target_calstd, p->native_calstd, instClamp); + return rv; } @@ -945,10 +1007,11 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ if ((p->need_wd_cal || p->need_t_cal) && p->noinitcalib == 0) { inst_cal_type calt = inst_calt_needed; inst_cal_cond calc = inst_calc_none; + inst_calc_id_type idtype; char id[CALIDLEN]; /* This could be automatic or need manual intervention */ - if ((rv = ss_calibrate_imp(p, &calt, &calc, id)) != inst_ok) { + if ((rv = ss_calibrate_imp(p, &calt, &calc, &idtype, id)) != inst_ok) { if (rv == inst_cal_setup) { return inst_needs_cal; /* Not automatic, needs a manual setup */ } @@ -1269,7 +1332,8 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } /* spectrum data is returned only if requested */ - if (p->mode & inst_mode_spectral) { + if (p->mode & inst_mode_spectral + || XCALSTD_NEEDED(p->target_calstd, p->native_calstd)) { ss_st rst; /* Return Spectrum Type (Reflectance/Density) */ ss_rvt rvf; /* Return Reference Valid Flag */ ss_aft af; /* Return filter being used (None/Pol/D65/UV/custom */ @@ -1294,6 +1358,12 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } } } + + /* Apply any XRGA conversion to the measurement */ + ipatch_convert_xrga(val, 1, + p->filt == ss_aft_PolFilter ? xcalstd_pol : xcalstd_nonpol, + p->target_calstd, p->native_calstd, clamp); + if (user_trig) return inst_user_trig; return rv; @@ -1342,6 +1412,7 @@ static inst_code ss_calibrate_imp( ss *p, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { inst_code rv = inst_ok; @@ -1350,6 +1421,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ss_owrt owr; /* Original white reference */ inst_cal_type needed, available; + *idtype = inst_calc_id_none; id[0] = '\000'; a1logd(p->log, 3, "ss calibrate called with calt = 0x%x, condition 0x%x, need w %d, t %d\n", *calt, *calc, p->need_wd_cal, p->need_t_cal); @@ -1407,6 +1479,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ /* Get the name of the expected white reference */ if ((rv = so_do_WhiteReferenceRequest(p, p->filt, &afilt, sp, &owr, id)) != inst_ok) return rv; + *idtype = inst_calc_id_ref_sn; if (p->noinitcalib == 0) { @@ -1457,6 +1530,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ a1logd(p->log, 3, "got filt %d, want %d\n",af,p->filt); + *idtype = filter_id[p->filt]; strcpy(id, filter_desc[p->filt]); *calc = inst_calc_change_filter; return inst_cal_setup; @@ -1543,6 +1617,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (i < 36) { *calc = inst_calc_message; + *idtype = inst_calc_id_trans_wl; strcpy(id, "Warning: Transmission light source is low at some wavelengths!"); rv = inst_ok; } @@ -1596,6 +1671,7 @@ inst_code ss_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ +inst_calc_id_type *idtype, /* Condition identifier type */ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ ) { ss *p = (ss *)pp; @@ -1605,7 +1681,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if (!p->inited) return inst_no_init; - return ss_calibrate_imp(p, calt, calc, id); + return ss_calibrate_imp(p, calt, calc, idtype, id); } /* Insert a compensation filter in the instrument readings */ @@ -1629,7 +1705,7 @@ char *filtername } else { xspect sp; int i; - if (read_xspect(&sp, filtername) != 0) { + if (read_xspect(&sp, NULL, filtername) != 0) { return inst_wrong_setup; } if (sp.spec_n != 36 || sp.spec_wl_short != 380.0 || sp.spec_wl_long != 730.0) { @@ -1944,6 +2020,35 @@ ss_get_set_opt(inst *pp, inst_opt_type m, ...) { break; } return inst_unsupported; + + /* Set the current xcalstd */ + } else if (m == inst_opt_set_xcalstd) { + xcalstd standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd); + va_end(args); + + p->target_calstd = standard; + + return inst_ok; + + /* Get the current effective xcalstd */ + } else if (m == inst_opt_get_xcalstd) { + xcalstd *standard; + va_list args; + + va_start(args, m); + standard = va_arg(args, xcalstd *); + va_end(args); + + if (p->target_calstd == xcalstd_native) + *standard = p->native_calstd; /* If not overridden */ + else + *standard = p->target_calstd; /* Overidden std. */ + + return inst_ok; } /* Record the trigger mode */ @@ -2002,6 +2107,34 @@ ss_get_set_opt(inst *pp, inst_opt_type m, ...) { return inst_ok; } + /* Return the white calibration tile spectrum for current filter */ + if (m == inst_opt_get_cal_tile_sp) { + ss_aft raf; + ss_owrt owr; + xspect *sp; + char dtn[19]; + inst_code rv; + va_list args; + int i; + + va_start(args, m); + sp = va_arg(args, xspect *); + va_end(args); + + /* Queries the spectra of the white tile reference for the desired filter */ + if ((rv = so_do_WhiteReferenceRequest(p, p->filt, &raf, sp->spec, &owr, dtn)) != inst_ok) + return rv; + + sp->spec_n = 36; + sp->spec_wl_short = 380; + sp->spec_wl_long = 730; + sp->norm = 100.0; + for (i = 0; i < sp->spec_n; i++) + sp->spec[i] *= 100.0; + + return inst_ok; + } + /* Use default implementation of other inst_opt_type's */ { va_list args; diff --git a/spectro/ss.h b/spectro/ss.h index bff0846..bc816e7 100644 --- a/spectro/ss.h +++ b/spectro/ss.h @@ -44,6 +44,10 @@ #include "inst.h" #include "ss_imp.h" +#ifdef __cplusplus + extern "C" { +#endif + #define SS_MAX_WR_SIZE 1000 /* Assumed maximum normal message query size */ #define SS_MAX_RD_SIZE 1000 /* Assumed maximum normal messagle answer size */ @@ -60,7 +64,7 @@ struct _ss { inst_mode mode; /* Currently instrument mode */ /* Desired measurement configuration */ - ss_aft filt; /* Filter type (None/UV/D65 etc.) */ + ss_aft filt; /* Filter type (None/UV/D65/Pol etc.) */ ss_dst dstd; /* Density standard (ANSI A/ANSI T/DIN etc.) */ ss_ilt illum; /* Illuminant type (A/C/D50 etc.) */ ss_ot obsv; /* Observer type (2deg/10deg) */ @@ -85,6 +89,9 @@ struct _ss { int compen; /* Compensation filter enabled */ double comp[36]; /* Compensation filter */ + xcalstd native_calstd; /* Instrument native calibration standard */ + xcalstd target_calstd; /* Returned calibration standard */ + #ifdef EMSST int tmode; /* Transmission mode */ ss_rt sbr; /* Standby reference */ @@ -111,5 +118,9 @@ struct _ss { /* Constructor */ extern ss *new_ss(icoms *icom, instType itype); +#ifdef __cplusplus + } +#endif + #define SS_H #endif /* SS_H */ diff --git a/spectro/ss_imp.c b/spectro/ss_imp.c index 6f184b3..05f0602 100644 --- a/spectro/ss_imp.c +++ b/spectro/ss_imp.c @@ -511,7 +511,7 @@ void ss_command(ss *p, double tmo) { p->sbuf[2] = '\00'; /* write_read terminates on nul */ p->rbuf = p->_rbuf; /* Reset read pointer */ - if ((se = p->icom->write_read(p->icom, p->_sbuf, 0, p->_rbuf, SS_MAX_RD_SIZE, NULL, "\n", 1, tmo)) != 0) { + if ((se = p->icom->write_read_ex(p->icom, p->_sbuf, 0, p->_rbuf, SS_MAX_RD_SIZE, NULL, "\n", 1, tmo, 1)) != 0) { p->snerr = icoms2ss_err(se); return; } @@ -631,7 +631,7 @@ char pn[9], /* Return the part number */ unsigned int *sn, /* Return serial number */ char sv[13] /* Return software version */ ) { - char rsv[17]; /* Space for resered field */ + char rsv[17]; /* Space for reserved field */ ss_add_soreq(p, ss_DeviceDataRequest); ss_command(p, DF_TMO); ss_sub_soans(p, ss_DeviceDataAnswer); @@ -866,13 +866,13 @@ int ct /* Color temperature to set for illuminant Dxx in deg K/100 */ return ss_inst_err(p); } -/* Queries the spectra of the white reference for the desired filter */ +/* Queries the spectra of the white tile reference for the desired filter */ inst_code so_do_WhiteReferenceRequest( ss *p, ss_aft af, /* Filter being queried (None/Pol/D65/UV/custom */ ss_aft *raf, /* Return filter being queried (None/Pol/D65/UV/custom */ double sp[36], /* Return 36 spectral values */ -ss_owrt *owr, /* Return original white reference */ +ss_owrt *owr, /* Return original white reference (i.e. factory/user) */ char dtn[19] /* Return name of data table */ ) { int i; @@ -890,6 +890,7 @@ char dtn[19] /* Return name of data table */ } /* Load spectra of a user defined white reference for the desired filter. */ +/* This lets the user override the factory white tile calibration */ /* A name can be given to the white reference. */ inst_code so_do_WhiteReferenceDownld( ss *p, @@ -1312,12 +1313,20 @@ ss_toost oo /* Activated/Deactivated */ /* Initialise the device. Scans the Spectrolino */ /* (Doesn't work when device is offline ) */ inst_code ss_do_ScanInitializeDevice(ss *p) { + inst_code rv; ss_add_ssreq(p, ss_InitializeDevice); ss_command(p, IT_TMO); ss_sub_ssans(p, ss_ErrorAnswer); ss_incorp_scanerr(p, ss_sub_1(p)); chended(p); - return ss_inst_err(p); + rv = ss_inst_err(p); + + if (rv != inst_ok) + return rv; + + /* Wait for Spectroscan to finish init. */ + msec_sleep(3000); + return rv; } /* Establish communications between the SpectroScan and Spectrolino */ diff --git a/spectro/ss_imp.h b/spectro/ss_imp.h index c298e80..ccd9ce3 100644 --- a/spectro/ss_imp.h +++ b/spectro/ss_imp.h @@ -37,6 +37,9 @@ and agreed to support. */ +#ifdef __cplusplus + extern "C" { +#endif /* Communication symbol definitions */ /* From the Gretag Spectrolino/Spectroscan */ @@ -1086,7 +1089,8 @@ ss_toost oo /* Activated/Deactivated */ /* Device Initialisation and configuration */ /* Initialise the device. Scans the Spectrolino */ -/* (Doesn't work when device is offline ) */ +/* (Doesn't work when device is offline, */ +/* takes some seconds for the device to recover after reply.) */ inst_code ss_do_ScanInitializeDevice(struct _ss *p); /* Establish communications between the SpectroScan and Spectrolino */ @@ -1331,5 +1335,9 @@ struct _ss *p, ss_sss *sss /* Return Special Status bits */ ); +#ifdef __cplusplus + } +#endif + #define SS_IMP_H #endif /* SS_IMP_H */ diff --git a/spectro/strange.cal b/spectro/strange.cal index 5f2e94d..1e9451d 100644 --- a/spectro/strange.cal +++ b/spectro/strange.cal @@ -2,7 +2,7 @@ CAL DESCRIPTOR "Argyll Device Calibration Curves" ORIGINATOR "Argyll synthcal" -CREATED "Mon Oct 26 01:10:48 2015" +CREATED "Wed Sep 28 02:35:54 2016" DEVICE_CLASS "DISPLAY" COLOR_REP "RGB" diff --git a/spectro/synthcal.c b/spectro/synthcal.c index 75d18be..14471f6 100644 --- a/spectro/synthcal.c +++ b/spectro/synthcal.c @@ -41,6 +41,7 @@ usage(int level) { fprintf(stderr,"Create a synthetic calibration file, Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); fprintf(stderr,"usage: synthcal [-options] outfile\n"); + fprintf(stderr," -r res Set the calibration resolution (default 256)\n"); fprintf(stderr," -t N i = input, o = output, d = display (default)\n"); fprintf(stderr," -d col_comb choose colorant combination from the following (default 3):\n"); for (i = 0; ; i++) { @@ -78,6 +79,7 @@ int main(int argc, char *argv[]) char *profDesc = NULL; /* Description */ int devtype = 2; /* debice type, 0 = in, 1 = out, 2 = display */ inkmask devmask = 0; /* ICX ink mask of device space */ + int calres = 256; /* Resolution of resulting file */ int devchan; /* Number of chanels in device space */ char *ident; /* Ink combination identifier (includes possible leading 'i') */ char *bident; /* Base ink combination identifier */ @@ -120,8 +122,17 @@ int main(int argc, char *argv[]) else if (argv[fa][1] == 'v') verb = 1; + /* Calibration file resolution */ + else if (argv[fa][1] == 'r') { + fa = nfa; + if (na == NULL) usage(0); + calres = atoi(na); + if (calres < 2 || calres > MAX_CAL_ENT) + usage(0); + } + /* Select the device type */ - else if (argv[fa][1] == 't' || argv[fa][1] == 'T') { + else if (argv[fa][1] == 't') { fa = nfa; if (na == NULL) usage(0); if (na[0] == 'i' || na[0] == 'I') @@ -280,7 +291,7 @@ int main(int argc, char *argv[]) /* Write out the resulting calibration file */ { - int i, j, calres = 256; /* 256 steps in calibration */ + int i, j; cgats *ocg; /* output cgats structure */ time_t clk = time(0); struct tm *tsp = localtime(&clk); diff --git a/spectro/usbio.c b/spectro/usbio.c index 9f8964a..fca997d 100644 --- a/spectro/usbio.c +++ b/spectro/usbio.c @@ -90,7 +90,7 @@ static int icoms_usb_wait_io( # ifdef NT # include "usbio_nt.c" # endif -# if defined(__APPLE__) +# if defined(UNIX_APPLE) # include "usbio_ox.c" # endif # if defined(UNIX_X11) @@ -491,7 +491,7 @@ double tout) /* Time out in seconds */ return ICOM_SYS; } - if (p->port_type(p) == icomt_usbserial) + if (p->dctype & icomt_fastserial) fastserial = 1; for (j = 0; j < bsize; j++) @@ -517,7 +517,7 @@ double tout) /* Time out in seconds */ int c, rv; int rsize = bsize; - /* If not a fast USB serial port, read in quanta size chunks */ + /* If not a fast USB/BT serial port, read in quanta size chunks */ if (!fastserial && rsize > p->rd_qa) rsize = p->rd_qa; @@ -614,8 +614,7 @@ char **pnames /* List of process names to try and kill before opening */ ) { a1logd(p->log, 8, "icoms_set_usb_port: About to set usb port characteristics\n"); - if (p->port_type(p) == icomt_usb - || p->port_type(p) == icomt_usbserial) { + if (p->port_type(p) & icomt_usb) { int rv; if (p->is_open) diff --git a/spectro/usbio.h b/spectro/usbio.h index 578bd83..2a01ea3 100644 --- a/spectro/usbio.h +++ b/spectro/usbio.h @@ -44,7 +44,7 @@ struct usb_idevice { # endif /* NT */ -# if defined(UNIX) && defined(__APPLE__) +# if defined(UNIX_APPLE) /* OS X structure version wrangling */ @@ -127,7 +127,7 @@ struct usb_idevice { }; # endif /* OS X */ -# if defined(UNIX) && !defined(__APPLE__) +# if defined(UNIX_X11) # if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) diff --git a/spectro/usbio_bsd.c b/spectro/usbio_bsd.c index 0af13a1..4b3e038 100644 --- a/spectro/usbio_bsd.c +++ b/spectro/usbio_bsd.c @@ -23,7 +23,7 @@ ( Most of the below code is stubbed out, with the Linux code as a placeholder. ) - BSD uses fd per end point, so simplifies things. + BSD uses fd per end point, so simplifies things (good). No clear ep or abort i/o though, so we could try clear halt, or close fd and see if that works in aborting transaction ? @@ -57,7 +57,7 @@ #define poll_x poll #endif -/* Add paths to USB connected instruments */ +/* Add paths to USB connected device */ /* Return an icom error */ int usb_get_paths( icompaths *p @@ -75,7 +75,7 @@ icompaths *p int vid, pid; int nconfig = 0, nep = 0; char *dpath; - instType itype; + devType itype; struct usb_idevice *usbd = NULL; a1logd(p->log, 6, "usb_get_paths: about to look through usb devices:\n"); @@ -193,7 +193,7 @@ icompaths *p break; } - a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->npaths); + a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->ndpaths[dtix_combined]); return ICOM_OK; } diff --git a/spectro/usbio_lx.c b/spectro/usbio_lx.c index cb9ec59..6d001a2 100644 --- a/spectro/usbio_lx.c +++ b/spectro/usbio_lx.c @@ -81,7 +81,7 @@ char *dpath /* path to device */ unsigned char buf[IUSB_DESC_TYPE_DEVICE_SIZE]; unsigned vid, pid, nep10 = 0xffff; unsigned int configix, nconfig, totlen; - instType itype; + devType itype; struct usb_idevice *usbd = NULL; int fd; /* device file descriptor */ @@ -322,7 +322,7 @@ icompaths *p } } - a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->npaths); + a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->ndpaths[dtix_combined]); return ICOM_OK; } @@ -384,6 +384,9 @@ void usb_close_port(icoms *p) { ioctl(p->usbd->fd, USBDEVFS_RELEASEINTERFACE, &iface); /* Workaround for some bugs - reset device on close */ + /* !!!! Alternative would be to do reset before open. + On Linux the path stays the same, so could do open/reset/open + */ if (p->uflags & icomuf_reset_before_close) { if ((rv = ioctl(p->usbd->fd, USBDEVFS_RESET, NULL)) != 0) { a1logd(p->log, 1, "usb_close_port: reset returned %d\n",rv); @@ -531,7 +534,8 @@ char **pnames /* List of process names to try and kill before opening */ /* Clear any errors. */ /* (Some I/F seem to hang if we do this, some seem to hang if we don't !) */ - /* The ColorMunki on Linux only starts every second time if we don't do this. */ + /* (The ColorMunki on some Linux's only starts every second time if we don't do this, */ + /* and on others, every second time if we do.) */ if (!(p->uflags & icomuf_no_open_clear)) { for (i = 0; i < 32; i++) { if (!p->ep[i].valid) @@ -701,7 +705,8 @@ static void *urb_reaper(void *context) { iurb = (usbio_urb *)out->usercontext; req = iurb->req; - a1logd(p->log, 8, "urb_reaper: urb reap URB %d with status %d bytes %d, urbs left %d\n",iurb->urbno, out->status, out->actual_length, req->nourbs-1); + a1logd(p->log, 8, "urb_reaper: urb reap URB %d with status %d, bytes %d, urbs left %d\n",iurb->urbno, out->status, out->actual_length, req->nourbs-1); + pthread_mutex_lock(&req->lock); /* Stop requester from missing reap */ req->nourbs--; /* We're reaped one */ @@ -778,7 +783,7 @@ static int icoms_usb_transaction( int i; in_usb_rw++; - a1logd(p->log, 8, "icoms_usb_transaction: req type 0x%x ep 0x%x size %d\n",ttype,endpoint,length); + a1logd(p->log, 8, "icoms_usb_transaction: req type 0x%x ep 0x%x size %d to %d\n",ttype,endpoint,length, timeout); if (!p->usbd->running) { in_usb_rw--; diff --git a/spectro/usbio_nt.c b/spectro/usbio_nt.c index f5c3af8..34426fd 100644 --- a/spectro/usbio_nt.c +++ b/spectro/usbio_nt.c @@ -99,14 +99,14 @@ int *retsz) { return ICOM_OK; } -/* Add paths to USB connected instruments */ +/* Add paths to USB connected device */ /* Return an icom error */ int usb_get_paths( icompaths *p ) { unsigned int vid, pid, nep10 = 0xffff; unsigned int configix, nconfig, nifce; - instType itype; + devType itype; struct usb_idevice *usbd = NULL; int rv, retsz, i; diff --git a/spectro/usbio_ox.c b/spectro/usbio_ox.c index d3da59a..2b09955 100644 --- a/spectro/usbio_ox.c +++ b/spectro/usbio_ox.c @@ -15,6 +15,8 @@ * see the License2.txt file for licencing details. */ +/* OS X I/O error codes are in IOKit/IOReturn.h */ + #include <sys/time.h> #include <CoreFoundation/CoreFoundation.h> @@ -62,7 +64,7 @@ icompaths *p CFNumberRef nconfref; /* No configurations */ CFNumberRef nepref, lidpref; /* No ep's, Location ID properties */ unsigned int vid = 0, pid = 0, nep, tnep, nconfig = 0, lid = 0; - instType itype; + devType itype; if ((ioob = IOIteratorNext(mit)) == 0) break; @@ -127,7 +129,7 @@ icompaths *p unsigned int config = 0; /* Get the configuration number */ - if ((nconfref = IORegistryEntryCreateCFProperty(ch1, CFSTR(kUSBNumEndpoints), + if ((nconfref = IORegistryEntryCreateCFProperty(ch1, CFSTR(kUSBConfigurationValue), kCFAllocatorDefault,kNilOptions)) != 0) { CFNumberGetValue(nconfref, kCFNumberIntType, &config); CFRelease(nconfref); @@ -157,16 +159,16 @@ icompaths *p /* If this device is HID, it will have already added to the paths list, */ /* so check for this and skip this device if it is already there. */ - for (i = 0; i < p->npaths; i++) { - if (p->paths[i]->vid == vid - && p->paths[i]->pid == pid - && p->paths[i]->hidd != NULL - && p->paths[i]->hidd->lid == lid) { + for (i = 0; i < p->ndpaths[dtix_combined]; i++) { + if (p->dpaths[i][dtix_combined]->vid == vid + && p->dpaths[i][dtix_combined]->pid == pid + && p->dpaths[i][dtix_combined]->hidd != NULL + && p->dpaths[i][dtix_combined]->hidd->lid == lid) { a1logd(p->log, 1, "usb_get_paths: Ignoring device because it is already in list as HID\n"); break; } } - if (i < p->npaths) { + if (i < p->ndpaths[dtix_combined]) { IOObjectRelease(ioob); /* Release found object */ free(usbd); @@ -195,7 +197,7 @@ icompaths *p } IOObjectRelease(mit); /* Release the itterator */ - a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->npaths); + a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->ndpaths[dtix_combined]); return ICOM_OK; } @@ -271,6 +273,8 @@ void usb_close_port(icoms *p) { /* Workaround for some bugs - reset device on close */ if (p->uflags & icomuf_reset_before_close) { IOReturn rv; + // ~~~~ may have to switch to ->USBDeviceReEnumerate(p->usbd->device, 0) ??? + // because new OS X 10.11 ignore ResetDevice ? if ((rv = (*(p->usbd->device))->ResetDevice(p->usbd->device)) != kIOReturnSuccess) { a1logd(p->log, 1, "usb_close_port: ResetDevice failed with 0x%x\n",rv); } diff --git a/spectro/webwin.c b/spectro/webwin.c index e13981a..1d5f713 100644 --- a/spectro/webwin.c +++ b/spectro/webwin.c @@ -70,7 +70,9 @@ char src_addr[20]; } mg_printf(conn, - "\r\n#%02X%02X%02X", + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n\r\n" + "#%02X%02X%02X", (int)(p->r_rgb[0] * 255.0 + 0.5), (int)(p->r_rgb[1] * 255.0 + 0.5), (int)(p->r_rgb[2] * 255.0 + 0.5)); @@ -93,7 +95,8 @@ static void *webwin_ehandler(enum mg_event event, } else if (strcmp(request_info->uri, "/webdisp.js") == 0) { #ifndef NEVER char *webdisp_js = - "\r\n" + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/javascript\r\n\r\n" "if (typeof XMLHttpRequest == \"undefined\") {\r\n" " XMLHttpRequest = function () {\r\n" " try { return new ActiveXObject(\"Msxml2.XMLHTTP.6.0\"); }\r\n" @@ -141,17 +144,26 @@ static void *webwin_ehandler(enum mg_event event, #else return NULL; /* Read webdisp.js */ #endif - } else { + } else if (strcmp(request_info->uri, "/") == 0) { mg_printf(conn, "HTTP/1.1 200 OK\r\n" "Cache-Control: no-cache\r\n" - "Content-Type: text/html\r\n\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + "<!DOCTYPE html>\r\n" "<html>\r\n" "<head>\r\n" "<title>ArgyllCMS Web Display</title>\r\n" "<script src=\"webdisp.js\"></script>\r\n" "</head>\r\n" + "<body>\r\n" + "</body>\r\n" "</html>\r\n" ); + } else { + mg_printf(conn, "HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/plain\r\n\r\n" + "404 Not Found: %s", + request_info->uri + ); } // "<script type=\"text/javascript\"src=\"webdisp.js\"></script>" @@ -230,9 +242,9 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ /* For video encoding the extra bits of precision are created by bit shifting */ /* rather than scaling, so we need to scale the fp value to account for this. */ - if (p->pdepth > 8) - p->r_rgb[j] = (p->s_rgb[j] * 255 * (1 << (p->pdepth - 8))) - /((1 << p->pdepth) - 1.0); + if (p->edepth > 8) + p->r_rgb[j] = (p->s_rgb[j] * 255 * (1 << (p->edepth - 8))) + /((1 << p->edepth) - 1.0); } } @@ -361,8 +373,11 @@ int ddebug /* >0 to print debug statements to stderr */ p->ncix = 1; - p->pdepth = 8; /* Assume this by API */ - p->edepth = 8; + p->fdepth = 8; /* Assume this by API */ + p->rdepth = p->fdepth; /* Assumed */ + p->ndepth = p->rdepth; /* Assumed */ + p->nent = 0; /* No ramdac */ + p->edepth = 8; /* Assumed */ /* Basic object is initialised, so create a web server */ diff --git a/spectro/xdg_bds.c b/spectro/xdg_bds.c index c1805ed..e24e4ab 100644 --- a/spectro/xdg_bds.c +++ b/spectro/xdg_bds.c @@ -169,20 +169,22 @@ static char *append(char *in, char *app) { return rv; } -/* Append a ':' or ';' then a string. Free in. Return NULL on error. */ +/* Append a ':' or ';' then a string. Free in. */ +/* Return NULL on error. */ static char *cappend(char *in, char *app) { - int inlen; + int inlen, aplen; char *rv; inlen = strlen(in); + aplen = strlen(app); - if ((rv = malloc(inlen + 1 + strlen(app) + 1)) == NULL) { + if ((rv = malloc(inlen + 1 + aplen + 1)) == NULL) { a1loge(g_log, 1, "xdg_bds: cappend malloc failed\n"); free(in); return NULL; } strcpy(rv, in); - if (inlen > 1) + if (inlen > 0 && in[inlen-1] != SSEP && aplen > 0) strcat(rv, SSEPS); strcat(rv, app); free(in); @@ -190,20 +192,22 @@ static char *cappend(char *in, char *app) { return rv; } -/* Append a '/' then a string. Free in. Return NULL on error. */ +/* Append a '/' then a string. Free in. */ +/* Return NULL on error. */ static char *dappend(char *in, char *app) { - int inlen; + int inlen, aplen; char *rv; inlen = strlen(in); + aplen = strlen(app); - if ((rv = malloc(inlen + 1 + strlen(app) + 1)) == NULL) { + if ((rv = malloc(inlen + 1 + aplen + 1)) == NULL) { a1loge(g_log, 1, "xdg_bds: dappend malloc failed\n"); free(in); return NULL; } strcpy(rv, in); - if (inlen > 1 && in[inlen-1] != '/') + if (inlen > 0 && in[inlen-1] != '/') strcat(rv, "/"); strcat(rv, app); free(in); @@ -332,7 +336,7 @@ int xdg_bds( if (getenv("HOME") != NULL) path = dappend(path, ".local/share"); #else -#ifdef __APPLE__ +#ifdef UNIX_APPLE path = dappend(path, "Library/Application Support"); #else /* Unix, Default */ path = dappend(path, ".local/share"); @@ -380,7 +384,7 @@ int xdg_bds( if (getenv("HOME") != NULL) path = dappend(path, ".config"); #else -#ifdef __APPLE__ +#ifdef UNIX_APPLE path = dappend(path, "Library/Preferences"); #else /* Unix, Default */ path = dappend(path, ".config"); @@ -435,7 +439,7 @@ int xdg_bds( else path = dappend(path, "Cache"); #else -#ifdef __APPLE__ +#ifdef UNIX_APPLE path = dappend(path, "Library/Caches"); #else /* Unix, Default */ path = dappend(path, ".cache"); @@ -477,7 +481,7 @@ int xdg_bds( } path = cappend(path, home); #else -#ifdef __APPLE__ +#ifdef UNIX_APPLE path = cappend(path, "/Library"); #else path = cappend(path, "/usr/local/share:/usr/share"); @@ -508,7 +512,7 @@ int xdg_bds( } path = cappend(path, home); #else -#ifdef __APPLE__ +#ifdef UNIX_APPLE path = cappend(path, "/Library/Preferences"); #else path = cappend(path, "/etc/xdg"); diff --git a/spectro/xdg_bds.h b/spectro/xdg_bds.h index 5c29790..4fedbea 100644 --- a/spectro/xdg_bds.h +++ b/spectro/xdg_bds.h @@ -70,7 +70,7 @@ typedef enum { #endif /* ONLY use this for xdg_data type */ -#ifdef __APPLE__ /* fudge to assist OS X migration from */ +#ifdef UNIX_APPLE /* fudge to assist OS X migration from */ #define XDG_FUDGE SSEPS "../" /* Library/color to Library/Application Support/ArgyllCMS */ #else #define XDG_FUDGE SSEPS diff --git a/spectro/xrga.c b/spectro/xrga.c new file mode 100644 index 0000000..0bf22fa --- /dev/null +++ b/spectro/xrga.c @@ -0,0 +1,224 @@ + +/* + * This file contains resources to translate colors to/from + * X-Rites XRGA calibration standard. This only applies to + * reflective measurements from historical Gretag-Macbeth & X-Rite + * instruments, and current X-Rite instruments. + */ + +/* + * Author: Graeme W. Gill + * Date: 9/2/2016 + * Version: 1.00 + * + * Copyright 2016 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- + * see the License2.txt file for licencing details. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#include <fcntl.h> +#if defined(UNIX) +# include <utime.h> +#else +# include <sys/utime.h> +#endif +#include <sys/stat.h> +#include <stdarg.h> +#ifndef SALONEINSTLIB +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#else /* !SALONEINSTLIB */ +#include "sa_config.h" +#include "numsup.h" +#endif /* !SALONEINSTLIB */ +#ifndef SALONEINSTLIB +# include "plot.h" +#endif +#include "xspect.h" +#include "insttypes.h" +#include "conv.h" +#include "icoms.h" +#include "inst.h" +#include "rspec.h" + +#include "xrga.h" + +/* A conversion equation */ +typedef struct { + double gain0, gain1; /* Destination gain at 550 nm, slope */ + double wl0; /* Wavlength shift at 550nm to source - assumed constant */ +} xrga_eqn; + +/* XRGA conversion values in order [pol][src][dst] */ +/* Parameters are gain at 550nm, slope of gain per nm, src wavelengh offset in nm. */ +/* These values were inferred from the results one gets from processing spectral */ +/* values though X-Rite's conversion routines. */ + +xrga_eqn xrga_equations[2][3][3] = { + { /* Unpolarized */ + { + { 1.000000, 0.000000, 0.000000 }, /* XRDI -> XRDI */ + { 1.000046, -0.000050, -1.400568 }, /* XRDI -> GMDI */ + { 1.000005, -0.000025, -0.400084 } /* XRDI -> XRGA */ + }, + { + { 1.000046, 0.000050, 1.400568 }, /* GMDI -> XRDI */ + { 1.000000, 0.000000, 0.000000 }, /* GMDI -> GMDI */ + { 1.000015, 0.000025, 1.000207 } /* GMDI -> XRGA */ + }, + { + { 1.000005, 0.000025, 0.400084 }, /* XRGA -> XRDI */ + { 1.000015, -0.000025, -1.000207 }, /* XRGA -> GMDI */ + { 1.000000, 0.000000, 0.000000 } /* XRGA -> XRGA */ + } + }, + { /* Polarized */ + { + { 1.000000, 0.000000, 0.000000 }, /* XRDI -> XRDI */ + { 1.028710, -0.000081, -1.399477 }, /* XRDI -> GMDI */ + { 1.000005, -0.000025, -0.400084 } /* XRDI -> XRGA */ + }, + { + { 0.971957, 0.000072, 1.398829 }, /* GMDI -> XRDI */ + { 1.000000, 0.000000, 0.000000 }, /* GMDI -> GMDI */ + { 0.971975, 0.000049, 0.998938 } /* GMDI -> XRGA */ + }, + { + { 1.000005, 0.000025, 0.400084 }, /* XRGA -> XRDI */ + { 1.028711, -0.000056, -0.999421 }, /* XRGA -> GMDI */ + { 1.000000, 0.000000, 0.000000 } /* XRGA -> XRGA */ + } + } +}; + + +/* Core conversion code. dst and src are assumed to be different xspect's */ +static void convert_xrga(xspect *dst, xspect *src, xrga_eqn *eq) { + int j; + + XSPECT_COPY_INFO(dst, src); /* Copy parameters */ + + for (j = 0; j < dst->spec_n; j++) { + double dw, sw, ga; + double spcing, f; + double y[4], yw; + double x[4]; + int i; + + dw = XSPECT_XWL(dst, j); /* Destination wavelength */ + sw = dw + eq->wl0; /* Source wavelength */ + ga = eq->gain0 + (dw - 550.0) * eq->gain1; /* Gain at this dest wl */ + + /* Compute fraction 0.0 - 1.0 out of known spectrum. */ + /* Place it so that the target wavelength lands in middle section */ + /* of Lagrange basis points. */ + spcing = (src->spec_wl_long - src->spec_wl_short)/(src->spec_n-1.0); + f = (sw - src->spec_wl_short) / (src->spec_wl_long - src->spec_wl_short); + f *= (src->spec_n - 1.0); + i = (int)floor(f); /* Base grid coordinate */ + + if (i < 1) /* Limit to valid Lagrange basis index range, */ + i = 1; /* and extrapolate from that at the ends. */ + else if (i > (src->spec_n - 3)) + i = (src->spec_n - 3); + + /* Setup the surrounding values */ + x[0] = src->spec_wl_short + (i-1) * spcing; + y[0] = src->spec[i-1]; + x[1] = src->spec_wl_short + i * spcing; + y[1] = src->spec[i]; + x[2] = src->spec_wl_short + (i+1) * spcing; + y[2] = src->spec[i+1]; + x[3] = src->spec_wl_short + (i+2) * spcing; + y[3] = src->spec[i+2]; + + + /* Compute interpolated value using Lagrange: */ + yw = y[0] * (sw-x[1]) * (sw-x[2]) * (sw-x[3])/((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3])) + + y[1] * (sw-x[0]) * (sw-x[2]) * (sw-x[3])/((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3])) + + y[2] * (sw-x[0]) * (sw-x[1]) * (sw-x[3])/((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3])) + + y[3] * (sw-x[0]) * (sw-x[1]) * (sw-x[2])/((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2])); + + yw *= ga; + + dst->spec[j] = yw; + } +} + +/* Apply a conversion from one calibration standard to another to an xspect */ +void xspec_convert_xrga(xspect *dst, xspect *srcp, xcalpol pol, xcalstd dsp, xcalstd ssp) { + xrga_eqn *eq; + xspect tmp, *src = srcp; + + /* If no conversion and no copy needed */ + if ((ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp) + && dst == src) + return; + + /* If no conversion needed */ + if (ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp) { + *dst = *src; /* Struct copy */ + return; + } + + /* If the dest is the same as the src, make a temporary copy */ + if (dst == src) { + tmp = *src; /* Struct copy */ + src = &tmp; + + } else { + XSPECT_COPY_INFO(dst, src); /* Copy parameters */ + } + + eq = &xrga_equations[pol][ssp][dsp]; + convert_xrga(dst, src, eq); +} + +/* Apply a conversion from one calibration standard to another to an array of ipatch's */ +void ipatch_convert_xrga(ipatch *vals, int nvals, + xcalpol pol, xcalstd dsp, xcalstd ssp, int clamp) { + xrga_eqn *eq; + xspect tmp; + xsp2cie *conv = NULL; /* Spectral to XYZ conversion object */ + int i; + + /* If no conversion needed */ + if (ssp == xcalstd_native || dsp == xcalstd_native + || dsp == ssp || nvals <= 0) + return; + + /* Conversion to use */ + eq = &xrga_equations[pol][ssp][dsp]; + + for (i = 0; i < nvals; i++) { + if (vals[i].mtype != inst_mrt_reflective + || vals[i].sp.spec_n <= 0) { + continue; + } + tmp = vals[i].sp; // Struct copy */ + convert_xrga(&vals[i].sp, &tmp, eq); + + /* Re-compute XYZ */ + if (vals[i].XYZ_v) { + if (conv == NULL) { + conv = new_xsp2cie(icxIT_D50, NULL, icxOT_CIE_1931_2, + NULL, icSigXYZData, (icxClamping)clamp); + } + conv->convert(conv, vals[i].XYZ, &vals[i].sp); + vals[i].XYZ_v = 1; + vals[i].XYZ[0] *= 100.0; /* Convert to % */ + vals[i].XYZ[1] *= 100.0; + vals[i].XYZ[2] *= 100.0; + } + } + if (conv != NULL) + conv->del(conv); +} diff --git a/spectro/xrga.h b/spectro/xrga.h new file mode 100644 index 0000000..0605fc8 --- /dev/null +++ b/spectro/xrga.h @@ -0,0 +1,86 @@ + +#ifndef XRGA_H +#define XRGA_H + +/* + * This file contains resources to translate colors to/from + * X-Rites XRGA calibration standard. This only applies to + * reflective measurements from historical Gretag-Macbeth & X-Rite + * instruments, and current X-Rite instruments. + */ + +/* + * Author: Graeme W. Gill + * Date: 9/2/2016 + * Version: 1.00 + * + * Copyright 2016 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- + * see the License2.txt file for licencing details. + */ + +#ifdef __cplusplus + extern "C" { +#endif + +typedef enum { + xcalstd_nonpol = 0, /* Unpolarized */ + xcalstd_pol = 1 /* Polarized */ +} xcalpol; + +/* Apply a conversion from one calibration standard to another to an xspect. */ +void xspec_convert_xrga(xspect *dst, xspect *srcp, xcalpol pol, xcalstd dsp, xcalstd ssp); + +/* Apply a conversion from one calibration standard to another to an array of ipatch's */ +void ipatch_convert_xrga(ipatch *vals, int nvals, + xcalpol pol, xcalstd dsp, xcalstd ssp, int clamp); + +/* Macro returns true if a conversion is needed */ +#define XCALSTD_NEEDED(ssp, dsp) \ + ((ssp) != xcalstd_native && (dsp) != xcalstd_native && (dsp) != (ssp)) + +#ifdef __cplusplus + } +#endif + +#endif /* XRGA_H */ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + |