diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2015-05-01 16:24:15 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2015-05-01 16:24:15 +0200 |
commit | a30ba67504ffd12c4db499adbb5ce47a7d1f6036 (patch) | |
tree | 9ae1a7e3849dda6bbb5c578232f6f2fa5b2e7e7e /spectro | |
parent | 89e99e8a827859729729dfc92d74be4a8f96f1a4 (diff) | |
parent | 094535c010320967639e8e86f974d878e80baa72 (diff) |
New release 1.7.0
Diffstat (limited to 'spectro')
91 files changed, 13145 insertions, 3388 deletions
diff --git a/spectro/Jamfile b/spectro/Jamfile index b5ad548..3aedce6 100644 --- a/spectro/Jamfile +++ b/spectro/Jamfile @@ -10,13 +10,15 @@ if $(OS) = MACOSX { ObjectCcFlags dispwin_dispwin : -ObjC ; } +DEFINES += ENABLE_FTDI ; + MADVRSOURCE = ; # Setup the right hardware access libraries if $(NT) { MADVRSOURCE = madvrwin.c ; - if $(USE_LIBUSB) = true { + if $(USE_LIBUSB) = true { DEFINES += USE_LIBUSB ; if $(USE_LIBUSB1) = true { DEFINES += USE_LIBUSB1 ; @@ -64,7 +66,7 @@ if $(UNIX) { } #Products -Libraries = libinsttypes libinst libdisp libconv libinstapp ; +Libraries = libinsttypes libdisptechs libinst libdisp libconv libinstapp ; Executables = dispwin synthcal dispread dispcal fakeread synthread chartread spotread illumread ccxxmake spec2cie average oeminst ; Headers = inst.h ; @@ -79,20 +81,20 @@ InstallFile $(DESTDIR)$(PREFIX)/$(REFSUBDIR) : $(Samples) ; if $(UNIX) && $(OS) != MACOSX { # Micro Unix CMM for handling monitor profile association CMMHDRS = ../ucmm ; - CMMLIBS = ../ucmm/libucmm ../jcnf/libjcnf ../jcnf/yajl/libyajl ; + CMMLIBS = ../ucmm/libucmm ../jcnf/libjcnf ../yajl/libyajl ; } HDRS = ../h ../numlib ../icc ../cgats ../rspl ../xicc ../gamut ../spectro - ../plot $(LIBUSBHDRS) $(CMMHDRS) ; + ../plot ../render ../ccast $(LIBUSBHDRS) $(CMMHDRS) ; # Instrument access library library SER_INSTS = dtp22.c dtp41.c dtp51.c ss.c ss_imp.c ; USB_INSTS = dtp20.c i1disp.c i1d3.c i1pro.c i1pro_imp.c munki.c munki_imp.c hcfr.c spyd2.c huey.c - colorhug.c usbio.c hidio.c ; + colorhug.c ex1.c usbio.c hidio.c ; -FAST_SER_INSTS = specbos.c ; +FAST_SER_INSTS = specbos.c kleink10.c ; SER_USB_INSTS = dtp92.c ; @@ -115,26 +117,30 @@ if $(USE_SERIAL) = true || $(USE_USB) = true { INST_SRCS += $(SER_USB_INSTS) ; } -if $(USE_DEMOINST) = true && [ GLOB . : demoinst.c ] { +if $(USE_DEMOINST) = true && [ GLOB [ NormPaths . ] : demoinst.c ] { echo "Compiling demo instrument support" ; DEFINES += ENABLE_DEMOINST ; INST_SRCS += demoinst.c ; } -Library libinst : inst.c insttypes.c icoms.c $(INST_SRCS) ; +Library libinst : inst.c insttypes.c icoms.c disptechs.c $(INST_SRCS) ; # Display access library ObjectKeep mongoose.c ; -Library libdisp : dispsup.c dispwin.c webwin.c $(MADVRSOURCE) : : : $(LibWinH) : mongoose ; +Library libdisp : dispsup.c dispwin.c webwin.c ccwin.c + $(MADVRSOURCE) : : : $(LibWinH) : mongoose ; -# Instrument types utility functions library. Use this instead of libinst when */ -# applications need to know about different instrument types, but not access them. */ +# Instrument and Display types utility functions library. Use these instead of libinst when */ +# applications need to know about different instrument or display types, but not access them. */ # (Note we're working around a bug in Jam caused by objects shared between libraries) Object insttypes2 : insttypes.c ; LibraryFromObjects libinsttypes : insttypes2 ; +Object disptechs2 : disptechs.c ; +LibraryFromObjects libdisptechs : disptechs2 ; + # System utility functions (keyboard, msec_*, thread) -Library libconv : xdg_bds.c aglob.c conv.c $(CONVFILE) : : : $(LibWinH) ; +Library libconv : xdg_bds.c aglob.c conv.c base64.c $(CONVFILE) : : : $(LibWinH) ; # Command line application instrument related convenience functions Library libinstapp : instappsup.c ; @@ -146,7 +152,10 @@ LINKLIBS = libinst libinstapp ../xicc/libxcolorants ../xicc/libxicc ../gamut/libgamut ../rspl/librspl ../cgats/libcgats - ../icc/libicc ../plot/libplot ../plot/libvrml ../numlib/libnum + ../icc/libicc ../plot/libplot ../plot/libvrml + ../ccast/libccast ../ccast/axTLS/libaxtls ../yajl/libyajl ../render/librender + $(TIFFLIB) $(JPEGLIB) $(PNGLIB) $(ZLIB) + ../numlib/libui ../numlib/libnum $(CMMLIBS) libconv ; if $(LIBUSB_IS_DLL) = true { @@ -169,7 +178,7 @@ Main spotread : spotread.c : : : : : ; # Test code if $(HOME) = "d:\\usr\\graeme" && $(PWD) = "/src/argyll/spectro" { - Main setoem : setoem.c : : : : : ; + Main setoem : setoem.c : : : : : ../numlib/libui ; } # CCMX and CCSStool @@ -188,12 +197,12 @@ Main dispcal : dispcal.c : : : ../target : : libdisp ; Main dispread : dispread.c : : : : : libdisp ; #display test window test/Lut loader utility -# [ Could avoid need for libisnt libusb etc. +# [ Could avoid need for libinst libusb etc. # by separating system dependent utils to a separate library .] -MainVariant dispwin : dispwin.c webwin.c $(MADVRSOURCE) : : STANDALONE_TEST : : mongoose : $(LibWin) ; +MainVariant dispwin : dispwin.c webwin.c ccwin.c $(MADVRSOURCE) : : STANDALONE_TEST : : mongoose : $(LibWin) ; -LINKLIBS = libinsttypes ../xicc/libxicc ../gamut/libgamut ../rspl/librspl - ../cgats/libcgats ../icc/libicc ../numlib/libnum ../plot/libplot +LINKLIBS = libinsttypes libdisptechs ../xicc/libxicc ../gamut/libgamut ../rspl/librspl + ../cgats/libcgats ../icc/libicc ../plot/libplot ../numlib/libnum ../numlib/libui ../plot/libvrml ; # Fake device print/read utility using ICC profile @@ -242,6 +251,9 @@ MainVariant xdg_bds : xdg_bds.c : : STANDALONE_TEST : : : libconv ; # test code #Main t : t.c ; #Main tt : tt.c ; +#Main ttt : ttt.c ; +#Main t1 : t1.c ; +#Main tt : tt.c ; #Main t8 : t8.c ; #Main t9 : t9.c ; #Main i1d3eval : i1d3eval.c ; @@ -252,8 +264,8 @@ if $(HOME) = "d:\\usr\\graeme" && $(PWD) = "/src/argyll/spectro" { # /SUBSYSTEM:WINDOWS on NT link ? # GuiBin oemdnld ; Main oemdnld : oemdnld.c : : : : oemarch vinflate inflate LzmaDec mongoose : libconv ; - Main fakeindev : fakeindev.c ; - Main cmtest : cmtest.c : : : : : libconv ; +# Main fakeindev : fakeindev.c ; +# Main cmtest : cmtest.c : : : : : libconv ; # Main webdisp : webdisp.c : : : : : libconv ; } diff --git a/spectro/Makefile.SA b/spectro/Makefile.SA index b1fe55f..2d57742 100644 --- a/spectro/Makefile.SA +++ b/spectro/Makefile.SA @@ -38,13 +38,13 @@ 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 spyd2setup.h spyd2PLD.h specbos.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) +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) -HEADERS = pollem.h conv.h aglob.h hidio.h icoms.h inst.c inst.h insttypeinst.h insttypes.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 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 # libinst objects -OBJS = conv$(SUFOBJ) aglob$(SUFOBJ) inst$(SUFOBJ) numsup$(SUFOBJ) rspl1$(SUFOBJ) icoms$(SUFOBJ) usbio$(SUFOBJ) hidio$(SUFOBJ) insttypes$(SUFOBJ) pollem$(SUFOBJ) xspect$(SUFOBJ) xdg_bds$(SUFOBJ) ccss$(SUFOBJ) ccmx$(SUFOBJ) pars$(SUFOBJ) cgats$(SUFOBJ) $(INSOBJS) +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) # instrument library @@ -76,6 +76,9 @@ hidio$(SUFOBJ): hidio.c $(HEADERS) insttypes$(SUFOBJ): insttypes.c $(HEADERS) $(CC) insttypes.c +disptechs$(SUFOBJ): disptechs.c $(HEADERS) + $(CC) disptechs.c + pollem$(SUFOBJ): pollem.c $(HEADERS) $(CC) pollem.c @@ -142,6 +145,9 @@ spyd2$(SUFOBJ): spyd2.c $(HEADERS) specbos$(SUFOBJ): specbos.c $(HEADERS) $(CC) specbos.c +kleink10$(SUFOBJ): kleink10.c $(HEADERS) + $(CC) kleink10.c + oemarch$(SUFOBJ): oemarch.c $(HEADERS) $(CC) oemarch.c diff --git a/spectro/afiles b/spectro/afiles index e3fee3e..ecea51d 100644 --- a/spectro/afiles +++ b/spectro/afiles @@ -19,6 +19,8 @@ webwin.h webwin.c madvrwin.h madvrwin.c +ccwin.h +ccwin.c mongoose.h mongoose.c insttypes.h @@ -26,6 +28,8 @@ insttypes.c insttypeinst.h inst.c inst.h +disptechs.h +disptechs.c instappsup.c instappsup.h dtp20.c @@ -58,10 +62,10 @@ hcfr.c hcfr.h spyd2.c spyd2.h -spyd2setup.h -spyd2PLD.h specbos.c specbos.h +kleink10.c +kleink10.h oemarch.h oemarch.c oeminst.c @@ -74,6 +78,8 @@ huey.c huey.h colorhug.c colorhug.h +ex1.c +ex1.h spec2cie.c average.c conv.h @@ -103,6 +109,8 @@ synthcal.c spotread.c fakeread.c synthread.c +base64.h +base64.c linear.sp SOtele.sp linear.cal diff --git a/spectro/aglob.c b/spectro/aglob.c index 8d4c846..e700f76 100644 --- a/spectro/aglob.c +++ b/spectro/aglob.c @@ -53,6 +53,31 @@ #include "numsup.h" #include "aglob.h" +/* + For MSWin should convert spath to UTF16 and call + wide version of FindFirstFileW with "\\?\" pre-pended to + the path. Convert results back to UTF8. (No Nt2K support ?) + + Will setting codepage 65001 work OK for this ? (Aparently not). + _setmbcp(65001) + + _wfindfirst() + _wfindnext() + _wfopen() + WideCharToMultiByte + MultiByteToWideCharTo + GetShortPathNameW to convert for libraries, but not 100% reliable + + See <http://utf8everywhere.org/> + <http://www.nubaria.com/en/blog/?p=289> + <http://stackoverflow.com/questions/166503/utf-8-in-windows> + <http://alfps.wordpress.com/2011/11/22/unicode-part-1-windows-console-io-approaches/> + + _setmode + _O_U8TEXT on files to output UTF8 ? + fopen(&fp, "newfile.txt", "rt+, ccs=UTF-8"); + + */ + /* Create the aglob */ /* Return nz on malloc error */ int aglob_create(aglob *g, char *spath) { diff --git a/spectro/average.c b/spectro/average.c index 5e659b8..5a8fb3f 100644 --- a/spectro/average.c +++ b/spectro/average.c @@ -1,6 +1,8 @@ /* * Argyll Color Correction System * Average one or more .ti3 (or other CGATS like) file values together. + * If just one file is supplied, all patches within it with + * the same device value are averaged together. * * Author: Graeme W. Gill * Date: 18/1/2011 @@ -85,7 +87,7 @@ int main(int argc, char *argv[]) { error_program = "average"; - if (argc <= 3) + if (argc < 2) usage("Too few arguments (%d, minimum is 2)",argc-1); /* Process the arguments */ @@ -147,7 +149,7 @@ int main(int argc, char *argv[]) { ninps--; /* Number of inputs */ - /* Open and read each input file */ + /* Open and read each input file, and create output file */ for (n = 0; n <= ninps; n++) { if ((inps[n].c = new_cgats()) == NULL) @@ -190,16 +192,18 @@ 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]); } - /* Duplicate all of the data */ - if ((setel = (cgats_set_elem *)malloc( - sizeof(cgats_set_elem) * inps[0].c->t[n].nfields)) == NULL) - error("Malloc failed!"); - - for (i = 0; i < inps[0].c->t[n].nsets; i++) { - inps[0].c->get_setarr(inps[0].c, n, i, setel); - ocg->add_setarr(ocg, n, setel); + if (ninps > 1) { + /* Duplicate all of the data */ + if ((setel = (cgats_set_elem *)malloc( + sizeof(cgats_set_elem) * inps[0].c->t[n].nfields)) == NULL) + error("Malloc failed!"); + + for (i = 0; i < inps[0].c->t[n].nsets; i++) { + inps[0].c->get_setarr(inps[0].c, n, i, setel); + ocg->add_setarr(ocg, n, setel); + } + free(setel); } - free(setel); } /* Figure out the indexes of the device channels */ @@ -295,44 +299,41 @@ int main(int argc, char *argv[]) { sizeof(cgats_set_elem) * inps[0].c->t[0].nfields)) == NULL) error("Malloc failed!"); - /* Process all the other input files */ - for (n = 1; n < ninps; n++) { + /* If averaging values within the one file */ + if (ninps == 1) { + int *valdone; + double npatches; + int k; + n = 0; /* Output set index */ - /* Check all the fields match */ - 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); - for (j = 0; j < inps[0].c->t[0].nfields; j++) { - if (inps[0].c->t[0].ftype[j] != inps[n].c->t[0].ftype[j]) - error ("File '%s' field no. %d named '%s' doesn't match file '%s' field '%s'", - inps[n].name, j, inps[n].c->t[0].fsym[j], inps[0].name, inps[0].c->t[0].fsym[j]); - } + if ((valdone = (int *)calloc(inps[0].c->t[0].nsets, sizeof(int))) == NULL) + error("Malloc failed!"); - /* If merging, append all the values */ - if (domerge) { - for (i = 0; i < inps[n].c->t[0].nsets; i++) { - inps[n].c->get_setarr(inps[n].c, 0, i, setel); - ocg->add_setarr(ocg, 0, setel); - } + for (i = 0; i < inps[0].c->t[0].nsets; i++) { - } else { /* Averaging */ - /* Check the number of values matches */ - 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 (i = 0; i < inps[n].c->t[0].nsets; i++) { + if (valdone[i]) + continue; + + inps[0].c->get_setarr(inps[0].c, 0, i, setel); + ocg->add_setarr(ocg, 0, setel); + npatches = 1.0; - /* Check that the device values match */ + /* 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[n].c->t[0].fdata[i][chix[j]]); + - *((double *)inps[0].c->t[0].fdata[k][chix[j]]); - if (diff > 0.001) - 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); + if (fabs(diff) > 0.001) { + break; + } + } + if (j < nchan) { + continue; } /* Add all the non-device real field values */ @@ -351,18 +352,13 @@ int main(int argc, char *argv[]) { if (jj < nchan) continue; - *((double *)ocg->t[0].fdata[i][j]) - += *((double *)inps[n].c->t[0].fdata[i][j]); + *((double *)ocg->t[0].fdata[n][j]) + += *((double *)inps[0].c->t[0].fdata[k][j]); } + npatches++; + valdone[k] = 1; } - } - } - - /* If averaging, divide out the number of files */ - if (!domerge) { - - for (i = 0; i < inps[n].c->t[0].nsets; i++) { - + /* Average them out */ for (j = 0; j < inps[0].c->t[0].nfields; j++) { int jj; @@ -378,7 +374,101 @@ int main(int argc, char *argv[]) { if (jj < nchan) continue; - *((double *)ocg->t[0].fdata[i][j]) /= (double)ninps; + *((double *)ocg->t[0].fdata[n][j]) /= npatches; + } + n++; /* One more output set */ + } + + free(valdone); + + /* Averaging patches between identical files, */ + /* or concatenating (merging) several files */ + } else { + /* Process all the other input files */ + for (n = 1; n < ninps; n++) { + + /* Check all the fields match */ + 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); + for (j = 0; j < inps[0].c->t[0].nfields; j++) { + if (inps[0].c->t[0].ftype[j] != inps[n].c->t[0].ftype[j]) + error ("File '%s' field no. %d named '%s' doesn't match file '%s' field '%s'", + inps[n].name, j, inps[n].c->t[0].fsym[j], inps[0].name, inps[0].c->t[0].fsym[j]); + } + + /* If merging, append all the values */ + if (domerge) { + for (i = 0; i < inps[n].c->t[0].nsets; i++) { + inps[n].c->get_setarr(inps[n].c, 0, i, setel); + ocg->add_setarr(ocg, 0, setel); + } + + } else { /* Averaging */ + /* Check the number of values matches */ + 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 (i = 0; i < inps[n].c->t[0].nsets; i++) { + + /* Check that 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[n].c->t[0].fdata[i][chix[j]]); + + if (fabs(diff) > 0.001) + 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 (!domerge) { + + 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; + } + if (jj < nchan) + continue; + + *((double *)ocg->t[0].fdata[i][j]) /= (double)ninps; + } } } } diff --git a/spectro/base64.c b/spectro/base64.c new file mode 100644 index 0000000..388d121 --- /dev/null +++ b/spectro/base64.c @@ -0,0 +1,215 @@ + +/* + * Argyll Color Correction System + * + * Very simple & concise base64 encoder/decoder + * + * Author: Graeme W. Gill + * + * Copyright 2014, 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> +# define DLL_LOCAL +#include "base64.h" + +static int enc(int val) { + val &= 0x3f; + + if (val <= 25) + return ('A' + val); + if (val <= 51) + return ('a' + val - 26); + if (val <= 61) + return ('0' + val - 52); + if (val == 62) + return '+'; + return '/'; +} + +/* Encode slen bytes into nul terminated string. */ +/* if dlen is !NULL, then set it to resulting string length excluding nul */ +/* We assume that the destination buffer is long enough at EBASE64LEN */ +void DLL_LOCAL ebase64(int *dlen, char *dst, unsigned char *src, int slen) { + unsigned char buf[3]; + int i, j; + + /* Do this 3 characters at a time */ + for (j = i = 0; i < slen; i += 3) { + int ib; + + /* Grab 3 bytes or zero pad, and count bits */ + buf[0] = src[i]; ib = 8; + buf[1] = (i+1) < slen ? (ib += 8, src[i+1]) : 0; + buf[2] = (i+2) < slen ? (ib += 8, src[i+2]) : 0; + + /* Output up to 4 encoded characters */ + dst[j] = enc(buf[0] >> 2), j++; + if (ib > 6) { + dst[j] = enc(buf[0] << 4 | buf[1] >> 4), j++; + if (ib > 12) { + dst[j] = enc(buf[1] << 2 | buf[2] >> 6), j++; + if (ib > 18) + dst[j] = enc(buf[2]), j++; + } + } + } + if (dlen != NULL) + *dlen = j; + + dst[j++] = '\000'; +} + +/* Return binary value corresponding to encoded character, */ +/* -1 for invalid character, -2 if nul terminator */ +/* (We're assuming ASCII encoding) */ +static int dec(int val) { + val &= 0xff; + + if (val == 0) + return -2; + if (val == '+') + return 62; + if (val == '/') + return 63; + if (val < '0') + return -1; + if (val <= '9') + return val - '0' + 52; + if (val < 'A') + return -1; + if (val <= 'Z') + return val - 'A'; + if (val < 'a') + return -1; + if (val <= 'z') + return val - 'a' + 26; + return -1; +} + +/* Decode nul terminated string into bytes, ignoring illegal characters. */ +/* if dlen is !NULL, then set it to resulting byte length */ +/* We assume that the destination buffer is long enough at DBASE64LEN */ +void DLL_LOCAL dbase64(int *dlen, unsigned char *dst, char *src) { + int buf[4]; + int j; + + /* Do this 4 characters at a time */ + for (j = 0;;) { + int v, ib; + + /* Grab 4 characters skipping illegals, and count bits */ + while ((buf[0] = dec(*src++)) == -1) + ; + if (buf[0] == -2) + break; + ib = 6; + while ((buf[1] = dec(*src++)) == -1) + ; + if (buf[1] != -2) { + ib += 6; + while ((buf[2] = dec(*src++)) == -1) + ; + if (buf[2] != -2) { + ib += 6; + while ((buf[3] = dec(*src++)) == -1) + ; + if (buf[3] != -2) + ib += 6; + else + buf[3] = 0; + } else + buf[2] = 0; + } else + buf[1] = 0; + + /* Output up to 3 decoded bytes */ + dst[j] = buf[0] << 2 | buf[1] >> 4, j++; + if (ib > 12) { + dst[j] = buf[1] << 4 | buf[2] >> 2, j++; + if (ib > 18) { + dst[j] = buf[2] << 6 | buf[3], j++; + continue; /* We didn't run out */ + } + } + break; /* Must be done if didn't continue */ + } + if (dlen != NULL) + *dlen = j; +} + + +#ifdef STANDALONE_TEST +/* test base64 code */ + +#include <numlib.h> + +#define MAXLEN 30 + +int main() { + unsigned char src[MAXLEN], check[MAXLEN]; + char dst[EBASE64LEN(MAXLEN) + 1]; + int n, slen, dlen, chlen, i, j; + + printf("Testing base64 encode/decode:\n"); + + for (n = 0; n < 10000000; n++) { + + /* Create a random binary */ + slen = i_rand(0, MAXLEN); + for (i = 0; i < slen; i++) + src[i] = i_rand(0, 255); + + /* Encode it */ + ebase64(&dlen, dst, src, slen); + + if (dlen != EBASE64LEN(slen)) { + error("%d: Wrong encoded length, src %d, is %d should be %d\n",n,slen,dlen,EBASE64LEN(slen)); + } + + /* Decode it */ + dbase64(&chlen, check, dst); + + if (chlen != DBASE64LEN(dlen)) { + error("%d: Wrong decoded length, enc %d, is %d should be %d\n",n,dlen,chlen,DBASE64LEN(dlen)); + } + + /* Check characters match */ + for (i = 0; i < slen; i++) { + if (src[i] != check[i]) { + for (j = 0; j < slen; j++) { + printf("%02X ",src[j]); + if ((j % 3) == 2) + printf(" "); + } + printf("\n"); + + for (j = 0; j < dlen; j++) { + printf("%c ",dst[j]); + if ((j % 4) == 3) + printf(" "); + } + printf("\n"); + for (j = 0; j < slen; j++) { + printf("%02X ",check[j]); + if ((j % 3) == 2) + printf(" "); + } + printf("\n\n"); + printf("src len %d, dst len %d\n",slen, dlen); + error("%d: Verify error at byte %d, is 0x%x should be 0x%x\n",n,i,check[i],src[i]); + } + } + + if ((n % 10000) == 0) + printf("done %d tests\n",n); + } + printf("Testing %d conversions base64 complete\n",n); +} + +#endif /* STANDALONE_TEST */ diff --git a/spectro/base64.h b/spectro/base64.h new file mode 100644 index 0000000..726f20a --- /dev/null +++ b/spectro/base64.h @@ -0,0 +1,33 @@ + +/* + * Argyll Color Correction System + * + * Very simple & concise base64 encoder/decoder + * + * Author: Graeme W. Gill + * + * Copyright 2014, 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. + */ + + +/* The maximum encoded length given decoded length */ +#define EBASE64LEN(len) (((len) * 4 + 2)/3) + +/* Encode slen bytes into nul terminated string. */ +/* if dlen is !NULL, then set it to resulting string length excluding nul */ +/* We assume that the destination buffer is long enough at EBASE64LEN */ +void ebase64(int *dlen, char *dst, unsigned char *src, int slen); + +/* The maximum decoded length given encoded length */ +#define DBASE64LEN(len) (((len) * 3)/4) + +/* Decode nul terminated string into bytes, ignoring illegal characters. */ +/* if dlen is !NULL, then set it to resulting byte length */ +/* We assume that the destination buffer is long enough at DBASE64LEN */ +void dbase64(int *dlen, unsigned char *dst, char *src); + + diff --git a/spectro/ccwin.c b/spectro/ccwin.c new file mode 100644 index 0000000..3c0d97f --- /dev/null +++ b/spectro/ccwin.c @@ -0,0 +1,693 @@ + +/* + * Argyll Color Correction System + * ChromeCast Display target patch window + * + * Author: Graeme W. Gill + * Date: 8/9/14 + * + * Copyright 2013, 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License.txt file for licencing details. + */ + +#include <stdio.h> +#include <string.h> +#ifdef NT +# include <winsock2.h> +#endif +#ifdef UNIX +# include <sys/types.h> +# include <ifaddrs.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef __FreeBSD__ +# include <sys/socket.h> +# endif /* __FreeBSD__ */ +#endif +#include "copyright.h" +#include "aconfig.h" +#include "icc.h" +#include "numsup.h" +#include "cgats.h" +#include "conv.h" +#include "dispwin.h" +#include "conv.h" +#include "mongoose.h" +#include "ccast.h" +#include "ccwin.h" +#include "render.h" + +#undef WRITE_PNG /* [und] Write each test patch to "ccwin.png" */ +#undef CCTEST_PATTERN /* [und] Create dev. spatial test pattern */ +#undef SEND_TEST_FILE /* [und] Send this file name to ChromeCast instead of pattern */ +#undef DO_TIMING /* [und] Print rendering timing */ +#undef DEBUG /* [und] */ + +#define DDITHER 1 /* 0 = no dither - quantize to PNG 8 bit RGB value */ + /* [def] 1 = use error diffusion dithering with ccast quant model */ + /* 2 = use crafted 4x4 dither cell */ + +#define VWIDTH 1920.0 /* Video stream and display size */ +#define VHEIGHT 1080.0 + +#define IWIDTH 1280.0 /* This is the native still image framebuffer size for ChromeCasts */ +#define IHEIGHT 720.0 + +//#define STANDALONE_TEST + +#ifdef DEBUG +#define errout stderr +# define debug(xx) fprintf(errout, xx ) +# define debug2(xx) fprintf xx +# define debugr(xx) fprintf(errout, xx ) +# define debugr2(xx) fprintf xx +# define debugrr(xx) fprintf(errout, xx ) +# define debugrr2(xx) fprintf xx +# define debugrr2l(lev, xx) fprintf xx +#else +#define errout stderr +# define debug(xx) +# define debug2(xx) +# define debugr(xx) if (p->ddebug) fprintf(errout, xx ) +# define debugr2(xx) if (p->ddebug) fprintf xx +# define debugrr(xx) if (callback_ddebug) fprintf(errout, xx ) +# define debugrr2(xx) if (callback_ddebug) fprintf xx +# define debugrr2l(lev, xx) if (callback_ddebug >= lev) fprintf xx +#endif + +/* ================================================================== */ + +/* Chromwin context and (possible) web server */ +typedef struct _chws { + int verb; + int ddebug; + + int direct; /* End PNG directly, rather than using web server */ + + struct mg_context *mg; /* Mongoose context (if needed) */ + char *ws_url; /* Web server URL for accessing server */ + +// double hoff, voff; /* Input position of test square */ + double x, y; /* position of test square in pixels */ + double w, h; /* size of test square in pixels */ + int pno; /* Index to generate a sequence of URLs */ + unsigned char *ibuf; /* Memory image of .png file */ + size_t ilen; + + ccast *cc; /* ChromeCast */ + + /* Update the png image */ + int (*update)(struct _chws *p, unsigned char *ibuf, size_t ilen, double *bg); + + /* Destroy ourselves */ + void (*del)(struct _chws *p); + +} chws; + +static void chws_del(chws *p) { + + if (p->mg != NULL) + mg_stop(p->mg); + + if (p->cc != NULL) + p->cc->del(p->cc); + + if (p->ibuf != NULL) + free(p->ibuf); + + if (p->ws_url != NULL) + free(p->ws_url); + + free(p); +} + +/* Change the .png being served */ +/* Return nz on error */ +static int chws_update(chws *p, unsigned char *ibuf, size_t ilen, double *bg) { + char url[200]; + + debug("\nUpdate png\n"); + + if (p->ibuf != NULL) + free(p->ibuf); + + p->ibuf = ibuf; + p->ilen = ilen; + + /* Send the PNG swatch direct */ + if (p->direct) { + double x, y, w, h; + /* Convert x,y,w,h to relative rather than pixel size */ + + debugr2((errout,"Got x %f y %f w %f h %f\n", p->x, p->y, p->w, p->h)); + + // Convert from quantized to direct loader parameters + if (p->w < IWIDTH) + x = p->x/(IWIDTH - p->w); + else + x = 0.0; + if (p->h < IHEIGHT) + y = p->y/(IHEIGHT - p->h); + else + y = 0.0; + w = p->w/(0.1 * IWIDTH); + h = p->h/(0.1 * IWIDTH); + + debugr2((errout,"Sending direct x %f y %f w %f h %f\n", x, y, w, h)); + + if (p->cc->load(p->cc, NULL, p->ibuf, p->ilen, bg, x, y, w, h)) { + debugr2((errout,"ccwin_update direct load failed\n")); + return 1; + } + + /* Using web server */ + } else { + +#ifdef SEND_TEST_FILE + sprintf(url, "%s%s",p->ws_url, SEND_TEST_FILE); +#else + sprintf(url, "%stpatch_%d.png",p->ws_url, ++p->pno); +#endif + if (p->cc->load(p->cc, url, NULL, 0, NULL, 0.0, 0.0, 0.0, 0.0)) { + debugr2((errout,"ccwin_update server load failed\n")); + return 1; + } + } + return 0; +} + +/* Web server event handler - return the current .png image */ +static void *ccwin_ehandler(enum mg_event event, + struct mg_connection *conn) { + const struct mg_request_info *request_info = mg_get_request_info(conn); + chws *p = (chws *)mg_get_user_data(conn); + char *cp; + char sbuf[200]; + + debugr2((errout,"ccwin_ehandler()\n")); + + if (event != MG_NEW_REQUEST) { + return NULL; + } + + debugr2((errout,"Event: uri = '%s'\n",request_info->uri)); + +#ifdef SEND_TEST_FILE +#pragma message("############################# ccwin.c SEND_TEST_FILE defined ! ##") + return NULL; +#endif + + if (p->ibuf != NULL && p->ilen > 0 + && (cp = strrchr(request_info->uri, '.')) != NULL + && strcmp(cp, ".png") == 0) { + + debugr2((errout,"Event: Loading %s\n",request_info->uri)); + + debugr2((errout,"Returning current png size %d bytes\n",(int)p->ilen)); + sprintf(sbuf, + "HTTP/1.1 200 OK\r\n" + "Content-Type: image/png\r\n" + "Content-Length: %d\r\n" + "\r\n" + ,(int)p->ilen); + + mg_write(conn, sbuf, strlen(sbuf)); + mg_write(conn, p->ibuf, p->ilen); + + } else { + debugr2((errout,"Bad request or png - returning 404\n")); + sprintf(sbuf, + "HTTP/1.0 404 Not Found\r\n" + "\r\n" + "<html><body><h1>Error 404 - Not Found</h1></body></html>"); + + mg_write(conn, sbuf, strlen(sbuf)); + } + + return "yes"; +} + +chws *new_chws( +ccast_id *cc_id, /* ChromeCast to open */ +double width, double height, /* Width and height as % */ +double hoff, double voff, /* Offset from center in fraction of screen, range -1.0 .. 1.0 */ +int verb, int ddebug) { + chws *p; + const char *options[3]; + char port[50]; + int portno = 0; /* Port number allocated */ + int forcedef = 0; /* Force default reciever app. */ + + if ((p = (chws *)calloc(sizeof(chws), 1)) == NULL) { + error("new_chws: calloc failed"); + return NULL; + } + + p->verb = verb; + p->ddebug = ddebug; + + p->update = chws_update; + p->del = chws_del; + + /* We make sure we round the test patch size and */ + /* location to integer resolution so that we can know */ + /* it's exact relationship to the upsampled pixel locations. */ + + /* Setup window size and position */ + /* The default size is 10% of the width */ + p->w = floor(width/100.0 * 0.1 * IWIDTH + 0.5); + if (p->w > IWIDTH) + p->w = IWIDTH; + p->h = floor(height/100.0 * 0.1 * IWIDTH + 0.5); + if (p->h > IHEIGHT) + p->h = IHEIGHT; + + // Make offset be on an even pixel boundary, so that we know + // the up-filter phase. + p->x = floor((hoff * 0.5 + 0.5) * (IWIDTH - p->w) + 0.5); + p->y = floor((voff * 0.5 + 0.5) * (IHEIGHT - p->h) + 0.5); + if (((int)p->x) & 1) + p->x++; + if (((int)p->y) & 1) + p->y++; + + if (verb) printf("Opening ChromeCast '%s'\n",cc_id->name); + +#ifdef SEND_TEST_FILE + forcedef = 1; +#endif + + /* Connect to the chrome cast */ + if ((p->cc = new_ccast(cc_id, forcedef)) == NULL) { + error("new_ccast: failed"); + chws_del(p); + return NULL; + } + + p->direct = p->cc->get_direct_send(p->cc); + + if (!p->direct) { + /* Create a web server */ + options[0] = "listening_ports"; +// sprintf(port,"%d", 0); /* Use any available */ + sprintf(port,"%d", 8081); /* Use fixed port for Linux firewall rule */ + options[1] = port; + options[2] = NULL; + + p->mg = mg_start(&ccwin_ehandler, (void *)p, options); + + if ((p->ws_url = mg_get_url(p->mg)) == NULL) { + debugr2((errout, "mg_get_url() failed\n")); + chws_del(p); + return NULL; + } + if (p->ddebug) + printf("Created .png server at '%s'\n",p->ws_url); + } + + return p; +} + + +/* ================================================================== */ + +/* Get RAMDAC values. ->del() when finished. */ +/* Return NULL if not possible */ +static ramdac *ccwin_get_ramdac(dispwin *p) { + debugr("webdisp doesn't have a RAMDAC\n"); + return NULL; +} + +/* Set the RAMDAC values. */ +/* Return nz if not possible */ +static int ccwin_set_ramdac(dispwin *p, ramdac *r, int persist) { + debugr("webdisp doesn't have a RAMDAC\n"); + return 1; +} + +/* ----------------------------------------------- */ +/* Install a display profile and make */ +/* it the default for this display. */ +/* Return nz if failed */ +int ccwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { + debugr("webdisp doesn't support installing profiles\n"); + return 1; +} + +/* Un-Install a display profile */ +/* Return nz if failed, */ +int ccwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { + debugr("webdisp doesn't support uninstalling profiles\n"); + return 1; +} + +/* Get the currently installed display profile. */ +/* Return NULL if failed. */ +icmFile *ccwin_get_profile(dispwin *p, char *name, int mxlen) { + debugr("webdisp doesn't support getting the current profile\n"); + return NULL; +} + +/* ----------------------------------------------- */ + +/* Change the window color. */ +/* Return 1 on error, 2 on window being closed */ +static int ccwin_set_color( +dispwin *p, +double r, double g, double b /* Color values 0.0 - 1.0 */ +) { + chws *ws = (chws *)p->pcntx; + int j; + double orgb[3]; /* Previous RGB value */ + double kr, kf; + int update_delay = 0; + + debugr("ccwin_set_color called\n"); + + if (p->nowin) { + debugr("ccwin_set_color: nowin - give up\n"); + return 1; + } + + orgb[0] = p->rgb[0]; p->rgb[0] = r; + orgb[1] = p->rgb[1]; p->rgb[1] = g; + orgb[2] = p->rgb[2]; p->rgb[2] = b; + + for (j = 0; j < 3; j++) { + if (p->rgb[j] < 0.0) + p->rgb[j] = 0.0; + else if (p->rgb[j] > 1.0) + p->rgb[j] = 1.0; + p->r_rgb[j] = p->s_rgb[j] = p->rgb[j]; + if (p->out_tvenc) { + p->r_rgb[j] = p->s_rgb[j] = ((235.0 - 16.0) * p->s_rgb[j] + 16.0)/255.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); + } + } + + /* This is probably not actually thread safe... */ + p->ncix++; + +#if DDITHER != 1 +# pragma message("############################# ccwin.c DDITHER != 1 ##") +#endif + + /* Turn the color into a png file */ + { + /* We want a raster of IWIDTH x IHEIGHT pixels for web server, */ + /* or p->w x p->h for PNG direct. */ + render2d *r; + prim2d *rct; + depth2d depth = bpc8_2d; +#if DDITHER == 1 + int dither = 0x8002; /* 0x8002 = error diffuse FG only */ +#elif DDITHER == 2 + int dither = 0x4000; /* 0x4000 = no dither but don't average pixels */ + /* so as to allow pattern to come through. */ +#else + int dither = 0; /* Don't dither in renderer */ +#endif + double hres = 1.0; /* Resoltion in pix/mm */ + double vres = 1.0; /* Resoltion in pix/mm */ + double iw, ih; /* Size of page in mm (pixels) */ + double bg[3]; /* Background color */ + color2d c; + unsigned char *ibuf; /* Memory image of .png file */ + size_t ilen; +#ifdef DO_TIMING + int stime; +#endif + + if (ws->direct) { + iw = ws->w; /* Requested size */ + ih = ws->h; + } else { + iw = IWIDTH; + ih = IHEIGHT; /* Size of page in mm */ + } + + if (p->blackbg) { + bg[0] = 0.0; + bg[1] = 0.0; + bg[2] = 0.0; + } else { + bg[0] = 0.5; + bg[1] = 0.5; + bg[2] = 0.5; + } + + debugr2((errout, "ccwin_set_color iw %f ih %f\n",iw,ih)); + + if ((r = new_render2d(iw, ih, NULL, hres, vres, rgb_2d, + 0, depth, dither, +#if DDITHER == 1 + ccastQuant, +#else + NULL, +#endif + NULL)) == NULL) { + error("ccwin: new_render2d() failed"); + } + + /* Set the background color */ + c[0] = bg[0]; + c[1] = bg[1]; + c[2] = bg[2]; + r->set_defc(r, c); + + c[0] = p->r_rgb[0]; + c[1] = p->r_rgb[1]; + c[2] = p->r_rgb[2]; + if (ws->direct) + r->add(r, rct = new_rect2d(r, 0.0, 0.0, ws->w, ws->h, c)) ; + else + r->add(r, rct = new_rect2d(r, ws->x, ws->y, ws->w, ws->h, c)) ; + +#if DDITHER == 2 /* Use dither pattern */ + { + double rgb[3]; + double dpat[CCDITHSIZE][CCDITHSIZE][3]; + double (*cpat)[MXPATSIZE][MXPATSIZE][TOTC2D]; + int i, j; + + /* Get a chrome cast dither pattern to match target color */ + for (i = 0; i < 3; i++) + rgb[i] = p->r_rgb[i] * 255.0; + get_ccast_dith(dpat, rgb); + + if ((cpat = malloc(sizeof(double) * MXPATSIZE * MXPATSIZE * TOTC2D)) == NULL) + error("ccwin: malloc of dither pattern failed"); + + for (i = 0; i < CCDITHSIZE; i++) { + for (j = 0; j < CCDITHSIZE; j++) { + int k = (((int)IHEIGHT-2) - j) % CCDITHSIZE; /* Flip to origin bot left */ + (*cpat)[i][k][0] = dpat[i][j][0]/255.0; /* (HEIGHT-2 is correct!) */ + (*cpat)[i][k][1] = dpat[i][j][1]/255.0; + (*cpat)[i][k][2] = dpat[i][j][2]/255.0; + } + } + + set_rect2d_dpat((rect2d *)rct, cpat, CCDITHSIZE, CCDITHSIZE); + } +#endif /* DDITHER == 2 */ + +#ifdef CCTEST_PATTERN +#pragma message("############################# ccwin.c TEST_PATTERN defined ! ##") + if (getenv("ARGYLL_CCAST_TEST_PATTERN") != NULL) { + verbose(0, "Writing test pattern to '%s'\n","testpattern.png"); + if (r->write(r, "testpattern.png", 1, NULL, NULL, png_file)) + error("ccwin: render->write failed"); + } +#else /* !CCTEST_PATTERN */ +# ifdef WRITE_PNG /* Write it to a file so that we can look at it */ +# pragma message("############################# spectro/ccwin.c WRITE_PNG is enabled ######") + if (r->write(r, "ccwin.png", 1, NULL, NULL, png_file)) + error("ccwin: render->write failed"); +# endif /* WRITE_PNG */ +#endif /* !CCTEST_PATTERN */ + +#ifdef DO_TIMING + stime = msec_time(); +#endif + if (r->write(r, "MemoryBuf", 1, &ibuf, &ilen, png_mem)) + error("ccwin: render->write failed"); +#ifdef DO_TIMING + stime = msec_time() - stime; + printf("render->write took %d msec\n",stime); +#endif + if (ws->update(ws, ibuf, ilen, bg)) + error("ccwin: color update failed"); + p->ccix = p->ncix; + } + + /* If update is notified asyncronously ... */ +// while(p->ncix != p->ccix) { +// msec_sleep(50); +// } +//printf("#################################################################\n"); +//printf("################# RGB update notified ################\n"); +//printf("#################################################################\n"); + + /* Allow for display update & instrument delays */ + update_delay = dispwin_compute_delay(p, orgb); + debugr2((errout, "ccwin_set_color delaying %d msec\n",update_delay)); + msec_sleep(update_delay); + + return 0; +} + +/* Set/unset the blackground color flag */ +/* Return nz on error */ +static int ccwin_set_bg(dispwin *p, int blackbg) { + p->blackbg = blackbg; + + return 0; +} + + +/* ----------------------------------------------- */ +/* Set the shell set color callout */ +void ccwin_set_callout( +dispwin *p, +char *callout +) { + debugr2((errout,"ccwin_set_callout called with '%s'\n",callout)); + + p->callout = strdup(callout); +} + +/* ----------------------------------------------- */ +/* Destroy ourselves */ +static void ccwin_del( +dispwin *p +) { + chws *ws; + + debugr("ccwin_del called\n"); + + if (p == NULL) + return; + + ws = (chws *)p->pcntx; + + if (ws != NULL) + ws->del(ws); + + if (p->name != NULL) + free(p->name); + if (p->description != NULL) + free(p->description); + if (p->callout != NULL) + free(p->callout); + + free(p); +} + +/* ----------------------------------------------- */ + +/* Create a web display test window, default grey */ +dispwin *new_ccwin( +ccast_id *cc_id, /* ChromeCast to open */ +double width, double height, /* Width and height in mm. (TV width assumed to b 1000mm) */ +double hoff, double voff, /* Offset from center in fraction of screen, range -1.0 .. 1.0 */ +int nowin, /* NZ if no window should be created - RAMDAC access only */ +int native, /* X0 = use current per channel calibration curve */ + /* X1 = set native linear output and use ramdac high precn. */ + /* 0X = use current color management cLut (MadVR) */ + /* 1X = disable color management cLUT (MadVR) */ +int *noramdac, /* Return nz if no ramdac access. native is set to X0 */ +int *nocm, /* Return nz if no CM cLUT access. native is set to 0X */ +int out_tvenc, /* 1 = use RGB Video Level encoding */ +int blackbg, /* NZ if whole screen should be filled with black */ +int verb, /* NZ for verbose prompts */ +int ddebug /* >0 to print debug statements to stderr */ +) { + dispwin *p = NULL; + char *cp; + chws *ws = NULL; + const char *options[3]; + + debug("new_ccwin called\n"); + + if ((p = (dispwin *)calloc(sizeof(dispwin), 1)) == NULL) { + if (ddebug) fprintf(stderr,"new_ccwin failed because malloc failed\n"); + return NULL; + } + + /* !!!! Make changes in dispwin.c & madvrwin.c as well !!!! */ + p->name = strdup("Web Window"); + p->width = width; + p->height = height; + p->nowin = nowin; + p->native = native; + p->out_tvenc = out_tvenc; + p->blackbg = blackbg; + p->ddebug = ddebug; + p->get_ramdac = ccwin_get_ramdac; + p->set_ramdac = ccwin_set_ramdac; + p->install_profile = ccwin_install_profile; + p->uninstall_profile = ccwin_uninstall_profile; + p->get_profile = ccwin_get_profile; + p->set_color = ccwin_set_color; + p->set_bg = ccwin_set_bg; + p->set_update_delay = dispwin_set_update_delay; + p->set_settling_delay = dispwin_set_settling_delay; + p->enable_update_delay = dispwin_enable_update_delay; + p->set_callout = ccwin_set_callout; + p->del = ccwin_del; + + if (noramdac != NULL) + *noramdac = 1; + p->native &= ~1; + + if (nocm != NULL) + *nocm = 1; + p->native &= ~2; + + p->rgb[0] = p->rgb[1] = p->rgb[2] = 0.5; /* Set Grey as the initial test color */ + + dispwin_set_default_delays(p); + + p->ncix = 1; + + p->pdepth = 8; /* Assume this by API */ + p->edepth = 8; + + /* Basic object is initialised, so create connection to ChromeCast */ + if ((ws = new_chws(cc_id, width, height, hoff, voff, verb, ddebug)) == NULL) { + if (ddebug) fprintf(stderr,"new_ccwin failed - new_chws() failed\n"); + return NULL; + } + + /* Extra delay ccast adds after confirming load */ + p->extra_update_delay = ws->cc->get_load_delay(ws->cc) / 1000.0; + + p->pcntx = (void *)ws; + + /* Create a suitable description */ + { + char buf[100]; + sprintf(buf,"ChromeCast '%s'",cc_id->name); + p->description = strdup(buf); + } + + // Set a default first color + if (ccwin_set_color(p, 128.0, 128.0, 128.0)) { + if (ddebug) fprintf(stderr,"new_ccwin set_color()\n"); + p->del(p); + return NULL; + } + + debugr("new_ccwin: return sucessfully\n"); + + return p; +} + diff --git a/spectro/ccwin.h b/spectro/ccwin.h new file mode 100644 index 0000000..4d32466 --- /dev/null +++ b/spectro/ccwin.h @@ -0,0 +1,40 @@ + +#ifndef CCWIN_H + +/* + * Argyll Color Correction System + * ChromeCast Display target patch window + * + * Author: Graeme W. Gill + * Date: 8/9/14 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License.txt file for licencing details. + */ + + +/* See ccast/ccmdns.h for function to get list of available ChromeCasts */ + +/* Create a ChromeCast display test window, default grey */ +dispwin *new_ccwin( +ccast_id *cc_id, /* ChromeCast to open */ +double width, double height, /* Width and height as multiplier of 10% width default. */ +double hoff, double voff, /* Offset from center in fraction of screen, range -1.0 .. 1.0 */ +int nowin, /* NZ if no window should be created - RAMDAC access only */ +int native, /* X0 = use current per channel calibration curve */ + /* X1 = set native linear output and use ramdac high precn. */ + /* 0X = use current color management cLut (MadVR) */ + /* 1X = disable color management cLUT (MadVR) */ +int *noramdac, /* Return nz if no ramdac access. native is set to X0 */ +int *nocm, /* Return nz if no CM cLUT access. native is set to 0X */ +int out_tvenc, /* 1 = use RGB Video Level encoding */ +int blackbg, /* NZ if whole screen should be filled with black */ +int verb, /* NZ for verbose prompts */ +int ddebug /* >0 to print debug statements to stderr */ +); + +#define CCWIN_H +#endif /* CCWIN_H */ diff --git a/spectro/ccxxmake.c b/spectro/ccxxmake.c index eff3afa..2df2e5f 100644 --- a/spectro/ccxxmake.c +++ b/spectro/ccxxmake.c @@ -27,17 +27,6 @@ /* TTBD: - Should add an option to set a UI_SELECTORS value. - - If any spectrometer gets a display type function (ie. refresh/non-refresh) - then it becomes difficult to know what to do with the -y option :- - - * Ignore the problem - don't set -y option on spectrometers. - Error shouldn't be significant for ref/nonref ? - - * Force the colorimeter to go first, record the ref/nonref state and - set in the spectrometer ? Make .ti3 file order the same for consistency ? - Would be nice to have a veryify option that produces a fit report of a matrix vs. the input files. @@ -70,7 +59,9 @@ #include "conv.h" #include "icoms.h" #include "inst.h" +#include "ccast.h" #include "dispwin.h" +#include "ui.h" #include "webwin.h" #ifdef NT # include "madvrwin.h" @@ -79,7 +70,9 @@ #include "ccss.h" #include "ccmx.h" #include "instappsup.h" -#include "spyd2setup.h" +#ifdef ENABLE_USB +# include "spyd2.h" +#endif #if defined (NT) #include <conio.h> @@ -105,15 +98,16 @@ static int gcc_bug_fix(int i) { /* device behaviour if not. */ void -usage(char *diag, ...) { +/* Flag = 0x0000 = default */ +/* Flag & 0x0001 = list ChromCast's */ +/* Flag & 0x0002 = list Technology choice */ +usage(int flag, char *diag, ...) { disppath **dp; icompaths *icmps = new_icompaths(0); inst2_capability cap = 0; fprintf(stderr,"Create CCMX or CCSS, Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); - if (setup_spyd2() == 2) - fprintf(stderr,"WARNING: This file contains a proprietary firmware image, and may not be freely distributed !\n"); if (diag != NULL) { va_list args; fprintf(stderr,"Diagnostic: "); @@ -122,16 +116,16 @@ usage(char *diag, ...) { va_end(args); fprintf(stderr,"\n"); } - fprintf(stderr,"usage: ccmxmake [-options] output.ccmx\n"); - fprintf(stderr," -v Verbose mode\n"); - fprintf(stderr," -S Create CCSS rather than CCMX\n"); - fprintf(stderr," -f file1.ti3[,file2.ti3] Create from one or two .ti3 files rather than measure.\n"); + fprintf(stderr,"usage: ccmxmake -t dtech [-options] output.ccmx\n"); + fprintf(stderr," -v Verbose mode\n"); + fprintf(stderr," -S Create CCSS rather than CCMX\n"); + fprintf(stderr," -f file1.ti3[,file2.ti3] Create from one or two .ti3 files rather than measure.\n"); #if defined(UNIX_X11) fprintf(stderr," -display displayname Choose X11 display name\n"); - fprintf(stderr," -d n[,m] Choose the display n from the following list (default 1)\n"); - fprintf(stderr," Optionally choose different display m for VideoLUT access\n"); + fprintf(stderr," -d n[,m] Choose the display n from the following list (default 1)\n"); + fprintf(stderr," Optionally choose different display m for VideoLUT access\n"); #else - fprintf(stderr," -d n Choose the display from the following list (default 1)\n"); + fprintf(stderr," -d n Choose the display from the following list (default 1)\n"); #endif dp = get_displays(); if (dp == NULL || dp[0] == NULL) @@ -146,38 +140,63 @@ usage(char *diag, ...) { } } free_disppaths(dp); - fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); + fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); + fprintf(stderr," -dcc[:n] Display via n'th ChromeCast (default 1, ? for list)\n"); + if (flag & 0x001) { + ccast_id **ids; + if ((ids = get_ccids()) == NULL) { + fprintf(stderr," ** Error discovering ChromCasts **\n"); + } else { + if (ids[0] == NULL) + fprintf(stderr," ** No ChromCasts found **\n"); + else { + int i; + for (i = 0; ids[i] != NULL; i++) + fprintf(stderr," %d = '%s'\n",i+1,ids[i]->name); + free_ccids(ids); + } + } + } #ifdef NT - fprintf(stderr," -dmadvr Display via MadVR Video Renderer\n"); + fprintf(stderr," -dmadvr Display via MadVR Video Renderer\n"); #endif -// fprintf(stderr," -d fake Use a fake display device for testing, fake%s if present\n",ICC_FILE_EXT); - fprintf(stderr," -p Use telephoto mode (ie. for a projector) (if available)\n"); +// fprintf(stderr," -d fake Use a fake display device for testing, fake%s if present\n",ICC_FILE_EXT); + fprintf(stderr," -p Use telephoto mode (ie. for a projector) (if available)\n"); cap = inst_show_disptype_options(stderr, " -y c|l ", icmps, 1); - fprintf(stderr," -P ho,vo,ss[,vs] Position test window and scale it\n"); - fprintf(stderr," ho,vi: 0.0 = left/top, 0.5 = center, 1.0 = right/bottom etc.\n"); - fprintf(stderr," ss: 0.5 = half, 1.0 = normal, 2.0 = double etc.\n"); - fprintf(stderr," -F Fill whole screen with black background\n"); + fprintf(stderr," -z disptype Different display type for spectrometer (see -y)\n"); + fprintf(stderr," -P ho,vo,ss[,vs] Position test window and scale it\n"); + fprintf(stderr," ho,vi: 0.0 = left/top, 0.5 = center, 1.0 = right/bottom etc.\n"); + fprintf(stderr," ss: 0.5 = half, 1.0 = normal, 2.0 = double etc.\n"); + fprintf(stderr," -F Fill whole screen with black background\n"); #if defined(UNIX_X11) - fprintf(stderr," -n Don't set override redirect on test window\n"); + fprintf(stderr," -n Don't set override redirect on test window\n"); #endif - fprintf(stderr," -N Disable initial calibration of instrument if possible\n"); - fprintf(stderr," -H Use high resolution spectrum mode (if available)\n"); -// fprintf(stderr," -V Use adaptive measurement mode (if available)\n"); - fprintf(stderr," -C \"command\" Invoke shell \"command\" each time a color is set\n"); - fprintf(stderr," -o observ Choose CIE Observer for CCMX spectrometer data:\n"); - fprintf(stderr," 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2\n"); - fprintf(stderr," -s steps Override default patch sequence combination steps (default %d)\n",DEFAULT_MSTEPS); - 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," -E desciption Override the default overall description\n"); - fprintf(stderr," -I displayname Set display make and model description\n"); - fprintf(stderr," -T displaytech Set display technology description (ie. CRT, LCD etc.)\n"); - fprintf(stderr," -U c Set UI selection character(s)\n"); - fprintf(stderr," -Y r|n Set or override refresh/non-refresh display type\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," -N Disable initial calibration of instrument if possible\n"); + fprintf(stderr," -H Use high resolution spectrum mode (if available)\n"); +// fprintf(stderr," -V Use adaptive measurement mode (if available)\n"); + fprintf(stderr," -C \"command\" Invoke shell \"command\" each time a color is set\n"); + fprintf(stderr," -o observ Choose CIE Observer for CCMX spectrometer data:\n"); + fprintf(stderr," 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2\n"); + fprintf(stderr," -s steps Override default patch sequence combination steps (default %d)\n",DEFAULT_MSTEPS); + 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," -E desciption Override the default overall description\n"); + fprintf(stderr," -I displayname Set display make and model description (optional)\n"); + if (flag & 0x0002) { + int i; + disptech_info *list = disptech_get_list(); + for (i = 0; list[i].dtech != disptech_end; i++) + fprintf(stderr," %s %s %s\n",i == 0 ? "-t" : " ", list[i].lsel,list[i].desc); + } else { + fprintf(stderr," -t dtech Set display technology type\n"); + fprintf(stderr," (Use -?? to list technology choices)\n"); + } + fprintf(stderr," -U c Set UI selection character(s)\n"); + fprintf(stderr," -Y r|n Set or override refresh/non-refresh display type\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," correction.ccmx | calibration.ccss\n"); - fprintf(stderr," File to save result to\n"); + fprintf(stderr," File to save result to\n"); if (icmps != NULL) icmps->del(icmps); exit(1); @@ -185,8 +204,7 @@ usage(char *diag, ...) { typedef double ary3[3]; -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { int i,j; int fa, nfa, mfa; /* current argument we're looking at */ disppath *disp = NULL; /* Display being used */ @@ -206,7 +224,8 @@ int main(int argc, char *argv[]) int comno = COMPORT; /* COM port used */ flow_control fc = fc_nc; /* Default flow control */ int highres = 0; /* High res mode if available */ - int dtype = 0; /* Display kind, 0 = default, 1 = CRT, 2 = LCD */ + int dtype = 0; /* Display kind, 0 = default, 1 = CRT, 2 = LCD, etc */ + int sdtype = -1; /* Spectro display kind, -1 = use dtype */ int refrmode = -1; /* Refresh mode */ double refrate = 0.0; /* 0.0 = default, > 0.0 = override refresh rate */ int cbid = 0; /* Calibration base display mode ID */ @@ -214,6 +233,9 @@ int main(int argc, char *argv[]) int tele = 0; /* NZ if telephoto mode */ int noinitcal = 0; /* Disable initial calibration */ int webdisp = 0; /* NZ for web display, == port number */ + int ccdisp = 0; /* NZ for ChromeCast, == list index */ + ccast_id **ccids = NULL; + ccast_id *ccid = NULL; #ifdef NT int madvrdisp = 0; /* NZ for MadVR display */ #endif @@ -223,22 +245,23 @@ int main(int argc, char *argv[]) ary3 *refs = NULL; /* Reference XYZ values */ int gotref = 0; char *refname = NULL; /* Name of reference instrument */ + char *reffile = NULL; /* Name of reference file */ ary3 *cols = NULL; /* Colorimeter XYZ values */ int gotcol = 0; char *colname = NULL; /* Name of colorimeter instrument */ + char *colfile = NULL; /* Name of colorimeter file */ col *rdcols = NULL; /* Internal storage of all the patch colors */ int saved = 0; /* Saved result */ char innames[2][MAXNAMEL+1] = { "\000", "\000" }; /* .ti3 input names */ - char outname[MAXNAMEL+1] = "\000"; /* ccmx output file name */ + char outname[MAXNAMEL+5+1] = "\000"; /* ccmx output file name */ char *description = NULL; /* Given overall description */ char *displayname = NULL; /* Given display name */ - char *displaytech = NULL; /* Given display technology */ + disptech_info *dtinfo = NULL; /* Display technology */ char *uisel = NULL; /* UI selection letters */ int rv; set_exe_path(argv[0]); /* Set global exe_path and error_program */ check_if_not_interactive(); - setup_spyd2(); /* Load firware if available */ /* Process the arguments */ mfa = 0; /* Minimum final arguments */ @@ -258,8 +281,10 @@ int main(int argc, char *argv[]) } } - if (argv[fa][1] == '?') { - usage("Usage requested"); + if (argv[fa][1] == '?' || argv[fa][1] == '-') { + if (argv[fa][2] == '?' || argv[fa][2] == '-') + usage(2, "Extended usage requested"); + usage(0, "Usage requested"); } else if (argv[fa][1] == 'v') { verb = 1; @@ -271,7 +296,7 @@ int main(int argc, char *argv[]) } else if (argv[fa][1] == 'f') { char *cna, *f1 = NULL; fa = nfa; - if (na == NULL) usage("Expect argument to input file flag -f"); + if (na == NULL) usage(0,"Expect argument to input file flag -f"); if ((cna = strdup(na)) == NULL) error("Malloc failed"); @@ -297,7 +322,19 @@ int main(int argc, char *argv[]) if (na[3] == ':') { webdisp = atoi(na+4); if (webdisp == 0 || webdisp > 65535) - usage("Web port number must be in range 1..65535"); + usage(0,"Web port number must be in range 1..65535"); + } + fa = nfa; + } else if (strncmp(na,"cc",2) == 0 + || strncmp(na,"CC",2) == 0) { + ccdisp = 1; + if (na[2] == ':') { + if (na[3] < '0' || na[3] > '9') + usage(0x0001,"Available ChromeCasts"); + + ccdisp = atoi(na+3); + if (ccdisp <= 0) + usage(0,"ChromCast number must be in range 1..N"); } fa = nfa; #ifdef NT @@ -311,10 +348,10 @@ int main(int argc, char *argv[]) int ix, iv; if (strcmp(&argv[fa][2], "isplay") == 0 || strcmp(&argv[fa][2], "ISPLAY") == 0) { - if (++fa >= argc || argv[fa][0] == '-') usage("Parameter expected following -display"); + if (++fa >= argc || argv[fa][0] == '-') usage(0,"Parameter expected following -display"); setenv("DISPLAY", argv[fa], 1); } else { - if (na == NULL) usage("Parameter expected following -d"); + if (na == NULL) usage(0,"Parameter expected following -d"); fa = nfa; if (strcmp(na,"fake") == 0 || strcmp(na,"FAKE") == 0) { fake = 1; @@ -328,14 +365,14 @@ int main(int argc, char *argv[]) if (disp != NULL) free_a_disppath(disp); if ((disp = get_a_display(ix-1)) == NULL) - usage("-d parameter %d out of range",ix); + usage(0,"-d parameter %d out of range",ix); if (iv > 0) disp->rscreen = iv-1; } } #else int ix; - if (na == NULL) usage("Parameter expected following -d"); + if (na == NULL) usage(0,"Parameter expected following -d"); fa = nfa; if (strcmp(na,"fake") == 0 || strcmp(na,"FAKE") == 0) { fake = 1; @@ -346,7 +383,7 @@ int main(int argc, char *argv[]) if (disp != NULL) free_a_disppath(disp); if ((disp = get_a_display(ix-1)) == NULL) - usage("-d parameter %d out of range",ix); + usage(0,"-d parameter %d out of range",ix); } #endif } @@ -358,9 +395,9 @@ int main(int argc, char *argv[]) /* COM port */ } else if (argv[fa][1] == 'c') { fa = nfa; - if (na == NULL) usage("Paramater expected following -c"); + if (na == NULL) usage(0,"Paramater expected following -c"); comno = atoi(na); - if (comno < 1 || comno > 40) usage("-c parameter %d out of range",comno); + if (comno < 1 || comno > 40) usage(0,"-c parameter %d out of range",comno); /* Telephoto */ } else if (argv[fa][1] == 'p') { @@ -369,7 +406,7 @@ int main(int argc, char *argv[]) /* Display type */ } else if (argv[fa][1] == 'y') { fa = nfa; - if (na == NULL) usage("Parameter expected after -y"); + if (na == NULL) usage(0,"Parameter expected after -y"); dtype = na[0]; /* For ccss, set a default */ @@ -379,22 +416,28 @@ int main(int argc, char *argv[]) refrmode = 0; } + /* Spectro display type */ + } else if (argv[fa][1] == 'z') { + fa = nfa; + if (na == NULL) usage(0,"Parameter expected after -z"); + sdtype = na[0]; + /* Test patch offset and size */ } else if (argv[fa][1] == 'P') { fa = nfa; - if (na == NULL) usage("Parameter expected after -P"); + if (na == NULL) usage(0,"Parameter expected after -P"); if (sscanf(na, " %lf,%lf,%lf,%lf ", &ho, &vo, &hpatscale, &vpatscale) == 4) { ; } else if (sscanf(na, " %lf,%lf,%lf ", &ho, &vo, &hpatscale) == 3) { vpatscale = hpatscale; } else { - usage("-P parameter '%s' not recognised",na); + usage(0,"-P parameter '%s' not recognised",na); } if (ho < 0.0 || ho > 1.0 || vo < 0.0 || vo > 1.0 || hpatscale <= 0.0 || hpatscale > 50.0 || vpatscale <= 0.0 || vpatscale > 50.0) - usage("-P parameters %f %f %f %f out of range",ho,vo,hpatscale,vpatscale); + usage(0,"-P parameters %f %f %f %f out of range",ho,vo,hpatscale,vpatscale); ho = 2.0 * ho - 1.0; vo = 2.0 * vo - 1.0; @@ -417,7 +460,7 @@ int main(int argc, char *argv[]) /* Spectral Observer type (only relevant for CCMX) */ } else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') { fa = nfa; - if (na == NULL) usage("Parameter expecte after -o"); + if (na == NULL) usage(0,"Parameter expecte after -o"); if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ spec = 2; observ = icxOT_CIE_1931_2; @@ -434,25 +477,25 @@ int main(int argc, char *argv[]) spec = 2; observ = icxOT_Shaw_Fairchild_2; } else - usage("-o parameter '%s' not recognised",na); + usage(0,"-o parameter '%s' not recognised",na); } else if (argv[fa][1] == 's') { fa = nfa; - if (na == NULL) usage("Parameter expecte after -s"); + if (na == NULL) usage(0,"Parameter expecte after -s"); msteps = atoi(na); if (msteps < 1 || msteps > 16) - usage("-s parameter value %d is outside the range 1 to 16",msteps); + usage(0,"-s parameter value %d is outside the range 1 to 16",msteps); /* Change color callout */ } else if (argv[fa][1] == 'C') { fa = nfa; - if (na == NULL) usage("Parameter expected after -C"); + if (na == NULL) usage(0,"Parameter expected after -C"); ccallout = na; /* Serial port flow control */ } else if (argv[fa][1] == 'W') { fa = nfa; - if (na == NULL) usage("Paramater expected following -W"); + if (na == NULL) usage(0,"Paramater expected following -W"); if (na[0] == 'n' || na[0] == 'N') fc = fc_none; else if (na[0] == 'h' || na[0] == 'H') @@ -460,7 +503,7 @@ int main(int argc, char *argv[]) else if (na[0] == 'x' || na[0] == 'X') fc = fc_XonXOff; else - usage("-W parameter '%c' not recognised",na[0]); + usage(0,"-W parameter '%c' not recognised",na[0]); } else if (argv[fa][1] == 'D') { debug = 1; @@ -472,24 +515,28 @@ int main(int argc, char *argv[]) } else if (argv[fa][1] == 'I') { fa = nfa; - if (na == NULL) usage("Expect argument to display description flag -I"); + if (na == NULL) usage(0,"Expect argument to display description flag -I"); displayname = strdup(na); - } else if (argv[fa][1] == 'T') { + } else if (argv[fa][1] == 't') { fa = nfa; - if (na == NULL) usage("Expect argument to display technology flag -T"); - displaytech = strdup(na); + if (na == NULL) usage(2,"Expect argument to display technology flag -t"); + dtinfo = disptech_get_list(); + if (na[1] != '\000') + usage(2,"Expect single character argument to display technology flag -t"); + if ((dtinfo = disptech_select(dtinfo, na[0])) == NULL) + usage(2,"-t parameter '%c' not recognized",na[0]); /* Copyright string */ } else if (argv[fa][1] == 'E') { fa = nfa; - if (na == NULL) usage("Expect argument to overall description flag -E"); + if (na == NULL) usage(0,"Expect argument to overall description flag -E"); description = strdup(na); /* Extra flags */ } else if (argv[fa][1] == 'Y') { if (na == NULL) - usage("Flag '-Y' expects extra flag"); + usage(0,"Flag '-Y' expects extra flag"); if (na[0] == 'r') { refrmode = 1; @@ -497,32 +544,32 @@ int main(int argc, char *argv[]) refrmode = 0; } else if (na[0] == 'R') { if (na[1] != ':') - usage("-Y R:rate syntax incorrect"); + usage(0,"-Y R:rate syntax incorrect"); refrate = atof(na+2); if (refrate < 5.0 || refrate > 150.0) - usage("-Y R:rate %f Hz not in valid range",refrate); + usage(0,"-Y R:rate %f Hz not in valid range",refrate); } else if (na[0] == 'A') { nadaptive = 1; } else { - usage("Flag '-Z %c' not recognised",na[0]); + usage(0,"Flag '-Z %c' not recognised",na[0]); } fa = nfa; /* UI selection character */ } else if (argv[fa][1] == 'U') { fa = nfa; - if (na == NULL || na[0] == '\000') usage("Expect argument to flag -U"); + if (na == NULL || na[0] == '\000') usage(0,"Expect argument to flag -U"); uisel = na; for (i = 0; uisel[i] != '\000'; i++) { if (!( (uisel[i] >= '0' && uisel[i] <= '9') || (uisel[i] >= 'A' && uisel[i] <= 'Z') || (uisel[i] >= 'a' && uisel[i] <= 'z'))) { - usage("-U character(s) must be 0-9,A-Z,a-z"); + usage(0,"-U character(s) must be 0-9,A-Z,a-z"); } } } else - usage("Flag '-%c' not recognised",argv[fa][1]); + usage(0,"Flag '-%c' not recognised",argv[fa][1]); } else break; @@ -530,17 +577,19 @@ int main(int argc, char *argv[]) /* Get the output ccmx file name argument */ if (fa >= argc) - usage("Output filname expected"); + usage(0,"Output filname expected"); strncpy(outname,argv[fa++],MAXNAMEL-1); outname[MAXNAMEL-1] = '\000'; + if (strrchr(outname, '.') == NULL) /* no extension */ + strcat(outname, doccss ? ".ccss" : ".ccmx"); if (fakeseq && doccss) error("Fake CCSS test not implemeted"); printf("\n"); - if (displayname == NULL && displaytech == NULL) - error("Either the display description (-I) or technology (-T) needs to be set"); + if (dtinfo == NULL) + error("Display technology (-t) must be set"); /* CCSS: See if we're working from a .ti3 file */ if (doccss && innames[0][0] != '\000') { @@ -617,7 +666,7 @@ int main(int argc, char *argv[]) cgf = NULL; if (description == NULL) { - char *disp = displaytech != NULL ? displaytech : displayname; + char *disp = displayname != NULL ? displayname : dtinfo->desc; char *tt = "CCSS for "; if ((description = malloc(strlen(disp) + strlen(tt) + 1)) == NULL) error("Malloc failed"); @@ -649,7 +698,7 @@ int main(int argc, char *argv[]) error("new_ccss() failed"); if (cc->set_ccss(cc, "Argyll ccxxmake", NULL, description, displayname, - displaytech, refrmode, uisel, refname, samples, npat)) { + dtinfo->dtech, refrmode, uisel, refname, samples, npat)) { error("set_ccss failed with '%s'\n",cc->err); } if(cc->write_ccss(cc, outname)) @@ -733,6 +782,7 @@ int main(int argc, char *argv[]) error("Found two spectral files - expect one colorimtric file"); current = refs; refname = strdup(cgf->t[0].kdata[ii]); + reffile = innames[n]; gotref = 1; } else if (strcmp(cgf->t[0].kdata[ti],"NO") == 0) { @@ -740,6 +790,7 @@ int main(int argc, char *argv[]) if (gotcol) { /* Copy what we though was cols to refs */ refname = colname; + reffile = colfile; for (i = 0; i < npat; i++) { refs[i][0] = cols[i][0]; refs[i][1] = cols[i][1]; @@ -767,6 +818,7 @@ int main(int argc, char *argv[]) } current = cols; colname = strdup(cgf->t[0].kdata[ii]); + colfile = innames[n]; gotcol = 1; } else { error ("Unknown INSTRUMENT_TYPE_SPECTRAL value '%s'",cgf->t[0].kdata[ti]); @@ -894,7 +946,7 @@ int main(int argc, char *argv[]) strcat(colname, ")"); } if (description == NULL) { - char *disp = displaytech != NULL ? displaytech : displayname; + char *disp = displayname != NULL ? displayname : dtinfo->desc; if ((description = malloc(strlen(colname) + strlen(disp) + 4)) == NULL) error("Malloc failed"); strcpy(description, colname); @@ -903,15 +955,15 @@ int main(int argc, char *argv[]) } if (refrmode < 0) - error("The display refresh mode is not known - use the -Y flag"); + error("The display refresh mode is not in '%s' - use the -Y flag",colfile); if (cbid == 0) - error("The calibration base display mode not specified in the .ti3 file"); + error("The calibration base display mode not specified in the '%s' file",colfile); if ((cc = new_ccmx()) == NULL) error("new_ccmx() failed"); - if (cc->create_ccmx(cc, description, colname, displayname, displaytech, + if (cc->create_ccmx(cc, description, colname, displayname, dtinfo->dtech, refrmode, cbid, uisel, refname, npat, refs, cols)) { error("create_ccmx failed with '%s'\n",cc->err); } @@ -924,7 +976,7 @@ int main(int argc, char *argv[]) } if(cc->write_ccmx(cc, outname)) - printf("\nWriting CCMX file '%s' failed\n",outname); + printf("\nWriting CCMX file '%s' failed with '%s'\n",outname,cc->err); else printf("\nWriting CCMX file '%s' succeeded\n",outname); cc->del(cc); @@ -935,12 +987,13 @@ int main(int argc, char *argv[]) /* No explicit display has been set */ if ( #ifndef SHOW_WINDOW_ONFAKE - !fake && + !fake #endif - webdisp == 0 #ifdef NT && madvrdisp == 0 #endif + && webdisp == 0 + && ccdisp == 0 && disp == NULL) { int ix = 0; #if defined(UNIX_X11) @@ -967,6 +1020,19 @@ int main(int argc, char *argv[]) displayname = strdup("fake display"); } + /* If we've requested ChromeCast, look it up */ + if (ccdisp) { + if ((ccids = get_ccids()) == NULL) + error("discovering ChromCasts failed"); + if (ccids[0] == NULL) + error("There are no ChromCasts to use\n"); + for (i = 0; ccids[i] != NULL; i++) + ; + if (ccdisp < 1 || ccdisp > i) + error("Chosen ChromCasts (%d) is outside list (1..%d)\n",ccdisp,i); + ccid = ccids[ccdisp-1]; + } + /* Create grid of device test values */ { int j; @@ -1139,7 +1205,8 @@ int main(int argc, char *argv[]) for (i = 0; ; i++) { if (paths[i] == NULL) break; - if (paths[i]->itype == instSpyder2 && setup_spyd2() == 0) + 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 fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name); @@ -1177,18 +1244,21 @@ int main(int argc, char *argv[]) /* Should we use current cal rather than native ??? */ if ((dr = new_disprd(&errc, icmps->get_path(icmps, comno), - fc, dtype, 1, tele, nadaptive, + fc, dtype, sdtype, 1, tele, nadaptive, noinitcal, 0, highres, refrate, 3, NULL, NULL, NULL, 0, disp, 0, blackbg, - override, webdisp, + override, webdisp, ccid, #ifdef NT madvrdisp, #endif ccallout, NULL, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, - NULL, NULL, 0, 2, icxOT_default, NULL, - 0, 0, "fake" ICC_FILE_EXT, g_log)) == NULL) - error("new_disprd failed with '%s'\n",disprd_err(errc)); + disptech_unknown, 0, NULL, NULL, 0, 2, icxOT_default, NULL, + 0, 0, "fake" ICC_FILE_EXT, g_log)) == NULL) { + printf("Opening the instrument failed with '%s'. Try selecting it again ?\n", + disprd_err(errc)); + continue; + } it = dr->it; @@ -1289,7 +1359,7 @@ int main(int argc, char *argv[]) } if (description == NULL) { - char *disp = displaytech != NULL ? displaytech : displayname; + char *disp = displayname != NULL ? displayname : dtinfo->desc; char *tt = "CCSS for "; if ((description = malloc(strlen(disp) + strlen(tt) + 1)) == NULL) error("Malloc failed"); @@ -1326,11 +1396,11 @@ int main(int argc, char *argv[]) error("new_ccss() failed"); if (cc->set_ccss(cc, "Argyll ccxxmake", NULL, description, displayname, - displaytech, refrmode, NULL, refname, samples, npat)) { + dtinfo->dtech, refrmode, NULL, refname, samples, npat)) { error("set_ccss failed with '%s'\n",cc->err); } if(cc->write_ccss(cc, outname)) - printf("\nWriting CCSS file '%s' failed\n",outname); + printf("\nWriting CCSS file '%s' failed with '%s'\n",outname,cc->err); else printf("\nWriting CCSS file '%s' succeeded\n",outname); cc->del(cc); @@ -1380,7 +1450,7 @@ int main(int argc, char *argv[]) if ((cc = new_ccmx()) == NULL) error("new_ccmx() failed"); - if (cc->create_ccmx(cc, description, colname, displayname, displaytech, + if (cc->create_ccmx(cc, description, colname, displayname, dtinfo->dtech, refrmode, cbid, uisel, refname, npat, refs, cols)) { error("create_ccmx failed with '%s'\n",cc->err); } @@ -1393,7 +1463,7 @@ int main(int argc, char *argv[]) } if(cc->write_ccmx(cc, outname)) - printf("\nWriting CCMX file '%s' failed\n",outname); + printf("\nWriting CCMX file '%s' failed with '%s'\n",outname,cc->err); else printf("\nWriting CCMX file '%s' succeeded\n",outname); cc->del(cc); @@ -1418,6 +1488,7 @@ int main(int argc, char *argv[]) free(displayname); if (icmps != NULL) icmps->del(icmps); + free_ccids(ccids); } #ifdef DEBUG diff --git a/spectro/chartread.c b/spectro/chartread.c index 376fc1c..cfbeee6 100644 --- a/spectro/chartread.c +++ b/spectro/chartread.c @@ -18,6 +18,10 @@ /* TTBD * + * When there is a strip misread, should offer the option + * of skipping the strip. + * + * * Should an a -X [xl] mode that reads a simple list of readings * from a file. * @@ -71,18 +75,19 @@ #include "numlib.h" #include "icc.h" #include "xicc.h" -#include "ccmx.h" -#include "ccss.h" #include "insttypes.h" #include "conv.h" #include "icoms.h" #include "inst.h" +#include "ccmx.h" +#include "ccss.h" #include "dispwin.h" +#include "ui.h" +#include "ccast.h" #include "dispsup.h" #include "alphix.h" #include "sort.h" #include "instappsup.h" -#include "spyd2setup.h" #include <stdarg.h> @@ -333,7 +338,7 @@ a1log *log /* verb, debug & error log */ return -1; } } else - printf("Display type ignored - instrument doesn't support display type\n"); + printf("Display type ignored - instrument doesn't support display type selection\n"); } if (spectral && !IMODETST(cap, inst_mode_spectral)) { @@ -357,7 +362,7 @@ a1log *log /* verb, debug & error log */ it->del(it); return -1; } - if ((rv = it->col_cor_mat(it, cx->matrix)) != inst_ok) { + if ((rv = it->col_cor_mat(it, cx->dtech, cx->cc_cbid, cx->matrix)) != inst_ok) { printf("\nSetting Colorimeter Correction Matrix failed with error :'%s' (%s)\n", it->inst_interp_error(it, rv), it->interp_error(it, rv)); cx->del(cx); @@ -395,7 +400,7 @@ a1log *log /* verb, debug & error log */ it->del(it); return -1; } - if ((rv = it->col_cal_spec_set(it, cs->samples, cs->no_samp)) != inst_ok) { + if ((rv = it->col_cal_spec_set(it, cs->dtech, cs->samples, cs->no_samp)) != inst_ok) { printf("\nSetting Colorimeter Calibration Spectral Samples failed with error :'%s' (%s)\n", it->inst_interp_error(it, rv), it->interp_error(it, rv)); cs->del(cs); @@ -447,7 +452,6 @@ a1log *log /* verb, debug & error log */ it->del(it); return -1; } - highres = 1; } else { a1logv(log, 1, "Modified patch consistency tolerance ignored - instrument doesn't support it\n"); } @@ -1072,7 +1076,7 @@ a1log *log /* verb, debug & error log */ /* 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)) + if ((rv = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0)) != inst_ok) { printf("\nCalibration failed with error :'%s' (%s)\n", it->inst_interp_error(it, rv), it->interp_error(it, rv)); @@ -1288,7 +1292,7 @@ a1log *log /* verb, debug & error log */ if (cap2 & inst2_no_feedback) bad_beep(); printf("\nStrip read failed because instruments needs calibration\n"); - ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL); + ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0); if (ev != inst_ok) { /* Abort or fatal error */ it->del(it); return -1; @@ -1574,7 +1578,7 @@ a1log *log /* verb, debug & error log */ /* 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)) + if ((rv = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0)) != inst_ok) { printf("\nCalibration failed with error :'%s' (%s)\n", it->inst_interp_error(it, rv), it->interp_error(it, rv)); @@ -1802,7 +1806,7 @@ a1log *log /* verb, debug & error log */ if (cap2 & inst2_no_feedback) bad_beep(); printf("\nSpot read failed because instruments needs calibration\n"); - ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL); + ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0); if (ev != inst_ok) { /* Abort or fatal error */ it->del(it); return -1; @@ -2127,8 +2131,6 @@ int main(int argc, char *argv[]) { set_exe_path(argv[0]); /* Set global exe_path and error_program */ check_if_not_interactive(); - setup_spyd2(); /* Load firware if available */ - if (argc <= 1) usage(); diff --git a/spectro/colorhug.c b/spectro/colorhug.c index 0152000..76e5bfb 100644 --- a/spectro/colorhug.c +++ b/spectro/colorhug.c @@ -140,7 +140,9 @@ colorhug_interp_error(inst *pp, int ec) { case COLORHUG_NO_COMS: return "Communications hasn't been established"; case COLORHUG_NOT_INITED: - return "Insrument hasn't been initialised"; + return "Instrument hasn't been initialised"; + case COLORHUG_WRONG_MODEL: + return "Attempt to use wrong command for model"; default: return "Unknown error code"; } @@ -390,8 +392,10 @@ colorhug_take_measurement(colorhug *p, double XYZ[3]) return ev; /* Convert to doubles */ - for (i = 0; i < 3; i++) + for (i = 0; i < 3; i++) { +//printf("%d raw = 0x%x\n",i,buf2int_le(obuf + i * 4)); XYZ[i] = p->postscale * buf2pfdouble(obuf + i * 4); + } } else { int icx = 64 + p->icx; unsigned char obuf[3 * 4]; @@ -411,8 +415,10 @@ colorhug_take_measurement(colorhug *p, double XYZ[3]) return ev; /* Convert to doubles */ - for (i = 0; i < 3; i++) + for (i = 0; i < 3; i++) { +//printf("%d raw = 0x%x\n",i,buf2int_le(obuf + i * 4)); XYZ[i] = buf2pfdouble(obuf + i * 4); + } } /* Apply the colorimeter correction matrix */ @@ -447,6 +453,7 @@ colorhug_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { a1logd(p->log, 3, "colorhug_init_coms: About to init USB\n"); /* Set config, interface, write end point, read end point */ + // ~~ does Linux need icomuf_reset_before_close ? Why ? if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, icomuf_detach, 0, NULL)) != ICOM_OK) { a1logd(p->log, 1, "colorhug_init_coms: set_usb_port failed ICOM err 0x%x\n",se); @@ -515,6 +522,9 @@ colorhug_set_multiplier (colorhug *p, int multiplier) inst_code ev; unsigned char ibuf[1]; + if (p->stype != ch_one) + return colorhug_interp_code((inst *)p, COLORHUG_WRONG_MODEL); + /* Set the desired multiplier */ ibuf[0] = multiplier; ev = colorhug_command(p, ch_set_mult, @@ -531,6 +541,9 @@ colorhug_set_integral (colorhug *p, int integral) inst_code ev; unsigned char ibuf[2]; + if (p->stype != ch_one) + return colorhug_interp_code((inst *)p, COLORHUG_WRONG_MODEL); + /* Set the desired integral time */ short2buf_le(ibuf + 0, integral); ev = colorhug_command(p, ch_set_integral, @@ -587,15 +600,18 @@ colorhug_init_inst(inst *pp) if (ev != inst_ok) return ev; - /* Turn the sensor on */ - ev = colorhug_set_multiplier(p, 0x03); - if (ev != inst_ok) - return ev; + if (p->stype == ch_one) { - /* Set the integral time to maximum precision */ - ev = colorhug_set_integral(p, 0xffff); - if (ev != inst_ok) - return ev; + /* Turn the sensor on */ + ev = colorhug_set_multiplier(p, 0x03); + if (ev != inst_ok) + return ev; + + /* Set the integral time to maximum precision */ + ev = colorhug_set_integral(p, 0xffff); + if (ev != inst_ok) + return ev; + } if (p->maj <= 1 && p->min <= 1 && p->uro <= 4) { @@ -691,9 +707,11 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } /* Read the XYZ value */ - if ((rv = colorhug_take_measurement(p, val->XYZ)) != inst_ok) { + rv = colorhug_take_measurement(p, val->XYZ); + + if (rv != inst_ok) return rv; - } + /* This may not change anything since instrument may clamp */ if (clamp) icmClamp3(val->XYZ, val->XYZ); @@ -709,27 +727,43 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ return rv; } +static inst_code set_base_disp_type(colorhug *p, int cbid); + /* Insert a colorimetric correction matrix */ inst_code colorhug_col_cor_mat( inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ +int cbid, /* Calibration display type base ID required, 1 if unknown */ double mtx[3][3] ) { colorhug *p = (colorhug *)pp; + inst_code ev; if (!p->gotcoms) return inst_no_coms; if (!p->inited) return inst_no_init; - - if (mtx == NULL) { + if ((ev = set_base_disp_type(p, cbid)) != inst_ok) + return ev; + if (mtx == NULL) icmSetUnity3x3(p->ccmat); - } else { - if (p->cbid == 0) { - a1loge(p->log, 1, "colorhug: can't set col_cor_mat over non-base display type\n"); - return inst_wrong_setup; - } + else icmCpy3x3(p->ccmat, mtx); + + p->dtech = dtech; + p->refrmode = disptech_get_id(dtech)->refr; + p->cbid = 0; /* Base has been overwitten */ + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); } return inst_ok; @@ -747,6 +781,7 @@ colorhug_interp_code(inst *pp, int ec) { case COLORHUG_INTERNAL_ERROR: case COLORHUG_NO_COMS: case COLORHUG_NOT_INITED: + case COLORHUG_WRONG_MODEL: return inst_internal_error | ec; case COLORHUG_COMS_FAIL: @@ -868,6 +903,7 @@ static inst_disptypesel colorhug_disptypesel[7] = { "l", /* sel */ "LCD, CCFL Backlight", /* desc */ 0, /* refr */ + disptech_lcd_ccfl, /* disptype */ 0 /* ix */ }, { @@ -876,6 +912,7 @@ static inst_disptypesel colorhug_disptypesel[7] = { "c", "CRT display", 0, + disptech_crt, 1 }, { @@ -884,6 +921,7 @@ static inst_disptypesel colorhug_disptypesel[7] = { "p", "Projector", 0, + disptech_dlp, 2 }, { @@ -892,6 +930,7 @@ static inst_disptypesel colorhug_disptypesel[7] = { "e", "LCD, White LED Backlight", 0, + disptech_lcd_wled, 3 }, { @@ -900,6 +939,7 @@ static inst_disptypesel colorhug_disptypesel[7] = { "F", "Factory matrix (For Calibration)", 0, + disptech_unknown, 10 }, { @@ -908,6 +948,7 @@ static inst_disptypesel colorhug_disptypesel[7] = { "R", "Raw Reading (For Factory matrix Calibration)", 0, + disptech_unknown, 11 }, { @@ -916,6 +957,7 @@ static inst_disptypesel colorhug_disptypesel[7] = { "", "", 0, + disptech_none, 0 } }; @@ -947,23 +989,67 @@ int recreate /* nz to re-check for new ccmx & ccss files */ return inst_ok; } -/* Given a display type entry, setup for that type */ +/* Given a display type implementation from inst_disptypesel */ static inst_code set_disp_type(colorhug *p, inst_disptypesel *dentry) { int ix; - /* The HW handles up to 6 calibrations */ - ix = dentry->ix; - if (ix != 10 && ix != 11 && (ix < 0 || ix > 3)) - return inst_unsupported; - - p->icx = ix; - p->refrmode = dentry->refr; - p->cbid = dentry->cbid; if (dentry->flags & inst_dtflags_ccmx) { + inst_code ev; + if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok) + return ev; icmCpy3x3(p->ccmat, dentry->mat); + p->dtech = dentry->dtech; + p->cbid = 0; /* Can't be a base type */ + } else { + + /* The HW handles up to 6 calibrations */ + ix = dentry->ix; + if (ix != 10 && ix != 11 && (ix < 0 || ix > 3)) + return inst_unsupported; + + p->icx = ix; + p->dtech = dentry->dtech; + p->cbid = dentry->cbid; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ icmSetUnity3x3(p->ccmat); } + p->refrmode = dentry->refr; + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + + return inst_ok; +} + +/* Set the display type */ +static inst_code colorhug_set_disptype(inst *pp, int ix) { + colorhug *p = (colorhug *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + colorhug_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + if (ix < 0 || ix >= p->ndtlist) + return inst_unsupported; + + dentry = &p->dtlist[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; + } return inst_ok; } @@ -994,30 +1080,56 @@ static inst_code set_default_disp_type(colorhug *p) { return inst_ok; } -/* Set the display type */ -static inst_code colorhug_set_disptype(inst *pp, int ix) { - colorhug *p = (colorhug *)pp; +/* Setup the display type to the given base type */ +static inst_code set_base_disp_type(colorhug *p, int cbid) { inst_code ev; - inst_disptypesel *dentry; + int i; + if (cbid == 0) { + a1loge(p->log, 1, "colorhug set_base_disp_type: can't set base display type of 0\n"); + return inst_wrong_setup; + } if (p->dtlist == NULL) { - if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, colorhug_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) return ev; } - if (ix < 0 || ix >= p->ndtlist) - return inst_unsupported; - - dentry = &p->dtlist[ix]; - - if ((ev = set_disp_type(p, dentry)) != inst_ok) { + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (!(p->dtlist[i].flags & inst_dtflags_ccmx) /* Prevent infinite recursion */ + && p->dtlist[i].cbid == cbid) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid); + return inst_wrong_setup; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { return ev; } return inst_ok; } +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code colorhug_get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + colorhug *p = (colorhug *)pp; + if (dtech != NULL) + *dtech = p->dtech; + if (refrmode != NULL) + *refrmode = p->refrmode; + if (cbid != NULL) + *cbid = p->cbid; + return inst_ok; +} + /* * Set or reset an optional mode. * @@ -1042,24 +1154,6 @@ colorhug_get_set_opt(inst *pp, inst_opt_type m, ...) if (!p->inited) return inst_no_init; - /* Get the display type information */ - if (m == inst_opt_get_dtinfo) { - va_list args; - int *refrmode, *cbid; - - va_start(args, m); - refrmode = va_arg(args, int *); - cbid = va_arg(args, int *); - va_end(args); - - if (refrmode != NULL) - *refrmode = p->refrmode; - if (cbid != NULL) - *cbid = p->cbid; - - return inst_ok; - } - /* Operate the LEDs */ if (m == inst_opt_get_gen_ledmask) { va_list args; @@ -1111,6 +1205,7 @@ extern colorhug *new_colorhug(icoms *icom, instType itype) { p->set_mode = colorhug_set_mode; p->get_disptypesel = colorhug_get_disptypesel; p->set_disptype = colorhug_set_disptype; + p->get_disptechi = colorhug_get_disptechi; p->get_set_opt = colorhug_get_set_opt; p->read_sample = colorhug_read_sample; p->col_cor_mat = colorhug_col_cor_mat; @@ -1120,7 +1215,11 @@ extern colorhug *new_colorhug(icoms *icom, instType itype) { p->icom = icom; p->itype = icom->itype; + if (itype == instColorHug2) + p->stype = ch_two; + icmSetUnity3x3(p->ccmat); + p->dtech = disptech_unknown; return p; } diff --git a/spectro/colorhug.h b/spectro/colorhug.h index a591608..90fff6f 100644 --- a/spectro/colorhug.h +++ b/spectro/colorhug.h @@ -57,7 +57,13 @@ #define COLORHUG_BAD_RD_LENGTH 0x26 #define COLORHUG_BAD_RET_CMD 0x27 #define COLORHUG_BAD_RET_STAT 0x28 +#define COLORHUG_WRONG_MODEL 0x29 +/* Sub-type of instrument */ +typedef enum { + ch_one = 0, /* Original ColorHug */ + ch_two = 1 /* ColorHug2 */ +} colorhug_model; /* COLORHUG communication object */ struct _colorhug { @@ -67,13 +73,16 @@ struct _colorhug { inst_opt_type trig; /* Reading trigger mode */ + colorhug_model stype; /* Instrument sub-model */ int maj, min, uro; /* Version number */ int ser_no; /* Serial number */ inst_disptypesel *dtlist; /* Display Type list */ int ndtlist; /* Number of valid dtlist entries */ int icx; /* Internal calibration matrix index, 11 = Raw */ - int cbid; /* calibration base ID, 0 if not a base */ + disptech dtech; /* Display technology enum */ + int cbid; /* current calibration base ID, 0 if not a base */ + int ucbid; /* Underlying base ID if being used for matrix, 0 othewise */ int refrmode; /* Refresh mode (always 0) */ double postscale; /* Post scale factor (for Raw) */ double ccmat[3][3]; /* Colorimeter correction matrix */ diff --git a/spectro/conv.c b/spectro/conv.c index 752d483..c0137ce 100644 --- a/spectro/conv.c +++ b/spectro/conv.c @@ -137,8 +137,9 @@ static int th_read_char(void *pp) { char buf[1]; DWORD bread; - if ((stdinh = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) + if ((stdinh = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) { return 0; + } if (ReadFile(stdinh, buf, 1, &bread, NULL) && bread == 1 @@ -168,7 +169,7 @@ int poll_con_char(void) { return 0; } - Sleep(1); /* We just hope 1 msec is enough for the thread to start */ + Sleep(100); /* We just hope 1 msec is enough for the thread to start */ CancelIo(stdinh); getch_thread->del(getch_thread); return c; @@ -278,6 +279,23 @@ void msec_beep(int delay, int freq, int msec) { /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +int acond_timedwait_imp(HANDLE cond, CRITICAL_SECTION *lock, int msec) { + int rv; + LeaveCriticalSection(lock); + rv = WaitForSingleObject(cond, msec); + EnterCriticalSection(lock); + + if (rv == WAIT_TIMEOUT) + return 1; + + if (rv != WAIT_OBJECT_0) + return 2; + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + #ifdef NEVER /* Not currently needed, or effective */ /* Set the current threads priority */ @@ -544,24 +562,48 @@ void msec_sleep(unsigned int msec) { #endif } -/* Provide substitute for clock_gettime() in OS X */ #if defined(__APPLE__) && !defined(CLOCK_MONOTONIC) + #include <mach/mach_time.h> -#define CLOCK_REALTIME 0 -#define CLOCK_MONOTONIC 0 -static int clock_gettime(int clk_id, struct timespec *t){ + +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(); - double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom); - double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9); - t->tv_sec = seconds; - t->tv_nsec = nseconds; - return 0; + if (startup == 0) + startup = time; + + mach_timebase_info(&timebase); + time -= startup; + usec = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e3); + + return usec; } -#endif + +#else /* Return the current time in msec */ /* since the first invokation of msec_time() */ @@ -580,7 +622,7 @@ unsigned int msec_time() { cv.tv_sec -= startup.tv_sec; if (startup.tv_nsec > cv.tv_nsec) { cv.tv_sec--; - cv.tv_nsec += 100000000; + cv.tv_nsec += 1000000000; } cv.tv_nsec -= startup.tv_nsec; @@ -617,6 +659,29 @@ double usec_time() { return rv; } +#endif + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ + +int acond_timedwait_imp(pthread_cond_t *cond, pthread_mutex_t *lock, int msec) { + struct timeval tv; + struct timespec ts; + int rv; + + // this is unduly complicated... + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec + msec/1000; + ts.tv_nsec = (tv.tv_usec + (msec % 1000) * 1000) * 1000L; + if (ts.tv_nsec > 1000000000L) { + ts.tv_nsec -= 1000000000L; + ts.tv_sec++; + } + + rv = pthread_cond_timedwait(cond, lock, &ts); + + return rv; +} + /* - - - - - - - - - - - - - - - - - - - - - - - - */ #ifdef NEVER /* Not currently needed, or effective */ @@ -907,8 +972,11 @@ int create_parent_directories(char *path) { static int th_kkill_nprocess(void *pp) { kkill_nproc_ctx *ctx = (kkill_nproc_ctx *)pp; + /* set result to 0 if it ever suceeds or there was no such process */ + ctx->th->result = -1; while(ctx->stop == 0) { - kill_nprocess(ctx->pname, ctx->log); + if (kill_nprocess(ctx->pname, ctx->log) >= 0) + ctx->th->result = 0; msec_sleep(20); /* Don't hog the CPU */ } ctx->done = 1; @@ -1120,6 +1188,10 @@ 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++) { @@ -1294,6 +1366,36 @@ double sa_LabDE(double *Lab0, double *Lab1) { 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 */ @@ -1479,4 +1581,32 @@ int sa_lu_psinvert(double **out, double **in, int m, int n) { #endif /* SALONEINSTLIB */ /* ============================================================= */ +/* Diagnostic aids */ + +// Print bytes as hex to debug log */ +void adump_bytes(a1log *log, char *pfx, unsigned char *buf, int base, int len) { + int i, j, ii; + char oline[200] = { '\000' }, *bp = oline; + for (i = j = 0; i < len; i++) { + if ((i % 16) == 0) + bp += sprintf(bp,"%s%04x:",pfx,base+i); + bp += sprintf(bp," %02x",buf[i]); + if ((i+1) >= len || ((i+1) % 16) == 0) { + for (ii = i; ((ii+1) % 16) != 0; ii++) + bp += sprintf(bp," "); + bp += sprintf(bp," "); + for (; j <= i; j++) { + if (!(buf[j] & 0x80) && isprint(buf[j])) + bp += sprintf(bp,"%c",buf[j]); + else + bp += sprintf(bp,"."); + } + bp += sprintf(bp,"\n"); + a1logd(log,0,"%s",oline); + bp = oline; + } + } +} + + diff --git a/spectro/conv.h b/spectro/conv.h index 5d63efe..7a7baca 100644 --- a/spectro/conv.h +++ b/spectro/conv.h @@ -43,62 +43,6 @@ #endif /* - - - - - - - - - - - - - - - - - - -- */ -/* System compatibility #defines */ -#if defined (NT) - -#ifndef sys_stat -# define sys_stat _stat -#endif -#ifndef sys_mkdir -# define sys_mkdir _mkdir -#endif -#ifndef sys_read -# define sys_read _read -#endif -#ifndef sys_utime -# define sys_utime _utime -# define sys_utimbuf _utimbuf -#endif -#ifndef sys_access -# define sys_access _access -#endif - -#ifndef snprintf -# define snprintf _snprintf -# define vsnprintf _vsnprintf -#endif -#ifndef stricmp -# define stricmp _stricmp -#endif - -#endif /* NT */ - -#if defined (UNIX) - -#ifndef sys_stat -# define sys_stat stat -#endif -#ifndef sys_mkdir -# define sys_mkdir mkdir -#endif -#ifndef sys_read -# define sys_read read -#endif -#ifndef sys_utime -# define sys_utime utime -# define sys_utimbuf utimbuf -#endif -#ifndef sys_access -# define sys_access access -#endif - -#ifndef stricmp -# define stricmp strcasecmp -#endif - -#endif /* UNIX */ - -/* - - - - - - - - - - - - - - - - - - -- */ /* System dependent convenience functions */ /* wait for and then return the next character from the keyboard */ @@ -145,21 +89,37 @@ int set_normal_priority(); #endif /* NEVER */ /* - - - - - - - - - - - - - - - - - - -- */ -/* An Argyll mutex */ +/* An Argyll mutex and condition */ /* amutex_trylock() returns nz if it can't lock the mutex */ +/* acond_timedwait() returns nz if it times out */ #ifdef NT # define amutex CRITICAL_SECTION -# define amutex_static(lock) CRITICAL_SECTION lock = { NULL, -1 } +# define amutex_static(lock) CRITICAL_SECTION lock = {(void*)-1,-1 } # define amutex_init(lock) InitializeCriticalSection(&(lock)) # define amutex_del(lock) DeleteCriticalSection(&(lock)) # define amutex_lock(lock) EnterCriticalSection(&(lock)) # define amutex_trylock(lock) (!TryEnterCriticalSection(&(lock))) # define amutex_unlock(lock) LeaveCriticalSection(&(lock)) + +# define acond HANDLE +# define acond_static(cond) pthread_cond_t (cond) = PTHREAD_COND_INITIALIZER +# define acond_init(cond) (cond = CreateEvent(NULL, 0, 0, NULL)) +# define acond_del(cond) CloseHandle(cond) +# define acond_wait(cond, lock) (LeaveCriticalSection(&(lock)), \ + WaitForSingleObject(cond, INFINITE), \ + EnterCriticalSection(&(lock))) +# define acond_signal(cond) SetEvent(cond) +# define acond_timedwait(cond, lock, msec) \ + acond_timedwait_imp(cond, &(lock), msec) + +int acond_timedwait_imp(HANDLE cond, CRITICAL_SECTION *lock, int msec); + #endif #ifdef UNIX + # define amutex pthread_mutex_t # define amutex_static(lock) pthread_mutex_t (lock) = PTHREAD_MUTEX_INITIALIZER # define amutex_init(lock) pthread_mutex_init(&(lock), NULL) @@ -167,8 +127,21 @@ int set_normal_priority(); # define amutex_lock(lock) pthread_mutex_lock(&(lock)) # define amutex_trylock(lock) pthread_mutex_trylock(&(lock)) # define amutex_unlock(lock) pthread_mutex_unlock(&(lock)) + +# define acond pthread_cond_t +# define acond_static(cond) pthread_cond_t (cond) = PTHREAD_COND_INITIALIZER +# define acond_init(cond) pthread_cond_init(&(cond), NULL) +# define acond_del(cond) pthread_cond_destroy(&(cond)) +# define acond_wait(cond, lock) pthread_cond_wait(&(cond), &(lock)) +# define acond_signal(cond) pthread_cond_signal(&(cond)) +# define acond_timedwait(cond, lock, msec) \ + acond_timedwait_imp(&(cond), &(lock), msec) + +int acond_timedwait_imp(pthread_cond_t *cond, pthread_mutex_t *lock, int msec); + #endif + /* - - - - - - - - - - - - - - - - - - -- */ /* An Argyll thread. */ @@ -198,7 +171,7 @@ struct _athread { }; typedef struct _athread athread; -/* Create and start a thread */ +/* Create and start a thread. Return NULL on error. */ /* Thread function should only return on completion or error. */ /* It should return 0 on completion or exit, nz on error. */ athread *new_athread(int (*function)(void *context), void *context); @@ -256,6 +229,7 @@ typedef enum { } 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]); @@ -271,6 +245,7 @@ double sa_LabDE(double *in0, double *in1); #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 @@ -280,6 +255,7 @@ double sa_LabDE(double *in0, double *in1); #define icmScale3 sa_Scale3 #define icmClamp3 sa_Clamp3 #define icmLabDE sa_LabDE +#define icmXYZ2Lab sa_XYZ2Lab /* A subset of numlib */ @@ -290,7 +266,10 @@ int sa_lu_psinvert(double **out, double **in, int m, int n); #endif /* SALONEINSTLIB */ /* - - - - - - - - - - - - - - - - - - -- */ +/* Diagnostic aids */ +// Print bytes as hex to debug log */ +void adump_bytes(a1log *log, char *pfx, unsigned char *buf, int base, int len); /* - - - - - - - - - - - - - - - - - - -- */ diff --git a/spectro/dispcal.c b/spectro/dispcal.c index b8f3ebe..0a0bc16 100644 --- a/spectro/dispcal.c +++ b/spectro/dispcal.c @@ -20,6 +20,13 @@ /* TTBD + Should shift to using xicc code for BT.1886 and target response + curve, for consistency with collink etc. + + Add support for black recalibration using i1pro or munki. + Setable timeout ? Need to allow placing instrument back + on screen. Need this to properly handle ss anyway ? + Calibrating the black point of a true power response device is very slow to converge - the jacobian is always underestimating the actual delta RGB needed because the @@ -82,11 +89,11 @@ in "Figure out the black point target" - Yes they are !! Verify probably shouldn't work this way. - Add DICOM support: + Add DICOM Part 14 GSDF support: + * Add absolute DICOM function target to dispcal. * Add 20% grey background full screen option + 10% patch recommendation - * Add "include Glare" option for contact instruments to dispsup. - * Add absolute DICOM function target. + * Add "include Glare" option for contact instruments to dispsup.c * Add DICOM mode black point hue handling (? what policy ?) * Add DICOM stats report (JND dE + mean + SD) to verify ?? */ @@ -111,14 +118,16 @@ #include "xicc.h" #include "xspect.h" #include "xcolorants.h" -#include "ccmx.h" -#include "ccss.h" #include "cgats.h" #include "insttypes.h" #include "conv.h" #include "icoms.h" #include "inst.h" +#include "ccmx.h" +#include "ccss.h" #include "dispwin.h" +#include "ui.h" +#include "ccast.h" #include "dispsup.h" #include "rspl.h" #include "moncurve.h" @@ -127,7 +136,9 @@ #include "icc.h" #include "sort.h" #include "instappsup.h" -#include "spyd2setup.h" /* Enable Spyder 2 access */ +#ifdef ENABLE_USB +# include "spyd2.h" +#endif #undef DEBUG #undef DEBUG_OFFSET /* Keep test window out of the way */ @@ -208,6 +219,7 @@ typedef struct { double gooff; /* Target output offset (normalised to Y max of 1.0) */ int nat; /* Flag - nz if native white target */ double nbrate; /* Neutral blend weight (power) */ + int bkhack; /* Flag - nz if black is hacked to be device zero */ /* Viewing conditions adjustment */ int vc; /* Flag, nz to enable viewing conditions adjustment */ @@ -1238,7 +1250,7 @@ static double comp_ct( if ((ct = icx_XYZ2ill_ct(ct_xyz, plank != 0 ? icxIT_Ptemp : icxIT_Dtemp, obType, NULL, xyz, NULL, dovct)) < 0) - error ("Got bad color temperature conversion\n"); + error("Got bad color temperature conversion\n"); if (de != NULL) { icmAry2XYZ(wN, ct_xyz); @@ -1271,6 +1283,193 @@ extern ICCLIB_API double bwXYZLabDE(icmXYZNumber *w, double *targ, double *act, } /* =================================================================== */ +#ifdef MEAS_RES + +#define NVAL 240 /* Nominal measurement value */ + +/* return the estimated RAMDAC precision. Return 0 if not certain */ +static int comp_ramdac_prec( + int base, /* Base quantization to test for */ + col *ttt /* Measurement values */ +) { + int i, j; + double min, max; + double val[17]; + double meas[17]; + + double scale; + double targ[17]; + double score[5]; + double bits[5]; + + double bcor, bcor2; + int bbits = 0, rbits; + + int verb = 0; + + /* Extract the measurements */ + for (i = 0; i < 17; i++) { + val[i] = (double)i; + meas[i] = ttt[i].XYZ[1]; + } +#ifdef DEBUG + fprintf(dbgo,"raw measurements:\n"); + do_plot(val, meas, NULL, NULL, 17); +#endif + + /* Determine min & max, and normalize the values */ + min = 1e9, max = -1e9; + for (i = 0; i < 17; i++) { + if (meas[i] < min) + min = meas[i]; + if (meas[i] > max) + max = meas[i]; + } + for (i = 0; i < 17; i++) { + meas[i] = (meas[i] - min)/(max - min); +//printf("meas[%d] = %f\n",i,meas[i]); + } + + /* Create score for each hypothesis */ + scale = 1.0; + for (j = 0; j < 5; j++) { + int k; + int step = 1 << (4 - j); + double v = 0.0; + double merr; + + bits[j] = 8.0 + j; + + /* Create the target response */ + for (i = 0; i < 17;) { + for (k = 0; k < step && (i+k) < 17; k++) { + targ[i + k] = v; +//printf("j %d: targ[%d] = %f\n",j,i+k,v); + } + v += step/16.0; + i += k; + } + + /* Tweak it for typical display non-linearity */ + min = 1e9, max = -1e9; + for (i = 0; i < 17; i++) { + targ[i] = pow((NVAL + targ[i]/16.0)/255.0, 2.2); + if (targ[i] < min) + min = targ[i]; + if (targ[i] > max) + max = targ[i]; + } + for (i = 0; i < 17; i++) + targ[i] = (targ[i] - min)/(max - min); + + /* Try and make fit a little better */ + /* with a crude optimisation */ + for (k = 0; k < 50; k++) { + + merr = 0.0; + for (i = 0; i < 17; i++) + merr += targ[i] - meas[i]; + merr /= 17.0; + + for (i = 0; i < 17; i++) { + targ[i] *= (1.0 + 0.5 * merr); + targ[i] -= 0.5 * merr; +// targ[i] -= merr; + } + } + + score[j] = 0.0; + for (i = 0; i < 17; i++) { + double tt = targ[i] - meas[i]; + score[j] += tt * tt; + } + score[j] *= scale; + scale *= 1.1; + +#ifdef DEBUG + printf("%d bits score %f\n",8+j,score[j]); + do_plot(val, meas, targ, NULL, 17); +#endif + } + + /* Pick the best score */ + bcor = bcor2 = 1e8; + bbits = 0; + + for (j = 0; j < 5; j++) { + if (score[j] < bcor) { + bcor2 = bcor; + bcor = score[j]; + bbits = 8+j; + } else if (score[j] < bcor2) + bcor2 = score[j]; + } + + rbits = bbits; + + /* Don't pick anything if it's not reasonably certain */ + if (bcor2/bcor < 1.3 + || (bcor2/bcor < 2.1 && bcor > 0.15) + ) rbits = 0; + +#ifdef DEBUG + printf("Win score %f by cor %f, ratio %f\n",bcor, bcor2 - bcor, bcor2/bcor); + printf("Best %d, returning %d bits\n",bbits,rbits); + do_plot10(bits, score, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5, 1); +#endif + + return rbits; +} + +static int meas_ramdac_prec(int base, disprd *dr) { + col ttt[17]; + int rv, n, rbits; + + /* setup the measurements */ + for (n = 0; n < 17; n++) { + int ii; + ii = (NVAL * 256); + ii += n * 16; + ttt[n].r = ttt[n].g = ttt[n].b = ii/(double)(65535); + } + + if ((rv = dr->read(dr, ttt, 17, 1, 17, 1, 0, instNoClamp)) != 0) { + warning("display read failed with '%s'\n",disprd_err(rv)); + return 0; + } + + rbits = comp_ramdac_prec(base, ttt); + + /* If it failed, try the average to two sets of measurements */ + if (rbits == 0) { + col t2[17]; + for (n = 0; n < 17; n++) + t2[n] = ttt[n]; + + if ((rv = dr->read(dr, t2, 17, 1, 17, 1, 0, instNoClamp)) != 0) { + warning("display read failed with '%s'\n",disprd_err(rv)); + return 0; + } + + for (n = 0; n < 17; n++) + ttt[n].XYZ[1] += t2[n].XYZ[1]; + + rbits = comp_ramdac_prec(base, ttt); + + /* As a last resort, try just the second measurement */ + if (rbits == 0) + rbits = comp_ramdac_prec(base, t2); + } + + return rbits; +} + +#undef NVAL + +#endif /* MEAS_RES */ + +/* =================================================================== */ + /* Default gamma */ double g_def_gamma = 2.4; @@ -1279,12 +1478,14 @@ double g_def_gamma = 2.4; Flags used: ABCDEFGHIJKLMNOPQRSTUVWXYZ - upper .......... ....... . ... + upper .......... ....... . .... lower ....... . ...... .... .. */ -void usage(char *diag, ...) { +/* Flag = 0x0000 = default */ +/* Flag & 0x0001 = list ChromCast's */ +void usage(int flag, char *diag, ...) { int i; disppath **dp; icompaths *icmps; @@ -1292,8 +1493,6 @@ void usage(char *diag, ...) { fprintf(stderr,"Calibrate a Display, Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); - if (setup_spyd2() == 2) - fprintf(stderr,"WARNING: This file contains a proprietary firmware image, and may not be freely distributed !\n"); if (diag != NULL) { va_list args; fprintf(stderr,"Diagnostic: "); @@ -1324,6 +1523,22 @@ void usage(char *diag, ...) { } free_disppaths(dp); fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); + fprintf(stderr," -dcc[:n] Display via n'th ChromeCast (default 1, ? for list)\n"); + if (flag & 0x001) { + ccast_id **ids; + if ((ids = get_ccids()) == NULL) { + fprintf(stderr," ** Error discovering ChromCasts **\n"); + } else { + if (ids[0] == NULL) + fprintf(stderr," ** No ChromCasts found **\n"); + else { + int i; + for (i = 0; ids[i] != NULL; i++) + fprintf(stderr," %d = '%s'\n",i+1,ids[i]->name); + free_ccids(ids); + } + } + } #ifdef NT fprintf(stderr," -dmadvr Display via MadVR Video Renderer\n"); #endif @@ -1336,7 +1551,8 @@ void usage(char *diag, ...) { for (i = 0; ; i++) { if (paths[i] == NULL) break; - if (paths[i]->itype == instSpyder2 && setup_spyd2() == 0) + 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 fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name); @@ -1372,6 +1588,7 @@ void usage(char *diag, ...) { fprintf(stderr," -a ambient Use viewing condition adjustment for ambient in Lux\n"); fprintf(stderr," -k factor Amount to correct black hue, 0 = none, 1 = full, Default = Automatic\n"); fprintf(stderr," -A rate Rate of blending from neutral to black point. Default %.1f\n",NEUTRAL_BLEND_RATE); + fprintf(stderr," -b Use forced black point hack\n"); fprintf(stderr," -B blkbright Set the target black brightness in cd/m^2\n"); fprintf(stderr," -e [n] Run n verify passes on final curves\n"); fprintf(stderr," -z Run only verify pass on installed calibration curves\n"); @@ -1452,6 +1669,7 @@ int main(int argc, char *argv[]) { double egamma = 0.0; /* Effective Gamma target, NZ if set */ double ambient = 0.0; /* NZ if viewing cond. adjustment to be used (Lux) */ double bkcorrect = -1.0; /* Level of black point correction, < 0 = auto */ + int bkhack = 0; double bkbright = 0.0; /* Target black brightness ( 0.0 == min) */ int quality = -99; /* Quality level, -2 = v, -1 = l, 0 = m, 1 = h, 2 = u */ int isteps = 22; /* Initial measurement steps/3 (medium) */ @@ -1464,6 +1682,9 @@ int main(int argc, char *argv[]) { int verify = 0; /* Do a verify after last refinement, 2 = do only verify. */ int nver = 0; /* Number of verify passes after refinement */ int webdisp = 0; /* NZ for web display, == port number */ + int ccdisp = 0; /* NZ for ChromeCast, == list index */ + ccast_id **ccids = NULL; + ccast_id *ccid = NULL; #ifdef NT int madvrdisp = 0; /* NZ for madvr display */ #endif @@ -1496,25 +1717,31 @@ int main(int argc, char *argv[]) { #if defined(__APPLE__) { - SInt32 MacVers; + SInt32 MacMajVers, MacMinVers, MacBFVers; /* Hmm. Maybe this should actually be 1.72 ?? */ g_def_gamma = 1.8; - /* OS X 10.6 uses a nominal gamma of 2.2 */ - if (Gestalt(gestaltSystemVersion, &MacVers) == noErr) { - if (MacVers >= 0x1060) { +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 + /* gestaltSystemVersionMajor etc. isn't supported on older systems, */ + /* although "Gestalt(gestaltSystemVersion, &MacVers)" is, but this */ + /* causes warning messages in 10.10. */ + + /* OS X 10.6+ uses a nominal gamma of 2.2 */ + if (Gestalt(gestaltSystemVersionMajor, &MacMajVers) == noErr + && Gestalt(gestaltSystemVersionMinor, &MacMinVers) == noErr + && Gestalt(gestaltSystemVersionBugFix, &MacBFVers) == noErr) { + if (MacMajVers >= 10 && MacMinVers >= 6) { g_def_gamma = 2.4; } } +#endif /* >= 1040 */ } #else g_def_gamma = 2.4; /* Typical CRT gamma */ #endif gamma = g_def_gamma; - setup_spyd2(); /* Load firware if available */ - x.gammat = gt_power ; /* Default gamma type */ x.egamma = 0.0; /* Default effective gamma none */ x.oofff = 1.0; /* Default is all output ofset */ @@ -1533,7 +1760,7 @@ int main(int argc, char *argv[]) { #endif if (argc <= 1) - usage("Too few arguments"); + usage(0,"Too few arguments"); /* Process the arguments */ mfa = 1; /* Minimum final arguments */ @@ -1556,7 +1783,7 @@ int main(int argc, char *argv[]) { } if (argv[fa][1] == '?' || argv[fa][1] == '-') { - usage("Usage requested"); + usage(0,"Usage requested"); } else if (argv[fa][1] == 'v') { verb = 1; @@ -1574,7 +1801,19 @@ int main(int argc, char *argv[]) { if (na[3] == ':') { webdisp = atoi(na+4); if (webdisp == 0 || webdisp > 65535) - usage("Web port number must be in range 1..65535"); + usage(0,"Web port number must be in range 1..65535"); + } + fa = nfa; + } else if (strncmp(na,"cc",2) == 0 + || strncmp(na,"CC",2) == 0) { + ccdisp = 1; + if (na[2] == ':') { + if (na[3] < '0' || na[3] > '9') + usage(0x0001,"Available ChromeCasts"); + + ccdisp = atoi(na+3); + if (ccdisp <= 0) + usage(0,"ChromCast number must be in range 1..N"); } fa = nfa; #ifdef NT @@ -1588,10 +1827,10 @@ int main(int argc, char *argv[]) { int ix, iv; if (strcmp(&argv[fa][2], "isplay") == 0 || strcmp(&argv[fa][2], "ISPLAY") == 0) { - if (++fa >= argc || argv[fa][0] == '-') usage("Parameter expected following -display"); + if (++fa >= argc || argv[fa][0] == '-') usage(0,"Parameter expected following -display"); setenv("DISPLAY", argv[fa], 1); } else { - if (na == NULL) usage("Parameter expected following -d"); + if (na == NULL) usage(0,"Parameter expected following -d"); fa = nfa; if (strcmp(na,"fake") == 0) { fake = 1; @@ -1603,14 +1842,14 @@ int main(int argc, char *argv[]) { if (disp != NULL) free_a_disppath(disp); if ((disp = get_a_display(ix-1)) == NULL) - usage("-d parameter %d out of range",ix); + usage(0,"-d parameter %d out of range",ix); if (iv > 0) disp->rscreen = iv-1; } } #else int ix; - if (na == NULL) usage("Parameter expected following -d"); + if (na == NULL) usage(0,"Parameter expected following -d"); fa = nfa; if (strcmp(na,"fake") == 0) { fake = 1; @@ -1619,7 +1858,7 @@ int main(int argc, char *argv[]) { if (disp != NULL) free_a_disppath(disp); if ((disp = get_a_display(ix-1)) == NULL) - usage("-d parameter %d out of range",ix); + usage(0,"-d parameter %d out of range",ix); } #endif } @@ -1646,13 +1885,13 @@ int main(int argc, char *argv[]) { } else if (argv[fa][1] == 'X') { int ix; fa = nfa; - if (na == NULL) usage("Parameter expected following -X"); + if (na == NULL) usage(0,"Parameter expected following -X"); strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000'; /* Drift Compensation */ } else if (argv[fa][1] == 'I') { fa = nfa; - if (na == NULL || na[0] == '\000') usage("Parameter expected after -I"); + if (na == NULL || na[0] == '\000') usage(0,"Parameter expected after -I"); for (i=0; ; i++) { if (na[i] == '\000') break; @@ -1661,13 +1900,13 @@ int main(int argc, char *argv[]) { else if (na[i] == 'w' || na[i] == 'W') wdrift = 1; else - usage("-I parameter '%c' not recognised",na[i]); + usage(0,"-I parameter '%c' not recognised",na[i]); } /* Spectral Observer type */ } else if (argv[fa][1] == 'Q') { fa = nfa; - if (na == NULL) usage("Parameter expecte after -Q"); + if (na == NULL) usage(0,"Parameter expecte after -Q"); if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ obType = icxOT_CIE_1931_2; } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */ @@ -1681,24 +1920,24 @@ int main(int argc, char *argv[]) { } else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */ obType = icxOT_Shaw_Fairchild_2; } else - usage("-Q parameter '%s' not recognised",na); + usage(0,"-Q parameter '%s' not recognised",na); /* Change color callout */ } else if (argv[fa][1] == 'C') { fa = nfa; - if (na == NULL) usage("Parameter expected after -C"); + if (na == NULL) usage(0,"Parameter expected after -C"); ccallout = na; /* Measure color callout */ } else if (argv[fa][1] == 'M') { fa = nfa; - if (na == NULL) usage("Parameter expected after -M"); + if (na == NULL) usage(0,"Parameter expected after -M"); mcallout = na; /* Serial port flow control */ } else if (argv[fa][1] == 'W') { fa = nfa; - if (na == NULL) usage("Paramater expected following -W"); + if (na == NULL) usage(0,"Paramater expected following -W"); if (na[0] == 'n' || na[0] == 'N') fc = fc_none; else if (na[0] == 'h' || na[0] == 'H') @@ -1706,7 +1945,7 @@ int main(int argc, char *argv[]) { else if (na[0] == 'x' || na[0] == 'X') fc = fc_XonXOff; else - usage("-W parameter '%c' not recognised",na[0]); + usage(0,"-W parameter '%c' not recognised",na[0]); /* Debug coms */ } else if (argv[fa][1] == 'D') { @@ -1721,21 +1960,21 @@ int main(int argc, char *argv[]) { /* Black point correction amount */ } else if (argv[fa][1] == 'k') { fa = nfa; - if (na == NULL) usage("Paramater expected following -k"); + if (na == NULL) usage(0,"Paramater expected following -k"); bkcorrect = atof(na); - if (bkcorrect < 0.0 || bkcorrect > 1.0) usage("-k parameter must be between 0.0 and 1.0"); + if (bkcorrect < 0.0 || bkcorrect > 1.0) usage(0,"-k parameter must be between 0.0 and 1.0"); /* Neutral blend rate (power) */ } else if (argv[fa][1] == 'A') { fa = nfa; - if (na == NULL) usage("Paramater expected following -A"); + if (na == NULL) usage(0,"Paramater expected following -A"); x.nbrate = atof(na); - if (x.nbrate < 0.05 || x.nbrate > 20.0) usage("-A parameter must be between 0.05 and 20.0"); + if (x.nbrate < 0.05 || x.nbrate > 20.0) usage(0,"-A parameter must be between 0.05 and 20.0"); /* Black brightness */ } else if (argv[fa][1] == 'B') { fa = nfa; - if (na == NULL) usage("Parameter expected after -B"); + if (na == NULL) usage(0,"Parameter expected after -B"); bkbright = atof(na); - if (bkbright <= 0.0 || bkbright > 100000.0) usage("-B parameter %f out of range",bkbright); + if (bkbright <= 0.0 || bkbright > 100000.0) usage(0,"-B parameter %f out of range",bkbright); /* Number of verify passes */ } else if (argv[fa][1] == 'e') { @@ -1757,9 +1996,9 @@ int main(int argc, char *argv[]) { /* COM port */ } else if (argv[fa][1] == 'c') { fa = nfa; - if (na == NULL) usage("Paramater expected following -c"); + if (na == NULL) usage(0,"Paramater expected following -c"); comport = atoi(na); - if (comport < 1 || comport > 50) usage("-c parameter %d out of range",comport); + if (comport < 1 || comport > 50) usage(0,"-c parameter %d out of range",comport); /* Telephoto */ } else if (argv[fa][1] == 'p') { @@ -1787,7 +2026,7 @@ int main(int argc, char *argv[]) { /* Fast Profile Description */ } else if (argv[fa][1] == 'O') { fa = nfa; - if (na == NULL) usage("Expect argument to profile description flag -O"); + if (na == NULL) usage(0,"Expect argument to profile description flag -O"); profDesc = na; /* Update calibration and (optionally) profile */ @@ -1798,7 +2037,7 @@ int main(int argc, char *argv[]) { /* Speed/Quality */ } else if (argv[fa][1] == 'q') { fa = nfa; - if (na == NULL) usage("Parameter expected following -q"); + if (na == NULL) usage(0,"Parameter expected following -q"); switch (na[0]) { case 'L': /* Test value */ quality = -3; @@ -1824,13 +2063,13 @@ int main(int argc, char *argv[]) { quality = 2; break; default: - usage("-q parameter '%c' not recognised",na[0]); + usage(0,"-q parameter '%c' not recognised",na[0]); } /* Display type */ } else if (argv[fa][1] == 'y') { fa = nfa; - if (na == NULL) usage("Parameter expected after -y"); + if (na == NULL) usage(0,"Parameter expected after -y"); dtype = na[0]; /* Daylight color temperature */ @@ -1842,31 +2081,35 @@ int main(int argc, char *argv[]) { if (na != NULL) { fa = nfa; temp = atof(na); - if (temp < 1000.0 || temp > 15000.0) usage("-%c parameter %f out of range",argv[fa][1], temp); + if (temp < 1000.0 || temp > 15000.0) usage(0,"-%c parameter %f out of range",argv[fa][1], temp); } /* White point as x, y */ } else if (argv[fa][1] == 'w') { fa = nfa; - if (na == NULL) usage("Parameter expected after -w"); + if (na == NULL) usage(0,"Parameter expected after -w"); if (sscanf(na, " %lf,%lf ", &wpx, &wpy) != 2) - usage("-w parameter '%s' not recognised",na); + usage(0,"-w parameter '%s' not recognised",na); /* Show CXT rather than VXT when adjusting native white point */ } else if (argv[fa][1] == 'L') { dovct = 0; - /* White brightness */ + /* Black point hack/White brightness */ } else if (argv[fa][1] == 'b') { - fa = nfa; - if (na == NULL) usage("Parameter expected after -b"); - tbright = atof(na); - if (tbright <= 0.0 || tbright > 100000.0) usage("-b parameter %f out of range",tbright); + if (na == NULL) { + bkhack = 1; + } else { + fa = nfa; + /* if (na == NULL) usage(0,"Parameter expected after -b"); */ + tbright = atof(na); + if (tbright <= 0.0 || tbright > 100000.0) usage(0,"-b parameter %f out of range",tbright); + } /* Target transfer curve */ } else if (argv[fa][1] == 'g') { fa = nfa; - if (na == NULL) usage("Parameter expected after -g"); + if (na == NULL) usage(0,"Parameter expected after -g"); if ((na[0] == 'l' || na[0] == 'L') && na[1] == '\000') x.gammat = gt_Lab; else if ((na[0] == 's' || na[0] == 'S') && na[1] == '\000') @@ -1877,16 +2120,16 @@ int main(int argc, char *argv[]) { x.gammat = gt_SMPTE240M; else { gamma = atof(na); - if (gamma <= 0.0 || gamma > 10.0) usage("-g parameter %f out of range",gamma); + if (gamma <= 0.0 || gamma > 10.0) usage(0,"-g parameter %f out of range",gamma); x.gammat = gt_power; } /* Effective gamma power */ } else if (argv[fa][1] == 'G') { fa = nfa; - if (na == NULL) usage("Parameter expected after -G"); + if (na == NULL) usage(0,"Parameter expected after -G"); egamma = atof(na); - if (egamma <= 0.0 || egamma > 10.0) usage("-G parameter %f out of range",egamma); + if (egamma <= 0.0 || egamma > 10.0) usage(0,"-G parameter %f out of range",egamma); x.gammat = gt_power; /* Degree of output offset */ @@ -1897,33 +2140,33 @@ int main(int argc, char *argv[]) { } else { x.oofff = atof(na); if (x.oofff < 0.0 || x.oofff > 1.0) - usage("-f parameter %f out of range",x.oofff); + usage(0,"-f parameter %f out of range",x.oofff); } /* Ambient light level */ } else if (argv[fa][1] == 'a') { fa = nfa; - if (na == NULL) usage("Parameter expected after -a"); + if (na == NULL) usage(0,"Parameter expected after -a"); ambient = atof(na); if (ambient < 0.0) - usage("-a parameter %f out of range",ambient); + usage(0,"-a parameter %f out of range",ambient); /* Test patch offset and size */ } else if (argv[fa][1] == 'P') { fa = nfa; - if (na == NULL) usage("Parameter expected after -P"); + if (na == NULL) usage(0,"Parameter expected after -P"); if (sscanf(na, " %lf,%lf,%lf,%lf ", &ho, &vo, &hpatscale, &vpatscale) == 4) { ; } else if (sscanf(na, " %lf,%lf,%lf ", &ho, &vo, &hpatscale) == 3) { vpatscale = hpatscale; } else { - usage("-P parameter '%s' not recognised",na); + usage(0,"-P parameter '%s' not recognised",na); } if (ho < 0.0 || ho > 1.0 || vo < 0.0 || vo > 1.0 || hpatscale <= 0.0 || hpatscale > 50.0 || vpatscale <= 0.0 || vpatscale > 50.0) - usage("-P parameters %f %f %f %f out of range",ho,vo,hpatscale,vpatscale); + usage(0,"-P parameters %f %f %f %f out of range",ho,vo,hpatscale,vpatscale); ho = 2.0 * ho - 1.0; vo = 2.0 * vo - 1.0; @@ -1934,35 +2177,50 @@ int main(int argc, char *argv[]) { /* Extra flags */ } else if (argv[fa][1] == 'Y') { if (na == NULL) - usage("Flag '-Y' expects extra flag"); + usage(0,"Flag '-Y' expects extra flag"); if (na[0] == 'R') { if (na[1] != ':') - usage("-Y R:rate syntax incorrect"); + usage(0,"-Y R:rate syntax incorrect"); refrate = atof(na+2); if (refrate < 5.0 || refrate > 150.0) - usage("-Y R:rate %f Hz not in valid range",refrate); + usage(0,"-Y R:rate %f Hz not in valid range",refrate); } else if (na[0] == 'A') { nadaptive = 1; } else if (na[0] == 'p') { noplace = 1; } else { - usage("Flag '-Y %c' not recognised",na[0]); + usage(0,"Flag '-Y %c' not recognised",na[0]); } fa = nfa; } else - usage("Flag '-%c' not recognised",argv[fa][1]); + usage(0,"Flag '-%c' not recognised",argv[fa][1]); } else break; } + if (bkhack && bkbright > 0.0) { + error("Can't use -b black point hack and set target black brightness"); + } + + if (bkhack && bkcorrect != 0.0) { + if (bkcorrect > 0.0) + warning("Due to -b flag, -k factor will be set to 0.0"); + bkcorrect = 0.0; + } + /* No explicit display has been set */ if ( #ifndef SHOW_WINDOW_ONFAKE - !fake && + !fake #endif - disp == NULL) { +#ifdef NT + && madvrdisp == 0 +#endif + && webdisp == 0 + && ccdisp == 0 + && disp == NULL) { int ix = 0; #if defined(UNIX_X11) char *dn, *pp; @@ -2019,9 +2277,22 @@ int main(int argc, char *argv[]) { if ((ipath = icmps->get_path(icmps, comport)) == NULL) error("No instrument at port %d",comport); + /* If we've requested ChromeCast, look it up */ + if (ccdisp) { + if ((ccids = get_ccids()) == NULL) + error("discovering ChromCasts failed"); + if (ccids[0] == NULL) + error("There are no ChromCasts to use\n"); + for (i = 0; ccids[i] != NULL; i++) + ; + if (ccdisp < 1 || ccdisp > i) + error("Chosen ChromCasts (%d) is outside list (1..%d)\n",ccdisp,i); + ccid = ccids[ccdisp-1]; + } + if (docalib) { - if ((rv = disprd_calibration(ipath, fc, dtype, 0, tele, nadaptive, nocal, disp, - webdisp, + if ((rv = disprd_calibration(ipath, fc, dtype, -1, 0, tele, nadaptive, nocal, disp, + webdisp, ccid, #ifdef NT madvrdisp, #endif @@ -2034,7 +2305,7 @@ int main(int argc, char *argv[]) { if (verify != 2 && doreport == 0) { /* Get the file name argument */ - if (fa >= argc || argv[fa][0] == '-') usage("Output filname parameter not found"); + if (fa >= argc || argv[fa][0] == '-') usage(0,"Output filname parameter not found"); strncpy(outname,argv[fa],MAXNAMEL-4); outname[MAXNAMEL-4] = '\000'; strcat(outname,".cal"); if (iccoutname[0] == '\000') { @@ -2061,14 +2332,16 @@ int main(int argc, char *argv[]) { native = 0; /* But measure current calibrated & CM response for verify or report calibrated */ /* Get ready to do some readings */ - if ((dr = new_disprd(&errc, ipath, fc, dtype, 0, tele, nadaptive, nocal, noplace, + if ((dr = new_disprd(&errc, ipath, fc, dtype, -1, 0, tele, nadaptive, nocal, noplace, highres, refrate, native, &noramdac, &nocm, NULL, 0, - disp, out_tvenc, blackbg, override, webdisp, + disp, out_tvenc, blackbg, override, webdisp, ccid, #ifdef NT madvrdisp, #endif ccallout, mcallout, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, + ccs != NULL ? ccs->dtech : cmx != NULL ? cmx->dtech : disptech_unknown, + cmx != NULL ? cmx->cc_cbid : 0, cmx != NULL ? cmx->matrix : NULL, ccs != NULL ? ccs->samples : NULL, ccs != NULL ? ccs->no_samp : 0, spec, obType, NULL, bdrift, wdrift, @@ -2136,6 +2409,7 @@ int main(int argc, char *argv[]) { cgamma = pop_gamma(tcols[0].XYZ[1], tcols[1].XYZ[1], tcols[2].XYZ[1]); #ifdef MEAS_RES +#ifdef NEVER // Use new code /* See if we can detect what sort of precision the LUT entries */ /* have. Our ability to detect this may be limited by the instrument */ /* (ie. Huey and Spyder 2) */ @@ -2175,7 +2449,7 @@ int main(int argc, char *argv[]) { if ((rv = dr->read(dr, ttt, res_samps, 1, res_samps, 1, 0, instNoClamp)) != 0) { dr->del(dr); error("display read failed with '%s'\n",disprd_err(rv)); - } + } /* Average the readings for each test value */ a0 = a1 = a2 = 0.0; for (n = 0; n < res_samps; n++) { @@ -2266,6 +2540,15 @@ int main(int argc, char *argv[]) { } } } +# else /* ! NEVER */ + /* See if we can deternine what sort of precision the LUT entries */ + /* have. Our ability to detect this may be limited by the instrument */ + /* (ie. Huey and Spyder 2) */ + /* We assume that we need to just detect 8 to 12 bits */ + if (doreport == 1) { + sigbits = meas_ramdac_prec(8, dr); + } +#endif /* NEVER */ #endif /* MEAS_RES */ if (doreport == 2) @@ -2285,9 +2568,9 @@ int main(int argc, char *argv[]) { #ifdef MEAS_RES if (doreport == 1) { if (sigbits == 0) { - warning("Unable to determine LUT entry bit depth - problems ?"); + warning("Unable to determine Video LUT entry bit depth"); } else { - printf("Effective LUT entry depth seems to be %d bits\n",sigbits); + printf("Effective Video LUT entry depth seems to be %d bits\n",sigbits); } } #endif /* MEAS_RES */ @@ -2356,11 +2639,11 @@ int main(int argc, char *argv[]) { if ((fi = icg->find_kword(icg, 0, "TARGET_WHITE_XYZ")) < 0) { dr->del(dr); - error ("Can't update '%s' - can't find field 'TARGET_WHITE_XYZ'",outname); + error("Can't update '%s' - can't find field 'TARGET_WHITE_XYZ'",outname); } if (sscanf(icg->t[0].kdata[fi], "%lf %lf %lf", &x.twh[0], &x.twh[1], &x.twh[2]) != 3) { dr->del(dr); - error ("Can't update '%s' - reading field 'TARGET_WHITE_XYZ' failed",outname); + error("Can't update '%s' - reading field 'TARGET_WHITE_XYZ' failed",outname); } x.nwh[0] = x.twh[0] / x.twh[1]; x.nwh[1] = x.twh[1] / x.twh[1]; @@ -2379,7 +2662,7 @@ int main(int argc, char *argv[]) { if ((fi = icg->find_kword(icg, 0, "TARGET_GAMMA")) < 0) { dr->del(dr); - error ("Can't update '%s' - can't find field 'TARGET_GAMMA'",outname); + error("Can't update '%s' - can't find field 'TARGET_GAMMA'",outname); } if (strcmp(icg->t[0].kdata[fi], "L_STAR") == 0) x.gammat = gt_Lab; @@ -2395,7 +2678,7 @@ int main(int argc, char *argv[]) { if (fabs(gamma) < 0.1 || fabs(gamma) > 5.0) { dr->del(dr); - error ("Can't update '%s' - field 'TARGET_GAMMA' has bad value %f",outname,fabs(gamma)); + error("Can't update '%s' - field 'TARGET_GAMMA' has bad value %f",outname,fabs(gamma)); } if (gamma < 0.0) { /* Effective gamma = actual power value */ egamma = -gamma; @@ -2410,21 +2693,40 @@ int main(int argc, char *argv[]) { } else { x.oofff = atof(icg->t[0].kdata[fi]); } + + if ((fi = icg->find_kword(icg, 0, "BLACK_POINT_HACK")) < 0) { + bkhack = 0; + } else { + if (strcmp(icg->t[0].kdata[fi], "YES") == 0 + || strcmp(icg->t[0].kdata[fi], "yes") == 0) { + bkhack = 1; + } else { + bkhack = 0; + } + } + if ((fi = icg->find_kword(icg, 0, "TARGET_BLACK_BRIGHTNESS")) < 0) { bkbright = 0.0; /* Native */ } else { bkbright = atof(icg->t[0].kdata[fi]); } - - + if (bkhack && bkbright > 0.0) { + error("Can't update '%s' - BLACK_POINT_HACK and TARGET_BLACK_BRIGHTNESS conflict",outname); + } if ((fi = icg->find_kword(icg, 0, "BLACK_POINT_CORRECTION")) < 0) { dr->del(dr); - error ("Can't update '%s' - can't find field 'BLACK_POINT_CORRECTION'",outname); + error("Can't update '%s' - can't find field 'BLACK_POINT_CORRECTION'",outname); } bkcorrect = atof(icg->t[0].kdata[fi]); if (bkcorrect < 0.0 || bkcorrect > 1.0) { dr->del(dr); - error ("Can't update '%s' - field 'BLACK_POINT_CORRECTION' has bad value %f",outname,bkcorrect); + error("Can't update '%s' - field 'BLACK_POINT_CORRECTION' has bad value %f",outname,bkcorrect); + } + + if (bkhack && bkcorrect != 0.0) { + if (bkcorrect > 0.0) + warning("Due to -b flag, -k factor will be set to 0.0"); + bkcorrect = 0.0; } if ((fi = icg->find_kword(icg, 0, "BLACK_NEUTRAL_BLEND_RATE")) < 0) { @@ -2433,13 +2735,13 @@ int main(int argc, char *argv[]) { x.nbrate = atof(icg->t[0].kdata[fi]); if (x.nbrate < 0.05 || x.nbrate > 20.0) { dr->del(dr); - error ("Can't update '%s' - field 'BLACK_NEUTRAL_BLEND_RATE' has bad value %f",outname,x.nbrate); + error("Can't update '%s' - field 'BLACK_NEUTRAL_BLEND_RATE' has bad value %f",outname,x.nbrate); } } if ((fi = icg->find_kword(icg, 0, "QUALITY")) < 0) { dr->del(dr); - error ("Can't update '%s' - can't find field 'QUALITY'",outname); + error("Can't update '%s' - can't find field 'QUALITY'",outname); } if (quality < -50) { /* User hasn't overridden quality */ if (strcmp(icg->t[0].kdata[fi], "ultra low") == 0) @@ -2456,7 +2758,7 @@ int main(int argc, char *argv[]) { quality = 2; else { dr->del(dr); - error ("Can't update '%s' - field 'QUALITY' has unrecognised value '%s'", + error("Can't update '%s' - field 'QUALITY' has unrecognised value '%s'", outname,icg->t[0].kdata[fi]); } } @@ -2477,7 +2779,7 @@ int main(int argc, char *argv[]) { } if ((rdv[k] = malloc(sizeof(mcvco) * nsamp)) == NULL) { dr->del(dr); - error ("Malloc of scattered data points failed"); + error("Malloc of scattered data points failed"); } } //printf("~1 allocated calibration curve objects\n"); @@ -2488,11 +2790,11 @@ int main(int argc, char *argv[]) { if ((si[k] = icg->find_field(icg, 0, fnames[k])) < 0) { dr->del(dr); - error ("Can't updata '%s' - can't find field '%s'",outname,fnames[k]); + error("Can't updata '%s' - can't find field '%s'",outname,fnames[k]); } if (icg->t[0].ftype[si[k]] != r_t) { dr->del(dr); - error ("Can't updata '%s' - field '%s' is wrong type",outname,fnames[k]); + error("Can't updata '%s' - field '%s' is wrong type",outname,fnames[k]); } } //printf("~1 Found calibration curve fields\n"); @@ -2522,16 +2824,16 @@ int main(int argc, char *argv[]) { //printf("~1 Reading device curve channel %d\n",k); if ((si[k] = icg->find_field(icg, 1, fnames[k])) < 0) { dr->del(dr); - error ("Can't updata '%s' - can't find field '%s'",outname,fnames[k]); + error("Can't updata '%s' - can't find field '%s'",outname,fnames[k]); } if (icg->t[1].ftype[si[k]] != r_t) { dr->del(dr); - error ("Can't updata '%s' - field '%s' is wrong type",outname,fnames[k]); + error("Can't updata '%s' - field '%s' is wrong type",outname,fnames[k]); } /* Create the model curves */ if ((pp = (double *)malloc(icg->t[1].nsets * sizeof(double))) == NULL) { dr->del(dr); - error ("Malloc of device curve parameters"); + error("Malloc of device curve parameters"); } for (i = 0; i < icg->t[1].nsets; i++) pp[i] = *((double *)icg->t[1].fdata[i][si[k]]); @@ -2554,19 +2856,19 @@ int main(int argc, char *argv[]) { if ((icco = new_icc()) == NULL) { dr->del(dr); - error ("Creation of ICC object to read profile '%s' failed",iccoutname); + error("Creation of ICC object to read profile '%s' failed",iccoutname); } /* Open up the profile for reading */ if ((ic_fp = new_icmFileStd_name(iccoutname,"r")) == NULL) { dr->del(dr); - error ("Can't open file '%s'",iccoutname); + error("Can't open file '%s'",iccoutname); } /* Read header etc. */ if ((rv = icco->read(icco,ic_fp,0)) != 0) { dr->del(dr); - error ("Reading profile '%s' failed with %d, %s",iccoutname, rv,icco->err); + error("Reading profile '%s' failed with %d, %s",iccoutname, rv,icco->err); } ic_fp->del(ic_fp); @@ -2663,6 +2965,8 @@ int main(int argc, char *argv[]) { else x.nat = 0; + x.bkhack = bkhack; + /* Say something about what we're doing */ if (verb) { if (out_tvenc) @@ -2696,6 +3000,8 @@ int main(int argc, char *argv[]) { printf("Target black brightness = %f cd/m^2\n",bkbright); else printf("Target black brightness = native brightness\n"); + if (bkhack) + printf("Black point device hack is enabled\n"); } switch(x.gammat) { @@ -3426,9 +3732,9 @@ int main(int argc, char *argv[]) { } else if (c == '7') { if (!verb) { /* Tell user command has been accepted */ if (verify == 2) - printf("Commencing device verification\n"); + printf("Commencing display verification\n"); else - printf("Commencing device calibration\n"); + printf("Commencing display calibration\n"); } break; } else if (c == '8' || c == 0x03 || c == 0x1b) { @@ -3451,10 +3757,10 @@ int main(int argc, char *argv[]) { /* Read the base test set */ { - icmXYZNumber mrd; /* Number for matrix */ - icmXYZNumber mgn; - icmXYZNumber mbl; - icmXYZNumber mwh; + double mrd[3]; /* Number for matrix */ + double mgn[3]; + double mbl[3]; + double mwh[3]; ramdac *or = NULL; col base[9] = { /* Base set of test colors */ @@ -3480,14 +3786,17 @@ int main(int argc, char *argv[]) { if (verb) { if (verify == 2) - printf("Commencing device verification\n"); + printf("Commencing display verification\n"); else - printf("Commencing device calibration\n"); + printf("Commencing display calibration\n"); } /* Switch to native for this, so the black calc is realistic. */ /* (Should we really get black aim from previous .cal though ???) */ if (verify == 2) { + if (fake) + error("Can't verify against current curves using fake device"); + if ((or = dr->dw->get_ramdac(dr->dw)) != NULL) { ramdac *r; if (verb) printf("Switching to native response for base measurements\n"); @@ -3543,10 +3852,10 @@ int main(int argc, char *argv[]) { icmAry2Ary(x.wh, base[ix_w1].XYZ); icmAry2XYZ(x.twN, x.wh); /* Use this as Lab reference white until we establish target */ - icmAry2XYZ(mrd, base[ix_r].XYZ); - icmAry2XYZ(mgn, base[ix_g].XYZ); - icmAry2XYZ(mbl, base[ix_b].XYZ); - icmAry2XYZ(mwh, base[ix_w1].XYZ); + icmAry2Ary(mrd, base[ix_r].XYZ); + icmAry2Ary(mgn, base[ix_g].XYZ); + icmAry2Ary(mbl, base[ix_b].XYZ); + icmAry2Ary(mwh, base[ix_w1].XYZ); if (verb) { printf("Black = XYZ %6.4f %6.4f %6.4f\n",x.bk[0],x.bk[1],x.bk[2]); @@ -3557,11 +3866,10 @@ int main(int argc, char *argv[]) { } /* Setup forward matrix */ - if (icmRGBprim2matrix(mwh, mrd, mgn, mbl, x.fm)) { + if (icmRGBXYZprim2matrix(mrd, mgn, mbl, mwh, x.fm)) { dr->del(dr); error("Aprox. fwd matrix unexpectedly singular\n"); } - icmTranspose3x3(x.fm, x.fm); /* Convert [RGB][XYZ] to [XYZ][RGB] */ #ifdef DEBUG if (verb) { @@ -3607,13 +3915,13 @@ int main(int argc, char *argv[]) { if ((cols = (col *)malloc(isteps * 4 * sizeof(col))) == NULL) { dr->del(dr); - error ("Malloc of array of readings failed"); + error("Malloc of array of readings failed"); } for (j = 0; j < 4; j++) { if ((asrgb[j] = (sxyz *)malloc(isteps * sizeof(sxyz))) == NULL) { free(cols); dr->del(dr); - error ("Malloc of array of readings failed"); + error("Malloc of array of readings failed"); } } @@ -3675,7 +3983,7 @@ int main(int argc, char *argv[]) { if ((sdv = malloc(sizeof(mcvco) * isteps)) == NULL) { free(cols); free(asrgb[0]); free(asrgb[1]); free(asrgb[2]); free(asrgb[3]); dr->del(dr); - error ("Malloc of scattered data points failed"); + error("Malloc of scattered data points failed"); } for (k = 0; k < 3; k++) { /* Create the model curves */ for (i = 0; i < isteps; i++) { @@ -3706,7 +4014,7 @@ int main(int argc, char *argv[]) { if ((x.rp = (optref *)malloc(sizeof(optref) * x.nrp)) == NULL) { free(cols); free(asrgb[0]); free(asrgb[1]); free(asrgb[2]); free(asrgb[3]); dr->del(dr); - error ("Malloc of measurement reference points failed"); + error("Malloc of measurement reference points failed"); } for (k = 0; k < 4; k++) { for (i = 0; i < isteps; i++) { @@ -3736,7 +4044,7 @@ int main(int argc, char *argv[]) { if ((sa = malloc(x.np * sizeof(double))) == NULL) { free(cols); free(asrgb[0]); free(asrgb[1]); free(asrgb[2]); free(asrgb[3]); dr->del(dr); - error ("Malloc of scattered data points failed"); + error("Malloc of scattered data points failed"); } for (i = 0; i < x.np; i++) @@ -3747,7 +4055,7 @@ int main(int argc, char *argv[]) { if (powell(&re, x.np, op, sa, 1e-5, 3000, dev_opt_func, (void *)&x, NULL, NULL) != 0) { free(cols); free(asrgb[0]); free(asrgb[1]); free(asrgb[2]); free(asrgb[3]); dr->del(dr); - error ("Model powell failed, re = %f",re); + error("Model powell failed, re = %f",re); } #else if (conjgrad(&re, x.np, op, sa, 1e-5, 3000, @@ -3851,7 +4159,7 @@ int main(int argc, char *argv[]) { set[0].r, set[0].g, set[0].b, alab[0], alab[1], alab[2], mlab[0], mlab[1], mlab[2],de); } anerr /= (double)(nn+3); - printf("Model maximum error (@ %f) = %f deltaE\n",mnv, mnerr); + printf("Model maximum error(@ %f) = %f deltaE\n",mnv, mnerr); printf("Model average error = %f deltaE\n",anerr); } #endif /* CHECK_MODEL */ @@ -3906,7 +4214,7 @@ int main(int argc, char *argv[]) { double sa = 0.1; if (powell(NULL, 1, &scale, &sa, 1e-7, 500, wp_opt_func, (void *)&x, NULL, NULL) != 0) - error ("WP scale powell failed"); + error("WP scale powell failed"); x.twh[0] *= scale; x.twh[1] *= scale; @@ -4146,7 +4454,7 @@ int main(int argc, char *argv[]) { for (j = 0; j < 3; j++) { if ((sdv[j] = malloc(sizeof(mcvco) * rsteps)) == NULL) { dr->del(dr); - error ("Malloc of scattered data points failed"); + error("Malloc of scattered data points failed"); } } @@ -4161,9 +4469,12 @@ int main(int argc, char *argv[]) { sdv[j][i].w = 1.0; } } - if (x.nat) /* Make curve go thought white if possible */ + if (x.nat) /* Make curve go thought white if possible by setting a weighting */ sdv[0][rsteps-1].w = sdv[1][rsteps-1].w = sdv[2][rsteps-1].w = 50.0; + if (x.bkhack) /* Make curve go thought black if possible by setting a weighting */ + sdv[0][0].w = sdv[1][0].w = sdv[2][0].w = 50.0; + /* Create an initial set of RAMDAC curves */ for (j = 0; j < 3; j++) x.rdac[j]->fit(x.rdac[j], 0, fitord, sdv[j], rsteps, RDAC_SMOOTH); @@ -4175,6 +4486,13 @@ int main(int argc, char *argv[]) { x.rdac[j]->force_1(x.rdac[j], 1.0); } + /* Make sure that if we are using black point hack, */ + /* that the curves go to a perfect 0.0 ... */ + if (x.bkhack) { + for (j = 0; j < 3; j++) + x.rdac[j]->force_0(x.rdac[j], 0.0); + } + for (j = 0; j < 3; j++) free (sdv[j]); } @@ -4207,7 +4525,8 @@ int main(int argc, char *argv[]) { dr->reset_targ_w(dr); /* Reset white drift target at start of main cal. */ /* Now we go into the main verify & refine loop */ - for (it = verify == 2 ? mxits : 0; it < mxits || verify != 0; + for (it = verify == 2 ? mxits : 0; + it < mxits || verify != 0; rsteps *= 2, errthr /= (it < mxits) ? pow(2.0,THRESH_SCALE_POW) : 1.0, it++) { int totmeas = 0; /* Total number of measurements in this pass */ col set[3]; /* Variable to read one to three values from the display */ @@ -4310,7 +4629,6 @@ int main(int argc, char *argv[]) { /* and we're not doing a verification, */ /* adjust all the other point targets txyz to track the white. */ if (x.nat && i == (rsteps-1) && it < mxits && asgrey.s[i].v == 1.0) { - icmAry2Ary(x.twh, asgrey.s[i].XYZ); /* Set current white */ icmAry2XYZ(x.twN, x.twh); /* Need this for Lab conversions */ init_csamp_txyz(&asgrey, &x, 1, verb); /* Recompute txyz's */ @@ -4321,6 +4639,15 @@ int main(int argc, char *argv[]) { } } + /* If black point hack and we've just measured it, */ + /* and we're not doing a verification, */ + if (x.bkhack && i == 0 && it < mxits && asgrey.s[i].v == 0.0) { + icmAry2Ary(x.tbk, asgrey.s[i].XYZ); /* Set current black */ + icmAry2XYZ(x.tbN, x.tbk); + init_csamp_txyz(&asgrey, &x, 1, verb); /* Recompute txyz's */ + icmAry2Ary(peqXYZ, asgrey.s[i].tXYZ); /* Fix peqXYZ */ + } + /* Compute the next change wanted to hit target */ icmSub3(asgrey.s[i].deXYZ, asgrey.s[i].tXYZ, asgrey.s[i].XYZ); @@ -4782,7 +5109,7 @@ int main(int argc, char *argv[]) { for (j = 0; j < 3; j++) { if ((sdv[j] = malloc(sizeof(mcvco) * asgrey.no)) == NULL) { dr->del(dr); - error ("Malloc of scattered data points failed"); + error("Malloc of scattered data points failed"); } } @@ -4803,6 +5130,9 @@ int main(int argc, char *argv[]) { if (x.nat) /* Make curve go thought white if possible */ sdv[0][rsteps-1].w = sdv[1][rsteps-1].w = sdv[2][rsteps-1].w = 10.0; + if (x.bkhack) /* Make curve go thought black if possible */ + sdv[0][0].w = sdv[1][0].w = sdv[2][0].w = 10.0; + for (j = 0; j < 3; j++) x.rdac[j]->fit(x.rdac[j], 0, fitord, sdv[j], asgrey.no, RDAC_SMOOTH); @@ -4813,6 +5143,13 @@ int main(int argc, char *argv[]) { x.rdac[j]->force_1(x.rdac[j], 1.0); } + /* Make sure that if we are using black hack black point, */ + /* that the curves go to a perfect 0.0 ... */ + if (x.bkhack) { + for (j = 0; j < 3; j++) + x.rdac[j]->force_0(x.rdac[j], 0.0); + } + for (j = 0; j < 3; j++) free(sdv[j]); #ifdef DEBUG_PLOT @@ -4840,6 +5177,10 @@ int main(int argc, char *argv[]) { } #endif } + + if (verify != 0) /* Do only a single pass */ + break; + } /* Next refine/verify loop */ free_alloc_csamp(&asgrey); /* We're done with test points */ @@ -4924,6 +5265,10 @@ int main(int argc, char *argv[]) { ocg->add_kword(ocg, 0, "TARGET_BLACK_BRIGHTNESS",buf, NULL); } + if (bkhack) { + ocg->add_kword(ocg, 0, "BLACK_POINT_HACK","YES", NULL); + } + /* Write rest of setup */ switch (quality) { case -3: /* Test value */ @@ -5042,15 +5387,15 @@ int main(int argc, char *argv[]) { icmVideoCardGamma *wo; if ((icco = new_icc()) == NULL) - error ("Creation of ICC object to read profile '%s' failed",iccoutname); + error("Creation of ICC object to read profile '%s' failed",iccoutname); /* Open up the profile for reading */ if ((ic_fp = new_icmFileStd_name(iccoutname,"r")) == NULL) - error ("Can't open file '%s'",iccoutname); + error("Can't open file '%s'",iccoutname); /* Read header etc. */ if ((rv = icco->read(icco,ic_fp,0)) != 0) - error ("Reading profile '%s' failed with %d, %s",iccoutname, rv,icco->err); + error("Reading profile '%s' failed with %d, %s",iccoutname, rv,icco->err); /* Read every tag */ if (icco->read_all_tags(icco) != 0) { @@ -5099,10 +5444,10 @@ int main(int argc, char *argv[]) { /* Open up the profile again writing */ if ((ic_fp = new_icmFileStd_name(iccoutname,"w")) == NULL) - error ("Can't open file '%s' for writing",iccoutname); + error("Can't open file '%s' for writing",iccoutname); if ((rv = icco->write(icco,ic_fp,0)) != 0) - error ("Write to file '%s' failed: %d, %s",iccoutname, rv,icco->err); + error("Write to file '%s' failed: %d, %s",iccoutname, rv,icco->err); if (verb) printf("Updated profile '%s'\n",iccoutname); @@ -5132,6 +5477,13 @@ int main(int argc, char *argv[]) { double calrgb[3]; /* 1.0 through calibration curves */ double clrgb[3]; /* 1.0 through calibration and linearization */ + /* Open up the file for writing */ + if ((wr_fp = new_icmFileStd_name(iccoutname,"w")) == NULL) + error("Write: Can't open file '%s'",iccoutname); + + if ((wr_icco = new_icc()) == NULL) + error("Write: Creation of ICC object failed"); + /* Lookup white and black points */ { int j; @@ -5210,7 +5562,7 @@ int main(int argc, char *argv[]) { #endif /* Adapt matrix */ icmAry2XYZ(swp, wp); - icmChromAdaptMatrix(ICM_CAM_MULMATRIX | ICM_CAM_BRADFORD, icmD50, swp, mat); + wr_icco->chromAdaptMatrix(wr_icco, ICM_CAM_MULMATRIX, icmD50, swp, mat); #ifdef NEVER { double rgb[3], xyz[3], lab[3]; @@ -5252,7 +5604,7 @@ int main(int argc, char *argv[]) { #endif /* Adapt matrix */ icmAry2XYZ(swp, wp); - icmChromAdaptMatrix(ICM_CAM_MULMATRIX | ICM_CAM_BRADFORD, icmD50, swp, mat); + wr_icco->chromAdaptMatrix(wr_icco, ICM_CAM_MULMATRIX, icmD50, swp, mat); #ifdef NEVER { double rgb[3], xyz[3], lab[3]; @@ -5266,13 +5618,6 @@ int main(int argc, char *argv[]) { } } - /* Open up the file for writing */ - if ((wr_fp = new_icmFileStd_name(iccoutname,"w")) == NULL) - error("Write: Can't open file '%s'",iccoutname); - - if ((wr_icco = new_icc()) == NULL) - error("Write: Creation of ICC object failed"); - /* Add all the tags required */ /* The header: */ @@ -5622,6 +5967,7 @@ int main(int argc, char *argv[]) { } free_a_disppath(disp); + free_ccids(ccids); return 0; } diff --git a/spectro/dispread.c b/spectro/dispread.c index 990501f..6af203b 100644 --- a/spectro/dispread.c +++ b/spectro/dispread.c @@ -16,6 +16,10 @@ /* TTBD + Add support for black recalibration using i1pro or munki. + Setable timeout ? Need to allow placing instrument back + on screen. Need this to properly handle ss anyway ? + Add bell at end of readings ? Ideally this should be changed to always create non-normalized (absolute) @@ -50,18 +54,22 @@ #include "aconfig.h" #include "numlib.h" #include "xspect.h" -#include "ccmx.h" -#include "ccss.h" #include "cgats.h" #include "insttypes.h" #include "conv.h" #include "icoms.h" #include "inst.h" +#include "ccmx.h" +#include "ccss.h" +#include "ccast.h" #include "dispwin.h" #include "dispsup.h" #include "sort.h" #include "instappsup.h" -#include "spyd2setup.h" /* Enable Spyder 2 access */ +#ifdef ENABLE_USB +# include "spyd2.h" +#endif +#include "ui.h" /* ------------------------------------------------------------------- */ #if defined(__APPLE__) && defined(__POWERPC__) @@ -84,12 +92,14 @@ static int gcc_bug_fix(int i) { Flags used: ABCDEFGHIJKLMNOPQRSTUVWXYZ - upper .... .... .. .. ... + upper .... .... .. .. ..... lower .. . . . . .. . */ -void usage(char *diag, ...) { +/* Flag = 0x0000 = default */ +/* Flag & 0x0001 = list ChromCast's */ +void usage(int flag, char *diag, ...) { int i; disppath **dp; icompaths *icmps; @@ -97,8 +107,6 @@ void usage(char *diag, ...) { fprintf(stderr,"Read a Display, Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); - if (setup_spyd2() == 2) - fprintf(stderr,"WARNING: This file contains a proprietary firmware image, and may not be freely distributed !\n"); if (diag != NULL) { va_list args; fprintf(stderr,"Diagnostic: "); @@ -129,6 +137,22 @@ void usage(char *diag, ...) { } free_disppaths(dp); fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); + fprintf(stderr," -dcc[:n] Display via n'th ChromeCast (default 1, ? for list)\n"); + if (flag & 0x001) { + ccast_id **ids; + if ((ids = get_ccids()) == NULL) { + fprintf(stderr," ** Error discovering ChromCasts **\n"); + } else { + if (ids[0] == NULL) + fprintf(stderr," ** No ChromCasts found **\n"); + else { + int i; + for (i = 0; ids[i] != NULL; i++) + fprintf(stderr," %d = '%s'\n",i+1,ids[i]->name); + free_ccids(ids); + } + } + } #ifdef NT fprintf(stderr," -dmadvr Display via MadVR Video Renderer\n"); #endif @@ -141,7 +165,8 @@ void usage(char *diag, ...) { for (i = 0; ; i++) { if (paths[i] == NULL) break; - if (paths[i]->itype == instSpyder2 && setup_spyd2() == 0) + 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 fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name); @@ -165,6 +190,7 @@ void usage(char *diag, ...) { fprintf(stderr," -n Don't set override redirect on test window\n"); #endif fprintf(stderr," -E Encode the test values for video range 16..235/255\n"); + fprintf(stderr," -Z nbits Quantize test values to fit in nbits\n"); fprintf(stderr," -J Run instrument calibration first (used rarely)\n"); fprintf(stderr," -N Disable initial calibration of instrument if possible\n"); fprintf(stderr," -H Use high resolution spectrum mode (if available)\n"); @@ -192,12 +218,13 @@ void usage(char *diag, ...) { } int main(int argc, char *argv[]) { - int i,j; + int i, j; int fa, nfa, mfa; /* current argument we're looking at */ disppath *disp = NULL; /* Display being used */ double hpatscale = 1.0, vpatscale = 1.0; /* scale factor for test patch size */ double ho = 0.0, vo = 0.0; /* Test window offsets, -1.0 to 1.0 */ int out_tvenc = 0; /* 1 to use RGB Video Level encoding */ + int qbits = 0; /* Quantization bits, 0 = not set */ int blackbg = 0; /* NZ if whole screen should be filled with black */ int verb = 0; int debug = 0; @@ -217,13 +244,16 @@ int main(int argc, char *argv[]) { int tele = 0; /* NZ if telephoto mode */ int noautocal = 0; /* Disable auto calibration */ int noplace = 0; /* Disable user instrument placement */ - int nonorm = 0; /* Disable normalisation */ + int donorm = 1; /* Enable Y = 100 normalisation */ char ccxxname[MAXNAMEL+1] = "\000"; /* Colorimeter Correction Matrix name */ ccmx *cmx = NULL; /* Colorimeter Correction Matrix */ ccss *ccs = NULL; /* Colorimeter Calibration Spectral Samples */ int spec = 0; /* Don't save spectral information */ icxObserverType obType = icxOT_default; int webdisp = 0; /* NZ for web display, == port number */ + int ccdisp = 0; /* NZ for ChromeCast, == list index */ + ccast_id **ccids = NULL; + ccast_id *ccid = NULL; #ifdef NT int madvrdisp = 0; /* NZ for MadVR display */ #endif @@ -261,7 +291,6 @@ int main(int argc, char *argv[]) { set_exe_path(argv[0]); /* Set global exe_path and error_program */ check_if_not_interactive(); - setup_spyd2(); /* Load firware if available */ #ifdef DEBUG_OFFSET ho = 0.8; @@ -273,7 +302,7 @@ int main(int argc, char *argv[]) { #endif if (argc <= 1) - usage("Too few arguments"); + usage(0,"Too few arguments"); if (ncal > MAX_CAL_ENT) error("Internal, ncal = %d > MAX_CAL_ENT %d\n",ncal,MAX_CAL_ENT); @@ -297,7 +326,7 @@ int main(int argc, char *argv[]) { } if (argv[fa][1] == '?' || argv[fa][1] == '-') { - usage("Usage requested"); + usage(0,"Usage requested"); } else if (argv[fa][1] == 'v') { verb = 1; @@ -311,7 +340,19 @@ int main(int argc, char *argv[]) { if (na[3] == ':') { webdisp = atoi(na+4); if (webdisp == 0 || webdisp > 65535) - usage("Web port number must be in range 1..65535"); + usage(0,"Web port number must be in range 1..65535"); + } + fa = nfa; + } else if (strncmp(na,"cc",2) == 0 + || strncmp(na,"CC",2) == 0) { + ccdisp = 1; + if (na[2] == ':') { + if (na[3] < '0' || na[3] > '9') + usage(0x0001,"Available ChromeCasts"); + + ccdisp = atoi(na+3); + if (ccdisp <= 0) + usage(0,"ChromCast number must be in range 1..N"); } fa = nfa; #ifdef NT @@ -325,10 +366,10 @@ int main(int argc, char *argv[]) { int ix, iv; if (strcmp(&argv[fa][2], "isplay") == 0 || strcmp(&argv[fa][2], "ISPLAY") == 0) { - if (++fa >= argc || argv[fa][0] == '-') usage("Parameter expected following -display"); + if (++fa >= argc || argv[fa][0] == '-') usage(0,"Parameter expected following -display"); setenv("DISPLAY", argv[fa], 1); } else { - if (na == NULL) usage("Parameter expected following -d"); + if (na == NULL) usage(0,"Parameter expected following -d"); fa = nfa; if (strcmp(na,"fake") == 0) { fake = 1; @@ -340,14 +381,14 @@ int main(int argc, char *argv[]) { if (disp != NULL) free_a_disppath(disp); if ((disp = get_a_display(ix-1)) == NULL) - usage("-d parameter %d out of range",ix); + usage(0,"-d parameter %d out of range",ix); if (iv > 0) disp->rscreen = iv-1; } } #else int ix; - if (na == NULL) usage("Parameter expected following -d"); + if (na == NULL) usage(0,"Parameter expected following -d"); fa = nfa; if (strcmp(na,"fake") == 0) { fake = 1; @@ -356,7 +397,7 @@ int main(int argc, char *argv[]) { if (disp != NULL) free_a_disppath(disp); if ((disp = get_a_display(ix-1)) == NULL) - usage("-d parameter %d out of range",ix); + usage(0,"-d parameter %d out of range",ix); } #endif } @@ -368,9 +409,9 @@ int main(int argc, char *argv[]) { /* COM port */ } else if (argv[fa][1] == 'c') { fa = nfa; - if (na == NULL) usage("Paramater expected following -c"); + if (na == NULL) usage(0,"Paramater expected following -c"); comport = atoi(na); - if (comport < 1 || comport > 50) usage("-c parameter %d out of range",comport); + if (comport < 1 || comport > 50) usage(0,"-c parameter %d out of range",comport); /* Telephoto */ } else if (argv[fa][1] == 'p') { @@ -379,13 +420,13 @@ int main(int argc, char *argv[]) { /* Display type */ } else if (argv[fa][1] == 'y') { fa = nfa; - if (na == NULL) usage("Parameter expected after -y"); + if (na == NULL) usage(0,"Parameter expected after -y"); dtype = na[0]; /* Calibration file */ } else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { - if (na == NULL) usage("Parameter expected after -%c",argv[fa][1]); + if (na == NULL) usage(0,"Parameter expected after -%c",argv[fa][1]); strncpy(calname,na,MAXNAMEL); calname[MAXNAMEL] = '\000'; if (argv[fa][1] == 'K') native |= 1; /* Use native linear & soft cal */ @@ -406,19 +447,19 @@ int main(int argc, char *argv[]) { /* Test patch offset and size */ } else if (argv[fa][1] == 'P') { fa = nfa; - if (na == NULL) usage("Parameter expected after -P"); + if (na == NULL) usage(0,"Parameter expected after -P"); if (sscanf(na, " %lf,%lf,%lf,%lf ", &ho, &vo, &hpatscale, &vpatscale) == 4) { ; } else if (sscanf(na, " %lf,%lf,%lf ", &ho, &vo, &hpatscale) == 3) { vpatscale = hpatscale; } else { - usage("-P parameter '%s' not recognised",na); + usage(0,"-P parameter '%s' not recognised",na); } if (ho < 0.0 || ho > 1.0 || vo < 0.0 || vo > 1.0 || hpatscale <= 0.0 || hpatscale > 50.0 || vpatscale <= 0.0 || vpatscale > 50.0) - usage("-P parameters %f %f %f %f out of range",ho,vo,hpatscale,vpatscale); + usage(0,"-P parameters %f %f %f %f out of range",ho,vo,hpatscale,vpatscale); ho = 2.0 * ho - 1.0; vo = 2.0 * vo - 1.0; @@ -429,6 +470,16 @@ int main(int argc, char *argv[]) { /* Video encoded output */ } else if (argv[fa][1] == 'E') { out_tvenc = 1; + if (qbits == 0) + qbits = 8; + + /* Specify quantization bits */ + } else if (argv[fa][1] == 'Z') { + fa = nfa; + if (na == NULL) usage(0,"Expected argument to -Z"); + qbits = atoi(na); + if (qbits < 1 || qbits > 32) + usage(0,"Argument to -Q must be between 1 and 32"); /* Force calibration */ } else if (argv[fa][1] == 'J') { @@ -442,21 +493,21 @@ int main(int argc, char *argv[]) { } else if (argv[fa][1] == 'H') { highres = 1; - /* No normalisation */ + /* Disable normalisation of values to white Y = 100 */ } else if (argv[fa][1] == 'w') { - nonorm = 1; + donorm = 0; /* Colorimeter Correction Matrix */ /* or Colorimeter Calibration Spectral Samples */ } else if (argv[fa][1] == 'X') { int ix; fa = nfa; - if (na == NULL) usage("Parameter expected following -X"); + if (na == NULL) usage(0,"Parameter expected following -X"); strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000'; } else if (argv[fa][1] == 'I') { fa = nfa; - if (na == NULL || na[0] == '\000') usage("Parameter expected after -I"); + if (na == NULL || na[0] == '\000') usage(0,"Parameter expected after -I"); for (i=0; ; i++) { if (na[i] == '\000') break; @@ -465,13 +516,13 @@ int main(int argc, char *argv[]) { else if (na[i] == 'w' || na[i] == 'W') wdrift = 1; else - usage("-I parameter '%c' not recognised",na[i]); + usage(0,"-I parameter '%c' not recognised",na[i]); } /* Spectral Observer type */ } else if (argv[fa][1] == 'Q') { fa = nfa; - if (na == NULL) usage("Parameter expecte after -Q"); + if (na == NULL) usage(0,"Parameter expecte after -Q"); if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ obType = icxOT_CIE_1931_2; } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */ @@ -485,25 +536,25 @@ int main(int argc, char *argv[]) { } else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */ obType = icxOT_Shaw_Fairchild_2; } else - usage("-Q parameter '%s' not recognised",na); + usage(0,"-Q parameter '%s' not recognised",na); /* Change color callout */ } else if (argv[fa][1] == 'C') { fa = nfa; - if (na == NULL) usage("Parameter expected after -C"); + if (na == NULL) usage(0,"Parameter expected after -C"); ccallout = na; /* Measure color callout */ } else if (argv[fa][1] == 'M') { fa = nfa; - if (na == NULL) usage("Parameter expected after -M"); + if (na == NULL) usage(0,"Parameter expected after -M"); mcallout = na; /* Serial port flow control */ } else if (argv[fa][1] == 'W') { fa = nfa; - if (na == NULL) usage("Parameter expected after -W"); + if (na == NULL) usage(0,"Parameter expected after -W"); if (na[0] == 'n' || na[0] == 'N') fc = fc_none; else if (na[0] == 'h' || na[0] == 'H') @@ -511,7 +562,7 @@ int main(int argc, char *argv[]) { else if (na[0] == 'x' || na[0] == 'X') fc = fc_XonXOff; else - usage("-W parameter '%s' not recognised",na); + usage(0,"-W parameter '%s' not recognised",na); } else if (argv[fa][1] == 'D') { debug = 1; @@ -525,32 +576,38 @@ int main(int argc, char *argv[]) { /* Extra flags */ } else if (argv[fa][1] == 'Y') { if (na == NULL) - usage("Flag '-Y' expects extra flag"); + usage(0,"Flag '-Y' expects extra flag"); if (na[0] == 'R') { if (na[1] != ':') - usage("-Y R:rate syntax incorrect"); + usage(0,"-Y R:rate syntax incorrect"); refrate = atof(na+2); if (refrate < 5.0 || refrate > 150.0) - usage("-Y R:rate %f Hz not in valid range",refrate); + usage(0,"-Y R:rate %f Hz not in valid range",refrate); } else if (na[0] == 'p') { noplace = 1; } else if (na[0] == 'A') { nadaptive = 1; } else { - usage("Flag '-Y %c' not recognised",na[0]); + usage(0,"Flag '-Y %c' not recognised",na[0]); } fa = nfa; } else - usage("Flag '-%c' not recognised",argv[fa][1]); + usage(0,"Flag '-%c' not recognised",argv[fa][1]); } else break; } /* No explicit display has been set */ - if (!fake && disp == NULL) { + if (!fake +#ifdef NT + && madvrdisp == 0 +#endif + && webdisp == 0 + && ccdisp == 0 + && disp == NULL) { int ix = 0; #if defined(UNIX_X11) char *dn, *pp; @@ -607,9 +664,22 @@ int main(int argc, char *argv[]) { if ((ipath = icmps->get_path(icmps, comport)) == NULL) error("No instrument at port %d",comport); + /* If we've requested ChromeCast, look it up */ + if (ccdisp) { + if ((ccids = get_ccids()) == NULL) + error("discovering ChromCasts failed"); + if (ccids[0] == NULL) + error("There are no ChromCasts to use\n"); + for (i = 0; ccids[i] != NULL; i++) + ; + if (ccdisp < 1 || ccdisp > i) + error("Chosen ChromCasts (%d) is outside list (1..%d)\n",ccdisp,i); + ccid = ccids[ccdisp-1]; + } + if (docalib) { - if ((rv = disprd_calibration(ipath, fc, dtype, 0, tele, nadaptive, noautocal, - disp, webdisp, + if ((rv = disprd_calibration(ipath, fc, dtype, -1, 0, tele, nadaptive, noautocal, + disp, webdisp, ccid, #ifdef NT madvrdisp, #endif @@ -621,7 +691,7 @@ int main(int argc, char *argv[]) { } /* Get the file name argument */ - if (fa >= argc || argv[fa][0] == '-') usage("Filname parameter not found"); + if (fa >= argc || argv[fa][0] == '-') usage(0,"Filname parameter not found"); strncpy(inname,argv[fa++],MAXNAMEL-4); inname[MAXNAMEL-4] = '\000'; strcpy(outname,inname); strcat(inname,".ti1"); @@ -683,10 +753,13 @@ int main(int argc, char *argv[]) { error("Malloc failed!"); /* Figure out the color space */ + /* Read all the test patches in, and quantize them */ if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0) error ("Input file '%s' doesn't contain keyword COLOR_REP",inname); if (strcmp(icg->t[0].kdata[fi],"RGB") == 0) { int ri, gi, bi; + double rgb[3]; + double qscale = (1 << qbits) - 1.0; dim = 3; if ((ri = icg->find_field(icg, 0, "RGB_R")) < 0) error ("Input file '%s' doesn't contain field RGB_R",inname); @@ -709,9 +782,22 @@ int main(int argc, char *argv[]) { ocg->add_field(ocg, 0, "XYZ_Z", r_t); for (i = 0; i < npat; i++) { cols[i].id = ((char *)icg->t[0].fdata[i][si]); - cols[i].r = *((double *)icg->t[0].fdata[i][ri]) / 100.0; - cols[i].g = *((double *)icg->t[0].fdata[i][gi]) / 100.0; - cols[i].b = *((double *)icg->t[0].fdata[i][bi]) / 100.0; + rgb[0] = *((double *)icg->t[0].fdata[i][ri]) / 100.0; + rgb[1] = *((double *)icg->t[0].fdata[i][gi]) / 100.0; + rgb[2] = *((double *)icg->t[0].fdata[i][bi]) / 100.0; + if (qbits > 0) { + double vr; + for (j = 0; j < 3; j++) { + rgb[j] *= qscale; + vr = floor(rgb[j] + 0.5); + if ((vr - rgb[j]) == 0.5 && (((int)vr) & 1) != 0) /* Round to even */ + vr -= 1.0; + rgb[j] = vr/qscale; + } + } + cols[i].r = rgb[0]; + cols[i].g = rgb[1]; + cols[i].b = rgb[2]; cols[i].XYZ[0] = cols[i].XYZ[1] = cols[i].XYZ[2] = -1.0; } } else @@ -809,14 +895,16 @@ int main(int argc, char *argv[]) { cal[0][0] = -1.0; /* Not used */ } - if ((dr = new_disprd(&errc, ipath, fc, dtype, 0, tele, nadaptive, noautocal, noplace, + if ((dr = new_disprd(&errc, ipath, fc, dtype, -1, 0, tele, nadaptive, noautocal, noplace, highres, refrate, native, &noramdac, &nocm, cal, ncal, disp, - out_tvenc, blackbg, override, webdisp, + out_tvenc, blackbg, override, webdisp, ccid, #ifdef NT madvrdisp, #endif ccallout, mcallout, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, + ccs != NULL ? ccs->dtech : cmx != NULL ? cmx->dtech : disptech_unknown, + cmx != NULL ? cmx->cc_cbid : 0, cmx != NULL ? cmx->matrix : NULL, ccs != NULL ? ccs->samples : NULL, ccs != NULL ? ccs->no_samp : 0, spec, obType, NULL, bdrift, wdrift, @@ -831,7 +919,7 @@ int main(int argc, char *argv[]) { /* Test the CRT with all of the test points */ if ((rv = dr->read(dr, cols, npat + xpat, 1, npat + xpat, 1, 0, instNoClamp)) != 0) { dr->del(dr); - error("test_crt returned error code %d\n",rv); + error("dispd->read returned error code %d\n",rv); } /* Note what instrument the chart was read with */ if (dr->it != NULL) { @@ -881,14 +969,13 @@ int main(int argc, char *argv[]) { wpat = npat; } - if (nonorm) - nn = 1.0; - else { + if (donorm) { if (cols[wpat].XYZ_v == 0) error("XYZ of white patch is not valid!\nCan't normalise value to white",i); nn = 100.0 / cols[wpat].XYZ[1]; /* Normalise Y of white to 100 */ - } + } else + nn = 1.0; for (i = 0; i < npat; i++) { @@ -973,7 +1060,7 @@ int main(int argc, char *argv[]) { ocg->add_kword(ocg, 0, "LUMINANCE_XYZ_CDM2",buf, NULL); } - if (nonorm == 0) + if (donorm) ocg->add_kword(ocg, 0, "NORMALIZED_TO_Y_100","YES", NULL); else ocg->add_kword(ocg, 0, "NORMALIZED_TO_Y_100","NO", NULL); @@ -1028,6 +1115,7 @@ int main(int argc, char *argv[]) { ocg->del(ocg); /* Clean up */ icg->del(icg); /* Clean up */ free_a_disppath(disp); + free_ccids(ccids); return 0; } diff --git a/spectro/dispsup.c b/spectro/dispsup.c index a27dab0..5751fed 100644 --- a/spectro/dispsup.c +++ b/spectro/dispsup.c @@ -42,18 +42,24 @@ #include "inst.h" #include "spyd2.h" #include "dispwin.h" -#include "dispsup.h" #include "webwin.h" +#include "ccast.h" +#include "ccwin.h" #ifdef NT # include "madvrwin.h" #endif + +#include "dispsup.h" #include "instappsup.h" +#undef DEBUG + #undef SIMPLE_MODEL /* Make fake device well behaved */ /* else has offsets, quantization, noise etc. */ #define DRIFT_IPERIOD 40 /* Number of samples between drift interpolation measurements */ #define DRIFT_EPERIOD 20 /* Number of samples between drift extrapolation measurements */ +#define DRIFT_MAXSECS 60 /* Number of seconds to time out previous drift value */ //#define DRIFT_IPERIOD 6 /* Test values */ //#define DRIFT_EPERIOD 3 @@ -68,6 +74,14 @@ #endif +#if defined(DEBUG) + +#define DBG(xxx) fprintf xxx ; +#define dbgo stderr +#else +#define DBG(xxx) +#endif /* DEBUG */ + /* -------------------------------------------------------- */ /* A default callback that can be provided as an argument to */ /* inst_handle_calibrate() to handle the display part of a */ @@ -79,7 +93,7 @@ inst_code setup_display_calibrate( disp_win_info *dwi /* Information to be able to open a display test patch */ ) { inst_code rv = inst_ok, ev; - dispwin *dw; /* Display window to display test patches on, NULL if none. */ +// dispwin *dw; /* Display window to display test patches on, NULL if none. */ a1logd(p->log,1,"setup_display_calibrate called with calc = 0x%x\n",calc); switch (calc) { @@ -103,6 +117,13 @@ inst_code setup_display_calibrate( a1logd(p->log,1,"inst_handle_calibrate failed to create test window 0x%x\n",inst_other_error); return inst_other_error; } + } else if (dwi->ccid != NULL) { + if ((dwi->_dw = new_ccwin(dwi->ccid, dwi->hpatsize, dwi->vpatsize, + dwi->ho, dwi->vo, 0, 0, NULL, NULL, dwi->out_tvenc, dwi->blackbg, + p->log->verb, p->log->debug)) == NULL) { + a1logd(p->log,1,"inst_handle_calibrate failed to create test window 0x%x\n",inst_other_error); + return inst_other_error; + } #ifdef NT } else if (dwi->madvrdisp != 0) { if ((dwi->_dw = new_madvrwin(dwi->hpatsize, dwi->vpatsize, @@ -127,6 +148,15 @@ inst_code setup_display_calibrate( dwi->_dw = dwi->dw; } + /* Set display rise & fall time more optimally */ + { + disptech dtech; + disptech_info *tinfo; + p->get_disptechi(p, &dtech, NULL, NULL); + tinfo = disptech_get_id(dtech); + dwi->dw->set_settling_delay(dwi->dw, tinfo->rise_time, tinfo->fall_time, -1.0); + } + if (calc == inst_calc_emis_white) { p->cal_gy_level = 1.0; dwi->_dw->set_color(dwi->_dw, 1.0, 1.0, 1.0); @@ -174,12 +204,14 @@ int disprd_calibration( icompath *ipath, /* Instrument path to open, &icomFakeDevice == fake */ flow_control fc, /* Serial flow control */ int dtype, /* Display type selection character */ +int sdtype, /* Spectro dtype, use dtype if -1 */ int docbid, /* NZ to only allow cbid dtypes */ int tele, /* NZ for tele mode, falls back to spot mode */ int nadaptive, /* NZ for non-adaptive mode */ int noinitcal, /* NZ to disable initial instrument calibration */ disppath *disp, /* display to calibrate. */ int webdisp, /* If nz, port number for web display */ +ccast_id *ccid, /* non-NULL for ChromeCast */ #ifdef NT int madvrdisp, /* NZ for MadVR display */ #endif @@ -205,6 +237,7 @@ a1log *log /* Verb, debug & error log */ memset((void *)&dwi, 0, sizeof(disp_win_info)); dwi.webdisp = webdisp; + dwi.ccid = ccid; #ifdef NT dwi.madvrdisp = madvrdisp; #endif @@ -280,6 +313,11 @@ a1log *log /* Verb, debug & error log */ } p->capabilities(p, &cap, &cap2, &cap3); + /* If this is a spectral instrument, and a different */ + /* spectral inst. dtype is supplied, then use it */ + if (IMODETST(cap, inst_mode_spectral) && sdtype >= 0) + dtype = sdtype; + /* Set the display type */ if (dtype != 0) { /* Given selection character */ if (cap2 & inst2_disptype) { @@ -297,7 +335,7 @@ a1log *log /* Verb, debug & error log */ return -1; } } else - printf("Display type ignored - instrument doesn't support display type\n"); + printf("Display type ignored - instrument doesn't support display type selection\n"); } /* Disable initial calibration of machine if selected */ @@ -310,7 +348,7 @@ a1log *log /* Verb, debug & error log */ } /* Do the calibration */ - rv = inst_handle_calibrate(p, inst_calt_available, inst_calc_none, setup_display_calibrate, &dwi); + rv = inst_handle_calibrate(p, inst_calt_available, inst_calc_none, setup_display_calibrate, &dwi, 0); setup_display_calibrate(p,inst_calc_none, &dwi); if (rv == inst_unsupported) { printf("No calibration available for instrument in this mode\n"); @@ -328,19 +366,55 @@ a1log *log /* Verb, debug & error log */ return 0; } -/* Set color to black after 50msec delay */ -int del_set_black(void *cx) { +/* The i1pro/munki seems to get into trouble if we do a meas_delay */ +/* on another thread - some conflict with the button monotitor thread. */ + +/* Set color to white after 200 msec delay */ +int del_set_white(void *cx) { disprd *p = (disprd *)cx; + inst_code ev; int rv; - msec_sleep(100); - if ((rv = p->dw->set_color(p->dw, 0.0, 0.0, 0.0)) != 0) { + msec_sleep(200); + + /* Start the patch change */ + /* This function may return some time before or after */ + /* the change actually arrives at the instrument. */ + if ((rv = p->dw->set_color(p->dw, 1.0, 1.0, 1.0)) != 0) { a1logd(p->log,1,"set_color() returned %d\n",rv); return 3; } + + /* Signal instrument that we've returned from the patch change function */ + /* (We're interested in any delay from this point in time until the */ + /* change arrives at the instrument, since this is the point that we */ + /* add extra delay at the end of the set_color() functions) */ + if ((ev = p->it->white_change(p->it, 0)) != inst_ok) { + a1logd(p->log,1,"white_change() returned 0x%x\n",ev); + return 3; + } + return 0; } +#ifdef NEVER +/* Implement scallout, which reports the XYZ measured */ +static void do_scallout(disprd *p, double *xyz) { + char *cmd; + + if (p->scallout == NULL) + return; + + if ((cmd = malloc(strlen(p->scallout) + 200)) == NULL) + error("Malloc of command string failed"); + + sprintf(cmd, "%s %f %f %f",p->scallout, xyz[0], xyz[1], xyz[2]); + if ((rv = system(cmd)) != 0) + error("System command '%s' failed with %d",cmd,rv); + free(cmd); +} +#endif + /* Take a series of readings from the display - implementation */ /* Return nz on fail/abort - see dispsup.h */ /* Use disprd_err() to interpret it */ @@ -377,39 +451,59 @@ static int disprd_read_imp( /* See if we should calibrate the display update */ if (!p->update_delay_set && (cap2 & inst2_meas_disp_update) != 0) { - int cdelay, mdelay; athread *th; + inst_code ev; + int mdelay; /* Display update delay*/ + int idelay = 0; /* Instrument reaction time */ + + /* Disable the update delay */ + p->dw->enable_update_delay(p->dw, 0); - /* Set white with a normal delay */ - if ((rv = p->dw->set_color(p->dw, 1.0, 1.0, 1.0)) != 0) { + /* Set black */ + if ((rv = p->dw->set_color(p->dw, 0.0, 0.0, 0.0)) != 0) { a1logd(p->log,1,"set_color() returned %d\n",rv); + p->dw->enable_update_delay(p->dw, 1); return 3; } + /* Wait for it to settle */ + msec_sleep(600); - /* Set a zero return delay */ - cdelay = p->dw->set_update_delay(p->dw, 0); + /* Init the white change stamp */ + p->it->white_change(p->it, 1); - /* Set black 50msec after this call */ - if ((th = new_athread(del_set_black, (void *)p)) == NULL) { + /* Set black 200 msec after this call and call white_change() */ + if ((th = new_athread(del_set_white, (void *)p)) == NULL) { a1logd(p->log,1,"failed to create thread to set_color()\n"); + p->dw->enable_update_delay(p->dw, 1); return 3; } + + /* If the test window is adding extra known delay after the patch */ + /* update, don't start looking for the transition too soon, */ + /* or we may exaust our 2.0 second scan window */ + if (p->dw->extra_update_delay > 0.2) { + a1logd(p->log,1,"update delay cal waiting %f secs to start scan\n", + p->dw->extra_update_delay - 0.2); + msec_sleep((int)((p->dw->extra_update_delay - 0.2) * 1000)); + } /* Measure the delay */ - if ((rv = p->it->meas_delay(p->it, &mdelay)) != inst_ok) { + if ((rv = p->it->meas_delay(p->it, &mdelay, &idelay)) != inst_ok) { a1logd(p->log,1,"warning, measure display update delay failed with '%s' (%s)\n", p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv)); - p->dw->set_update_delay(p->dw, cdelay); /* Restore the default */ + mdelay = PATCH_UPDATE_DELAY; + /* idelay will be set to a default by meas_delay() */ } else { - mdelay -= 100; /* Correct for delay on set black */ - if (mdelay < 0) - mdelay = 0; a1logv(p->log, 1, "Measured display update delay of %d msec",mdelay); - mdelay += mdelay/2 + 100; - p->dw->set_update_delay(p->dw, mdelay); - a1logv(p->log, 1, ", using delay of %d msec\n",mdelay); + mdelay += mdelay/3 + 100; /* Margin - +30% + 100msec */ } + p->dw->set_update_delay(p->dw, mdelay, idelay); + a1logv(p->log, 1, ", using delay of %d msec & %d msec inst reaction\n",mdelay, idelay); p->update_delay_set = 1; + + /* Re-enable update delay */ + p->dw->enable_update_delay(p->dw, 1); + th->del(th); } @@ -537,11 +631,19 @@ static int disprd_read_imp( dwi.dw = p->dw; /* Set window to use */ printf("\nSample read failed because instruments needs calibration\n"); rv = inst_handle_calibrate(p->it, inst_calt_needed, inst_calc_none, - setup_display_calibrate, &dwi); + setup_display_calibrate, &dwi, 0); setup_display_calibrate(p->it, inst_calc_none, &dwi); if (rv != inst_ok) { /* Abort or fatal error */ return 1; } + + printf("Place instrument back on test window.\n"); + printf("Hit Esc or Q to give up, any other key to continue:"); fflush(stdout); + if ((ch = next_con_char()) == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') { + printf("\n"); + return 1; + } + printf("\n"); continue; /* Deal with a bad sensor position */ @@ -594,8 +696,9 @@ static int disprd_read_imp( return 2; } } - continue; } + printf("\nSample read failed with unhandled error.\n"); + return 2; } else { break; /* Sucesful reading */ } @@ -654,41 +757,46 @@ static int disprd_read_drift( int off, poff; int boff, eoff, dno; /* b&w offsets and count */ -//printf("~1 DRIFT_EPERIOD %d, DRIFT_IPERIOD %d, npat %d\n",DRIFT_EPERIOD,DRIFT_IPERIOD,npat); + DBG((dbgo,"DRIFT_EPERIOD %d, DRIFT_IPERIOD %d, npat %d\n",DRIFT_EPERIOD,DRIFT_IPERIOD,npat)) /* Figure number and offset for b&w */ - if (p->bdrift == 0) { /* Must be just wdrift */ + if (p->bdrift == 0) { /* Must be just wdrift */ boff = eoff = 1; dno = 1; } else if (p->wdrift == 0) { /* Must be just bdrift */ boff = eoff = 0; dno = 1; - } else { /* Must be both */ - boff = eoff = 0; + } else { /* Must be both */ + boff = 0; + eoff = 1; dno = 2; } + /* Make sure these jave been initialised */ + p->last_bw[0].r = + p->last_bw[0].g = + p->last_bw[0].b = 0.0; + p->last_bw[1].r = + p->last_bw[1].g = + p->last_bw[1].b = 1.0; + /* If the last drift readings are invalid or too old, */ /* or if we will use interpolation, read b&w */ -#ifdef NEVER - printf("last_bw_v = %d (%d)\n",p->last_bw_v,p->last_bw_v == 0); - printf("npat = %d > %d, serno %d, last serno %d (%d)\n",npat, DRIFT_EPERIOD, p->serno,p->last_bw[eoff].serno,(npat > DRIFT_EPERIOD && p->serno > p->last_bw[eoff].serno)); - printf("serno - lastserno %d > %d (%d)\n",p->serno - p->last_bw[eoff].serno, DRIFT_EPERIOD,(p->serno - p->last_bw[eoff].serno) > DRIFT_EPERIOD); - printf("msec %d - last %d = %d > 10000 (%d)\n",msec_time(),p->last_bw[boff].msec,msec_time() - p->last_bw[boff].msec,(msec_time() - p->last_bw[eoff].msec) > 10000); -#endif /* NEVER */ +#ifdef DEBUG + DBG((dbgo,"last_bw_v = %d (%d)\n",p->last_bw_v,p->last_bw_v == 0)) + DBG((dbgo,"npat = %d > %d, serno %d, last serno %d (%d)\n",npat, DRIFT_EPERIOD, p->serno,p->last_bw[eoff].serno,(npat > DRIFT_EPERIOD && p->serno > p->last_bw[eoff].serno))) + DBG((dbgo,"serno - lastserno %d > %d (%d)\n",p->serno - p->last_bw[eoff].serno, DRIFT_EPERIOD,(p->serno - p->last_bw[eoff].serno) > DRIFT_EPERIOD)) + DBG((dbgo,"msec %d - last %d = %d > %d (%d)\n",msec_time(),p->last_bw[boff].msec,msec_time() - p->last_bw[boff].msec,DRIFT_MAXSECS * 1000, (msec_time() - p->last_bw[eoff].msec) > (DRIFT_MAXSECS * 1000))) +#endif /* DEBUG */ if (p->last_bw_v == 0 /* There are none */ || (npat > DRIFT_EPERIOD && p->serno > p->last_bw[eoff].serno) /* We will interpolate */ || (p->serno - p->last_bw[eoff].serno - dno) > DRIFT_EPERIOD /* extrapolate would be too far */ - || (msec_time() - p->last_bw[eoff].msec) > 10000) { /* The current is too long ago */ + || (msec_time() - p->last_bw[eoff].msec) > (DRIFT_MAXSECS * 1000)) { /* The current is too long ago */ + + DBG((dbgo,"Reading a beginning set of %d b/w drift compensation patches\n",dno)) + a1logd(p->log,2, "Reading a beginning set of %d b/w drift compensation patches\n",dno); -//printf("~1 refreshing last bw\n"); /* Read the black and/or white drift patch */ - p->last_bw[0].r = - p->last_bw[0].g = - p->last_bw[0].b = 0.0; - p->last_bw[1].r = - p->last_bw[1].g = - p->last_bw[1].b = 1.0; if ((rv = disprd_read_imp(p, &p->last_bw[boff], dno, spat, tpat, 0, 1, tc, 0)) != 0) { return rv; } @@ -709,7 +817,7 @@ static int disprd_read_drift( /* Figure out the number of drift samples we need */ ndrift += (npat-1)/DRIFT_IPERIOD; -//printf("~1 spat %d, npat = %d, tpat %d, ndrift = %d\n",spat,npat,tpat,ndrift); + DBG((dbgo,"spat %d, npat = %d, tpat %d, ndrift = %d\n",spat,npat,tpat,ndrift)) if ((dss = (dsamples *)calloc(sizeof(dsamples), ndrift)) == NULL) { a1logd(p->log,1, "malloc of %d dsamples failed\n",ndrift); @@ -718,7 +826,7 @@ static int disprd_read_drift( /* Set up bookeeping */ fper = (double)npat/(ndrift-1.0); -//printf("~1 fper = %f\n",fper); + DBG((dbgo,"fper = %f\n",fper)) foff = 0.0; for (poff = off = i = 0; i < ndrift; i++) { dss[i].off = off; @@ -729,7 +837,7 @@ static int disprd_read_drift( else dss[i].count = 0; poff = off; -//printf("~1 dss[%d] off = %d, count = %d\n",i, dss[i].off,dss[i].count); + DBG((dbgo,"dss[%d] off = %d, count = %d\n",i, dss[i].off,dss[i].count)) dss[i].dcols[0].r = dss[i].dcols[0].g = @@ -748,6 +856,8 @@ static int disprd_read_drift( dss[i].dcols[1] = p->last_bw[1]; } else { /* Read the black and/or white drift patchs before next batch */ + DBG((dbgo,"Reading another set of %d b/w drift compensation patches\n",dno)) + a1logd(p->log,2, "Reading another set of %d b/w drift compensation patches\n",dno); if ((rv = disprd_read_imp(p, &dss[i].dcols[boff], dno, spat+dss[i].off, tpat, 0, 1, tc, 0)) != 0) { free(dss); return rv; @@ -760,6 +870,8 @@ static int disprd_read_drift( } } /* Read the black and/or white drift patchs after last batch */ + DBG((dbgo,"Reading an end set of %d b/w drift compensation patches\n",dno)) + a1logd(p->log,2, "Reading an end set of %d b/w drift compensation patches\n",dno); if ((rv = disprd_read_imp(p, &dss[i].dcols[boff], dno, spat+dss[i].off-1, tpat, 0, 1, tc, 0)) != 0) { free(dss); return rv; @@ -773,10 +885,10 @@ static int disprd_read_drift( p->targ_w = p->last_bw[1]; p->targ_w_v = 1; -//printf("~1 ref b = %f %f %f\n", p->ref_bw[0].XYZ[0], p->ref_bw[0].XYZ[1], p->ref_bw[0].XYZ[2]); -//printf("~1 ref w = %f %f %f\n", p->ref_bw[1].XYZ[0], p->ref_bw[1].XYZ[1], p->ref_bw[1].XYZ[2]); -//printf("~1 ndrift-1 b = %f %f %f\n", dss[ndrift-1].dcols[0].XYZ[0], dss[ndrift-1].dcols[0].XYZ[1], dss[ndrift-1].dcols[0].XYZ[2]); -//printf("~1 ndrift-1 w = %f %f %f\n", dss[ndrift-1].dcols[1].XYZ[0], dss[ndrift-1].dcols[1].XYZ[1], dss[ndrift-1].dcols[1].XYZ[2]); + DBG((dbgo,"ref b = %f %f %f\n", p->ref_bw[0].XYZ[0], p->ref_bw[0].XYZ[1], p->ref_bw[0].XYZ[2])) + DBG((dbgo,"ref w = %f %f %f\n", p->ref_bw[1].XYZ[0], p->ref_bw[1].XYZ[1], p->ref_bw[1].XYZ[2])) + DBG((dbgo,"ndrift-1 b = %f %f %f\n", dss[ndrift-1].dcols[0].XYZ[0], dss[ndrift-1].dcols[0].XYZ[1], dss[ndrift-1].dcols[0].XYZ[2])) + DBG((dbgo,"ndrift-1 w = %f %f %f\n", dss[ndrift-1].dcols[1].XYZ[0], dss[ndrift-1].dcols[1].XYZ[1], dss[ndrift-1].dcols[1].XYZ[2])) /* Apply the drift compensation using interpolation */ for (i = 0; i < (ndrift-1); i++) { @@ -785,9 +897,11 @@ static int disprd_read_drift( int k = dss[i].off + j; double we; /* Interpolation weight of eairlier value */ col bb, ww; /* Interpolated black and white */ -//double uXYZ[3]; -//icmCpy3(uXYZ, cols[k].XYZ); -//printf("~1 patch %d = %f %f %f\n", k, cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2]); +#ifdef DEBUG + double uXYZ[3]; + icmCpy3(uXYZ, cols[k].XYZ); + DBG((dbgo,"patch %d = %f %f %f\n", k, cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2])) +#endif if (p->bdrift) { we = (double)(dss[i+1].dcols[0].msec - cols[k].msec)/ (double)(dss[i+1].dcols[0].msec - dss[i].dcols[0].msec); @@ -797,7 +911,7 @@ static int disprd_read_drift( bb.XYZ[e] = we * dss[i].dcols[0].XYZ[e] + (1.0 - we) * dss[i+1].dcols[0].XYZ[e]; } -//printf("~1 bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2]); + DBG((dbgo,"bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2])) } if (cols[k].sp.spec_n > 0) { for (e = 0; e < cols[k].sp.spec_n; e++) { @@ -815,7 +929,7 @@ static int disprd_read_drift( ww.XYZ[e] = we * dss[i].dcols[1].XYZ[e] + (1.0 - we) * dss[i+1].dcols[1].XYZ[e]; } -//printf("~1 ww = %f %f %f\n", ww.XYZ[0], ww.XYZ[1], ww.XYZ[2]); + DBG((dbgo,"ww = %f %f %f\n", ww.XYZ[0], ww.XYZ[1], ww.XYZ[2])) } if (cols[k].sp.spec_n > 0) { for (e = 0; e < cols[k].sp.spec_n; e++) { @@ -832,7 +946,7 @@ static int disprd_read_drift( for (e = 0; e < 3; e++) { bb.XYZ[e] *= p->ref_bw[1].XYZ[e]/ww.XYZ[e]; } -//printf("~1 wcomp bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2]); + DBG((dbgo,"wcomp bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2])) } if (cols[k].sp.spec_n > 0) { for (e = 0; e < cols[k].sp.spec_n; e++) { @@ -846,7 +960,7 @@ static int disprd_read_drift( for (e = 0; e < 3; e++) { cols[k].XYZ[e] += p->ref_bw[0].XYZ[e] - bb.XYZ[e]; } -//printf("~1 bcomp patch = %f %f %f\n", cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2]); + DBG((dbgo,"bcomp patch = %f %f %f\n", cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2])) } if (cols[k].sp.spec_n > 0) { for (e = 0; e < cols[k].sp.spec_n; e++) { @@ -860,7 +974,7 @@ static int disprd_read_drift( for (e = 0; e < 3; e++) { cols[k].XYZ[e] *= p->targ_w.XYZ[e]/ww.XYZ[e]; } -//printf("~1 wcomp patch = %f %f %f\n", cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2]); + DBG((dbgo,"wcomp patch = %f %f %f\n", cols[k].XYZ[0], cols[k].XYZ[1], cols[k].XYZ[2])) } if (cols[k].sp.spec_n > 0) { for (e = 0; e < cols[k].sp.spec_n; e++) { @@ -868,7 +982,7 @@ static int disprd_read_drift( } } } -//printf("~1 %d: drift change %f DE\n",k,icmXYZLabDE(&icmD50, uXYZ, cols[k].XYZ)); + DBG((dbgo,"%d: drift change %f DE\n",k,icmXYZLabDE(&icmD50, uXYZ, cols[k].XYZ))) } } free(dss); @@ -876,7 +990,7 @@ static int disprd_read_drift( /* Else too small a batch, use extrapolation from the last b&w */ } else { -//printf("~1 doing small number of readings\n"); + DBG((dbgo,"doing small number of readings\n")) /* Read the small number of patches */ if ((rv = disprd_read_imp(p, cols,npat,spat,tpat,acr,0,tc,0)) != 0) return rv; @@ -885,30 +999,32 @@ static int disprd_read_drift( /* Set the white drift reference to be the last one */ p->targ_w = p->last_bw[1]; p->targ_w_v = 1; -//printf("~1 set white drift target\n"); + DBG((dbgo,"set white drift target\n")) } -//printf("~1 ref b = %f %f %f\n", p->ref_bw[0].XYZ[0], p->ref_bw[0].XYZ[1], p->ref_bw[0].XYZ[2]); -//printf("~1 ref w = %f %f %f\n", p->ref_bw[1].XYZ[0], p->ref_bw[1].XYZ[1], p->ref_bw[1].XYZ[2]); -//printf("~1 last b = %f %f %f\n", p->last_bw[0].XYZ[0], p->last_bw[0].XYZ[1], p->last_bw[0].XYZ[2]); -//printf("~1 last w = %f %f %f\n", p->last_bw[1].XYZ[0], p->last_bw[1].XYZ[1], p->last_bw[1].XYZ[2]); -//printf("~1 target w = %f %f %f\n", p->targ_w.XYZ[0], p->targ_w.XYZ[1], p->targ_w.XYZ[2]); + DBG((dbgo,"ref b = %f %f %f\n", p->ref_bw[0].XYZ[0], p->ref_bw[0].XYZ[1], p->ref_bw[0].XYZ[2])) + DBG((dbgo,"ref w = %f %f %f\n", p->ref_bw[1].XYZ[0], p->ref_bw[1].XYZ[1], p->ref_bw[1].XYZ[2])) + DBG((dbgo,"last b = %f %f %f\n", p->last_bw[0].XYZ[0], p->last_bw[0].XYZ[1], p->last_bw[0].XYZ[2])) + DBG((dbgo,"last w = %f %f %f\n", p->last_bw[1].XYZ[0], p->last_bw[1].XYZ[1], p->last_bw[1].XYZ[2])) + DBG((dbgo,"target w = %f %f %f\n", p->targ_w.XYZ[0], p->targ_w.XYZ[1], p->targ_w.XYZ[2])) /* Apply the drift compensation using extrapolation */ for (j = 0; j < npat; j++) { double we; /* Interpolation weight of eairlier value */ col bb, ww; /* Interpolated black and white */ -//printf("~1 patch %d = %f %f %f\n", j, cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2]); -//double uXYZ[3]; -//icmCpy3(uXYZ, cols[j].XYZ); +#ifdef DEBUG + double uXYZ[3]; + DBG((dbgo,"patch %d = %f %f %f\n", j, cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2])) + icmCpy3(uXYZ, cols[j].XYZ); +#endif if (p->bdrift) { bb = p->last_bw[0]; -//printf("~1 bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2]); + DBG((dbgo,"bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2])) } if (p->wdrift) { ww = p->last_bw[1]; -//printf("~1 ww = %f %f %f\n", ww.XYZ[0], ww.XYZ[1], ww.XYZ[2]); + DBG((dbgo,"ww = %f %f %f\n", ww.XYZ[0], ww.XYZ[1], ww.XYZ[2])) } if (p->bdrift) { @@ -918,7 +1034,7 @@ static int disprd_read_drift( for (e = 0; e < 3; e++) { bb.XYZ[e] *= p->ref_bw[1].XYZ[e]/ww.XYZ[e]; } -//printf("~1 wcomp bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2]); + DBG((dbgo,"wcomp bb = %f %f %f\n", bb.XYZ[0], bb.XYZ[1], bb.XYZ[2])) } if (cols[j].sp.spec_n > 0) { for (e = 0; e < cols[j].sp.spec_n; e++) { @@ -932,7 +1048,7 @@ static int disprd_read_drift( for (e = 0; e < 3; e++) { cols[j].XYZ[e] += p->ref_bw[0].XYZ[e] - bb.XYZ[e]; } -//printf("~1 bcomp patch = %f %f %f\n", cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2]); + DBG((dbgo,"bcomp patch = %f %f %f\n", cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2])) } if (cols[j].sp.spec_n > 0) { for (e = 0; e < cols[j].sp.spec_n; e++) { @@ -946,7 +1062,7 @@ static int disprd_read_drift( for (e = 0; e < 3; e++) { cols[j].XYZ[e] *= p->targ_w.XYZ[e]/ww.XYZ[e]; } -//printf("~1 wcomp patch = %f %f %f\n", cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2]); + DBG((dbgo,"wcomp patch = %f %f %f\n", cols[j].XYZ[0], cols[j].XYZ[1], cols[j].XYZ[2])) } if (cols[j].sp.spec_n > 0) { for (e = 0; e < cols[j].sp.spec_n; e++) { @@ -954,7 +1070,7 @@ static int disprd_read_drift( } } } -//printf("~1 %d: drift change %f DE\n",j,icmXYZLabDE(&icmD50, uXYZ, cols[j].XYZ)); + DBG((dbgo,"%d: drift change %f DE\n",j,icmXYZLabDE(&icmD50, uXYZ, cols[j].XYZ))) } } @@ -1178,7 +1294,7 @@ int disprd_ambient( dwi.dw = p->dw; /* Set window to use */ printf("\nSample read failed because instruments needs calibration\n"); rv = inst_handle_calibrate(p->it, inst_calt_needed, inst_calc_none, - setup_display_calibrate, &dwi); + setup_display_calibrate, &dwi, 0); setup_display_calibrate(p->it,inst_calc_none, &dwi); if (rv != inst_ok) { /* Abort or fatal error */ return 1; @@ -1281,10 +1397,10 @@ static int disprd_fake_read( int tc, /* If nz, termination key */ instClamping clamp /* NZ if clamp XYZ/Lab to be +ve */ ) { - icmXYZNumber white; /* White point */ - icmXYZNumber red; /* Red colorant */ - icmXYZNumber green; /* Green colorant */ - icmXYZNumber blue; /* Blue colorant */ + double white[3]; /* White point */ + double red[3]; /* Red colorant */ + double green[3]; /* Green colorant */ + double blue[3]; /* Blue colorant */ double doff[3]; /* device offsets */ double mat[3][3]; /* Destination matrix */ double xmat[3][3]; /* Extra matrix */ @@ -1302,37 +1418,36 @@ static int disprd_fake_read( ttpat = npat; /* Setup fake device */ - white.X = br * 0.955; /* Somewhere between D50 and D65 */ - white.Y = br * 1.00; - white.Z = br * 0.97; - red.X = br * 0.41; - red.Y = br * 0.21; - red.Z = br * 0.02; - green.X = br * 0.30; - green.Y = br * 0.55; - green.Z = br * 0.15; - blue.X = br * 0.15; - blue.Y = br * 0.10; - blue.Z = br * 0.97; + white[0] = br * 0.955; /* Somewhere between D50 and D65 */ + white[1] = br * 1.00; + white[2] = br * 0.97; + red[0] = br * 0.41; + red[1] = br * 0.21; + red[2] = br * 0.02; + green[0] = br * 0.30; + green[1] = br * 0.55; + green[2] = br * 0.15; + blue[0] = br * 0.15; + blue[1] = br * 0.10; + blue[2] = br * 0.97; #ifdef SIMPLE_MODEL doff[0] = doff[1] = doff[2] = 0.0; /* Input offset */ ooff[0] = ooff[1] = ooff[2] = 0.0; /* Output offset */ #else - /* Input offset, equivalent to RGB offsets having various values */ - doff[0] = 0.05; - doff[1] = 0.06; - doff[2] = 0.07; + /* Input offset added to RGB, equivalent to RGB offsets having various values */ + doff[0] = -0.03; + doff[1] = 0.07; + doff[2] = -0.08; + /* Output offset - equivalent to flare [range 0.0 - 1.0] */ ooff[0] = 0.03; ooff[1] = 0.04; ooff[2] = 0.09; #endif - if (icmRGBprim2matrix(white, red, green, blue, mat)) + if (icmRGBXYZprim2matrix(red, green, blue, white, mat)) error("Fake read unexpectedly got singular matrix\n"); - icmTranspose3x3(mat, mat); /* Convert [RGB][XYZ] to [XYZ][RGB] */ - icmSetUnity3x3(xmat); if (p->fake2 == 1) { @@ -1816,6 +1931,7 @@ static int config_inst_displ(disprd *p) { inst2_capability cap2; inst3_capability cap3; inst_mode mode = 0; + int dtype = p->dtype; int rv; p->it->capabilities(p->it, &cap, &cap2, &cap3); @@ -1887,12 +2003,17 @@ static int config_inst_displ(disprd *p) { p->spectral = 0; } + /* If this is a spectral instrument, and a different */ + /* spectral inst. dtype is supplied, then use it */ + if (IMODETST(cap, inst_mode_spectral) && p->sdtype >= 0) + dtype = p->sdtype; + /* Set the display type */ - if (p->dtype != 0) { + if (dtype != 0) { if (cap2 & inst2_disptype) { int ix; - if ((ix = inst_get_disptype_index(p->it, p->dtype, p->docbid)) < 0) { - a1logd(p->log,1,"Display type selection '%c' is not valid for instrument\n",p->dtype); + if ((ix = inst_get_disptype_index(p->it, dtype, p->docbid)) < 0) { + a1logd(p->log,1,"Display type selection '%c' is not valid for instrument\n",dtype); if (p->docbid) return 16; return 15; @@ -1903,14 +2024,9 @@ static int config_inst_displ(disprd *p) { return 15; } } else - printf("Display type ignored - instrument doesn't support display type\n"); + printf("Display type ignored - instrument doesn't support display type selection\n"); } - /* Get the refresh mode and cbid */ - if (cap2 & inst2_disptype) { - p->it->get_set_opt(p->it, inst_opt_get_dtinfo, &p->refrmode, &p->cbid); - } - /* Disable initcalibration of machine if selected */ if (p->noinitcal != 0) { if ((rv = p->it->get_set_opt(p->it,inst_opt_noinitcalib, 0)) != inst_ok) { @@ -1934,17 +2050,21 @@ static int config_inst_displ(disprd *p) { } p->it->capabilities(p->it, &cap, &cap2, &cap3); + /* Set calibration matrix */ if (p->ccmtx != NULL) { if ((cap2 & inst2_ccmx) == 0) { a1logd(p->log,1,"Instrument doesn't support ccmx correction\n"); return 10; } - if ((rv = p->it->col_cor_mat(p->it, p->ccmtx)) != inst_ok) { + if ((rv = p->it->col_cor_mat(p->it, p->cc_dtech, p->cc_cbid, p->ccmtx)) != inst_ok) { a1logd(p->log,1,"col_cor_mat returned '%s' (%s)\n", p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv)); return 2; } } + + /* Get the refresh mode and cbid */ + p->it->get_disptechi(p->it, NULL, &p->refrmode, &p->cbid); /* Observer */ if ((cap2 & inst2_ccss) != 0 && p->obType != icxOT_default) { @@ -1969,7 +2089,7 @@ static int config_inst_displ(disprd *p) { return 11; } - if ((rv = p->it->col_cal_spec_set(p->it, p->sets, p->no_sets)) != inst_ok) { + if ((rv = p->it->col_cal_spec_set(p->it, p->cc_dtech, p->sets, p->no_sets)) != inst_ok) { a1logd(p->log,1,"col_cal_spec_set returned '%s' (%s)\n", p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv)); return 2; @@ -2012,6 +2132,7 @@ int *errc, /* Error code. May be NULL (could use log for this instead?) icompath *ipath, /* Instrument path to open, &icomFakeDevice == fake */ flow_control fc, /* Flow control */ int dtype, /* Display type selection character */ +int sdtype, /* Spectro dtype, use dtype if -1 */ int docbid, /* NZ to only allow cbid dtypes */ int tele, /* NZ for tele mode. Falls back to display mode */ int nadaptive, /* NZ for non-adaptive mode */ @@ -2032,15 +2153,19 @@ int out_tvenc, /* 1 = use RGB Video Level encoding */ int blackbg, /* NZ if whole screen should be filled with black */ int override, /* Override_redirect on X11 */ int webdisp, /* If nz, port number for web color display */ +ccast_id *ccid, /* non-NULL for ChromeCast */ #ifdef NT int madvrdisp, /* NZ for MadVR display */ #endif char *ccallout, /* Shell callout on set color */ char *mcallout, /* Shell callout on measure color (forced fake) */ +//char *scallout, /* Shell callout on results of measure color */ double hpatsize, /* Size of dispwin */ double vpatsize, double ho, /* Horizontal offset */ double vo, /* Vertical offset */ +disptech cc_dtech, /* Display tech to go with ccmtx or sets */ +int cc_cbid, /* cbid to go with ccmtx or sets */ double ccmtx[3][3], /* Colorimeter Correction matrix, NULL if none */ xspect *sets, /* CCSS Set of sample spectra, NULL if none */ int no_sets, /* CCSS Number on set, 0 if none */ @@ -2074,7 +2199,9 @@ a1log *log /* Verb, debug & error log */ p->ambient = disprd_ambient; p->fake_name = fake_name; - p->ccmtx = ccmtx; + p->cc_dtech = cc_dtech; + p->cc_cbid = cc_cbid; + p->ccmtx = ccmtx; /* CCMX */ p->sets = sets; p->no_sets = no_sets; /* CCSS */ p->spectral = spectral; @@ -2083,6 +2210,7 @@ a1log *log /* Verb, debug & error log */ p->bdrift = bdrift; p->wdrift = wdrift; p->dtype = dtype; + p->sdtype = sdtype; p->docbid = docbid; p->refrmode = -1; /* Unknown */ p->cbid = 0; /* Unknown */ @@ -2095,6 +2223,7 @@ a1log *log /* Verb, debug & error log */ if (mcallout != NULL) ipath = &icomFakeDevice; /* Force fake device */ p->mcallout = mcallout; +// p->scallout = scallout; p->ipath = ipath; p->br = baud_19200; p->fc = fc; @@ -2218,7 +2347,7 @@ a1log *log /* Verb, debug & error log */ } if (webdisp != 0) { - /* Open web display */ + /* Open web display - no black bg since we assume window only */ if ((p->dw = new_webwin(webdisp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm, uout_tvenc, 0, p->log->verb, p->log->debug)) == NULL) { a1logd(log,1,"new_disprd failed because new_webwin failed\n"); @@ -2226,6 +2355,14 @@ a1log *log /* Verb, debug & error log */ if (errc != NULL) *errc = 3; return NULL; } + } else if (ccid != NULL) { + if ((p->dw = new_ccwin(ccid, hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm, + uout_tvenc, 0, p->log->verb, p->log->debug)) == NULL) { + a1logd(log,1,"new_disprd failed because new_ccwin('%s') failed\n",ccid->name); + p->del(p); + if (errc != NULL) *errc = 3; + return NULL; + } #ifdef NT } else if (madvrdisp != 0) { if (out_tvenc) { @@ -2236,7 +2373,7 @@ a1log *log /* Verb, debug & error log */ } if ((p->dw = new_madvrwin(hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm, out_tvenc, - blackbg, p->log->verb, p->log->debug)) == NULL) { + 0, p->log->verb, p->log->debug)) == NULL) { a1logd(log,1,"new_disprd failed because new_madvrwin failed\n"); p->del(p); if (errc != NULL) *errc = 3; @@ -2323,7 +2460,7 @@ a1log *log /* Verb, debug & error log */ dwi.dw = p->dw; /* Set window to use */ rv = inst_handle_calibrate(p->it, inst_calt_needed, inst_calc_none, - setup_display_calibrate, &dwi); + setup_display_calibrate, &dwi, 0); setup_display_calibrate(p->it,inst_calc_none, &dwi); printf("\n"); if (rv != inst_ok) { /* Abort or fatal error */ @@ -2338,7 +2475,7 @@ a1log *log /* Verb, debug & error log */ } } - if (!p->noinitplace) { + if (p->it != NULL && !p->noinitplace) { inst2_capability cap2; p->it->capabilities(p->it, NULL, &cap2, NULL); @@ -2369,26 +2506,38 @@ a1log *log /* Verb, debug & error log */ printf("\n"); } - if (webdisp == 0 -#ifdef NT - && madvrdisp == 0 -#endif - ) { - /* Close the positioning window */ - if (p->dw != NULL) { - p->dw->del(p->dw); - } - - /* Open display window again for measurement */ - if ((p->dw = new_dispwin(disp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm, - uout_tvenc, blackbg, override, p->log->debug)) == NULL) { - a1logd(log,1,"new_disprd failed new_dispwin failed\n"); - p->del(p); - if (errc != NULL) *errc = 3; - return NULL; + /* All except web disp can have a black backround when running tests */ + if (webdisp == 0 && blackbg) { + + if (p->dw->set_bg(p->dw, blackbg)) { /* Have to re-create window */ + + /* Close the positioning window */ + if (p->dw != NULL) { + p->dw->del(p->dw); + } + + /* We happen to know that only the default dispwin needs re-creating */ + + /* Open display window again for measurement */ + if ((p->dw = new_dispwin(disp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm, + uout_tvenc, blackbg, override, p->log->debug)) == NULL) { + a1logd(log,1,"new_disprd failed new_dispwin failed\n"); + p->del(p); + if (errc != NULL) *errc = 3; + return NULL; + } } } + /* Set display rise & fall time more optimally */ + if (p->it != NULL) { + disptech dtech; + disptech_info *tinfo; + p->it->get_disptechi(p->it, &dtech, NULL, NULL); + tinfo = disptech_get_id(dtech); + p->dw->set_settling_delay(p->dw, tinfo->rise_time, tinfo->fall_time, -1.0); + } + /* Set color change callout */ if (ccallout) { p->dw->set_callout(p->dw, ccallout); diff --git a/spectro/dispsup.h b/spectro/dispsup.h index 1d291af..e8c3f76 100644 --- a/spectro/dispsup.h +++ b/spectro/dispsup.h @@ -15,9 +15,12 @@ * see the License.txt file for licencing details. */ -/* A helper function to handle presenting a display test patch */ +/* Helper functions to handle presenting a display test patch */ + +/* Struct used for calibration */ struct _disp_win_info { int webdisp; /* nz if web display is to be used */ + ccast_id *ccid; /* non-NULL for ChromeCast */ #ifdef NT int madvrdisp; /* NZ for MadVR display */ #endif @@ -47,12 +50,14 @@ int disprd_calibration( icompath *ipath, /* Instrument path to open, &icomFakeDevice == fake */ flow_control fc, /* Serial flow control */ int dtype, /* Display type, 0 = unknown, 1 = CRT, 2 = LCD */ +int sdtype, /* Spectro dtype, use dtype if -1 */ int docbid, /* NZ to only allow cbid dtypes */ int tele, /* NZ for tele mode */ int nadaptive, /* NZ for non-adaptive mode */ int noinitcal, /* NZ to disable initial instrument calibration */ disppath *screen, /* Screen to calibrate. */ int webdisp, /* If nz, port number for web display */ +ccast_id *ccid, /* non-NULL for ChromeCast */ #ifdef NT int madvrdisp, /* NZ for MadVR display */ #endif @@ -107,19 +112,23 @@ struct _disprd { int ncal; /* Number of entries used in cal[] */ icmLuBase *fake_lu; char *mcallout; /* fake instrument shell callout */ +// char *scallout; /* measurement XYZ value callout */ icompath *ipath; /* Instrument path to open, &icomFakeDevice == fake */ baud_rate br; flow_control fc; inst *it; /* Instrument */ int dtype; /* Display type, 0 = unknown, 1 = CRT, 2 = LCD */ + int sdtype; /* Spectro dtype */ int docbid; /* NZ to only allow cbid dtypes */ int refrmode; /* Refresh display mode, -1 if unknow, 0 = if no, 1 if yes */ - int cbid; /* The Calibration Base display mode ID, 0 if unknown */ + int cbid; /* The current Calibration Base display mode ID, 0 if unknown */ int tele; /* NZ for tele mode */ int nadaptive; /* NZ for non-adaptive mode */ int highres; /* Use high res mode if available */ double refrate; /* If != 0.0, set display refresh rate calibration */ int update_delay_set; /* NZ if we've calibrated the disp. update delay, or tried and failed */ + disptech cc_dtech; /* Display tech used with ccmtx or sets */ + int cc_cbid; /* cbid to be used with ccmtx * or sets */ double (*ccmtx)[3]; /* Colorimeter Correction Matrix, NULL if none */ icxObserverType obType; /* CCSS Observer */ xspect *custObserver; /* CCSS Optional custom observer */ @@ -222,6 +231,7 @@ int *errc, /* Error code. May be NULL */ icompath *ipath, /* Instrument path to open, &icomFakeDevice == fake */ flow_control fc, /* Serial flow control */ int dtype, /* Display type, 0 = unknown, 1 = CRT, 2 = LCD */ +int sdtype, /* Spectro dtype, use dtype if -1 */ int docbid, /* NZ to only allow cbid dtypes */ int tele, /* NZ for tele mode */ int nadaptive, /* NZ for non-adaptive mode */ @@ -242,15 +252,19 @@ int out_tvenc, /* 1 = use RGB Video Level encoding */ int blackbg, /* NZ if whole screen should be filled with black */ int override, /* Override_redirect on X11 */ int webdisp, /* If nz, port number for web display */ +ccast_id *ccid, /* non-NULL for ChromeCast */ #ifdef NT int madvrdisp, /* NZ for MadVR display */ #endif char *ccallout, /* Shell callout on set color */ char *mcallout, /* Shell callout on measure color (forced fake) */ +//char *scallout, /* Shell callout on results of measure color */ double hpatsize, /* Size of dispwin */ double vpatsize, double ho, /* Horizontal offset */ double vo, /* Vertical offset */ +disptech cc_dtech, /* Display tech to go with ccmtx or sets, disptech_unknown if not used */ +int cc_cbid, /* cbid to go with ccmtx or sets, 0 if not used */ double ccmtx[3][3], /* Colorimeter Correction matrix, NULL if none */ xspect *sets, /* CCSS Set of sample spectra, NULL if none */ int no_sets, /* CCSS Number on set, 0 if none */ diff --git a/spectro/disptechs.c b/spectro/disptechs.c new file mode 100644 index 0000000..05b9045 --- /dev/null +++ b/spectro/disptechs.c @@ -0,0 +1,726 @@ + + /* Standardized display types */ + + /* Standardized display types for use with libinst */ + +/* + * Argyll Color Correction System + * + * Author: Graeme W. Gill + * Date: 14/5/2014 + * + * Copyright 2014 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> +#ifndef SALONEINSTLIB +#include "copyright.h" +#include "aconfig.h" +#include "icc.h" +#else +#include "sa_config.h" +#endif /* !SALONEINSTLIB */ +#include "numsup.h" +#include "conv.h" +#include "disptechs.h" + +/* Other selection characters used: + + "n" Non-refresh (Generic) + "r" Refresh (Generic) + "F" Factory base calibration + "R" Raw sensor values + "g" Generic + + oemarch: + "C" CMF + "U" Custom + kleink10: + "P" DLP projector using ambient + "E" SMPTE C + "P" Klein DLP Lux + "d" Klein LED Bk LCD + "O" Sony EL OLED + "z" Eizo CG LCD + */ + +static disptech_info disptech_info_array[] = { + { + disptech_none, /* Not applicable entry. Must be first */ + "None", /* because disptech_get_list() assumes so */ + "None", + 0, + 0.001, + 0.001, + NULL + }, + + { + disptech_crt, + "CRT", + "CRT", + 1, + DISPTECH_CRT_RISE, + DISPTECH_CRT_FALL, + "c" + }, + { + disptech_plasma, + "Plasma", + "Plasma", + 1, + DISPTECH_CRT_RISE, + DISPTECH_CRT_FALL, + "m" + }, + + { + disptech_lcd, + "LCD", + "LCD", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "l" + }, + { + disptech_lcd_ccfl, + "LCD CCFL", + "LCD CCFL", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "l" + }, + { + disptech_lcd_ccfl_ips, + "LCD CCFL IPS", + "LCD CCFL IPS", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "l" + }, + { disptech_lcd_ccfl_vpa, + "LCD CCFL VPA", + "LCD CCFL VPA", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "l" + }, + { disptech_lcd_ccfl_tft, + "LCD CCFL TFT", + "LCD CCFL TFT", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "l" + + }, + { disptech_lcd_ccfl_wg, + "LCD CCFL Wide Gamut", + "LCD CCFL Wide Gamut", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "L" + }, + { disptech_lcd_ccfl_wg_ips, + "LCD CCFL Wide Gamut IPS", + "LCD CCFL Wide Gamut IPS", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "L" + }, + { disptech_lcd_ccfl_wg_vpa, + "LCD CCFL Wide Gamut VPA", + "LCD CCFL Wide Gamut VPA", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "L" + }, + { disptech_lcd_ccfl_wg_tft, + "LCD CCFL Wide Gamut TFT", + "LCD CCFL Wide Gamut TFT", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "L" + }, + { disptech_lcd_wled, + "LCD White LED", + "LCD White LED", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "e" + }, + { disptech_lcd_wled_ips, + "LCD White LED IPS", + "LCD White LED IPS", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "e" + }, + { disptech_lcd_wled_vpa, + "LCD White LED VPA", + "LCD White LED VPA", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "e" + }, + { disptech_lcd_wled_tft, + "LCD White LED TFT", + "LCD White LED TFT", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "e" + }, + { disptech_lcd_rgbled, + "LCD RGB LED", + "LCD RGB LED", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "b" + }, + { disptech_lcd_rgbled_ips, + "LCD RGB LED IPS", + "LCD RGB LED IPS", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "b" + }, + { disptech_lcd_rgbled_vpa, + "LCD RGB LED VPA", + "LCD RGB LED VPA", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "b" + }, + { disptech_lcd_rgbled_tft, + "LCD RGB LED TFT", + "LCD RGB LED TFT", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "b" + + }, + { disptech_lcd_rgledp, + "LCD RG Phosphor", + "LCD RG Phosphor", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "h" + }, + { disptech_lcd_rgledp_ips, + "LCD RG Phosphor IPS", + "LCD RG Phosphor IPS", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "h" + }, + { disptech_lcd_rgledp_vpa, + "LCD RG Phosphor VPA", + "LCD RG Phosphor VPA", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "h" + }, + { disptech_lcd_rgledp_tft, + "LCD RG Phosphor TFT", + "LCD RG Phosphor TFT", + 0, + DISPTECH_LCD_RISE, + DISPTECH_LCD_FALL, + "h" + }, + + { disptech_oled, + "LED OLED", + "LED OLED", + 0, + DISPTECH_LED_RISE, + DISPTECH_LED_FALL, + "o" + }, + { disptech_amoled, + "LED AMOLED", + "LED AMOLED", + 0, + DISPTECH_LED_RISE, + DISPTECH_LED_FALL, + "a" + }, + + { disptech_dlp, + "Projector", + "DLP Projector", + 1, + DISPTECH_DLP_RISE, + DISPTECH_DLP_FALL, + "p" + }, + { disptech_dlp_rgb, + "Projector RGB Filter Wheel", + "DLP Projector RGB Filter Wheel", + 1, + DISPTECH_DLP_RISE, + DISPTECH_DLP_FALL, + "p" + }, + { disptech_dlp_rgbw, + "Projector RGBW Filter Wheel", + "DPL Projector RGBW Filter Wheel", + 1, + DISPTECH_DLP_RISE, + DISPTECH_DLP_FALL, + "p" + }, + { disptech_dlp_rgbcmy, + "Projector RGBCMY Filter Wheel", + "DLP Projector RGBCMY Filter Wheel", + 1, + DISPTECH_DLP_RISE, + DISPTECH_DLP_FALL, + "p" + }, + + { + disptech_unknown, + "Unknown", + "Unknown", + 1, + DISPTECH_WORST_RISE, + DISPTECH_WORST_FALL, + "u" + }, + + { + disptech_end /* End marker */ + } +}; + + +static unknown_ix = -1; + +static find_unknown() { + int i; + + for (i = 0; disptech_info_array[i].dtech != disptech_end; i++) { + if (disptech_info_array[i].dtech == disptech_unknown) { + unknown_ix = i; + break; + } + } +} + +/* Given the enum id, return the matching disptech_info entry */ +/* Return the disptech_unknown entry if not matched */ +disptech_info *disptech_get_id(disptech id) { + int i; + + for (i = 0; disptech_info_array[i].dtech != disptech_end; i++) { + if (disptech_info_array[i].dtech == id) + return &disptech_info_array[i]; + } + + if (unknown_ix < 0) + find_unknown(); + return &disptech_info_array[unknown_ix]; +} + +/* Given the string id, return the matching disptech_info entry */ +/* Return the disptech_unknown entry if not matched */ +disptech_info *disptech_get_strid(char *strid) { + int i; + + for (i = 0; disptech_info_array[i].dtech != disptech_end; i++) { + if (strcmp(disptech_info_array[i].strid, strid) == 0) + return &disptech_info_array[i]; + } + + if (unknown_ix < 0) + find_unknown(); + return &disptech_info_array[unknown_ix]; +} + +/* For each selector we need to: + + check each selector char + if already used, + remove it. + if no selector remain, + allocate a free one. + mark all used selectors + + We treat the first selector as more important + than any aliases that come after it, so we need + to do two passes to resolve what gets used. +*/ + +/* Set the selection characters */ +/* return NZ if we have run out */ +/* If flag & 3 == 1, deal with all selectors & remove any already used */ +/* If flag & 3 == 2, deal with just primary selectors & remove any already used */ +/* If flag & 3 == 3, deal with just secondary selectors & remove any already used */ +/* If flag & 4, allocate selectors to those that don't have any */ +int disptechs_set_sel(int flag, char *sel, char *usels, int *k, char *asels) { + char *d, *s, i; + + /* First remove any used chars from selector */ + if (flag & 3) { + for (i = 0, d = s = sel; *s != '\000'; s++, i++) { + if (((flag & 3) == 3 && i == 0) /* Ignore and keep primary selector */ + || ((flag & 3) == 2 && i == 1)) { /* Ignore and keep secondary selectors */ + *d++ = *s; + continue; + } + if (usels[*s] == 0) { /* If selector is not currently used */ + *d++ = *s; + usels[*s] = 1; + } + } + *d = '\000'; + } + + /* Add a selector if we need one */ + if ((flag & 4) && sel[0] == '\000') { + + /* Locate the next unused selector */ + for (;;) { + if (asels[*k] == '\000') /* Run out of selectors */ + return 1; + if (usels[*k] == 0) + break; + (*k)++; + } + sel[0] = asels[*k]; + sel[1] = '\000'; + usels[sel[0]] = 1; + (*k)++; + } + + return 0; +} + +/* Return the display tech list with unique lsel lectors */ +disptech_info *disptech_get_list() { + disptech_info *list = &disptech_info_array[1]; /* skip disptech_none entry */ + + int i, k; + char usels[256]; /* Used selectors */ + static char *asels = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + for (i = 0; i < 256; i++) + usels[i] = 0; + k = 0; /* Next selector index */ + + /* Add entries from the static list and their primary selectors */ + for (i = 0; list[i].dtech != disptech_end; i++) { + strcpy(list[i].lsel, list[i].sel); + + if (disptechs_set_sel(2, list[i].lsel, usels, &k, asels)) { + a1loge(g_log, 1, "disptech_get_list run out of selectors\n"); + break; + } + } + + /* Create needed selectors */ + for (i = 0; list[i].dtech != disptech_end; i++) + disptechs_set_sel(4, list[i].lsel, usels, &k, asels); + + /* Verify or delete any secondary selectors from the list */ + for (i = 0; list[i].dtech != disptech_end; i++) + disptechs_set_sel(3, list[i].lsel, usels, &k, asels); + + return list; +} + +/* Locate the display list item that matches the given selector. */ +/* Return NULL if not found */ +disptech_info *disptech_select(disptech_info *list, char c) { + int i; + + for (i = 0; list[i].dtech != disptech_end; i++) { + if (c == list[i].lsel[0]) + return &list[i]; + } + + return NULL; +} + + +/* ------------------------------------------- */ + +/* + Display settling time model. This is primarily tailored + to phosphor type response (ie. CRT or Plasma), but LCD + should be somewhat similar + + Outline: + + Use sRGB as the device model. + + For the target RGB, compute the partial derivative of + delta E with respect to R, G & B, and then multiply + that by desired dE accuracy to get the target R, G & B + delta from the target, and then put that into + the exponential rise/fall model to compute settling time. + Choose the worst of the 3. + + Should really change the code to compute partial derivative + directly, to speed code up. + + We assume the phosphor stimulus is the proportional to the + light output required (ie. that the CRT/encoding non-linearity + is in the electron gun, not the electron->phoshor->light mechanism. ) +*/ + +/* Convert gamma encoded rgb to linear light rgb */ +static void rgb2rgbl(double *rgbl, double *rgb) { + int i; + for (i = 0; i < 3; i++) { + if (rgb[i] < 0.04045) + rgbl[i] = rgb[i]/12.92; + else + rgbl[i] = pow((rgb[i] + 0.055)/1.055, 2.4); + } +} + +/* Convert linear light rgb to L*a*b* */ +static void rgbl2lab(double *lab, double *rgbl) { + int i; + double xyz[3]; + static double mat[3][3] = { + { 0.412391, 0.212639, 0.019331 }, /* Red */ + { 0.357584, 0.715169, 0.119195 }, /* Green */ + { 0.180481, 0.072192, 0.950532 } /* Blue */ + }; + + icmMulBy3x3(xyz, mat, rgbl); + icmXYZ2Lab(&icmD65, lab, xyz); +} + +#ifdef NEVER +/* Convert linear light rgb to L*a*b* with partial derivatives */ +static void ddrgbl2lab(double *lab, double dout[3][3], double *rgbl) { + int i, j, k; + double xyz[3]; + static double mat[9] = { + 0.412391, 0.212639, 0.019331, /* Red */ + 0.357584, 0.715169, 0.119195, /* Green */ + 0.180481, 0.072192, 0.950532 /* Blue */ + }; + double dxyzlab[3][3]; /* Part. Deriv of [xyz] with respect to [rgb] */ + double dlabxyz[3][3]; /* Part. Deriv of [lab] from [xyz] */ + + icxdpdiiMulBy3x3Parm(xyz, dxyzlab, mat, rgbl); + icxdXYZ2Lab(&icmD65, lab, dlabxyz, xyz); + + /* Compute the partial derivative of Lab from rgb */ + for (k = 0; k < 3; k++) { + for (j = 0; j < 3; j++) { + dout[k][j] = 0.0; + for (i = 0; i < 3; i++) { + dout[k][j] += dlabxyz[k][i] * dxyzlab[i][j]; + } + } + } +} +#endif /* NEVER */ + +/* Convert linear light rgb to L*a*b* delta E partial derivatives */ +static void drgbl2lab(/* double *lab, */double deout[3], double *rgbl) { + int i, j, k; + static double mat[3][3] = { + { 0.412391, 0.212639, 0.019331 }, /* Red */ + { 0.357584, 0.715169, 0.119195 }, /* Green */ + { 0.180481, 0.072192, 0.950532 } /* Blue */ + }; + double xyz[3]; + double wp[3], tin[3], dtin[3]; + double dlabxyz[3][3]; /* Part. Deriv of [lab] from [xyz] */ + double dout[3][3]; /* Part. Deriv of [lab] from [rgb] */ + + /* rgb to XYZ */ + for (i = 0; i < 3; i++) { + xyz[i] = 0.0; + for (j = 0; j < 3; j++) { + xyz[i] += mat[i][j] * rgbl[j]; + } + } + + /* XYZ to perceptual Lab with partial derivatives. */ + wp[0] = icmD65.X, wp[1] = icmD65.Y, wp[2] = icmD65.Z; + + for (i = 0; i < 3; i++) { + tin[i] = xyz[i]/wp[i]; + dtin[i] = 1.0/wp[i]; + + if (tin[i] > 0.008856451586) { + dtin[i] *= pow(tin[i], -2.0/3.0) / 3.0; + tin[i] = pow(tin[i],1.0/3.0); + } else { + dtin[i] *= 7.787036979; + tin[i] = 7.787036979 * tin[i] + 16.0/116.0; + } + } + +/* lab[0] = 116.0 * tin[1] - 16.0; */ + dlabxyz[0][0] = 0.0; + dlabxyz[0][1] = 116.0 * dtin[1]; + dlabxyz[0][2] = 0.0; + +/* lab[1] = 500.0 * (tin[0] - tin[1]); */ + dlabxyz[1][0] = 500.0 * dtin[0]; + dlabxyz[1][1] = 500.0 * -dtin[1]; + dlabxyz[1][2] = 0.0; + +/* lab[2] = 200.0 * (tin[1] - tin[2]); */ + dlabxyz[2][0] = 0.0 * mat[0][1]; + dlabxyz[2][1] = 200.0 * dtin[1]; + dlabxyz[2][2] = 200.0 * -dtin[2]; + + /* Compute the partial derivative of delta E from rgb */ + for (j = 0; j < 3; j++) { + deout[j] = 0.0; + for (k = 0; k < 3; k++) { + dout[k][j] = 0.0; + for (i = 0; i < 3; i++) { + dout[k][j] += dlabxyz[k][i] * mat[i][j]; + } + deout[j] += dout[k][j] * dout[k][j]; + } + deout[j] = sqrt(deout[j]); + } +} + +double disp_settle_time(double *orgb, double *nrgb, double rise, double fall, double dE) { + int i, j; + double orgbl[3], nrgbl[3]; /* Linear light RGB */ + double drgb[3]; /* Partial derivative of RGB wrt to dE at new rgb */ + double argbl[3]; /* Acceptable RGB */ + double stime[3]; /* Settling time */ + double kr, kf; + double xtime = 0.0; + + /* Convert rgb's to linear light rgb */ + rgb2rgbl(orgbl, orgb); + rgb2rgbl(nrgbl, nrgb); + +//printf("orgb = %f %f %f\n", orgb[0], orgb[1], orgb[2]); +//printf("nrgb = %f %f %f\n", nrgb[0], nrgb[1], nrgb[2]); +//printf("orgbl = %f %f %f\n", orgbl[0], orgbl[1], orgbl[2]); +//printf("nrgbl = %f %f %f\n", nrgbl[0], nrgbl[1], nrgbl[2]); +//printf("dE = %f\n", dE); + + /* Compute partial derivative */ + drgbl2lab(drgb, nrgbl); +//printf("drgb = %f %f %f\n", drgb[0], drgb[1], drgb[2]); + +#ifdef NEVER + /* Calculate partial derivative explicitely to check */ + { + double rlab[3], lab[3]; + double xdrgb[3]; /* Partial derivative of RGB wrt to dE at new rgb */ + + /* Reference Lab */ + rgbl2lab(rlab, nrgbl); +//printf("rlab = %f %f %f\n", rlab[0], rlab[1], rlab[2]); + + for (j = 0; j < 3; j++) { + double del; + + if (nrgbl[j] > 0.5) + del = -1e-6; + else + del = 1e-6; + + nrgbl[j] += del; + rgbl2lab(lab, nrgbl); + nrgbl[j] -= del; +//printf("check pde of in %d = lab %f, %f, %f\n",j, (lab[0] - rlab[0])/del, (lab[1] - rlab[1])/del, (lab[2] - rlab[2])/del); + xdrgb[j] = icmLabDE(rlab, lab)/fabs(del); + } +printf("chk drgb = %f %f %f\n", xdrgb[0], xdrgb[1], xdrgb[2]); + } +#endif /* NEVER */ + + /* Compute rgb value that would give targ delta E */ + for (j = 0; j < 3; j++) { + double del; + + del = dE/drgb[j]; + + if (orgbl[j] < nrgbl[j]) { + argbl[j] = nrgbl[j] - del; + if (argbl[j] < orgbl[j]) + argbl[j] = orgbl[j]; + } else { + argbl[j] = nrgbl[j] + del; + if (argbl[j] > orgbl[j]) + argbl[j] = orgbl[j]; + } + } + +//{ double rlab[3], lab[3]; +//rgbl2lab(lab, argbl); +//rgbl2lab(rlab, nrgbl); +//printf("argbl = %f %f %f\n", argbl[0], argbl[1], argbl[2]); +//printf("dE for argbl = %f\n",icmLabDE(rlab, lab)); +//} + + /* Compute the modelled time from orgbl to argbl */ + kr = rise/log(1.0 - 0.9); /* Exponent constant for 90% change*/ + kf = fall/log(1.0 - 0.9); /* Exponent constant for 90% change*/ + + for (j = 0; j < 3; j++) { + double el, dl, n, t; + + dl = (argbl[j] - orgbl[j])/(nrgbl[j] - orgbl[j]); + + if (fabs(dl) < 1e-6) { + stime[j] = 0.0; + continue; + } + + if (nrgbl[j] > orgbl[j]) + stime[j] = kr * log(1.0 - dl); + else + stime[j] = kf * log(1.0 - dl); + + if (stime[j] > xtime && stime[j] < 5.0) + xtime = stime[j]; + } +//printf("stime = %f %f %f\n", stime[0], stime[1], stime[2]); +//printf("returning = %f\n",xtime); + + return xtime; +} + + + + + diff --git a/spectro/disptechs.h b/spectro/disptechs.h new file mode 100644 index 0000000..8c0a416 --- /dev/null +++ b/spectro/disptechs.h @@ -0,0 +1,149 @@ + +#ifndef DISPTYPES_H + + /* Standardized display types for use with libinst */ + +/* + * Argyll Color Correction System + * + * Author: Graeme W. Gill + * Date: 14/5/2014 + * + * Copyright 2014 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 + +/* See <http://www.tftcentral.co.uk/> for some numbers on response times */ + +/* These are intended to be conservative numbers for 90% transition times */ +/* The settling model will compute the time necessary for a 0.1 dE error */ +/* from this using an exponenial decay model. */ + +#define DISPTECH_WORST_RISE 0.100 +#define DISPTECH_WORST_FALL 0.250 + +#define DISPTECH_CRT_RISE 0.040 +#define DISPTECH_CRT_FALL 0.250 + +#define DISPTECH_LCD_RISE 0.100 +#define DISPTECH_LCD_FALL 0.050 + +#define DISPTECH_LED_RISE 0.001 +#define DISPTECH_LED_FALL 0.001 + +#define DISPTECH_DLP_RISE 0.002 +#define DISPTECH_DLP_FALL 0.002 + +/* Type of display technology */ +typedef enum { + + disptech_unknown = 0x0000, /* Unknown dislay type */ + + disptech_none = 0x0001, /* display type is inaplicable (ie. ambient) */ + + disptech_crt = 0x1000, /* Generic CRT */ + + disptech_plasma = 0x2000, /* Generic Plasma */ + + disptech_lcd = 0x3000, /* Generic LCD */ + + disptech_lcd_ccfl = 0x3100, /* LCD with CCFL */ + disptech_lcd_ccfl_ips = 0x3110, /* IPS LCD with CCFL */ + disptech_lcd_ccfl_vpa = 0x3120, /* VPA LCD with CCFL */ + disptech_lcd_ccfl_tft = 0x3130, /* TFT LCD with CCFL */ + + disptech_lcd_ccfl_wg = 0x3200, /* Wide gamut LCD with CCFL */ + disptech_lcd_ccfl_wg_ips = 0x3210, /* IPS wide gamut LCD with CCFL */ + disptech_lcd_ccfl_wg_vpa = 0x3220, /* VPA wide gamut LCD with CCFL */ + disptech_lcd_ccfl_wg_tft = 0x3230, /* TFT wide gamut LCD with CCFL */ + + disptech_lcd_wled = 0x3300, /* LCD with white LED */ + disptech_lcd_wled_ips = 0x3310, /* IPS LCD with white LED */ + disptech_lcd_wled_vpa = 0x3320, /* VPA LCD with white LED */ + disptech_lcd_wled_tft = 0x3330, /* TFT LCD with white LED */ + + disptech_lcd_rgbled = 0x3400, /* LCD with RGB LED */ + disptech_lcd_rgbled_ips = 0x3410, /* IPS LCD with RGB LED */ + disptech_lcd_rgbled_vpa = 0x3420, /* VPA LCD with RGB LED */ + disptech_lcd_rgbled_tft = 0x3430, /* TFT LCD with RGB LED */ + + disptech_lcd_rgledp = 0x3500, /* IPS LCD with RG LED + Phosphor */ + disptech_lcd_rgledp_ips = 0x3510, /* IPS LCD with RG LED + Phosphor */ + disptech_lcd_rgledp_vpa = 0x3520, /* VPA LCD with RG LED + Phosphor */ + disptech_lcd_rgledp_tft = 0x3530, /* TFT LCD with RG LED + Phosphor */ + + disptech_oled = 0x4000, /* Organic LED */ + disptech_amoled = 0x4010, /* Active Matrix Organic LED */ + + disptech_dlp = 0x5000, /* Generic Digital Light Processing projector */ + disptech_dlp_rgb = 0x5010, /* DLP projector with RGB filter */ + disptech_dlp_rgbw = 0x5020, /* DLP projector with RGBW filter */ + disptech_dlp_rgbcmy = 0x5030, /* DLP projector with RGBCMY filter */ + + disptech_end = 0xffffffff /* List end marker */ + +} disptech; + +#ifdef __cplusplus + } +#endif + +/* Information defined by instrument type */ +struct _disptech_info { + + disptech dtech; /* Enumeration */ + + char *strid; /* String ID */ + char *desc; /* Desciption and identification string */ + + int refr; /* Refresh mode flag */ + + double rise_time; /* rise time to 90% in seconds */ + double fall_time; /* fall time to 90% in seconds */ + + char *sel; /* Default command line selector (may be NULL) */ + + /* Private: */ + + char lsel[10]; /* Unique list selector for ui */ + +}; typedef struct _disptech_info disptech_info; + + +/* Given the enum id, return the matching disptech_info entry */ +/* Return the disptech_unknown entry if not matched */ +disptech_info *disptech_get_id(disptech id); + +/* Given the string id, return the matching disptech_info entry */ +/* Return the disptech_unknown entry if not matched */ +disptech_info *disptech_get_strid(char *strid); + +/* Return the display tech list with unique lsel lectors */ +disptech_info *disptech_get_list(); + +/* Locate the display list item that matches the given selector. */ +/* Return NULL if not found */ +disptech_info *disptech_select(disptech_info *list, char c); + +/* - - - - - - - - - - - */ + +/* utility function, used by disptech_get_list & inst_creat_disptype_list() */ +int disptechs_set_sel(int flag, char *sel, char *usels, int *k, char *asels); + +/* - - - - - - - - - - */ +/* Display settling time model */ + +double disp_settle_time(double *orgb, double *nrgb, double rise, double fall, double dE); + + +#define DISPTYPES_H +#endif /* DISPTYPES_H */ + diff --git a/spectro/dispwin.c b/spectro/dispwin.c index 74186cf..d5b0797 100644 --- a/spectro/dispwin.c +++ b/spectro/dispwin.c @@ -49,11 +49,16 @@ #include "copyright.h" #include "aconfig.h" #include "icc.h" -#include "numsup.h" +#include "numlib.h" #include "cgats.h" #include "conv.h" +#include "xicc.h" +#include "disptechs.h" #include "dispwin.h" +#include "ui.h" #include "webwin.h" +#include "ccast.h" +#include "ccwin.h" #ifdef NT # include "madvrwin.h" #endif @@ -391,8 +396,8 @@ disppath **get_displays() { /* We could possibly use NSScreen instead of CG here, - but we'd need to have a an NSApp first, so perhaps not. - + If we're using libui, then we have an NSApp, so + this would be possible. */ int i; @@ -2386,7 +2391,7 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { return 1; } - if ((basename = PathFindFileName(fullpath)) == NULL) { + if ((basename = PathFindFileNameX(fullpath)) == NULL) { debugr2((errout,"Locating base name in '%s' failed\n",fname)); free(fullpath); return 1; @@ -2501,8 +2506,9 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { gid = atoi(gids); if (setegid(gid) || seteuid(uid)) { debugr("seteuid or setegid failed\n"); + } else { + debug2((errout,"Set euid %d and egid %d\n",uid,gid)); } - debug2((errout,"Set euid %d and egid %d\n",uid,gid)); } /* If setting local system profile and not effective root, but sudo */ } else if (scope != p_scope_user && getuid() == 0 && geteuid() != 0) { @@ -2510,8 +2516,8 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { && getenv("SUDO_GID") != NULL) { debugr("We're setting a system profile running as user - revert to root\n"); - setegid(getgid()); - seteuid(getuid()); + if (setegid(getgid()) || seteuid(getuid())) + debugr("seteuid or setegid failed\n"); } } #endif /* OS X || Linux */ @@ -2699,6 +2705,7 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { if (cd_found) ev = cd_edid_install_profile(p->edid, p->edid_len, sc, fname); + // Hmm. We're relying on colord error codes being in sync with ucmm. else ev = ucmm_install_monitor_profile(sc, p->edid, p->edid_len, p->name, fname); @@ -2750,7 +2757,7 @@ int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { return 1; } - if ((basename = PathFindFileName(fullpath)) == NULL) { + if ((basename = PathFindFileNameX(fullpath)) == NULL) { debugr2((errout,"Locating base name in '%s' failed\n",fname)); free(fullpath); return 1; @@ -3374,8 +3381,14 @@ void (*dispwin_term)(int sig) = SIG_DFL; static dispwin *signal_dispwin = NULL; static void dispwin_sighandler(int arg) { + static amutex_static(lock); dispwin *pp, *np; + /* Make sure we don't re-enter */ + if (amutex_trylock(lock)) { + return; + } + /* Restore all dispwin's Ramdacs & screen savers */ for (pp = signal_dispwin; pp != NULL; pp = np) { np = pp->next; @@ -3391,6 +3404,8 @@ static void dispwin_sighandler(int arg) { dispwin_int(arg); if (arg == SIGTERM && dispwin_term != SIG_DFL && dispwin_term != SIG_IGN) dispwin_term(arg); + + amutex_unlock(lock); exit(0); } @@ -3454,8 +3469,6 @@ typedef struct { #endif } osx_cntx_t; -static void OSX_ProcessEvents(dispwin *p); - // - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3505,7 +3518,7 @@ unsigned char emptyCursor[43] = { frect = NSMakeRect(p->tx, p->ty, (1.0 + p->tw), (1.0 + p->th)); #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 - /* Use matching profile to (hopefully) trigger null color transform */ + /* Use matching profile to (hopefully) trigger null color transform. */ /* This doesn't work on < 10.6 though. */ if (cx->nscs != NULL) { CGFloat rgb[4]; @@ -3571,7 +3584,7 @@ unsigned char emptyCursor[43] = { /* Create our window */ static void create_my_win(NSRect rect, osx_cntx_t *cx) { dispwin *p = cx->p; - SInt32 MacVers; + SInt32 MacMajVers, MacMinVers, MacBFVers; void *cspr = NULL; /* ColorSync profile ref. */ int i; @@ -3660,8 +3673,11 @@ static void create_my_win(NSRect rect, osx_cntx_t *cx) { #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 /* >= 10.6+ device colors don't work on secondary display, need null transform. */ /* < 10.6 null transform doesn't work. */ - if (Gestalt(gestaltSystemVersion, &MacVers) == noErr - && MacVers >= 0x1060 + + if (Gestalt(gestaltSystemVersionMajor, &MacMajVers) == noErr + && Gestalt(gestaltSystemVersionMinor, &MacMinVers) == noErr + && Gestalt(gestaltSystemVersionBugFix, &MacBFVers) == noErr + && MacMajVers >= 10 && MacMinVers >= 6 && cx->nscs == NULL) { warning("Unable to create null color transform - test colors may be wrong!"); } @@ -3682,13 +3698,13 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ int j; double orgb[3]; /* Previous RGB value */ double kr, kf; - int update_delay = p->update_delay; - double xdelay = 0.0; /* Extra delay for response time */ + int update_delay = 0; debugr("dispwin_set_color called\n"); - if (p->nowin) + if (p->nowin) { return 1; + } orgb[0] = p->rgb[0]; p->rgb[0] = r; orgb[1] = p->rgb[1]; p->rgb[1] = g; @@ -3803,9 +3819,12 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ return 2; } - /* We're creating and draining a pool here to ensure that all the */ - /* auto release objects get drained when we're finished (?) */ - NSAutoreleasePool *tpool = [NSAutoreleasePool new]; + /* If we may be in a different thread to the main thread or */ + /* the application thread, establish our own pool. */ + NSAutoreleasePool *tpool = nil; + if (pthread_self() != ui_thid + && pthread_self() != ui_main_thid) + tpool = [NSAutoreleasePool new]; /* Stop the system going to sleep */ UpdateSystemActivity(OverallAct); @@ -3833,10 +3852,8 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ /* Trigger an update that fills window with r_rgb[] */ [((osx_cntx_t *)(p->osx_cntx))->view setNeedsDisplay: YES ]; - /* Process events */ - OSX_ProcessEvents(p); - - [tpool release]; + if (tpool != nil) + [tpool release]; #endif /* __APPLE__ */ @@ -3883,43 +3900,13 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ free(cmd); } - /* Don't want extra delay if we're measuring update delay */ - if (update_delay != 0 && p->do_resp_time_del) { - /* Compute am expected response time for the change in level */ - kr = DISPLAY_RISE_TIME/log(1 - 0.9); /* Exponent constant */ - kf = DISPLAY_FALL_TIME/log(1 - 0.9); /* Exponent constant */ -//printf("~1 k2 = %f\n",k2); - for (j = 0; j < 3; j++) { - double el, dl, n, t; - - el = pow(p->rgb[j], 2.2); - dl = el - pow(orgb[j], 2.2); /* Change in level */ - if (fabs(dl) > 0.01) { /* More than 1% change in level */ - n = DISPLAY_SETTLE_AIM * el; - if (n < DISPLAY_ABS_AIM) - n = DISPLAY_ABS_AIM; -//printf("~1 sl %f, el %f, log (%f / %f)\n",sl,el,n,fabs(sl - el)); - if (dl > 0.0) - t = kr * log(n/dl); - else - t = kf * log(n/-dl); - - if (t > xdelay) - xdelay = t; - } - } -//printf("~1 xdelay = %f secs\n",xdelay); - xdelay *= 1000.0; /* To msec */ - /* This is kind of a fudge since update delay is after latency, */ - /* but displays with long delay (ie. CRT) have short latency, and visa versa */ - if ((int)xdelay > update_delay) - update_delay = (int)xdelay; - } + update_delay = dispwin_compute_delay(p, orgb); - /* Allow some time for the display to update before */ - /* a measurement can take place. This allows for CRT */ - /* refresh, or LCD processing/update time, + */ - /* display settling time (quite long for smaller LCD changes). */ + /* Allow some time for the display & instrument to update */ + /* before a measurement can take place. This allows for CRT refresh, */ + /* or LCD processing/update time, + display settling time (quite long for */ + /* smaller LCD changes), and any instrument reaction time. */ + debugr2((errout, "dispwin_set_color delaying %d msec\n",update_delay)); msec_sleep(update_delay); if (p->cberror) { /* Callback routine failed */ @@ -3930,17 +3917,66 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ } /* ----------------------------------------------- */ -/* Set an update delay, and return the previous value */ -/* Value can be set to zero, but othewise will be forced */ -/* to be >= min_update_delay */ -static int dispwin_set_update_delay( -dispwin *p, -int update_delay) { - int cval = p->update_delay; - p->update_delay = update_delay; - if (update_delay != 0 && p->update_delay < p->min_update_delay) - p->update_delay = p->min_update_delay; - return cval; + +/* Set a patch delay and instrument reaction time values. */ +/* The overall delay between patch change and triggering */ +/* the instrument is (patch_delay + display_settle - inst_reaction) */ +/* and will never be less than the min_update_delay value. */ +void dispwin_set_update_delay(dispwin *p, int patch_delay, int inst_reaction) { + p->patch_delay = patch_delay; + p->inst_reaction = inst_reaction; +} + +/* Set/unset the blackground color flag */ +/* Return nz on error */ +static int dispwin_set_bg(dispwin *p, int blackbg) { + return 1; /* Need to re-create window */ +} + +/* Set the display settling time constants. Use -ve value to leave current value */ +/* unchanged. (These values are used as part of the update delay calculations - see above). */ +void dispwin_set_settling_delay(dispwin *p, double rise_time, double fall_time, double de_aim) { + if (rise_time >= 0.0) + p->rise_time = rise_time; + if (fall_time >= 0.0) + p->fall_time = fall_time; + if (de_aim >= 0.0) + p->de_aim = de_aim; +} + +/* Enable or disable the update delay. This is used to disable the update delay */ +/* when measuring the patch_delay and inst_reaction */ +void dispwin_enable_update_delay(dispwin *p, int enable) { + p->do_update_del = enable; +} + +/* Return the update delay we should use (msec) */ +int dispwin_compute_delay(dispwin *p, double *orgb) { + int update_delay = 0, disp_settle = 0; + + if (p->do_update_del == 0) { + return 0; + } + + if (p->do_resp_time_del) { + double xdelay; + + /* Compute am expected response time for the change in level, */ + /* to achieve 0.1 delta E */ + xdelay = disp_settle_time(orgb, p->rgb, p->rise_time * p->settle_mult, + p->fall_time * p->settle_mult, p->de_aim); + disp_settle = (int)(xdelay * 1000.0 + 0.5); + } + + update_delay = p->patch_delay + disp_settle - p->inst_reaction; + + /* Enforce minimum delay */ + if (update_delay < p->min_update_delay) + update_delay = p->min_update_delay; + + if (p->ddebug) fprintf(stderr,"dispwin: update delay %d msec = patch_delay %d + disp_settle %d - inst_reaction %d\n", update_delay, p->patch_delay, disp_settle, p->inst_reaction); + + return update_delay; } /* ----------------------------------------------- */ @@ -4192,34 +4228,6 @@ static LRESULT CALLBACK MainWndProc( #endif /* NT */ -#ifdef __APPLE__ - -/* Not the event handler (because events are handled by the Cocoa objects) */ -/* but a function to call to process events after an update */ -static void OSX_ProcessEvents(dispwin *p) { - NSEvent *event; - NSDate *to; - - /* We're creating and draining a pool here to ensure that all the */ - /* auto release objects get drained when we're finished (?) */ -// NSAutoreleasePool *tpool = [NSAutoreleasePool new]; - - /* Wait until the events are done */ - to = [NSDate dateWithTimeIntervalSinceNow:0.01]; /* autorelease ? */ - for (;;) { - /* Hmm. Assume event is autorelease */ - if ((event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:to inMode:NSDefaultRunLoopMode dequeue:YES]) != nil) { - [NSApp sendEvent:event]; - } else { - break; - } - } -// [tpool release]; -} - -#endif /* __APPLE__ */ - #if defined(UNIX_X11) /* None */ #endif /* UNXI X11 */ @@ -4329,6 +4337,42 @@ int win_message_thread(void *pp) { #endif /* NT */ +/* Set the defauly update delay values */ +void dispwin_set_default_delays(dispwin *p) { + char *cp; + + p->min_update_delay = 20; + + if ((cp = getenv("ARGYLL_MIN_DISPLAY_UPDATE_DELAY_MS")) != NULL) { + p->min_update_delay = atoi(cp); + if (p->min_update_delay < 20) + p->min_update_delay = 20; + if (p->min_update_delay > 60000) + p->min_update_delay = 60000; + debugr2((errout, "new_dispwin: Minimum display update delay set to %d msec\n",p->min_update_delay)); + } + + p->settle_mult = 1.0; + + if ((cp = getenv("ARGYLL_DISPLAY_SETTLE_TIME_MULT")) != NULL) { + p->settle_mult = atof(cp); + if (p->settle_mult < 1e-6) + p->settle_mult = 1e-6; + if (p->settle_mult > 1e4) + p->settle_mult = 1e4; + debugr2((errout, "new_dispwin: Settling time multiplier %f\n",p->settle_mult)); + } + + p->patch_delay = PATCH_UPDATE_DELAY; /* Default patch delay */ + p->inst_reaction = INSTRUMENT_REACTIONTIME; /* Default inst delay */ + p->rise_time = DISPLAY_RISE_TIME; + p->fall_time = DISPLAY_FALL_TIME; + p->de_aim = DISPLAY_SETTLE_AIM; + + p->do_resp_time_del = 1; /* Default this to on */ + p->do_update_del = 1; /* Default this to on */ +} + /* Create a RAMDAC access and display test window, default grey */ dispwin *new_dispwin( disppath *disp, /* Display to calibrate. */ @@ -4347,7 +4391,6 @@ int override, /* NZ if override_redirect is to be used on X11 */ int ddebug /* >0 to print debug statements to stderr */ ) { dispwin *p = NULL; - char *cp; debug("new_dispwin called\n"); @@ -4367,39 +4410,29 @@ int ddebug /* >0 to print debug statements to stderr */ } /* !!!! Make changes in webwin.c as well !!!! */ + p->width = width; + p->height = height; p->nowin = nowin; p->native = native; p->out_tvenc = out_tvenc; p->blackbg = blackbg; p->ddebug = ddebug; - p->get_ramdac = dispwin_get_ramdac; - p->set_ramdac = dispwin_set_ramdac; - p->install_profile = dispwin_install_profile; - p->uninstall_profile = dispwin_uninstall_profile; - p->get_profile = dispwin_get_profile; - p->set_color = dispwin_set_color; - p->set_update_delay = dispwin_set_update_delay; - p->set_callout = dispwin_set_callout; - p->del = dispwin_del; + p->get_ramdac = dispwin_get_ramdac; + p->set_ramdac = dispwin_set_ramdac; + p->install_profile = dispwin_install_profile; + p->uninstall_profile = dispwin_uninstall_profile; + p->get_profile = dispwin_get_profile; + p->set_color = dispwin_set_color; + p->set_bg = dispwin_set_bg; + p->set_update_delay = dispwin_set_update_delay; + p->set_settling_delay = dispwin_set_settling_delay; + p->enable_update_delay = dispwin_enable_update_delay; + p->set_callout = dispwin_set_callout; + p->del = dispwin_del; p->rgb[0] = p->rgb[1] = p->rgb[2] = 0.5; /* Set Grey as the initial test color */ - p->min_update_delay = 20; - - if ((cp = getenv("ARGYLL_MIN_DISPLAY_UPDATE_DELAY_MS")) != NULL) { - p->min_update_delay = atoi(cp); - if (p->min_update_delay < 20) - p->min_update_delay = 20; - if (p->min_update_delay > 60000) - p->min_update_delay = 60000; - debugr2((errout, "new_dispwin: Minimum display update delay set to %d msec\n",p->min_update_delay)); - } - - p->update_delay = DISPLAY_UPDATE_DELAY; /* Default update delay */ - if (p->update_delay < p->min_update_delay) - p->update_delay = p->min_update_delay; - - p->do_resp_time_del = 1; /* Default this to on */ + dispwin_set_default_delays(p); /* Basic object is initialised, so create a window */ @@ -4566,7 +4599,7 @@ int ddebug /* >0 to print debug statements to stderr */ } debugr2((errout,"new_dispwin: found pixel depth %d bits\n",p->pdepth)); } - /* Get frame buffer depth for sanity check */ + /* Get frame buffer depth for sanity check, but don't actually make used of it */ dispmode = CGDisplayCopyDisplayMode(p->ddid); pixenc = CGDisplayModeCopyPixelEncoding(dispmode); @@ -4619,25 +4652,23 @@ int ddebug /* >0 to print debug statements to stderr */ debugr2((errout, "new_dispwin: About to open display '%s'\n",disp->name)); - /* We're creating and draining a pool here to ensure that all the */ - /* auto release objects get drained when we're finished (?) */ - NSAutoreleasePool *tpool = [NSAutoreleasePool new]; + /* If we may be in a different thread to the main thread or */ + /* the application thread, establish our own pool. */ + NSAutoreleasePool *tpool = nil; + if (pthread_self() != ui_thid + && pthread_self() != ui_main_thid) + tpool = [NSAutoreleasePool new]; - /* If we don't have an application object, create one. */ - /* (This should go in a common library) */ - /* Note that we don't actually clean this up on exit - */ - /* possibly we can't. */ + /* If there is no NSApp, then we haven't run main() in libui before */ + /* main() in the application. */ if (NSApp == nil) { -// static NSAutoreleasePool *pool; /* Pool used for NSApp */ -// pool = [NSAutoreleasePool new]; - NSApp = [NSApplication sharedApplication]; /* Creates NSApp */ - [NSApp finishLaunching]; - /* We seem to need this, because otherwise we don't get focus automatically */ - [NSApp activateIgnoringOtherApps: YES]; + fprintf(stderr,"NSApp is nil - need to rename main() to main() and link with libui !\n"); + exit(1); } if ((cx = (osx_cntx_t *)calloc(sizeof(osx_cntx_t), 1)) == NULL) { - [tpool release]; + if (tpool != nil) + [tpool release]; debugr2((errout,"new_dispwin: Malloc failed (osx_cntx_t)\n")); dispwin_del(p); return NULL; @@ -4684,9 +4715,8 @@ int ddebug /* >0 to print debug statements to stderr */ create_my_win(wrect, cx); - OSX_ProcessEvents(p); - - [tpool release]; + if (tpool != nil) + [tpool release]; p->winclose = 0; } @@ -4780,6 +4810,9 @@ int ddebug /* >0 to print debug statements to stderr */ } //p->pdepth = DefaultDepth(p->mydisplay, p->myscreen)/3; + + // Hmm. Should we explicitly get the root window visual, + // since our test window inherits it from root ? myvisual = DefaultVisual(p->mydisplay, p->myscreen); p->pdepth = myvisual->bits_per_rgb; p->edepth = 16; @@ -5072,6 +5105,7 @@ int ddebug /* >0 to print debug statements to stderr */ debugr("Unable to access VideoLUT\n"); if (noramdac != NULL) *noramdac = 1; + p->native = native &= ~1; p->oor = p->or = p->r = NULL; } @@ -5086,7 +5120,7 @@ int ddebug /* >0 to print debug statements to stderr */ if (nocm != NULL) *nocm = 1; - p->native &= ~2; + p->native = native &= ~2; } debugr("new_dispwin: return sucessfully\n"); @@ -5344,7 +5378,9 @@ static int gcc_bug_fix(int i) { #include "numlib.h" -static void usage(char *diag, ...) { +/* Flag = 0x0000 = default */ +/* Flag & 0x0001 = list ChromCast's */ +static void usage(int flag, char *diag, ...) { disppath **dp; fprintf(stderr,"Test display patch window, Set Video LUTs, Install profiles, Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); @@ -5377,7 +5413,23 @@ static void usage(char *diag, ...) { } } free_disppaths(dp); - fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); + fprintf(stderr," -dweb[:port] Display via web server at port (default 8080)\n"); + fprintf(stderr," -dcc[:n] Display via n'th ChromeCast (default 1, ? for list)\n"); + if (flag & 0x001) { + ccast_id **ids; + if ((ids = get_ccids()) == NULL) { + fprintf(stderr," ** Error discovering ChromCasts **\n"); + } else { + if (ids[0] == NULL) + fprintf(stderr," ** No ChromCasts found **\n"); + else { + int i; + for (i = 0; ids[i] != NULL; i++) + fprintf(stderr," %d = '%s'\n",i+1,ids[i]->name); + free_ccids(ids); + } + } + } #ifdef NT fprintf(stderr," -dmadvr Display via MadVR Video Renderer\n"); #endif @@ -5385,7 +5437,7 @@ static void usage(char *diag, ...) { fprintf(stderr," -P ho,vo,ss[,vs] Position test window and scale it\n"); fprintf(stderr," -F Fill whole screen with black background\n"); fprintf(stderr," -i Run forever with random values\n"); - fprintf(stderr," -G filename Display RGB colors from CGATS file\n"); + fprintf(stderr," -G filename Display RGB colors from CGATS (ie .ti1) file\n"); fprintf(stderr," -C r.rr,g.gg,b.bb Add this RGB color to list to be displayed\n"); fprintf(stderr," -m Manually cycle through values\n"); fprintf(stderr," -f Test grey ramp fade\n"); @@ -5417,6 +5469,7 @@ main(int argc, char *argv[]) { int verb = 0; /* Verbose flag */ int ddebug = 0; /* debug level */ int webdisp = 0; /* NZ for web display, == port number */ + int ccdisp = 0; /* NZ for ChromeCast, == list index */ #ifdef NT int madvrdisp = 0; /* NZ for MadVR display */ #endif @@ -5476,7 +5529,7 @@ main(int argc, char *argv[]) { } if (argv[fa][1] == '?') - usage("Usage requested"); + usage(0,"Usage requested"); else if (argv[fa][1] == 'v') verb = 1; @@ -5488,6 +5541,7 @@ main(int argc, char *argv[]) { ddebug = atoi(na); fa = nfa; } + g_log->debug = ddebug; callback_ddebug = ddebug; /* dispwin global */ } @@ -5499,7 +5553,19 @@ main(int argc, char *argv[]) { if (na[3] == ':') { webdisp = atoi(na+4); if (webdisp == 0 || webdisp > 65535) - usage("Web port number must be in range 1..65535"); + usage(0,"Web port number must be in range 1..65535"); + } + fa = nfa; + } else if (strncmp(na,"cc",2) == 0 + || strncmp(na,"CC",2) == 0) { + ccdisp = 1; + if (na[2] == ':') { + if (na[3] < '0' || na[3] > '9') + usage(0x0001,"Available ChromeCasts"); + + ccdisp = atoi(na+3); + if (ccdisp <= 0) + usage(0,"ChromCast number must be in range 1..N"); } fa = nfa; #ifdef NT @@ -5514,10 +5580,10 @@ main(int argc, char *argv[]) { /* X11 type display name. */ if (strcmp(&argv[fa][2], "isplay") == 0 || strcmp(&argv[fa][2], "ISPLAY") == 0) { - if (++fa >= argc || argv[fa][0] == '-') usage("-DISPLAY parameter missing"); + if (++fa >= argc || argv[fa][0] == '-') usage(0,"-DISPLAY parameter missing"); setenv("DISPLAY", argv[fa], 1); } else { - if (na == NULL) usage("-d parameter missing"); + if (na == NULL) usage(0,"-d parameter missing"); fa = nfa; if (sscanf(na, "%d,%d",&ix,&iv) != 2) { ix = atoi(na); @@ -5526,19 +5592,19 @@ main(int argc, char *argv[]) { if (disp != NULL) free_a_disppath(disp); if ((disp = get_a_display(ix-1)) == NULL) - usage("-d parameter '%s' is out of range",na); + usage(0,"-d parameter '%s' is out of range",na); if (iv > 0) disp->rscreen = iv-1; } #else int ix; - if (na == NULL) usage("-d parameter is missing"); + if (na == NULL) usage(0,"-d parameter is missing"); fa = nfa; ix = atoi(na); if (disp != NULL) free_a_disppath(disp); if ((disp = get_a_display(ix-1)) == NULL) - usage("-d parameter '%s' is out of range",na); + usage(0,"-d parameter '%s' is out of range",na); #endif } } @@ -5546,19 +5612,19 @@ main(int argc, char *argv[]) { /* Test patch offset and size */ else if (argv[fa][1] == 'P') { fa = nfa; - if (na == NULL) usage("-p parameters are missing"); + if (na == NULL) usage(0,"-p parameters are missing"); if (sscanf(na, " %lf,%lf,%lf,%lf ", &ho, &vo, &hpatscale, &vpatscale) == 4) { ; } else if (sscanf(na, " %lf,%lf,%lf ", &ho, &vo, &hpatscale) == 3) { vpatscale = hpatscale; } else { - usage("-p parameters '%s' is badly formatted",na); + usage(0,"-p parameters '%s' is badly formatted",na); } if (ho < 0.0 || ho > 1.0 || vo < 0.0 || vo > 1.0 || hpatscale <= 0.0 || hpatscale > 50.0 || vpatscale <= 0.0 || vpatscale > 50.0) - usage("-p parameters '%s' is out of range",na); + usage(0,"-p parameters '%s' is out of range",na); ho = 2.0 * ho - 1.0; vo = 2.0 * vo - 1.0; @@ -5579,21 +5645,21 @@ main(int argc, char *argv[]) { /* CGATS patch color file */ else if (argv[fa][1] == 'G') { fa = nfa; - if (na == NULL) usage("-G parameter is missing"); + if (na == NULL) usage(0,"-G parameter is missing"); strncpy(pcname,na,MAXNAMEL); pcname[MAXNAMEL] = '\000'; } /* Manual color */ else if (argv[fa][1] == 'C') { fa = nfa; if (nmrgb >= 10) - usage("Can only be up to 10 -C values"); - if (na == NULL) usage("-C parameters are missing"); + usage(0,"Can only be up to 10 -C values"); + if (na == NULL) usage(0,"-C parameters are missing"); if (sscanf(na, "%lf,%lf,%lf ",&mrgb[nmrgb][0],&mrgb[nmrgb][1],&mrgb[nmrgb][2]) != 3) - usage("-C parameters '%s' are badly formatted",na); + usage(0,"-C parameters '%s' are badly formatted",na); if (mrgb[nmrgb][0] < 0.0 || mrgb[nmrgb][0] > 1.0 || mrgb[nmrgb][1] < 0.0 || mrgb[nmrgb][1] > 1.0 || mrgb[nmrgb][2] < 0.0 || mrgb[nmrgb][2] > 1.0) - usage("-C parameters %f %f %f are out of range 0.0 - 1.0",mrgb[nmrgb][0],mrgb[nmrgb][1],mrgb[nmrgb][2]); + usage(0,"-C parameters %f %f %f are out of range 0.0 - 1.0",mrgb[nmrgb][0],mrgb[nmrgb][1],mrgb[nmrgb][2]); nmrgb++; } else if (argv[fa][1] == 'f') @@ -5608,7 +5674,7 @@ main(int argc, char *argv[]) { else if (argv[fa][1] == 's') { fa = nfa; - if (na == NULL) usage("-s parameter is missing"); + if (na == NULL) usage(0,"-s parameter is missing"); strncpy(sname,na,MAXNAMEL); sname[MAXNAMEL] = '\000'; } @@ -5632,7 +5698,7 @@ main(int argc, char *argv[]) { else if (argv[fa][1] == 'S') { fa = nfa; - if (na == NULL) usage("-S parameter is missing"); + if (na == NULL) usage(0,"-S parameter is missing"); if (na[0] == 'n' || na[0] == 'N') scope = p_scope_network; else if (na[0] == 'l' || na[0] == 'L') @@ -5641,7 +5707,7 @@ main(int argc, char *argv[]) { scope = p_scope_user; } else - usage("Unknown flag '%s'",argv[fa]); + usage(0,"Unknown flag '%s'",argv[fa]); } else break; @@ -5652,7 +5718,8 @@ main(int argc, char *argv[]) { #ifdef NT && madvrdisp == 0 #endif - && webdisp == 0) { + && webdisp == 0 + && ccdisp == 0) { int ix = 0; #if defined(UNIX_X11) char *dn, *pp; @@ -5678,7 +5745,7 @@ main(int argc, char *argv[]) { } #if defined(UNIX_X11) - if (webdisp == 0 && daemonmode) { + if (webdisp == 0 && ccdisp == 0 && daemonmode) { return x11_daemon_mode(disp, verb, ddebug); } #endif @@ -5700,7 +5767,6 @@ main(int argc, char *argv[]) { if (ramd != 0 || sname[0] != '\000' || clear != 0 || verify != 0 || loadfile != 0 || installprofile != 0 || loadprofile != 0) nowin = 1; - if (webdisp != 0) { if ((dw = new_webwin(webdisp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, &noramdac, &nocm, out_tvenc, blackbg, verb, ddebug)) == NULL) { @@ -5708,6 +5774,32 @@ main(int argc, char *argv[]) { return -1; } + } else if (ccdisp != 0) { + ccast_id **ids; + if ((ids = get_ccids()) == NULL) { + printf("Error - discovering ChromCasts failed\n"); + return -1; + } + if (ids[0] == NULL) { + printf("Error - there are no ChromCasts to use\n"); + return -1; + } + for (i = 0; ids[i] != NULL; i++) + ; + if (ccdisp < 1 || ccdisp > i) { + printf("Error - chosen ChromCasts (%d) is outside list (1..%d)\n",ccdisp,i); + return -1; + } + + if ((dw = new_ccwin(ids[ccdisp-1], 100.0 * hpatscale, 100.0 * vpatscale, + ho, vo, nowin, native, &noramdac, &nocm, out_tvenc, + blackbg, verb, ddebug)) == NULL) { + printf("Error - new_ccwin failed!\n"); + free_ccids(ids); + return -1; + } + free_ccids(ids); + #ifdef NT } else if (madvrdisp != 0) { if (out_tvenc) { @@ -5717,7 +5809,7 @@ main(int argc, char *argv[]) { if ((dw = new_madvrwin(100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, &noramdac, &nocm, out_tvenc, blackbg, verb, ddebug)) == NULL) { - printf("Error - new_madvrwin failed!\n"); + printf("Error - new_madvrwin failed! (Is it running and up to date?)\n"); return -1; } #endif diff --git a/spectro/dispwin.h b/spectro/dispwin.h index 1206661..43e23d2 100644 --- a/spectro/dispwin.h +++ b/spectro/dispwin.h @@ -15,15 +15,18 @@ * see the License.txt file for licencing details. */ -#define DISPLAY_UPDATE_DELAY 200 /* default minimum display update delay allowance */ +/* + * Hmm. Should make display settling time a user overridable parameter, + * to allow for very fast response displays such as oled ? + */ -/* Display rise and fall time model. This is CRT like */ -#define DISPLAY_RISE_TIME 0.03 /* Assumed rise time to 90% of target level */ -#define DISPLAY_FALL_TIME 0.12 /* Assumed fall time to 90% of target level */ -#define DISPLAY_SETTLE_AIM 0.01 /* Aim for 1% of true level */ -#define DISPLAY_ABS_AIM 0.0001 /* Aim for .01% of true absolute level */ +#define PATCH_UPDATE_DELAY 200 /* default & minimum patch update delay allowance */ +#define INSTRUMENT_REACTIONTIME 0 /* default nominal instrument reaction time */ -int do_plot(double *x, double *y1, double *y2, double *y3, int n); +/* Display rise and fall time model. This is CRT like */ +#define DISPLAY_RISE_TIME 0.04 /* Assumed rise time to 90% of target level */ +#define DISPLAY_FALL_TIME 0.25 /* Assumed fall time to 90% of target level */ +#define DISPLAY_SETTLE_AIM 0.1 /* Aim for 0.2 Delta E */ #ifdef NT #define OEMRESOURCE @@ -63,9 +66,9 @@ WINSHLWAPI LPSTR WINAPI PathFindFileNameA(LPCSTR); WINSHLWAPI LPWSTR WINAPI PathFindFileNameW(LPCWSTR); #ifdef UNICODE -#define PathFindFileName PathFindFileNameW +#define PathFindFileNameX PathFindFileNameW #else -#define PathFindFileName PathFindFileNameA +#define PathFindFileNameX PathFindFileNameA #endif #endif /* NT */ @@ -104,7 +107,7 @@ typedef enum { /* Structure to store infomation about possible displays */ typedef struct { char *name; /* Display name */ - char *description; /* Description of display */ + char *description; /* Description of display or URL */ int sx,sy; /* Displays offset in pixels */ int sw,sh; /* Displays width and height in pixels*/ #ifdef NT @@ -172,8 +175,10 @@ struct _ramdac { /* - - - - - - - - - - - - - - - - - - - - - - - */ /* Dispwin object */ - -/* !!!! Make changes in dispwin.c, webwin.c & madvrwin.c !!!! */ +/* This is used by all the different test patch window types, */ +/* dispwin, webwin, madvrwin and ccwin. +/* !!!! Make changes in dispwin.c, webwin.c, madvrwin.c & ccwin.c !!!! */ +/* !!!! if this structure gets changed. !!!! */ struct _dispwin { @@ -193,9 +198,17 @@ struct _dispwin { double s_rgb[3]; /* Current color (possibly scaled range) */ double r_rgb[3]; /* Current color (raster value) */ int out_tvenc; /* 1 to use RGB Video Level encoding */ - int update_delay; /* Update latency delay in msec, default 200 */ - int min_update_delay; /* Minimum update latency delay, default 20, overriden by EV */ + int patch_delay; /* Measured patch update latency delay in msec, default 200 */ + int inst_reaction; /* Measured instrument reaction time delay in msec, default 0 */ + double rise_time; /* Display settling rise time */ + double fall_time; /* Display settling fall time */ + double de_aim; /* Display settling deltaE aim */ + int min_update_delay; /* Minimum overall update latency delay, default 20, */ + /* overriden by EnvVar */ + double settle_mult; /* Settling time multiplier */ int do_resp_time_del; /* NZ to compute and use expected display response time */ + int do_update_del; /* NZ to do update delay */ + double extra_update_delay; /* Test window internal extra delay (used in delay cal.) */ int nowin; /* Don't create a test window */ int native; /* X0 = use current per channel calibration curve */ /* X1 = set native linear output and use ramdac high precision */ @@ -204,6 +217,7 @@ struct _dispwin { ramdac *oor; /* Original orgininal ramdac contents, NULL if not accessible */ ramdac *or; /* Original ramdac contents, NULL if not accessible, restored on exit */ ramdac *r; /* Ramdac in use for native mode or general use */ + double width, height; /* Orginial size in mm or % */ int blackbg; /* NZ if black full screen background */ char *callout; /* if not NULL - set color Shell callout routine */ @@ -254,7 +268,7 @@ struct _dispwin { Atom icc_out_atom; /* ICC profile atom for this output */ #endif /* randr >= V 1.2 */ - /* Test windo access */ + /* Test window access */ Window mywindow; GC mygc; @@ -277,7 +291,7 @@ struct _dispwin { #endif /* UNIX_X11 */ - void *pcntx; /* Private context (ie., webwin) */ + void *pcntx; /* Private context (ie., webwin, ccwin) */ volatile unsigned int ncix, ccix; /* Counters to trigger webwin colorchange */ volatile int mg_stop; /* Stop flag */ @@ -315,15 +329,29 @@ struct _dispwin { /* Return nz on error */ int (*set_color)(struct _dispwin *p, double r, double g, double b); + /* Set/unset the blackground color flag. */ + /* Will only change on next set_col() */ + /* Return nz on error */ + int (*set_bg)(struct _dispwin *p, int blackbg); + /* Optional - may be NULL */ /* set patch info */ /* Return nz on error */ int (*set_pinfo)(struct _dispwin *p, int pno, int tno); - /* Set an update delay, and return the previous value */ - /* Note that 0 is a special case and forces a zero delay */ - /* in spite of min_update_delay and do_resp_time_del */ - int (*set_update_delay)(struct _dispwin *p, int update_delay); + /* Set a patch delay and instrument reaction time values. */ + /* The overall delay between patch change and triggering */ + /* the instrument is (patch_delay + display_settle - inst_reaction) */ + /* and will never be less than the min_update_delay value. */ + void (*set_update_delay)(struct _dispwin *p, int patch_delay, int inst_reaction); + + /* Set the display settling time constants. Use -ve value to leave current value */ + /* unchanged. (These values are used as part of the update delay calculations - see above). */ + void (*set_settling_delay)(struct _dispwin *p, double rise_time, double fall_time, double de_aim); + + /* Enable or disable the update delay. This is used to disable the update delay */ + /* when measuring the patch_delay and inst_reaction */ + void (*enable_update_delay)(struct _dispwin *p, int enable); /* Set a shell set color callout command line */ void (*set_callout)(struct _dispwin *p, char *callout); @@ -352,11 +380,18 @@ dispwin *new_dispwin( ); /* Shared implementation */ +void dispwin_set_default_delays(dispwin *p); +void dispwin_set_update_delay(dispwin *p, int patch_delay, int inst_reaction); +void dispwin_set_settling_delay(dispwin *p, double rise_time, double fall_time, double de_aim); +void dispwin_enable_update_delay(dispwin *p, int enable); +int dispwin_compute_delay(dispwin *p, double *orgb); + ramdac *dispwin_clone_ramdac(ramdac *r); void dispwin_setlin_ramdac(ramdac *r); void dispwin_del_ramdac(ramdac *r); + #define DISPWIN_H #endif /* DISPWIN_H */ diff --git a/spectro/dtp20.c b/spectro/dtp20.c index b378f50..a460bb8 100644 --- a/spectro/dtp20.c +++ b/spectro/dtp20.c @@ -36,6 +36,24 @@ and agreed to support. */ +/* + CS comand values: + + 00 - Instrument Not Ready + 01 - Instrument Ready, Empty + 02 - Instrument Ready, TID has been scanned + 03 - Instrument Ready, Complete target has been measured + 04 - Instrument is Busy ? + 05 - + 06 - Error ? + 07 - Hardware Error + 08 - Clear Database Mode + 09 - Calibration Mode + 10 - + 11 - + 12 - + */ + #include <stdio.h> #include <stdlib.h> #include <ctype.h> @@ -62,6 +80,8 @@ static inst_code activate_mode(dtp20 *p); #define MAX_MES_SIZE 500 /* Maximum normal message reply size */ #define MAX_RD_SIZE 100000 /* Maximum reading messagle reply size */ +#define STRIP_READ_TIMEOUT 10.0 + /* Extract an error code from a reply string */ /* Return -1 if no error code can be found */ static int @@ -112,7 +132,7 @@ double to) { /* Timout in seconts */ int ntc = 1; /* Number of terminating characters */ int rv, se, insize; - a1logd(p->log, 4, "dtp20: Sending '%s'",icoms_fix(in)); + a1logd(p->log, 4, "dtp20: Sending '%s' bsize %d, to %f\n",icoms_fix(in),bsize,to); insize = strlen(in); if (insize > 0) { @@ -122,7 +142,7 @@ double to) { /* Timout in seconts */ } } - if ((se = p->icom->read(p->icom, out, bsize, tc, ntc, to)) != 0) { + if ((se = p->icom->read(p->icom, out, bsize, NULL, tc, ntc, to)) != 0) { a1logd(p->log, 1, "dtp20: read response failed ICOM err 0x%x\n",se); return dtp20_interp_code((inst *)p, icoms2dtp20_err(se)); } @@ -135,7 +155,7 @@ double to) { /* Timout in seconts */ if (rv != DTP20_OK) { /* Clear the error */ char buf[MAX_MES_SIZE]; p->icom->usb_control(p->icom, 0x41, 0x00, 0x00, 0x00, (unsigned char *)"CE\r", 3, 0.5); - p->icom->read(p->icom, buf, MAX_MES_SIZE, tc, ntc, 0.5); + p->icom->read(p->icom, buf, MAX_MES_SIZE, NULL, tc, ntc, 0.5); } } } @@ -234,7 +254,7 @@ dtp20_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* Print the general information returned by instrument */ if (p->log->verb) { int i, j; - if ((ev = dtp20_command(p, "GI\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) { + if ((ev = dtp20_command(p, "GI\r", buf, MAX_MES_SIZE, 2.0)) != inst_ok) { a1logd(p->log, 1, "dtp20: GI command failed with ICOM err 0x%x\n",ev); return ev; } @@ -433,7 +453,7 @@ ipatch *vals) { /* Pointer to array of values */ return inst_protocol_error; if (cs != 3) { /* Seems to be no chart saved, but double check, in case of old firmware ( < 1.03) */ - if ((ev = dtp20_command(p, "00TS\r", buf, MAX_RD_SIZE, 0.5)) != inst_ok) + if ((ev = dtp20_command(p, "00TS\r", buf, MAX_RD_SIZE, 2.0)) != inst_ok) return inst_nonesaved; if (sscanf(buf," %d ", &cs) != 1) return inst_nonesaved; @@ -442,7 +462,7 @@ ipatch *vals) { /* Pointer to array of values */ } /* Get the TID */ - if ((ev = dtp20_command(p, "ST\r", buf, MAX_RD_SIZE, 0.5)) != inst_ok) + if ((ev = dtp20_command(p, "ST\r", buf, MAX_RD_SIZE, 2.0)) != inst_ok) return ev; if (sscanf(buf,"Strip Length: %d Total Patches: %d Patch Width: %lf mm Gap Width: %lf mm" " User 1: %d User 2: %d User 3: %d User 4: %d User 5: %d User 6: %d" @@ -487,7 +507,7 @@ ipatch *vals) { /* Pointer to array of values */ return ev; if ((ev = dtp20_command(p, "0518CF\r", buf, MAX_RD_SIZE, 0.5)) != inst_ok) return ev; - if ((ev = dtp20_command(p, cmd, buf, MAX_RD_SIZE, 0.5)) != inst_ok) + if ((ev = dtp20_command(p, cmd, buf, MAX_RD_SIZE, STRIP_READ_TIMEOUT)) != inst_ok) return ev; /* Parse the buffer */ @@ -639,7 +659,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ if (sscanf(buf, " %d ", &stat) != 1) stat = 6; - /* Ignore benign status */ + /* Ignore benign status - wait for busy or error */ if (stat != 4 && stat != 6 && stat != 7) { /* Not ready - Check for user trigger or command */ if (p->uicallback != NULL) { @@ -714,7 +734,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ return ev; if ((ev = dtp20_command(p, "0518CF\r", buf, MAX_RD_SIZE, 0.5)) != inst_ok) return ev; - if ((ev = dtp20_command(p, "01TS\r", buf, MAX_RD_SIZE, 0.5)) != inst_ok) + if ((ev = dtp20_command(p, "01TS\r", buf, MAX_RD_SIZE, STRIP_READ_TIMEOUT)) != inst_ok) return ev; /* Parse the buffer */ @@ -739,6 +759,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ vals[i].sp.spec_n = 0; vals[i].duration = 0.0; tp += strlen(tp) + 1; + } if (p->mode & inst_mode_spectral) { @@ -751,7 +772,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ return ev; /* Read the strip */ - if ((ev = dtp20_bin_command(p, "01TS\r", buf, 62 * npatch, 5.0)) != inst_ok) + if ((ev = dtp20_bin_command(p, "01TS\r", buf, 62 * npatch, STRIP_READ_TIMEOUT)) != inst_ok) return ev; /* Get each patches spectra */ @@ -772,7 +793,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ vals[i].sp.norm = 100.0; } - /* Set to ASCII */ + /* Set back to ASCII */ if ((ev = dtp20_command(p, "001BCF\r", buf, MAX_MES_SIZE, 0.5)) != inst_ok) return ev; /* Set back to D50 2 degree */ @@ -780,8 +801,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ return ev; } - - /* Wait for status to change */ + /* Wait for instrument to become not busy */ for (;;) { if ((ev = dtp20_command(p, "CS\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) { return ev; @@ -860,7 +880,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ int stat; if (sscanf(buf, " %d ", &stat) != 1) stat = 6; - /* Ingnore benign status */ + /* Ignore benign status - wait for busy or error */ if (stat != 4 && stat != 6 && stat != 7) { msec_sleep(200); continue; @@ -918,6 +938,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ p->savix++; sprintf(cmd, "%03d01GM\r",p->savix); + /* Disable multiple data output */ if ((ev = dtp20_command(p, "001ACF\r", buf, MAX_MES_SIZE, 0.5)) != inst_ok) return ev; @@ -971,6 +992,7 @@ 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) { int j; @@ -1016,7 +1038,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ return ev; p->savix = 0; - /* Wait for status to change */ + /* Wait for instrument to become not busy */ for (;;) { if ((ev = dtp20_command(p, "CS\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) { return ev; @@ -1060,7 +1082,7 @@ static inst_code dtp20_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_t /* returning inst_needs_cal. Initially us an inst_cal_cond of inst_calc_none, */ /* and then be prepared to setup the right conditions, or ask the */ /* user to do so, each time the error inst_cal_setup is returned. */ -inst_code dtp20_calibrate( +static inst_code dtp20_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ @@ -1104,6 +1126,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ } if (*calt & inst_calt_ref_white) { + int i; if (*calc != inst_calc_man_ref_white) { char *cp; @@ -1120,6 +1143,18 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if ((ev = dtp20_command(p, "CR\r", buf, MAX_MES_SIZE, 4.5)) != inst_ok) return ev; + /* Wait for instrument to become not busy */ + for (;;) { + if ((ev = dtp20_command(p, "CS\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) { + return ev; + } else { + int stat = 4; + if (sscanf(buf, " %d ", &stat) != 1 || stat != 4) + break; + msec_sleep(200); + } + } + p->need_cal = 0; *calt &= ~inst_calt_ref_white; } @@ -1392,7 +1427,7 @@ static void set_capabilities(dtp20 *p) { /* Return the instrument capabilities */ -void dtp20_capabilities(inst *pp, +static void dtp20_capabilities(inst *pp, inst_mode *cap1, inst2_capability *cap2, inst3_capability *cap3) { @@ -1506,7 +1541,7 @@ inst_opt_type m, /* Requested status type */ *fe |= inst_stat_savdrd_chart; } else { /* Seems to be no chart saved, but double check, in case of old firmware */ - if ((ev = dtp20_command(p, "00TS\r", buf, MAX_MES_SIZE, 0.5)) == inst_ok) { + if ((ev = dtp20_command(p, "00TS\r", buf, MAX_MES_SIZE, 2.0)) == inst_ok) { if (sscanf(buf," %d ", &cs) == 1) { if (cs != 0) { *fe |= inst_stat_savdrd_chart; @@ -1562,7 +1597,7 @@ inst_opt_type m, /* Requested status type */ *no_patches = *no_rows = *pat_per_row = *chart_id = *missing_row = -1; /* Get the TID */ - if ((ev = dtp20_command(p, "ST\r", buf, MAX_RD_SIZE, 0.5)) != inst_ok) + if ((ev = dtp20_command(p, "ST\r", buf, MAX_RD_SIZE, 2.0)) != inst_ok) return ev; if (sscanf(buf,"Strip Length: %d Total Patches: %d Patch Width: %lf mm Gap Width: %lf mm" " User 1: %d User 2: %d User 3: %d User 4: %d User 5: %d User 6: %d" diff --git a/spectro/dtp20.h b/spectro/dtp20.h index f1e578a..c575c67 100644 --- a/spectro/dtp20.h +++ b/spectro/dtp20.h @@ -1,4 +1,4 @@ -#ifndef DTP41_H +#ifndef DTP20_H /* * Argyll Color Correction System diff --git a/spectro/dtp22.c b/spectro/dtp22.c index 927d833..a80f887 100644 --- a/spectro/dtp22.c +++ b/spectro/dtp22.c @@ -131,7 +131,7 @@ dtp22_fcommand( double to) { /* Timout in seconds */ int se, rv = DTP22_OK; - if ((se = p->icom->write_read(p->icom, in, out, bsize, tc, ntc, to)) != 0) { + if ((se = p->icom->write_read(p->icom, in, 0, out, bsize, NULL, tc, ntc, to)) != 0) { a1logd(p->log, 1, "dtp22_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in)); return icoms2dtp22_err(se); } @@ -145,7 +145,7 @@ dtp22_fcommand( rv &= inst_imask; if (rv != DTP22_OK) { /* Clear the error */ char buf[MAX_MES_SIZE]; - p->icom->write_read(p->icom, "CE\r", buf, MAX_MES_SIZE, ">", 1, 0.5); + p->icom->write_read(p->icom, "CE\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.5); } } } @@ -249,7 +249,7 @@ dtp22_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { return ev; /* Change the baud rate to the rate we've been told */ - if ((se = p->icom->write_read(p->icom, brc[bi], buf, MAX_MES_SIZE, ">", 1, .2)) != 0) { + if ((se = p->icom->write_read(p->icom, brc[bi], 0, buf, MAX_MES_SIZE, NULL, ">", 1, .2)) != 0) { if (extract_ec(buf) != DTP22_OK) return inst_coms_fail; } @@ -261,7 +261,7 @@ dtp22_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", buf, MAX_MES_SIZE, ">", 1, 0.1); + p->icom->write_read(p->icom, "\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.1); /* Check instrument is responding, and reset it again. */ if ((ev = dtp22_command(p, "\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok @@ -481,7 +481,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ /* Wait for the microswitch to be triggered, or the user to trigger */ for (;;) { - if ((se = p->icom->read(p->icom, buf, MAX_MES_SIZE, ">", 1, 1.0)) != 0) { + if ((se = p->icom->read(p->icom, buf, MAX_MES_SIZE, NULL, ">", 1, 1.0)) != 0) { if ((se & ICOM_TO) == 0) { /* Some sort of read error */ /* Disable the read microswitch */ dtp22_command(p, "2PB\r", buf, MAX_MES_SIZE, 0.2); @@ -701,14 +701,14 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ goto do_exit; /* Issue white calibration */ - if ((se = p->icom->write(p->icom, "1CA\r", 0.5)) != ICOM_OK) { + if ((se = p->icom->write(p->icom, "1CA\r", 0, 0.5)) != ICOM_OK) { ev = dtp22_interp_code((inst *)p, icoms2dtp22_err(se)); goto do_exit; } /* Wait for the microswitch to be triggered, or a user trigger via uicallback */ for (;;) { - if ((se = p->icom->read(p->icom, buf, MAX_MES_SIZE, ">", 1, 1.0)) != 0) { + if ((se = p->icom->read(p->icom, buf, MAX_MES_SIZE, NULL, ">", 1, 1.0)) != 0) { if ((se & ICOM_TO) == 0) { /* Some sort of read error */ ev = dtp22_interp_code((inst *)p, icoms2dtp22_err(se)); goto do_exit; diff --git a/spectro/dtp41.c b/spectro/dtp41.c index 4717ed4..a2f7e83 100644 --- a/spectro/dtp41.c +++ b/spectro/dtp41.c @@ -115,7 +115,7 @@ int ntc, /* Number of terminating characters */ double to) { /* Timout in seconts */ int rv, se; - if ((se = p->icom->write_read(p->icom, in, out, bsize, tc, ntc, to)) != 0) { + if ((se = p->icom->write_read(p->icom, in, 0, out, bsize, NULL, tc, ntc, to)) != 0) { a1logd(p->log, 1, "dtp41_fcommand: serial i/o failure 0x%x on write_read '%s'\n",se,icoms_fix(in)); return icoms2dtp41_err(se); } @@ -126,7 +126,7 @@ double to) { /* Timout in seconts */ rv &= inst_imask; if (rv != DTP41_OK) { /* Clear the error */ char buf[MAX_MES_SIZE]; - p->icom->write_read(p->icom, "CE\r", buf, MAX_MES_SIZE, ">", 1, 0.5); + p->icom->write_read(p->icom, "CE\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.5); } } } @@ -228,13 +228,13 @@ dtp41_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { return ev; /* Set the handshaking (cope with coms breakdown) */ - if ((se = p->icom->write_read(p->icom, fcc, buf, MAX_MES_SIZE, ">", 1, 1.5)) != 0) { + if ((se = p->icom->write_read(p->icom, fcc, 0, buf, MAX_MES_SIZE, NULL, ">", 1, 1.5)) != 0) { if (extract_ec(buf) != DTP41_OK) return inst_coms_fail; } /* Change the baud rate to the rate we've been told (cope with coms breakdown) */ - if ((se = p->icom->write_read(p->icom, brc[bi], buf, MAX_MES_SIZE, ">", 1, 1.5)) != 0) { + if ((se = p->icom->write_read(p->icom, brc[bi], 0, buf, MAX_MES_SIZE, NULL, ">", 1, 1.5)) != 0) { if (extract_ec(buf) != DTP41_OK) return inst_coms_fail; } @@ -247,7 +247,7 @@ dtp41_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", buf, MAX_MES_SIZE, ">", 1, 0.5); + p->icom->write_read(p->icom, "\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.5); /* Check instrument is responding */ if ((ev = dtp41_command(p, "\r", buf, MAX_MES_SIZE, 1.5)) != inst_ok) { diff --git a/spectro/dtp51.c b/spectro/dtp51.c index ac497d9..062ad7f 100644 --- a/spectro/dtp51.c +++ b/spectro/dtp51.c @@ -118,7 +118,7 @@ dtp51_fcommand( double to) { /* Timout in seconts */ int rv, se; - if ((se = p->icom->write_read(p->icom, in, out, bsize, tc, ntc, to)) != 0) { + if ((se = p->icom->write_read(p->icom, in, 0, out, bsize, NULL, tc, ntc, to)) != 0) { a1logd(p->log, 1, "dtp51_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in)); return icoms2dtp51_err(se); } @@ -129,7 +129,7 @@ dtp51_fcommand( rv &= inst_imask; if (rv != DTP51_OK) { /* Clear the error */ char buf[MAX_MES_SIZE]; - p->icom->write_read(p->icom, "CE\r", buf, MAX_MES_SIZE, ">", 1, 0.5); + p->icom->write_read(p->icom, "CE\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.5); } } } @@ -158,7 +158,7 @@ double to) { /* Timout in seconts */ int ntc = 1; /* Number of terminating characters */ int rv, se; - if ((se = p->icom->read(p->icom, out, bsize, tc, ntc, to)) != 0) { + if ((se = p->icom->read(p->icom, out, bsize, NULL, tc, ntc, to)) != 0) { a1logd(p->log, 1, "dtp51_fcommand: serial i/o failure on read\n"); return icoms2dtp51_err(se); } @@ -169,7 +169,7 @@ double to) { /* Timout in seconts */ rv &= inst_imask; if (rv != DTP51_OK) { /* Clear the error */ char buf[MAX_MES_SIZE]; - p->icom->write_read(p->icom, "CE\r", buf, MAX_MES_SIZE, ">", 1, 0.5); + p->icom->write_read(p->icom, "CE\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.5); } } } @@ -262,7 +262,7 @@ dtp51_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { return ev; /* Change the baud rate to the rate we've been told */ - if ((se = p->icom->write_read(p->icom, brc[bi], buf, MAX_MES_SIZE, ">", 1, 1.5)) != 0) { + if ((se = p->icom->write_read(p->icom, brc[bi], 0, buf, MAX_MES_SIZE, NULL, ">", 1, 1.5)) != 0) { if (extract_ec(buf) != DTP51_OK) return inst_coms_fail; } @@ -275,7 +275,7 @@ dtp51_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", buf, MAX_MES_SIZE, ">", 1, 0.5); + p->icom->write_read(p->icom, "\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.5); /* Check instrument is responding */ if ((ev = dtp51_command(p, "\r", buf, MAX_MES_SIZE, 1.5)) != inst_ok) diff --git a/spectro/dtp92.c b/spectro/dtp92.c index f86672e..9524326 100644 --- a/spectro/dtp92.c +++ b/spectro/dtp92.c @@ -61,6 +61,8 @@ #define DEFFC fc_none #define DEF_TIMEOUT 0.5 +#define MED_TIMEOUT 2.5 +#define MEAS_TIMEOUT 10.0 #define IGNORE_NEEDS_OFFSET_DRIFT_CAL_ERR @@ -120,7 +122,7 @@ dtp92_fcommand( double to) { /* Timout in seconds */ int rv, se; - if ((se = p->icom->write_read(p->icom, in, out, bsize, tc, ntc, to)) != 0) { + if ((se = p->icom->write_read(p->icom, in, 0, out, bsize, NULL, tc, ntc, to)) != 0) { a1logd(p->log, 1, "dtp92_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in)); return icoms2dtp92_err(se); } @@ -137,7 +139,7 @@ dtp92_fcommand( rv &= inst_imask; if (rv != DTP92_OK) { /* Clear the error */ char buf[MAX_MES_SIZE]; - p->icom->write_read(p->icom, "CE\r", buf, MAX_MES_SIZE, ">", 1, 0.5); + p->icom->write_read(p->icom, "CE\r", 0, buf, MAX_MES_SIZE, NULL, ">", 1, 0.5); } } } @@ -182,6 +184,7 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { if (p->icom->port_type(p->icom) == icomt_usb) { #ifdef ENABLE_USB + int wr_ep, rd_ep; a1logd(p->log, 2, "dtp92_init_coms: About to init USB\n"); @@ -197,20 +200,27 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* 0x02 o Bulk 0x02 o Intr. */ /* */ /* Set config, interface, write end point, read end point, read quanta */ - if (itype == instDTP94) - se = p->icom->set_usb_port(p->icom, 1, 0x02, 0x81, icomuf_none, 0, NULL); - else - se = p->icom->set_usb_port(p->icom, 1, 0x01, 0x81, icomuf_none, 0, NULL); - if (se != ICOM_OK) { + if (itype == instDTP94) { + wr_ep = 0x02; + rd_ep = 0x81; + } else { + wr_ep = 0x01; + rd_ep = 0x81; + } + + /* The DTP94 will babble if a measure is interrupted, so reset it */ + /* on close to make sure it restarts correctly. */ + if ((se = p->icom->set_usb_port(p->icom, 1, wr_ep, rd_ep, icomuf_reset_before_close, + 0, NULL)) != ICOM_OK) { a1logd(p->log, 1, "dtp92_init_coms: set_usb_port failed ICOM err 0x%x\n",se); return dtp92_interp_code((inst *)p, icoms2dtp92_err(se)); } /* Blind reset it twice - it seems to sometimes hang up */ /* otherwise under OSX */ - dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, 0.5); - dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, 0.5); + dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, DEF_TIMEOUT); + dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, DEF_TIMEOUT); #else /* !ENABLE_USB */ a1logd(p->log, 1, "dtp92: Failed to find USB connection to instrument\n"); return inst_coms_fail; @@ -265,7 +275,7 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { return dtp92_interp_code((inst *)p, icoms2dtp92_err(se)); } - if (((ev = dtp92_command(p, "\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) + 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 */ @@ -293,7 +303,7 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { return ev; /* Change the baud rate to the rate we've been told */ - if ((se = p->icom->write_read(p->icom, brc[bi], buf, MAX_MES_SIZE, ">", 1, .2)) != 0) { + if ((se = p->icom->write_read(p->icom, brc[bi], 0, buf, MAX_MES_SIZE, NULL, ">", 1, .2)) != 0) { if (extract_ec(buf) != DTP92_OK) return inst_coms_fail; } @@ -306,7 +316,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", buf, MAX_MES_SIZE, ">", 1, 0.1); + 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; @@ -318,7 +328,7 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* Check instrument is responding, and reset it again. */ if ((ev = dtp92_command(p, "\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok - || (ev = dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, 2.0)) != inst_ok) { + || (ev = dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, MED_TIMEOUT)) != inst_ok) { a1logd(p->log, 1, "dtp92_init_coms: failed with ICOM 0x%x\n",ev); @@ -331,7 +341,7 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { char tbuf[100]; double odv = 0.0; printf("Got Offset Drift Cal error. Will try re-writing it\n"); - if ((ev = dtp92_command(p, "SD\r", buf, MAX_MES_SIZE, 2.0)) != inst_ok) + if ((ev = dtp92_command(p, "SD\r", buf, MAX_MES_SIZE, MED_TIMEOUT)) != inst_ok) error("Reading current offset drift value failed"); if (sscanf(buf, "%lf<", &odv) != 1) error("Unable to parse offset drift value"); @@ -380,11 +390,11 @@ dtp92_init_inst(inst *pp) { return inst_internal_error; /* Must establish coms before calling init */ /* Reset it ( without disconnecting USB or resetting baud rate etc.) */ - if ((ev = dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, 2.0)) != inst_ok) + if ((ev = dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, MED_TIMEOUT)) != inst_ok) return ev; /* Get the model and version number */ - if ((ev = dtp92_command(p, "SV\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) + if ((ev = dtp92_command(p, "SV\r", buf, MAX_MES_SIZE, MED_TIMEOUT)) != inst_ok) return ev; /* Check that it is a DTP92, DTP92Q or DTP94 */ @@ -433,12 +443,12 @@ dtp92_init_inst(inst *pp) { return ev; /* Set to factory calibration */ - if ((ev = dtp92_command(p, "EFC\r", buf, MAX_MES_SIZE, 0.5)) != inst_ok) + if ((ev = dtp92_command(p, "EFC\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; if (p->itype == instDTP94) { /* Compensate for offset drift */ - if ((ev = dtp92_command(p, "0117CF\r", buf, MAX_MES_SIZE, 0.5)) != inst_ok) + if ((ev = dtp92_command(p, "0117CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; } @@ -600,7 +610,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ /* Take a reading */ /* (DTP94 has optional parameters, but the default is what we want, XYZ in cd/m^2) */ - if ((rv = dtp92_command(p, "RM\r", buf, MAX_RD_SIZE, 10.0)) != inst_ok) { + if ((rv = dtp92_command(p, "RM\r", buf, MAX_RD_SIZE, MEAS_TIMEOUT)) != inst_ok) { if ((rv & inst_imask) == DTP92_NEEDS_OFFSET_CAL) p->need_offset_cal = 1; else if ((rv & inst_imask) == DTP92_NEEDS_RATIO_CAL) /* DTP92 only */ @@ -660,6 +670,9 @@ double *ref_rate if (!p->inited) return inst_no_init; + if (ref_rate != NULL) + *ref_rate = 0.0; + /* Measure the refresh rate */ rv = dtp92_command(p, "00103RM\r", buf, MAX_RD_SIZE, 5.0); @@ -671,38 +684,64 @@ double *ref_rate if (sscanf(buf, "Hz %lf ", &refrate) != 1) { a1logd(p->log, 1, "dtp92_read_refrate rate: failed to parse string '%s'\n",buf); - *ref_rate = 0.0; return inst_misread; } - if (refrate == 0.0) { + + if (refrate == 0.0) return inst_misread; - } - *ref_rate = refrate; + + + if (ref_rate != NULL) + *ref_rate = refrate; + return inst_ok; } +static inst_code set_base_disp_type(dtp92 *p, int cbid); + /* Insert a colorimetric correction matrix in the instrument XYZ readings */ /* This is only valid for colorimetric instruments. */ /* To remove the matrix, pass NULL for the filter filename */ static inst_code dtp92_col_cor_mat( inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ \ +int cbid, /* Calibration display type base ID, 1 if unknown */\ double mtx[3][3] ) { dtp92 *p = (dtp92 *)pp; + inst_code ev; if (!p->gotcoms) return inst_no_coms; if (!p->inited) return inst_no_init; + if ((ev = set_base_disp_type(p, cbid)) != inst_ok) + return ev; if (mtx == NULL) icmSetUnity3x3(p->ccmat); else icmCpy3x3(p->ccmat, mtx); - + + p->dtech = dtech; + p->refrmode = disptech_get_id(dtech)->refr; + p->cbid = 0; /* Can't be base type now */ + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + return inst_ok; } + /* Return needed and available inst_cal_type's */ static inst_code dtp92_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) { dtp92 *p = (dtp92 *)pp; @@ -786,7 +825,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ } /* Do offset calibration */ - if ((ev = dtp92_command(p, "CO\r", buf, MAX_RD_SIZE, 12)) != inst_ok) + if ((ev = dtp92_command(p, "CO\r", buf, MAX_RD_SIZE, 12.0)) != inst_ok) return ev; *calt &= inst_calt_emis_offset; @@ -834,6 +873,9 @@ double *ref_rate if (!p->inited) return inst_no_init; + if (ref_rate != NULL) + *ref_rate = 0.0; + /* Get the last readings refresh rate */ rv = dtp92_command(p, "10103RM\r", buf, MAX_RD_SIZE, 5.0); @@ -845,13 +887,11 @@ double *ref_rate if (sscanf(buf, "Hz %lf ", &refrate) != 1) { a1logd(p->log, 1, "dtp92_read_refresh rate: failed to parse string '%s'\n",buf); - *ref_rate = 0.0; return inst_misread; } - if (refrate == 0.0) { + if (refrate == 0.0) return inst_misread; - } - *ref_rate = refrate; + return inst_ok; } @@ -987,7 +1027,7 @@ dtp92_del(inst *pp) { } /* Return the instrument mode capabilities */ -void dtp92_capabilities(inst *pp, +static void dtp92_capabilities(inst *pp, inst_mode *pcap1, inst2_capability *pcap2, inst3_capability *pcap3) { @@ -1020,7 +1060,7 @@ inst3_capability *pcap3) { } /* Check device measurement mode */ -inst_code dtp92_check_mode(inst *pp, inst_mode m) { +static inst_code dtp92_check_mode(inst *pp, inst_mode m) { inst_mode cap; if (!pp->gotcoms) @@ -1043,7 +1083,7 @@ inst_code dtp92_check_mode(inst *pp, inst_mode m) { } /* Set device measurement mode */ -inst_code dtp92_set_mode(inst *pp, inst_mode m) { +static inst_code dtp92_set_mode(inst *pp, inst_mode m) { inst_code ev; if ((ev = dtp92_check_mode(pp, m)) != inst_ok) @@ -1052,13 +1092,14 @@ inst_code dtp92_set_mode(inst *pp, inst_mode m) { return inst_ok; } -inst_disptypesel dtp92_disptypesel[2] = { +static inst_disptypesel dtp92_disptypesel[2] = { { inst_dtflags_default, /* flags */ 2, /* cbid */ "c", /* sel */ "CRT display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 0 /* ix */ }, { @@ -1067,17 +1108,19 @@ inst_disptypesel dtp92_disptypesel[2] = { "", "", 0, + disptech_none, 0 } }; -inst_disptypesel dtp94_disptypesel[4] = { +static inst_disptypesel dtp94_disptypesel[4] = { { inst_dtflags_default, 1, "l", "LCD display", 0, + disptech_lcd, 2 }, { @@ -1086,6 +1129,7 @@ inst_disptypesel dtp94_disptypesel[4] = { "c", /* sel */ "CRT display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 1 /* ix */ }, { @@ -1094,6 +1138,7 @@ inst_disptypesel dtp94_disptypesel[4] = { "g", "Generic display", 0, /* Might be auto refresh detect ? */ + disptech_unknown, 0 }, { @@ -1102,6 +1147,7 @@ inst_disptypesel dtp94_disptypesel[4] = { "", "", 0, + disptech_none, 0 } }; @@ -1144,36 +1190,83 @@ int recreate /* nz to re-check for new ccmx & ccss files */ /* Given a display type entry, setup for that type */ static inst_code set_disp_type(dtp92 *p, inst_disptypesel *dentry) { - p->icx = dentry->ix; - p->cbid = dentry->cbid; + if (dentry->flags & inst_dtflags_ccmx) { + inst_code ev; + if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok) + return ev; + icmCpy3x3(p->ccmat, dentry->mat); + p->dtech = dentry->dtech; + p->cbid = 0; /* Can't be a base type */ + + } else { /* Native */ + p->icx = dentry->ix; + p->dtech = dentry->dtech; + p->cbid = dentry->cbid; + p->ucbid = dentry->cbid; /* is underying base if dentry is base selection */ + + if (p->itype == instDTP92) { + if (p->icx != 0) + return inst_unsupported; + + } else { /* DTP94 */ + static char buf[MAX_MES_SIZE]; + inst_code ev; + + if (p->icx == 0) { /* Generic/Non-specific */ + if ((ev = dtp92_command(p, "0016CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) + return ev; + } else if (p->icx == 1) { /* CRT */ + if ((ev = dtp92_command(p, "0116CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) + return ev; + } else if (p->icx == 2) { /* LCD */ + if ((ev = dtp92_command(p, "0216CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) + return ev; + } else { + return inst_unsupported; + } + } + icmSetUnity3x3(p->ccmat); + } p->refrmode = dentry->refr; - if (p->itype == instDTP92) { - if (p->icx != 0) - return inst_unsupported; + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } - } else { /* DTP94 */ - static char buf[MAX_MES_SIZE]; - inst_code ev; - - if (p->icx == 0) { /* Generic/Non-specific */ - if ((ev = dtp92_command(p, "0016CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) - return ev; - } else if (p->icx == 1) { /* CRT */ - if ((ev = dtp92_command(p, "0116CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) - return ev; - } else if (p->icx == 2) { /* LCD */ - if ((ev = dtp92_command(p, "0216CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) - return ev; - } else { - return inst_unsupported; - } + return inst_ok; +} + +/* Set the display type */ +static inst_code dtp92_set_disptype(inst *pp, int ix) { + dtp92 *p = (dtp92 *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; } - if (dentry->flags & inst_dtflags_ccmx) { - icmCpy3x3(p->ccmat, dentry->mat); - } else { - icmSetUnity3x3(p->ccmat); + if (ix < 0 || ix >= p->ndtlist) + return inst_unsupported; + + dentry = &p->dtlist[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; } return inst_ok; @@ -1205,35 +1298,56 @@ static inst_code set_default_disp_type(dtp92 *p) { return inst_ok; } -/* Set the display type */ -static inst_code dtp92_set_disptype(inst *pp, int ix) { - dtp92 *p = (dtp92 *)pp; +/* Setup the display type to the given base type */ +static inst_code set_base_disp_type(dtp92 *p, int cbid) { inst_code ev; - inst_disptypesel *dentry; - - if (!p->gotcoms) - return inst_no_coms; - if (!p->inited) - return inst_no_init; + int i; + if (cbid == 0) { + a1loge(p->log, 1, "dtp92 set_base_disp_type: can't set base display type of 0\n"); + return inst_wrong_setup; + } if (p->dtlist == NULL) { - if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, - p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, + dtp92_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) return ev; } - if (ix < 0 || ix >= p->ndtlist) - return inst_unsupported; - - dentry = &p->dtlist[ix]; - - if ((ev = set_disp_type(p, dentry)) != inst_ok) { + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (!(p->dtlist[i].flags & inst_dtflags_ccmx) /* Prevent infinite recursion */ + && p->dtlist[i].cbid == cbid) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid); + return inst_wrong_setup; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { return ev; } return inst_ok; } +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code dtp92_get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + dtp92 *p = (dtp92 *)pp; + if (dtech != NULL) + *dtech = p->dtech; + if (refrmode != NULL) + *refrmode = p->refrmode; + if (cbid != NULL) + *cbid = p->cbid; + return inst_ok; +} + /* * set or reset an optional mode * @@ -1259,24 +1373,6 @@ dtp92_get_set_opt(inst *pp, inst_opt_type m, ...) if (!p->inited) return inst_no_init; - /* Get the display type information */ - if (m == inst_opt_get_dtinfo) { - va_list args; - int *refrmode, *cbid; - - va_start(args, m); - refrmode = va_arg(args, int *); - cbid = va_arg(args, int *); - va_end(args); - - if (refrmode != NULL) - *refrmode = p->refrmode; - if (cbid != NULL) - *cbid = p->cbid; - - return inst_ok; - } - return inst_unsupported; } @@ -1297,6 +1393,7 @@ extern dtp92 *new_dtp92(icoms *icom, instType itype) { p->set_mode = dtp92_set_mode; p->get_disptypesel = dtp92_get_disptypesel; p->set_disptype = dtp92_set_disptype; + p->get_disptechi = dtp92_get_disptechi; p->get_set_opt = dtp92_get_set_opt; p->read_sample = dtp92_read_sample; p->read_refrate = dtp92_read_refrate; @@ -1312,6 +1409,7 @@ extern dtp92 *new_dtp92(icoms *icom, instType itype) { icmSetUnity3x3(p->ccmat); /* Set the colorimeter correction matrix to do nothing */ set_base_disptype_list(p); + p->dtech = disptech_unknown; return p; } diff --git a/spectro/dtp92.h b/spectro/dtp92.h index 7add3e0..317fb7a 100644 --- a/spectro/dtp92.h +++ b/spectro/dtp92.h @@ -85,7 +85,9 @@ struct _dtp92 { inst_disptypesel *dtlist; /* Display Type list */ int ndtlist; /* Number of valid dtlist entries */ int icx; /* Internal calibration index, 0 = CRT, 1 = LCD */ - int cbid; /* calibration base ID, 0 if not a base */ + disptech dtech; /* Display technology enum */ + int cbid; /* current calibration base ID, 0 if not a base */ + int ucbid; /* Underlying base ID if being used for matrix, 0 othewise */ int refrmode; /* 0 for constant, 1 for refresh display */ double ccmat[3][3]; /* Colorimeter correction matrix */ diff --git a/spectro/ex1.c b/spectro/ex1.c new file mode 100644 index 0000000..06f5597 --- /dev/null +++ b/spectro/ex1.c @@ -0,0 +1,1299 @@ + +/* + * Argyll Color Correction System + * + * Image Engineering EX1 related functions + * + * Author: Graeme W. Gill + * Date: 4/4/2015 + * + * Copyright 1996 - 2015, 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. + * + * Based on specbos.c + */ + +/* + If you make use of the instrument driver code here, please note + that it is the author(s) of the code who take responsibility + for its operation. Any problems or queries regarding driving + instruments with the Argyll drivers, should be directed to + the Argyll's author(s), and not to any other party. + + If there is some instrument feature or function that you + would like supported here, it is recommended that you + contact Argyll's author(s) first, rather than attempt to + modify the software yourself, if you don't have firm knowledge + of the instrument communicate protocols. There is a chance + that an instrument could be damaged by an incautious command + sequence, and the instrument companies generally cannot and + will not support developers that they have not qualified + and agreed to support. + */ + +/* + + TTBD: + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <time.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 */ +#include "xspect.h" +#include "insttypes.h" +#include "conv.h" +#include "icoms.h" +#include "ex1.h" + +static inst_code ex1_interp_code(inst *pp, int ec); + +#define MAX_MES_SIZE 500 /* Maximum normal message reply size */ +#define MAX_RD_SIZE 8000 /* Maximum reading message reply size */ + +/* Interpret an icoms error into a EX1 error */ +static int icoms2ex1_err(int se) { + if (se != ICOM_OK) { + if (se & ICOM_TO) + return EX1_TIMEOUT; + return EX1_COMS_FAIL; + } + return EX1_OK; +} + +/* Debug - dump a command packet at debug level deb1 */ +static void dump_command(ex1 *p, ORD8 *buf, int len, int debl) { + if (debl < p->log->debug) + return; + + if (len < 64) { + a1logd(p->log, 4, "Command packet too short (%d bytes)\n",len); + return; + } + + if (buf[0] != 0xC1 || buf[1] != 0xC0) { + a1logd(p->log, 4, "Command missing start bytes (0x%02x, 0x%02x)\n",buf[0],buf[1]); + } + + // ~~~~999 + // etc. +} + +/* Do a full command/response exchange with the ex1 */ +/* (This level is not multi-thread safe) */ +/* Return the ex1 error code. */ +static int +ex1_fcommand( +ex1 *p, +char *in, /* In string */ +char *out, /* Out string buffer */ +int bsize, /* Out buffer size */ +double to, /* Timeout in seconds */ +int ntc, /* Number or termination chars */ +int ctype, /* 0 = normal, 1 = *init, 2 = refr reading */ +int nd /* nz to disable debug messages */ +) { + int se; + int bread = 0; + char *cp, *tc = "", *dp; + + // ~~~99 + return EX1_NOT_IMP; + + if (ctype == 0) + tc = "\r\006\025"; /* Return, Ack or Nak */ + else if (ctype == 1) + tc = "\007\025"; /* Bell or Nak */ + else if (ctype == 2) + tc = "\r\025"; /* Return or Nak */ + + se = p->icom->write_read(p->icom, in, 0, out, bsize, &bread, tc, ntc, to); + + /* Because we are sometimes waiting for 3 x \r characters to terminate the read, */ + /* we will instead time out on getting a single NAK (\025), so convert timout */ + /* with bytes to non-timeout, so that we can process the error. */ + if (se == ICOM_TO && bread > 0) + se = ICOM_OK; + + if (se != 0) { + if (!nd) a1logd(p->log, 1, "ex1_fcommand: serial i/o failure on write_read '%s' 0x%x\n",icoms_fix(in),se); + return icoms2ex1_err(se); + } + + /* See if there was an error, and remove any enquire codes */ + for (dp = cp = out; *cp != '\000' && (dp - out) < bsize; cp++) { + if (*cp == '\025') { /* Got a NAK */ + char buf[100]; + + if ((se = p->icom->write_read(p->icom, "*stat:err?\r", 0, buf, 100, NULL, "\r", 1, 1.0)) != 0) { + if (!nd) a1logd(p->log, 1, "ex1_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in)); + return icoms2ex1_err(se);; + } + if (sscanf(buf, "Error Code: %d ",&se) != 1) { + if (!nd) a1logd(p->log, 1, "ex1_fcommand: failed to parse error code '%s'\n",icoms_fix(buf)); + return EX1_DATA_PARSE_ERROR; + } + + if (!nd) a1logd(p->log, 1, "Got ex1 error code %d\n",se); + break; + } + if (*cp == '\005') /* Got an Enquire */ + continue; /* remove it */ + *dp = *cp; + dp++; + } + out[bsize-1] = '\000'; + + if (!nd) a1logd(p->log, 4, "ex1_fcommand: command '%s' returned '%s' bytes %d, err 0x%x\n", + icoms_fix(in), icoms_fix(out),strlen(out), se); + return se; +} + +/* Do a normal command/response echange with the ex1. */ +/* (This level is not multi-thread safe) */ +/* Return the inst code */ +static inst_code +ex1_command( +struct _ex1 *p, +char *in, /* In string */ +char *out, /* Out string buffer */ +int bsize, /* Out buffer size */ +double to) { /* Timout in seconds */ + int rv = ex1_fcommand(p, in, out, bsize, to, 1, 0, 0); + return ex1_interp_code((inst *)p, rv); +} + +/* Read another line of response */ +/* (This level is not multi-thread safe) */ +/* Return the inst code */ +static int +ex1_readresp( +struct _ex1 *p, +char *out, /* Out string buffer */ +int bsize, /* Out buffer size */ +double to /* Timeout in seconds */ +) { + int rv, se; + char *cp, *tc = "\r\006\025"; /* Return, Ack or Nak */ + + // ~~~999 + return inst_unsupported; + + if ((se = p->icom->read(p->icom, out, bsize, NULL, tc, 1, to)) != 0) { + a1logd(p->log, 1, "ex1_readresp: serial i/o failure\n"); + return icoms2ex1_err(se); + } + return inst_ok; +} + +/* Establish communications with a ex1 */ +/* Return EX1_COMS_FAIL on failure to establish communications */ +static inst_code +ex1_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { + ex1 *p = (ex1 *) pp; + char buf[MAX_MES_SIZE]; + baud_rate brt[] = { baud_921600, baud_115200, baud_38400, baud_nc }; + instType itype = pp->itype; + int se; + + inst_code ev = inst_ok; + + a1logd(p->log, 2, "ex1_init_coms: called\n"); + + amutex_lock(p->lock); + + if (p->icom->port_type(p->icom) != icomt_usb) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "ex1_init_coms: wrong communications type for device!\n"); + return inst_coms_fail; + } + + // ~~~99 check it is responding. + + // ~~~99 check the model type + + a1logd(p->log, 2, "ex1_init_coms: init coms has suceeded\n"); + + p->gotcoms = 1; + + amutex_unlock(p->lock); + + return inst_ok; +} + +/* Initialise the EX1 */ +/* return non-zero on an error, with dtp error code */ +static inst_code +ex1_init_inst(inst *pp) { + ex1 *p = (ex1 *)pp; + char mes[100]; + char buf[MAX_MES_SIZE]; + inst_code ev = inst_ok; + + a1logd(p->log, 2, "ex1_init_inst: called\n"); + + if (p->gotcoms == 0) + return inst_internal_error; /* Must establish coms before calling init */ + + amutex_lock(p->lock); + + /* Restore the instrument to it's default settings */ + if ((ev = ex1_command(p, "*conf:default\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) + return ev; + + /* Set calibration type to auto on ambient cap */ + if ((ev = ex1_command(p, "*para:calibn 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + /* Set auto exposure/integration time */ + /* Set calibration type to auto on ambient cap */ + if ((ev = ex1_command(p, "*para:expo 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok + || (ev = ex1_command(p, "*para:adapt 2\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + p->measto = 20.0; /* default */ + + if (p->model == 1211) + p->measto = 5.0; /* Same overall time as i1d3 ?? */ + else if (p->model == 1201) + p->measto = 15.0; + + /* Set maximum integration time to speed up display measurement */ + sprintf(mes, "*conf:maxtin %d\r", (int)(p->measto * 1000.0+0.5)); + if ((ev = ex1_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + /* Set the measurement function to be Radiometric spectrum */ + if ((ev = ex1_command(p, "*conf:func 6\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + /* Set the measurement format to ASCII */ + if ((ev = ex1_command(p, "*conf:form 4\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + if ((ev = ex1_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, "ex1_init_inst: failed to parse start wave\n"); + return ev; + } + a1logd(p->log, 1, " Short wl range %f\n",p->wl_short); + + if ((ev = ex1_command(p, "*para:wavend?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + if (sscanf(buf, "Predefined end wave: %lf ",&p->wl_long) != 1) { + amutex_unlock(p->lock); + a1loge(p->log, 1, "ex1_init_inst: failed to parse end wave\n"); + return ev; + } + if (p->wl_long > 830.0) /* Could go to 1000 with 1211 */ + p->wl_long = 830.0; + + a1logd(p->log, 1, " Long wl range %f\n",p->wl_long); + + 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 = ex1_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 = ex1_command(p, "*conf:aver 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + if (p->log->verb) { + int val; + char *sp; + + if ((ev = ex1_command(p, "*idn?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + if ((sp = strrchr(buf, '\r')) != NULL) + *sp = '\000'; + a1logv(p->log, 1, " Identificaton: %s\n",buf); + + if ((ev = ex1_command(p, "*vers?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + if ((sp = strrchr(buf, '\r')) != NULL) + *sp = '\000'; + a1logv(p->log, 1, " Firmware: %s\n",buf); + + if ((ev = ex1_command(p, "*para:spnum?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + if (sscanf(buf, "spectrometer number: %d ",&val) == 1) { + a1logv(p->log, 1, " Spectrometer number: %d\n",val); + } + + if ((ev = ex1_command(p, "*para:serno?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + if (sscanf(buf, "serial number: %d ",&val) == 1) { + a1logv(p->log, 1, " Serial number: %d\n",val); + } + } + + p->inited = 1; + a1logd(p->log, 2, "ex1_init_inst: instrument inited OK\n"); + amutex_unlock(p->lock); + + return inst_ok; +} + +static inst_code ex1_imp_measure_set_refresh(ex1 *p); +static inst_code ex1_imp_set_refresh(ex1 *p); + +/* Get the ambient diffuser position */ +/* (This is not multithread safe) */ +static inst_code +ex1_get_diffpos( + ex1 *p, /* Object */ + int *pos, /* 0 = display, 1 = ambient */ + int nd /* nz = no debug message */ +) { + char buf[MAX_RD_SIZE]; + int ec; + + /* See if we're in emissive or ambient mode */ + if ((ec = ex1_fcommand(p, "*contr:mhead?\r", buf, MAX_MES_SIZE, 1.0, 1, 0, nd)) != inst_ok) { + return ex1_interp_code((inst *)p, ec); + } + if (sscanf(buf, "mhead: %d ",pos) != 1) { + a1logd(p->log, 2, "ex1_init_coms: unrecognised measuring head string '%s'\n",icoms_fix(buf)); + return inst_protocol_error; + } + return inst_ok; +} + +/* Get the target laser state */ +/* (This is not multithread safe) */ +static inst_code +ex1_get_target_laser( + ex1 *p, /* Object */ + int *laser, /* 0 = off, 1 = on */ + int nd /* nz = no debug message */ +) { + char buf[MAX_RD_SIZE]; + int ec; + int lstate; + + if ((ec = ex1_fcommand(p, "*contr:laser?\r", buf, MAX_MES_SIZE, 1.0, 1, 0, nd)) != inst_ok) { + return ex1_interp_code((inst *)p, ec); + } + if (sscanf(buf, "laser: %d ",&lstate) != 1) { + a1loge(p->log, 2, "ex1_get_target_laser: failed to parse laser state\n"); + return inst_protocol_error; + } + *laser = lstate; + return inst_ok; +} + +/* Read a single sample */ +/* Return the dtp error code */ +static inst_code +ex1_read_sample( +inst *pp, +char *name, /* Strip name (7 chars) */ +ipatch *val, /* Pointer to instrument patch value */ +instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ + ex1 *p = (ex1 *)pp; + char buf[MAX_RD_SIZE]; + int ec; + int user_trig = 0; + int pos = -1; + inst_code rv = inst_protocol_error; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + amutex_lock(p->lock); + + if (p->trig == inst_opt_trig_user) { + amutex_unlock(p->lock); + + if (p->uicallback == NULL) { + a1logd(p->log, 1, "ex1: inst_opt_trig_user but no uicallback function set!\n"); + return inst_unsupported; + } + + for (;;) { + if ((rv = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) { + if (rv == inst_user_abort) { + return rv; /* Abort */ + } + if (rv == inst_user_trig) { + user_trig = 1; + break; /* Trigger */ + } + } + msec_sleep(200); + } + /* Notify of trigger */ + if (p->uicallback) + p->uicallback(p->uic_cntx, inst_triggered); + amutex_lock(p->lock); + + /* Progromatic Trigger */ + } else { + /* Check for abort */ + if (p->uicallback != NULL + && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) { + amutex_unlock(p->lock); + return rv; /* Abort */ + } + } + + /* See if we're in emissive or ambient mode */ + if ((rv = ex1_get_diffpos(p, &pos, 0) ) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + + /* Attempt a refresh display frame rate calibration if needed */ + if (p->refrmode != 0 && p->rrset == 0) { + a1logd(p->log, 1, "ex1: need refresh rate calibration before measure\n"); + if ((rv = ex1_imp_measure_set_refresh(p)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + } + + /* Trigger a measurement */ + if ((ec = ex1_fcommand(p, "*init\r", buf, MAX_MES_SIZE, 5.0 * p->measto + 10.0 , 1, 1, 0)) != EX1_OK) { + amutex_unlock(p->lock); + return ex1_interp_code((inst *)p, ec); + } + + + + if (ec == EX1_OK) { + + if (sscanf(buf, " X: %lf Y: %lf Z: %lf ", + &val->XYZ[0], &val->XYZ[1], &val->XYZ[2]) != 3) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "ex1_read_sample: failed to parse '%s'\n",buf); + return inst_protocol_error; + } + + amutex_unlock(p->lock); + return ex1_interp_code((inst *)p, ec); + } + + /* This may not change anything since instrument may clamp */ + if (clamp) + icmClamp3(val->XYZ, val->XYZ); + val->loc[0] = '\000'; + if (p->mode & inst_mode_ambient) { + val->mtype = inst_mrt_ambient; + } else + val->mtype = inst_mrt_emission; + val->XYZ_v = 1; /* These are absolute XYZ readings */ + val->sp.spec_n = 0; + val->duration = 0.0; + rv = inst_ok; + + + /* spectrum data is returned only if requested */ + if (p->mode & inst_mode_spectral) { + int tries, maxtries = 5; + int i, xsize; + char *cp, *ncp; + + /* (Format 12 doesn't seem to work on the 1211) */ + /* (Format 9 reportedly doesn't work on the 1201) */ + /* The folling works on the 1211 and is reported to work on the 1201 */ + + /* Because the ex1 doesn't use flow control in its */ + /* internal serial communications, it may overrun */ + /* the FT232R buffer, so retry fetching the spectra if */ + /* we get a comm error or parsing error. */ + for (tries = 0;;) { + + /* Fetch the spectral readings */ + ec = ex1_fcommand(p, "*fetch:sprad\r", buf, MAX_RD_SIZE, 2.0, 2+p->nbands+1, 0, 0); + tries++; + if (ec != EX1_OK) { + if (tries > maxtries) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "ex1_fcommand: failed with 0x%x\n",ec); + return ex1_interp_code((inst *)p, ec); + } + continue; /* Retry the fetch */ + } + + val->sp.spec_n = p->nbands; + val->sp.spec_wl_short = p->wl_short; + val->sp.spec_wl_long = p->wl_long; + + /* Spectral data is in W/nm/m^2 */ + val->sp.norm = 1.0; + cp = buf; + for (i = -2; i < val->sp.spec_n; i++) { + if ((ncp = strchr(cp, '\r')) == NULL) { + a1logd(p->log, 1, "ex1_read_sample: failed to parse spectra at %d/%d\n",i+1,val->sp.spec_n); + if (tries > maxtries) { + amutex_unlock(p->lock); + return inst_protocol_error; + } + continue; /* Retry the fetch and parse */ + } + *ncp = '\000'; + if (i >= 0) { + a1logd(p->log, 6, "sample %d/%d got %f from '%s'\n",i+1,val->sp.spec_n,atof(cp),cp); + val->sp.spec[i] = 1000.0 * atof(cp); /* Convert to mW/m^2/nm */ + if (p->mode & inst_mode_ambient) + val->mtype = inst_mrt_ambient; + } + cp = ncp+1; + } + /* We've parsed correctly, so don't retry */ + break; + } + a1logd(p->log, 1, "ex1_read_sample: got total %d samples/%d expected in %d tries\n",i,val->sp.spec_n, tries); + } + amutex_unlock(p->lock); + + + if (user_trig) + return inst_user_trig; + return rv; +} + +/* Set the instrument to match the current refresh settings */ +/* (Not thread safe) */ +static inst_code +ex1_imp_set_refresh(ex1 *p) { + char buf[MAX_MES_SIZE]; + inst_code rv; + + if (p->model == 1201) + return inst_unsupported; + + /* Set synchronised read if we should do so */ + if (p->refrmode != 0 && p->refrvalid) { + char mes[100]; + if ((rv = ex1_command(p, "*conf:cycmod 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + sprintf(mes,"*conf:cyctim %f\r",p->refperiod * 1e6); + if ((rv = ex1_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + a1logd(p->log,5,"ex1_imp_set_refresh set refresh rate to %f Hz\n",1.0/p->refperiod); + } else { + if ((rv = ex1_command(p, "*conf:cycmod 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + a1logd(p->log,5,"ex1_imp_set_refresh set non-refresh mode\n"); + } + return inst_ok; +} + +/* Implementation of read refresh rate */ +/* (Not thread safe) */ +/* Return 0.0 if none detectable */ +static inst_code +ex1_imp_measure_refresh( +ex1 *p, +double *ref_rate +) { + char buf[MAX_MES_SIZE], *cp; + double refperiod = 0.0; + int ec; + inst_code rv; + + if (ref_rate != NULL) + *ref_rate = 0.0; + + if (p->model == 1201) + return inst_unsupported; + + /* Make sure the target laser is off */ + if ((rv = ex1_command(p, "*contr:laser 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + + if ((ec = ex1_fcommand(p, "*contr:cyctim 200 4000\r", buf, MAX_MES_SIZE, 5.0, 1, 2, 0)) != EX1_OK) { + return ex1_interp_code((inst *)p, ec); + } + + if ((cp = strchr(buf, 'c')) == NULL) + cp = buf; + if (sscanf(cp, "cyctim[ms]: %lf ", &refperiod) != 1) { + a1logd(p->log, 1, "ex1_read_refrate rate: failed to parse string '%s'\n",icoms_fix(buf)); + *ref_rate = 0.0; + return inst_misread; + } + + if (refperiod == 0.0) + *ref_rate = 0.0; + else + *ref_rate = 1000.0/refperiod; + + return inst_ok; +} + +/* Read an emissive refresh rate */ +static inst_code +ex1_read_refrate( +inst *pp, +double *ref_rate +) { + ex1 *p = (ex1 *)pp; + char buf[MAX_MES_SIZE]; + double refrate; + inst_code rv; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if (ref_rate != NULL) + *ref_rate = 0.0; + + amutex_lock(p->lock); + if ((rv = ex1_imp_measure_refresh(p, &refrate)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + amutex_unlock(p->lock); + + if (refrate == 0.0) + return inst_misread; + + if (ref_rate != NULL) + *ref_rate = refrate; + + return inst_ok; +} + +/* Measure and then set refperiod, refrate if possible */ +/* (Not thread safe) */ +static inst_code +ex1_imp_measure_set_refresh( + ex1 *p /* Object */ +) { + inst_code rv; + double refrate = 0.0; + int mul; + double pval; + + if ((rv = ex1_imp_measure_refresh(p, &refrate)) != inst_ok) { + return rv; + } + + if (refrate != 0.0) { + p->refrate = refrate; + p->refrvalid = 1; + p->refperiod = 1.0/refrate; + } else { + p->refrate = 0.0; + p->refrvalid = 0; + p->refperiod = 0.0; + } + p->rrset = 1; + + if ((rv = ex1_imp_set_refresh(p)) != inst_ok) { + return rv; + } + + return inst_ok; +} + +/* Measure and then set refperiod, refrate if possible */ +static inst_code +ex1_measure_set_refresh( + ex1 *p /* Object */ +) { + int rv; + + amutex_lock(p->lock); + rv = ex1_imp_measure_set_refresh(p); + amutex_unlock(p->lock); + return rv; +} + +/* Return needed and available inst_cal_type's */ +static inst_code ex1_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) { + ex1 *p = (ex1 *)pp; + inst_cal_type n_cals = inst_calt_none; + inst_cal_type a_cals = inst_calt_none; + + if (p->refrmode != 0) { + if (p->rrset == 0) + n_cals |= inst_calt_ref_freq; + a_cals |= inst_calt_ref_freq; + } + + if (pn_cals != NULL) + *pn_cals = n_cals; + + if (pa_cals != NULL) + *pa_cals = a_cals; + + return inst_ok; +} + +/* Request an instrument calibration. */ +inst_code ex1_calibrate( +inst *pp, +inst_cal_type *calt, /* Calibration type to do/remaining */ +inst_cal_cond *calc, /* Current condition/desired condition */ +char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ +) { + ex1 *p = (ex1 *)pp; + inst_code ev; + inst_cal_type needed, available; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + id[0] = '\000'; + + if ((ev = ex1_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) + return ev; + + /* Translate inst_calt_all/needed into something specific */ + if (*calt == inst_calt_all + || *calt == inst_calt_needed + || *calt == inst_calt_available) { + if (*calt == inst_calt_all) + *calt = (needed & inst_calt_n_dfrble_mask) | inst_calt_ap_flag; + else if (*calt == inst_calt_needed) + *calt = needed & inst_calt_n_dfrble_mask; + else if (*calt == inst_calt_available) + *calt = available & inst_calt_n_dfrble_mask; + + a1logd(p->log,4,"ex1_calibrate: doing calt 0x%x\n",calt); + + if ((*calt & inst_calt_n_dfrble_mask) == 0) /* Nothing todo */ + return inst_ok; + } + + /* See if it's a calibration we understand */ + if (*calt & ~available & inst_calt_all_mask) { + return inst_unsupported; + } + + if ((*calt & inst_calt_ref_freq) && p->refrmode != 0) { + inst_code ev = inst_ok; + + + if (*calc != inst_calc_emis_80pc) { + *calc = inst_calc_emis_80pc; + return inst_cal_setup; + } + + /* Do refresh display rate calibration */ + if ((ev = ex1_measure_set_refresh(p)) != inst_ok) + return ev; + + *calt &= ~inst_calt_ref_freq; + } + return inst_ok; +} + +/* Return the last calibrated refresh rate in Hz. Returns: */ +static inst_code ex1_get_refr_rate(inst *pp, +double *ref_rate +) { + ex1 *p = (ex1 *)pp; + if (p->refrvalid) { + *ref_rate = p->refrate; + return inst_ok; + } else if (p->rrset) { + *ref_rate = 0.0; + return inst_misread; + } + return inst_needs_cal; +} + +/* Set the calibrated refresh rate in Hz. */ +/* Set refresh rate to 0.0 to mark it as invalid */ +/* Rates outside the range 5.0 to 150.0 Hz will return an error */ +static inst_code ex1_set_refr_rate(inst *pp, +double ref_rate +) { + ex1 *p = (ex1 *)pp; + inst_code rv; + + a1logd(p->log,5,"ex1_set_refr_rate %f Hz\n",ref_rate); + + if (ref_rate != 0.0 && (ref_rate < 5.0 || ref_rate > 150.0)) + return inst_bad_parameter; + + p->refrate = ref_rate; + if (ref_rate == 0.0) + p->refrvalid = 0; + else { + p->refperiod = 1.0/ref_rate; + p->refrvalid = 1; + } + p->rrset = 1; + + /* Set the instrument to given refresh rate */ + amutex_lock(p->lock); + if ((rv = ex1_imp_set_refresh(p)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + amutex_unlock(p->lock); + + return inst_ok; +} + + +/* Error codes interpretation */ +static char * +ex1_interp_error(inst *pp, int ec) { +// ex1 *p = (ex1 *)pp; + ec &= inst_imask; + switch (ec) { + case EX1_INTERNAL_ERROR: + return "Internal software error"; + case EX1_TIMEOUT: + return "Communications timeout"; + case EX1_COMS_FAIL: + return "Communications failure"; + case EX1_UNKNOWN_MODEL: + return "Not a JETI ex1"; + case EX1_DATA_PARSE_ERROR: + return "Data from ex1 didn't parse as expected"; + + + case EX1_OK: + return "No device error"; + + case EX1_NOT_IMP: + return "Not implemented"; + + default: + return "Unknown error code"; + } +} + + +/* Convert a machine specific error code into an abstract dtp code */ +static inst_code +ex1_interp_code(inst *pp, int ec) { + + ec &= inst_imask; + switch (ec) { + + case EX1_OK: + return inst_ok; + +// return inst_internal_error | ec; + +// return inst_coms_fail | ec; + +// return inst_unknown_model | ec; + +// return inst_protocol_error | ec; + +// return inst_wrong_config | ec; + +// return inst_bad_parameter | ec; + +// return inst_misread | ec; + +// return inst_hardware_fail | ec; + + case EX1_NOT_IMP: + return inst_unsupported | ec; + } + return inst_other_error | ec; +} + +/* Destroy ourselves */ +static void +ex1_del(inst *pp) { + if (pp != NULL) { + ex1 *p = (ex1 *)pp; + if (p->icom != NULL) + p->icom->del(p->icom); + amutex_del(p->lock); + free(p); + } +} + +/* Return the instrument mode capabilities */ +static void ex1_capabilities(inst *pp, +inst_mode *pcap1, +inst2_capability *pcap2, +inst3_capability *pcap3) { + ex1 *p = (ex1 *)pp; + inst_mode cap1 = 0; + inst2_capability cap2 = 0; + + cap1 |= inst_mode_emis_tele +// | inst_mode_ambient // ?? is it + | inst_mode_colorimeter + | inst_mode_spectral + | inst_mode_emis_refresh_ovd + | inst_mode_emis_norefresh_ovd + ; + + /* can inst2_has_sensmode, but not report it asynchronously */ + cap2 |= inst2_prog_trig + | inst2_user_trig + | inst2_disptype + | inst2_has_target /* Has a laser target */ + ; + +// ~~~~9999 + if (p->model != 1201) { + cap2 |= inst2_emis_refr_meas; + cap2 |= inst2_set_refresh_rate; + cap2 |= inst2_get_refresh_rate; + } + + if (pcap1 != NULL) + *pcap1 = cap1; + if (pcap2 != NULL) + *pcap2 = cap2; + if (pcap3 != NULL) + *pcap3 = inst3_none; +} + +/* Return current or given configuration available measurement modes. */ +/* NOTE that conf_ix values shoudn't be changed, as it is used as a persistent key */ +static inst_code ex1_meas_config( +inst *pp, +inst_mode *mmodes, +inst_cal_cond *cconds, +int *conf_ix +) { + ex1 *p = (ex1 *)pp; + inst_code ev; + inst_mode mval; + int pos; + + if (mmodes != NULL) + *mmodes = inst_mode_none; + if (cconds != NULL) + *cconds = inst_calc_unknown; + + if (conf_ix == NULL + || *conf_ix < 0 + || *conf_ix > 1) { + /* Return current configuration measrement modes */ + amutex_lock(p->lock); + if ((ev = ex1_get_diffpos(p, &pos, 0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + amutex_unlock(p->lock); + } else { + /* Return given configuration measurement modes */ + pos = *conf_ix; + } + + if (pos == 1) { + mval = inst_mode_emis_ambient; + } else if (pos == 0) { + mval |= inst_mode_emis_tele; + } + + /* Add the extra dependent and independent modes */ + mval |= inst_mode_emis_refresh_ovd + | inst_mode_emis_norefresh_ovd + | inst_mode_colorimeter + | inst_mode_spectral; + + if (mmodes != NULL) + *mmodes = mval; + + /* Return configuration index returned */ + if (conf_ix != NULL) + *conf_ix = pos; + + return inst_ok; +} + +/* Check device measurement mode */ +static inst_code ex1_check_mode(inst *pp, inst_mode m) { + inst_mode cap; + + if (!pp->gotcoms) + return inst_no_coms; + if (!pp->inited) + return inst_no_init; + + pp->capabilities(pp, &cap, NULL, NULL); + + /* Simple test */ + if (m & ~cap) + return inst_unsupported; + + /* Only tele emission mode supported */ + if (!IMODETST(m, inst_mode_emis_tele) + && !IMODETST(m, inst_mode_emis_ambient)) { + return inst_unsupported; + } + + return inst_ok; +} + +/* Set device measurement mode */ +static inst_code ex1_set_mode(inst *pp, inst_mode m) { + ex1 *p = (ex1 *)pp; + int refrmode; + inst_code ev; + + if ((ev = ex1_check_mode(pp, m)) != inst_ok) + return ev; + + p->mode = m; + + + if (p->model != 1201) { /* Can't set refresh mode on 1201 */ + + /* Effective refresh mode may change */ + refrmode = p->refrmode; + if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */ + refrmode = 0; + } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) { + refrmode = 1; + } + + if (p->refrmode != refrmode) { + p->rrset = 0; /* This is a hint we may have swapped displays */ + p->refrvalid = 0; + } + p->refrmode = refrmode; + } + + return inst_ok; +} + +static inst_disptypesel ex1_disptypesel[3] = { + { + inst_dtflags_default, + 1, + "nl", + "Non-Refresh display", + 0, + disptech_lcd, + 0 + }, + { + inst_dtflags_none, /* flags */ + 2, /* cbid */ + "rc", /* sel */ + "Refresh display", /* desc */ + 1, /* refr */ + disptech_crt, /* disptype */ + 1 /* ix */ + }, + { + inst_dtflags_end, + 0, + "", + "", + 0, + disptech_none, + 0 + } +}; + +/* Get mode and option details */ +static inst_code ex1_get_disptypesel( +inst *pp, +int *pnsels, /* Return number of display types */ +inst_disptypesel **psels, /* Return the array of display types */ +int allconfig, /* nz to return list for all configs, not just current. */ +int recreate /* nz to re-check for new ccmx & ccss files */ +) { + ex1 *p = (ex1 *)pp; + inst_code rv = inst_ok; + + if ((!allconfig && (p->mode & inst_mode_ambient)) /* If set to Ambient */ + || p->model == 1201) { /* Or 1201, return empty list */ + + if (pnsels != NULL) + *pnsels = 0; + + if (psels != NULL) + *psels = NULL; + + return inst_ok; + } + + + if (pnsels != NULL) + *pnsels = 2; + + if (psels != NULL) + *psels = ex1_disptypesel; + + return inst_ok; +} + +/* Given a display type entry, setup for that type */ +static inst_code set_disp_type(ex1 *p, inst_disptypesel *dentry) { + inst_code rv; + int refrmode; + + refrmode = dentry->refr; + + a1logd(p->log,5,"ex1 set_disp_type refmode %d\n",refrmode); + + if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */ + refrmode = 0; + } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) { + refrmode = 1; + } + + if (p->refrmode != refrmode) + p->rrset = 0; /* This is a hint we may have swapped displays */ + p->refrmode = refrmode; + + amutex_lock(p->lock); + if ((rv = ex1_imp_set_refresh(p)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + amutex_unlock(p->lock); + + return inst_ok; +} + +/* Set the display type - refresh or not */ +static inst_code ex1_set_disptype(inst *pp, int ix) { + ex1 *p = (ex1 *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if (p->model == 1201) /* No display type to select on 1201 */ + return inst_unsupported; + + if (ix < 0 || ix >= 2) + return inst_unsupported; + + a1logd(p->log,5,"ex1 ex1_set_disptype ix %d\n",ix); + dentry = &ex1_disptypesel[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; + } + + return inst_ok; +} + +/* + * set or reset an optional mode + * + * Some options talk to the instrument, and these will + * error if it hasn't been initialised. + */ +static inst_code +ex1_get_set_opt(inst *pp, inst_opt_type m, ...) +{ + ex1 *p = (ex1 *)pp; + char buf[MAX_MES_SIZE]; + inst_code ev = inst_ok; + + a1logd(p->log, 5, "ex1_get_set_opt: opt type 0x%x\n",m); + + /* Record the trigger mode */ + if (m == inst_opt_trig_prog + || m == inst_opt_trig_user) { + p->trig = m; + return inst_ok; + } + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + return inst_unsupported; +} + +/* Constructor */ +extern ex1 *new_ex1(icoms *icom, instType itype) { + ex1 *p; + if ((p = (ex1 *)calloc(sizeof(ex1),1)) == NULL) { + a1loge(icom->log, 1, "new_ex1: malloc failed!\n"); + return NULL; + } + + p->log = new_a1log_d(icom->log); + + p->init_coms = ex1_init_coms; + p->init_inst = ex1_init_inst; + p->capabilities = ex1_capabilities; + p->meas_config = ex1_meas_config; + p->check_mode = ex1_check_mode; + p->set_mode = ex1_set_mode; + p->get_disptypesel = ex1_get_disptypesel; + p->set_disptype = ex1_set_disptype; + p->get_set_opt = ex1_get_set_opt; + p->read_sample = ex1_read_sample; + p->read_refrate = ex1_read_refrate; + p->get_n_a_cals = ex1_get_n_a_cals; + p->calibrate = ex1_calibrate; + p->get_refr_rate = ex1_get_refr_rate; + p->set_refr_rate = ex1_set_refr_rate; + p->interp_error = ex1_interp_error; + p->del = ex1_del; + + p->icom = icom; + p->itype = icom->itype; + + amutex_init(p->lock); + + return p; +} + diff --git a/spectro/ex1.h b/spectro/ex1.h new file mode 100644 index 0000000..456ab75 --- /dev/null +++ b/spectro/ex1.h @@ -0,0 +1,88 @@ +#ifndef EX1_H + +/* + * Argyll Color Correction System + * + * Image Engineering EX1 + * + * Author: Graeme W. Gill + * Date: 4/4/2015 + * + * Copyright 2001 - 2015, 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. + * + * Based on spebos.h + */ + +/* + If you make use of the instrument driver code here, please note + that it is the author(s) of the code who take responsibility + for its operation. Any problems or queries regarding driving + instruments with the Argyll drivers, should be directed to + the Argyll's author(s), and not to any other party. + + If there is some instrument feature or function that you + would like supported here, it is recommended that you + contact Argyll's author(s) first, rather than attempt to + modify the software yourself, if you don't have firm knowledge + of the instrument communicate protocols. There is a chance + that an instrument could be damaged by an incautious command + sequence, and the instrument companies generally cannot and + will not support developers that they have not qualified + and agreed to support. + */ + +#include "inst.h" + +/* Fake Error codes */ +#define EX1_INTERNAL_ERROR 0xff01 /* Internal software error */ +#define EX1_TIMEOUT 0xff02 /* Communication timeout */ +#define EX1_COMS_FAIL 0xff03 /* Communication failure */ +#define EX1_UNKNOWN_MODEL 0xff04 /* Not a JETI ex1 */ +#define EX1_DATA_PARSE_ERROR 0xff05 /* Read data parsing error */ + + +/* Real instrument error code */ +#define EX1_OK 0 +#define EX1_NOT_IMP 1 + +/* Internal software errors */ +#define EX1_INT_THREADFAILED 1000 + +/* EX1 communication object */ +struct _ex1 { + INST_OBJ_BASE + + amutex lock; /* Command lock (not necessary) */ + + int model; /* ex1 model number */ + + inst_mode mode; /* Currently instrument mode */ + + int refrmode; /* nz if in refresh display mode */ + /* (1201 has a refresh mode ?? but can't measure frequency) */ + int rrset; /* Flag, nz if the refresh rate has been determined */ + double refperiod; /* if > 0.0 in refmode, target int time quantization */ + double refrate; /* Measured refresh rate in Hz */ + int refrvalid; /* nz if refrate is valid */ + + inst_opt_type trig; /* Reading trigger mode */ + + double measto; /* Expected measurement timeout value */ + int nbands; /* Number of spectral bands */ + double wl_short; + double wl_long; + + /* Other state */ + + }; typedef struct _ex1 ex1; + +/* Constructor */ +extern ex1 *new_ex1(icoms *icom, instType itype); + + +#define EX1_H +#endif /* EX1_H */ diff --git a/spectro/fakeread.c b/spectro/fakeread.c index ce06e93..892f548 100644 --- a/spectro/fakeread.c +++ b/spectro/fakeread.c @@ -19,7 +19,10 @@ ie. Add cgats i/o to cctiff Add other processing steps such as TV, BT.1886, random to cctiff. - Do we need to deterct & mark display values normalized to Y = 100 ?? + If a display profile has the icSigLuminanceTag, the value + should be used to set the .ti3 LUMINANCE_XYZ_CDM2 value, + and set NORMALIZED_TO_Y_100 to YES. + */ @@ -36,7 +39,9 @@ #include "numlib.h" #include "cgats.h" #include "xicc.h" +#include "bt1886.h" #include "icc.h" +#include "ui.h" void usage(char *diag, ...) { fprintf(stderr,"Fake test chart reader - lookup values in ICC/MPP profile, Version %s\n", @@ -63,16 +68,17 @@ void usage(char *diag, ...) { fprintf(stderr," C Rec2020 Constant Luminance YCbCr UHD (16-235,240)/255 \"TV\" levels\n"); fprintf(stderr," -p separation.%s Use device link separation profile on input\n",ICC_FILE_EXT_ND); fprintf(stderr," -E flag Video decode separation device output. See -e above\n"); + fprintf(stderr," -Z nbits Quantize test values to fit in nbits\n"); fprintf(stderr," -k file.cal Apply calibration (include in .ti3 output)\n"); fprintf(stderr," -i file.cal Include calibration in .ti3 output, but don't apply it\n"); fprintf(stderr," -K file.cal Apply inverse calibration\n"); fprintf(stderr," -r level Add average random deviation of <level>%% to device values (after sep. & cal.)\n"); fprintf(stderr," -0 pow Apply power to device chanel 0-9\n"); - fprintf(stderr," -b output.%s Apply BT.1886-like mapping with effective gamma 2.2\n",ICC_FILE_EXT_ND); - fprintf(stderr," -b g.g:output.%s Apply BT.1886-like mapping with effective gamma g.g\n",ICC_FILE_EXT_ND); - fprintf(stderr," -B output.%s Apply BT.1886 mapping with technical gamma 2.4\n",ICC_FILE_EXT_ND); - fprintf(stderr," -B g.g:output.%s Apply BT.1886 mapping with technical gamma g.g\n",ICC_FILE_EXT_ND); + fprintf(stderr," -B display.%s Use BT.1886 source EOTF with technical gamma 2.4\n",ICC_FILE_EXT_ND); + fprintf(stderr," -b g.g:display.%s Use BT.1886-like source EOTF with effective gamma g.g\n",ICC_FILE_EXT_ND); + fprintf(stderr," -b p.p:g.g:display.%s Use effective gamma g.g source EOTF with p.p prop. output black point offset\n",ICC_FILE_EXT_ND); + fprintf(stderr," -g g.g:display.%s Use effective gamma g.g source EOTF with all output black point offset\n",ICC_FILE_EXT_ND); fprintf(stderr," -I intent r = relative colorimetric, a = absolute (default)\n"); fprintf(stderr," -A L,a,b Scale black point to target Lab value\n"); fprintf(stderr," -l Output Lab rather than XYZ\n"); @@ -80,6 +86,7 @@ void usage(char *diag, ...) { fprintf(stderr," -R level Add average random deviation of <level>%% to output PCS values\n"); fprintf(stderr," -u Make random deviations have uniform distributions rather than normal\n"); fprintf(stderr," -S seed Set random seed\n"); + fprintf(stderr," -U Reverse convert PCS to device, output_r.ti3\n"); fprintf(stderr," profile.[%s|mpp|ti3] ICC, MPP profile or TI3 to use\n",ICC_FILE_EXT_ND); fprintf(stderr," outfile Base name for input[ti1]/output[ti3] file\n"); exit(1); @@ -90,12 +97,15 @@ int main(int argc, char *argv[]) int j; int fa, nfa, mfa; /* current argument we're looking at */ int verb = 0; /* Verbose flag */ + int revlookup = 0; /* Do PCS to device space conversion */ int dosep = 0; /* Use separation before profile */ int bt1886 = 0; /* 1 to apply BT.1886 black point & effective gamma to input */ /* 2 to apply BT.1886 black point & technical gamma to input */ + double outoprop = 0.0; /* Proportion of black output offset, 0.0 .. 1.0. 0.0 == BT.1886 */ double egamma = 2.2; /* effective BT.1886 style gamma to ain for */ double tgamma = 2.4; /* technical BT.1886 style gamma to ain for */ bt1886_info bt; /* BT.1886 adjustment info */ + int islab = 0; /* Input has Lab rather than XYZ */ int dolab = 0; /* Output Lab rather than XYZ */ int gfudge = 0; /* Do grey fudge, 1 = W->RGB, 2 = K->xxxK */ double chpow[10] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; @@ -109,8 +119,8 @@ int main(int argc, char *argv[]) static char calname[MAXNAMEL+1] = { 0 }; /* device calibration */ static char profname[MAXNAMEL+1] = { 0 }; /* ICC or MPP Profile name */ static char inname[MAXNAMEL+1] = { 0 }; /* Input cgats file base name */ - static char outname[MAXNAMEL+1] = { 0 }; /* Output cgats file base name */ - static char oprofname[MAXNAMEL+1] = { 0 }; /* BT.1886 output profile name */ + static char outname[MAXNAMEL+3] = { 0 }; /* Output cgats file base name */ + static char odispname[MAXNAMEL+1] = { 0 }; /* BT.1886 display profile name */ cgats *icg; /* input cgats structure */ cgats *ocg; /* output cgats structure */ int nmask = 0; /* Test chart device colorant mask */ @@ -119,11 +129,12 @@ int main(int argc, char *argv[]) int si; /* Sample id index */ int ti; /* Temp index */ int fi; /* Colorspace index */ - int inti3 = 0; /* Input is a renamed .ti3 file rather than .ti1 */ + int inti3 = 0; /* Input is a .ti3 format rather than .ti1 */ /* TV encode/decode of separation/calibration device link */ int in_tvenc = 0; /* 1 to use RGB Video Level encoding, 2 = Rec601, etc. */ int out_tvenc = 0; /* 1 to use RGB Video Level encoding, 2 = Rec601, etc. */ + int qbits = 0; /* Quantization bits, 0 = not set */ /* ICC separation/calibration device link profile */ icmFile *sep_fp = NULL; /* Color profile file */ @@ -154,12 +165,12 @@ int main(int argc, char *argv[]) double spec_wl_long; /* Last reading wavelength in nm (longest) */ /* TI3 based fake read */ - cgats *ti3 = NULL; /* input cgats structure */ - int ti3_npat = 0; /* Number of patches in reference file */ + cgats *ti3 = NULL; /* input cgats structure */ + int ti3_npat = 0; /* Number of patches in reference file */ int ti3_chix[ICX_MXINKS]; /* Device chanel indexes */ - int ti3_pcsix[3]; /* Device chanel indexes */ + int ti3_pcsix[3]; /* PCS chanel indexes */ int ti3_spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */ - int ti3_isLab = 0; /* Flag indicating PCS for TI3 file */ + int ti3_isLab = 0; /* Flag indicating PCS for TI3 file */ int rv = 0; int inn, outn; /* Number of channels for conversion input, output */ @@ -245,9 +256,21 @@ int main(int argc, char *argv[]) } if (argv[fa][1] == 'e') in_tvenc = enc; - else + else { out_tvenc = enc; + if (qbits == 0) + qbits = 8; + } + fa = nfa; + } + + /* Specify quantization bits */ + else if (argv[fa][1] == 'Z') { fa = nfa; + if (na == NULL) usage("Expected argument to -Z"); + qbits = atoi(na); + if (qbits < 1 || qbits > 32) + usage("Argument to -Q must be between 1 and 32"); } /* Separation */ @@ -259,40 +282,57 @@ int main(int argc, char *argv[]) strncpy(sepname,na,MAXNAMEL); sepname[MAXNAMEL] = '\000'; } - /* BT.1886 modifier */ + /* Gamma curve modifier */ else if (argv[fa][1] == 'b' - || argv[fa][1] == 'B') { + || argv[fa][1] == 'B' + || argv[fa][1] == 'g' + || argv[fa][1] == 'G') { char *cp; - bt1886 = 1; - if (argv[fa][1] == 'B') - bt1886 = 2; - - if (na == NULL) usage("BT.1886 flag (-%c) needs an argument",argv[fa][1]); - fa = nfa; + if (na == NULL) usage("Gamma curve flag (-%c) needs an argument",argv[fa][1]); - if ((cp = strchr(na, ':')) != NULL) { - double gamma = 0.0; + bt1886 = 1; /* Effective */ + if (argv[fa][1] == 'B' || argv[fa][1] == 'G') + bt1886 = 2; /* Technical */ + + if (argv[fa][1] == 'g' || argv[fa][1] == 'G') + outoprop = 1.0; + + /* Grab the filename */ + if ((cp = strrchr(na, ':')) != NULL) { + double gamma, opr; + + strncpy(odispname,cp+1,MAXNAMEL); odispname[MAXNAMEL] = '\000'; *cp = '\000'; - cp++; - gamma = atof(na); - if (gamma < 0.01 || gamma > 10.0) usage("BT.1886 gamma is out of range"); - if (bt1886 == 1) - egamma = gamma; - else - tgamma = gamma; - } else { - cp = na; + + if (sscanf(na,"%lf:%lf",&opr, &gamma) == 2) { + outoprop = opr; + if (bt1886 == 1) + egamma = gamma; + else + tgamma = gamma; + + } else if (sscanf(na,"%lf",&gamma) == 1) { + if (bt1886 == 1) + egamma = gamma; + else + tgamma = gamma; + } else { + usage("Gamma curve (-%c) arguments not recognised",argv[fa][1]); + } + + } else { /* No outoprop or gamma, just filanem */ + strncpy(odispname,na,MAXNAMEL); odispname[MAXNAMEL] = '\000'; } - strncpy(oprofname,cp,MAXNAMEL); oprofname[MAXNAMEL] = '\000'; + fa = nfa; /* Used argument */ } /* Lab */ - else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') + else if (argv[fa][1] == 'l') dolab = 1; /* Uniform distrivuted errors */ - else if (argv[fa][1] == 'u' || argv[fa][1] == 'U') + else if (argv[fa][1] == 'u') unidist = 1; /* Random seed value */ @@ -370,6 +410,11 @@ int main(int argc, char *argv[]) } } + /* Reverse lookup */ + else if (argv[fa][1] == 'U') { + revlookup = 1; + } + else usage("Unrecognised flag"); } @@ -382,11 +427,80 @@ int main(int argc, char *argv[]) if (fa >= argc || argv[fa][0] == '-') usage("Missing basename argument"); strncpy(inname,argv[fa],MAXNAMEL-4); inname[MAXNAMEL-4] = '\000'; - strcat(inname,".ti1"); + if (revlookup) + strcat(inname,".ti3"); + else + strcat(inname,".ti1"); strncpy(outname,argv[fa],MAXNAMEL-4); outname[MAXNAMEL-4] = '\000'; + if (revlookup) + strcat(outname,"_r"); strcat(outname,".ti3"); rand32(seed); /* Init seed */ + + if (revlookup && ( + dosep + || calname[0] != '\000' + || dospec + || tbp[0] >= 0.0 + || bt1886)) + error("Can't do separation, apply calibration, do spectral, black point scale, bt.1886, with reverse lookup"); + + /* Deal with input CGATS files */ + icg = new_cgats(); /* Create a CGATS structure */ + icg->add_other(icg, "CTI1"); /* our special input type is Calibration Target Information 1 */ + icg->add_other(icg, "CTI3"); /* also accept renamed .ti3 file */ + + if (icg->read_name(icg, inname)) + error("CGATS file read error : %s",icg->err); + + 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 CTI1 format file"); + if (icg->t[0].oi == 1) + inti3 = 1; /* It's a renamed .ti3 file */ + if (icg->ntables != 1 && icg->ntables != 2 && icg->ntables != 3) + error ("Input file doesn't contain one, two or three tables"); + + if ((npat = icg->t[0].nsets) <= 0) + error ("No sets of data"); + + /* Figure out the color space of the .ti1 */ + if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0) + error ("Input file doesn't contain keyword COLOR_REP"); + + if (inti3) { + char *rbuf, *outc; + + if ((rbuf = strdup(icg->t[0].kdata[fi])) == NULL) + error("Malloc failed"); + + /* Split COLOR_REP into device and PCS space */ + if ((outc = strchr(rbuf, '_')) == NULL) + error("Input file '%s' COLOR_REP '%s' invalid", inname, icg->t[0].kdata[fi]); + *outc++ = '\000'; + + if ((nmask = icx_char2inkmask(rbuf)) == 0) { + error ("Input file '%s' keyword COLOR_REP has unknown device value '%s'",inname,rbuf); + } + + if (strcmp(outc,"LAB") == 0) + islab = 1; + else if (strcmp(outc,"XYZ") == 0) + islab = 0; + else + error("Input .ti3 file PCS is neither LAB nor XYZ"); + dolab = islab; + + free(rbuf); + } else { + if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) == 0) + error ("Input file '%s' keyword COLOR_REP has unknown value '%s'",inname, icg->t[0].kdata[fi]); + } + + if (revlookup && !inti3) { + error("reverse lookup expects .ti3 file as input"); + } + /* Deal with separation */ if (dosep) { if ((sep_fp = new_icmFileStd_name(sepname,"r")) == NULL) @@ -439,7 +553,7 @@ int main(int argc, char *argv[]) if ((icc_icco = new_icc()) == NULL) error ("Creation of ICC object failed"); - /* Deal with ICC profile */ + /* Read ICC profile */ if ((rv = icc_icco->read(icc_icco,icc_fp,0)) == 0) { /* Embed any calibration in the output if it's present */ @@ -451,12 +565,22 @@ int main(int argc, char *argv[]) printf("Embedding calibration curves from ICC profile in output\n"); } - /* Get a Device to PCS conversion object */ - if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, intent, - dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) { - if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, icmDefaultIntent, - dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) - error ("%d, %s",icc_icco->errc, icc_icco->err); + if (revlookup) { + /* Get a PCS to Device conversion object */ + if ((icc_luo = icc_icco->get_luobj(icc_icco, icmBwd, intent, + islab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) { + if ((icc_luo = icc_icco->get_luobj(icc_icco, icmBwd, icmDefaultIntent, + islab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) + error ("%d, %s",icc_icco->errc, icc_icco->err); + } + } else { + /* Get a Device to PCS conversion object */ + if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, intent, + dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) { + if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, icmDefaultIntent, + dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) + error ("%d, %s",icc_icco->errc, icc_icco->err); + } } /* Get details of conversion */ @@ -504,6 +628,9 @@ int main(int argc, char *argv[]) /* If we don't have an ICC lookup object, look for an MPP */ if (icc_luo == NULL) { + if (revlookup) + error("Reverse lookup using MPP not supported"); + if ((mlu = new_mpp()) == NULL) error ("Creation of MPP object failed"); @@ -537,6 +664,9 @@ int main(int argc, char *argv[]) char *ti3_bident; int ti3_nchan; + if (revlookup) + error("Reverse lookup using TI3 as conversion not supported"); + ti3 = new_cgats(); /* Create a CGATS structure */ ti3->add_other(ti3, "CTI3");/* our special input type is Calibration Target Information 3 */ @@ -666,14 +796,14 @@ int main(int argc, char *argv[]) lu = (icmLuMatrix *)icc_luo; /* Safe to coerce - we have checked it's matrix. */ /* Open up output profile used for BT.1886 black point */ - if ((ofp = new_icmFileStd_name(oprofname,"r")) == NULL) - error ("Can't open file '%s'",oprofname); + if ((ofp = new_icmFileStd_name(odispname,"r")) == NULL) + error ("Can't open file '%s'",odispname); if ((oicco = new_icc()) == NULL) error ("Creation of ICC object failed"); if (oicco->read(oicco,ofp,0)) - error ("Unable to read '%s'",oprofname); + error ("Unable to read '%s'",odispname); if ((oluo = oicco->get_luobj(oicco, icmFwd, icRelativeColorimetric, icSigXYZData, icmLuOrdNorm)) == NULL) @@ -690,30 +820,35 @@ int main(int argc, char *argv[]) oicco->del(oicco); ofp->del(ofp); + bt1886_setup(&bt, &lu->pcswht, bp, outoprop, + bt1886 == 1 ? egamma : tgamma, bt1886 == 1 ? 1 : 0); + + if (verb) + printf("Gamma Curve: Using ouput black offset proportion %f\n",outoprop); + if (bt1886 == 1) { /* Using effective gamma */ - tgamma = xicc_tech_gamma(egamma, bp[1]); if (verb) - printf("BT.1886: Technical gamma %f used to achieve effective gamma %f\n", - tgamma, egamma); + printf("Gamma Curve: Technical gamma %f used to achieve effective gamma %f\n", + bt.gamma, egamma); } else { if (verb) - printf("BT.1886: Using technical gamma %f\n",tgamma); + printf("Gamma Curve: Using technical gamma %f\n",bt.gamma); } - bt1886_setup(&bt, bp, tgamma); if (verb) { - printf("BT.1886: target out black rel XYZ = %f %f %f, Lab %f %f %f\n", + printf("Gamma Curve: target out black rel XYZ = %f %f %f, Lab %f %f %f\n", bp[0],bp[1],bp[2], bt.outL, bt.tab[0], bt.tab[1]); - printf("BT.1886: Y input offset = %f\n", bt.ingo); - printf("BT.1886: Y output scale = %f\n", bt.outsc); + printf("Gamma Curve: Y input offset = %f\n", bt.ingo); + printf("Gamma Curve: Y output scale = %f\n", bt.outsc); + printf("Gamma Curve: Y output offset = %f\n", bt.outo); } /* Check black point now produced by input profile with bt.1886 adjustment */ rgb[0] = rgb[1] = rgb[2] = 0.0; - lu->fwd_curve(lu, rgb, rgb); + bt1886_fwd_curve(&bt, rgb, rgb); lu->fwd_matrix(lu, rgb, rgb); - bt1886_apply(&bt, lu, rgb, rgb); + bt1886_wp_adjust(&bt, rgb, rgb); if (verb) printf("BT.1886: check input black point rel. XYZ %f %f %f\n", rgb[0],rgb[1],rgb[2]); if (verb > 1) { int i, no = 21; @@ -725,18 +860,12 @@ int main(int argc, char *argv[]) double vi[3], vo[3], Lab[3]; double loglog = 0.0; - if (v <= 0.081) - vv = v/4.5; - else - vv = pow((0.099 + v)/1.099, 1.0/0.45); - - vi[0] = vv * 0.9642; /* To D50 XYZ */ - vi[1] = vv * 1.0000; - vi[2] = vv * 0.8249; - - bt1886_apply(&bt, lu, vo, vi); /* BT.1886 mapping */ + vi[0] = vi[1] = vi[2] = v; + bt1886_fwd_curve(&bt, vo, vi); + lu->fwd_matrix(lu, vo, vo); + bt1886_wp_adjust(&bt, vo, vo); - icmXYZ2Lab(&icmD50, Lab, vo); + icmXYZ2Lab(&lu->pcswht, Lab, vo); if (v > 1e-9 && vo[1] > 1e-9 && fabs(v - 1.0) > 1e-9) loglog = log(vo[1])/log(v); @@ -764,49 +893,6 @@ int main(int argc, char *argv[]) fflush(stdout); } - /* Deal with input CGATS files */ - icg = new_cgats(); /* Create a CGATS structure */ - icg->add_other(icg, "CTI1"); /* our special input type is Calibration Target Information 1 */ - icg->add_other(icg, "CTI3"); /* also accept renamed .ti3 file */ - - if (icg->read_name(icg, inname)) - error("CGATS file read error : %s",icg->err); - - 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 CTI1 format file"); - if (icg->t[0].oi == 1) - inti3 = 1; /* It's a renamed .ti3 file */ - if (icg->ntables != 1 && icg->ntables != 2 && icg->ntables != 3) - error ("Input file doesn't contain one, two or three tables"); - - if ((npat = icg->t[0].nsets) <= 0) - error ("No sets of data"); - - /* Figure out the color space of the .ti1 */ - if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0) - error ("Input file doesn't contain keyword COLOR_REP"); - - if (inti3) { - char *rbuf, *outc; - - if ((rbuf = strdup(icg->t[0].kdata[fi])) == NULL) - error("Malloc failed"); - - /* Split COLOR_REP into device and PCS space */ - if ((outc = strchr(rbuf, '_')) == NULL) - error("Input file '%s' COLOR_REP '%s' invalid", inname, icg->t[0].kdata[fi]); - *outc++ = '\000'; - - if ((nmask = icx_char2inkmask(rbuf)) == 0) { - error ("Input file '%s' keyword COLOR_REP has unknown device value '%s'",inname,rbuf); - } - - free(rbuf); - } else { - if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) == 0) - error ("Input file '%s' keyword COLOR_REP has unknown value '%s'",inname, icg->t[0].kdata[fi]); - } - /* Setup output cgats file */ ocg = new_cgats(); /* Create a CGATS structure */ ocg->add_other(ocg, "CTI3"); /* our special type is Calibration Target Information 3 */ @@ -873,11 +959,12 @@ int main(int argc, char *argv[]) { int i, j, ii; int chix[ICX_MXINKS]; /* Device chanel indexes */ + int pcsix[3]; /* PCS chanel indexes (for revlookup) */ char *ident, *bident; int nsetel = 0; cgats_set_elem *setel; /* Array of set value elements */ - nchan = icx_noofinks(nmask); + nchan = icx_noofinks(nmask); /* No. device channels */ ident = icx_inkmask2char(nmask, 1); bident = icx_inkmask2char(nmask, 0); @@ -921,10 +1008,19 @@ int main(int argc, char *argv[]) gfudge = 1; else if (nmask == ICX_K && ins == icSigCmykData) gfudge = 2; /* Should allow for other colorant combo's that include black */ - else if (icx_colorant_comb_match_icc(nmask, ins) == 0) { - error("ICC device space '%s' dosen't match TI1 '%s'", - icm2str(icmColorSpaceSignature, ins), - ident); // Should free(). + else { + if (!revlookup) { + if (icx_colorant_comb_match_icc(nmask, ins) == 0) + error("ICC device space '%s' dosen't match TI1 '%s'", + icm2str(icmColorSpaceSignature, ins), + ident); // Should free(). + } else { + if (icx_colorant_comb_match_icc(nmask, outs) == 0) + error("ICC device space '%s' dosen't match TI1 '%s'", + icm2str(icmColorSpaceSignature, ins), + ident); // Should free(). + + } } } else if (mlu != NULL) { /* Check if mpp is compatible with .ti1 */ @@ -953,6 +1049,7 @@ int main(int argc, char *argv[]) nsetel += nchan; /* For device values */ nsetel += 3; /* For XYZ/Lab */ + /* Locate device fields in source file, and add to output file */ for (j = 0; j < nchan; j++) { int imask; char fname[100]; @@ -970,6 +1067,19 @@ int main(int argc, char *argv[]) chix[j] = ii; } + /* Find PCS fields if doing reverse lookup */ + if (revlookup) { + for (j = 0; j < 3; j++) { + int ii; + + if ((ii = icg->find_field(icg, 0, islab ? labfname[j] : xyzfname[j])) < 0) + error ("Input file doesn't contain field %s",islab ? labfname[j] : xyzfname[j]); + if (icg->t[0].ftype[ii] != r_t) + error ("Field %s is wrong type",islab ? labfname[j] : xyzfname[j]); + pcsix[j] = ii; + } + } + /* Add PCS fields */ for (j = 0; j < 3; j++) { ocg->add_field(ocg, 0, dolab ? labfname[j] : xyzfname[j], r_t); @@ -1009,261 +1119,312 @@ int main(int argc, char *argv[]) if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * nsetel)) == NULL) error("Malloc failed!"); - /* Read all the test patches in, convert them, */ + /* Read all the device test patches in, convert them to PCS, */ /* and write them out. */ - for (i = 0; i < npat; i++) { - int k = 0; - char *id; - double odev[ICX_MXINKS], dev[ICX_MXINKS], sep[ICX_MXINKS], PCS[3]; - xspect out; - - id = ((char *)icg->t[0].fdata[i][si]); - for (j = 0; j < nchan; j++) { - double dv = *((double *)icg->t[0].fdata[i][chix[j]]) / 100.0; - odev[j] = dev[j] = sep[j] = dv; - } - - if (gfudge) { - int nch; - - if (dosep) /* Figure number of channels into conversion */ - nch = sep_inn; - else - nch = inn; - - if (gfudge == 1) { /* Convert W -> RGB */ - double wval = dev[0]; - for (j = 0; j < nch; j++) - dev[j] = sep[j] = wval; - - } else { /* Convert K->xxxK */ - int kch; - int inmask; - double kval = dev[0]; - + if (!revlookup) { + for (i = 0; i < npat; i++) { + int k = 0; + char *id; + double odev[ICX_MXINKS], dev[ICX_MXINKS], sep[ICX_MXINKS], PCS[3]; + xspect out; + double qscale = (1 << qbits) - 1.0; + + for (j = 0; j < nchan; j++) { + double dv = *((double *)icg->t[0].fdata[i][chix[j]]) / 100.0; + if (qbits > 0) { + double vr; + dv *= qscale; + vr = floor(dv + 0.5); + if ((vr - dv) == 0.5 && (((int)vr) & 1) != 0) /* Round to even */ + vr -= 1.0; + dv = vr/qscale; + } + odev[j] = dev[j] = sep[j] = dv; + } + + if (gfudge) { + int nch; + if (dosep) /* Figure number of channels into conversion */ - inmask = sep_nmask; + nch = sep_inn; else - inmask = cnv_nmask; - - if (inmask == 0) - error("Input colorspace ambiguous - can't determine if it has black"); - - if ((kch = icx_ink2index(inmask, ICX_BLACK)) == -1) - error("Can't find black colorant for K fudge"); - for (j = 0; j < nch; j++) { - if (j == kch) - dev[j] = sep[j] = kval; + nch = inn; + + if (gfudge == 1) { /* Convert W -> RGB */ + double wval = dev[0]; + for (j = 0; j < nch; j++) + dev[j] = sep[j] = wval; + + } else { /* Convert K->xxxK */ + int kch; + int inmask; + double kval = dev[0]; + + if (dosep) /* Figure number of channels into conversion */ + inmask = sep_nmask; else - dev[j] = sep[j] = 0.0; - } - } - } - - if (dosep) { - if (in_tvenc != 0) { - if (in_tvenc == 1) { /* Video 16-235 range */ - icmRGB_2_VidRGB(dev, dev); - } else if (in_tvenc == 2) { /* Rec601 YCbCr */ - icmRec601_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); - } else if (in_tvenc == 3) { /* Rec709 YCbCr */ - icmRec709_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); - } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ - icmRec709_50_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); - } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ - icmRec2020_NCL_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); - } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ - icmRec2020_CL_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); + inmask = cnv_nmask; + + if (inmask == 0) + error("Input colorspace ambiguous - can't determine if it has black"); + + if ((kch = icx_ink2index(inmask, ICX_BLACK)) == -1) + error("Can't find black colorant for K fudge"); + for (j = 0; j < nch; j++) { + if (j == kch) + dev[j] = sep[j] = kval; + else + dev[j] = sep[j] = 0.0; + } } } - - if (sep_luo->lookup(sep_luo, sep, dev) > 1) - error ("%d, %s",icc_icco->errc,icc_icco->err); - - if (out_tvenc != 0) { - if (out_tvenc == 1) { /* Video 16-235 range */ - icmVidRGB_2_RGB(sep, sep); - } else if (out_tvenc == 2) { /* Rec601 YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec601_YPbPr_2_RGBd(sep, sep); - } else if (out_tvenc == 3) { /* Rec709 1150/60/2:1 YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec709_YPbPr_2_RGBd(sep, sep); - } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec709_50_YPbPr_2_RGBd(sep, sep); - } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec2020_NCL_YPbPr_2_RGBd(sep, sep); - } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec2020_CL_YPbPr_2_RGBd(sep, sep); + + if (dosep) { + if (in_tvenc != 0) { + if (in_tvenc == 1) { /* Video 16-235 range */ + icmRGB_2_VidRGB(dev, dev); + } else if (in_tvenc == 2) { /* Rec601 YCbCr */ + icmRec601_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (in_tvenc == 3) { /* Rec709 YCbCr */ + icmRec709_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ + icmRec709_50_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRec2020_NCL_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRec2020_CL_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } } - } - } - - /* Do calibration */ - if (applycal && cal != NULL) { - if (applycal == 1) - cal->interp(cal, sep, sep); - else if (applycal == 2) { - if (cal->inv_interp(cal, sep, sep)) - warning("Inverse calibration of patch %d failed",i+1); - } - } - - /* Add randomness and non-linearity to device values. */ - /* rdlevel = avg. dev. */ - /* Note dev/sep is 0-1.0 at this stage */ - for (j = 0; j < inn; j++) { - double dv = sep[j]; - if (rdlevel > 0.0) { - double rr; - if (unidist) - rr = d_rand(-2.0 * rdlevel, 2.0 * rdlevel); - else - rr = 1.2533 * rdlevel * norm_rand(); - dv += rr; - if (dv < 0.0) - dv = 0.0; - else if (dv > 1.0) - dv = 1.0; - } - if (j < 10 && chpow[j] != 1.0) { - dv = pow(dv, chpow[j]); - } - sep[j] = dv; - } - - /* Do color conversion */ - if (icc_luo != NULL) { - if (bt1886) { - icmLuMatrix *lu = (icmLuMatrix *)icc_luo; /* Safe to coerce */ -//printf("Matrix dev in: %s\n",icmPdv(inn, sep)); - lu->fwd_curve(lu, sep, sep); -//printf("Matrix after in curve: %s\n",icmPdv(inn, sep)); - lu->fwd_matrix(lu, PCS, sep); -//printf("Matrix after matrix XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); - bt1886_apply(&bt, lu, PCS, PCS); -//printf("Matrix after bt1186 XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); - lu->fwd_abs(lu, PCS, PCS); -//printf("Matrix after abs %s\n",icmPdv(3, PCS)); - } else { - if (icc_luo->lookup(icc_luo, PCS, sep) > 1) + + if (sep_luo->lookup(sep_luo, sep, dev) > 1) error ("%d, %s",icc_icco->errc,icc_icco->err); - } - - if (tbp[0] >= 0) { /* Doing black point scaling */ - - for (j = 0; j < 3; j++) - PCS[j] -= wp[j]; - icmMulBy3x3(PCS, bpt, PCS); - for (j = 0; j < 3; j++) - PCS[j] += wp[j]; + + if (out_tvenc != 0) { + if (out_tvenc == 1) { /* Video 16-235 range */ + icmVidRGB_2_RGB(sep, sep); + } else if (out_tvenc == 2) { /* Rec601 YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec601_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 3) { /* Rec709 1150/60/2:1 YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec709_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec709_50_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec2020_NCL_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec2020_CL_YPbPr_2_RGBd(sep, sep); + } + } } - } else if (mlu != NULL) { - mlu->lookup(mlu, PCS, sep); - if (dospec && spec_n > 0) { - mlu->lookup_spec(mlu, &out, sep); + /* Do calibration */ + if (applycal && cal != NULL) { + if (applycal == 1) + cal->interp(cal, sep, sep); + else if (applycal == 2) { + if (cal->inv_interp(cal, sep, sep)) + warning("Inverse calibration of patch %d failed",i+1); + } } - } else if (ti3 != NULL) { - int m; - double bdif = 1e6; - int bix = -1; - - /* Search for the closest device values in TI3 file */ - for (m = 0; m < ti3_npat; m++) { - double dif; - - for (dif = 0.0, j = 0; j < nchan; j++) { - double xx; - - xx = (*((double *)ti3->t[0].fdata[m][ti3_chix[j]]) / 100.0) - sep[j]; - dif += xx * xx; + + /* Add randomness and non-linearity to device values. */ + /* rdlevel = avg. dev. */ + /* Note dev/sep is 0-1.0 at this stage */ + for (j = 0; j < inn; j++) { + double dv = sep[j]; + if (rdlevel > 0.0) { + double rr; + if (unidist) + rr = d_rand(-2.0 * rdlevel, 2.0 * rdlevel); + else + rr = 1.2533 * rdlevel * norm_rand(); + dv += rr; + if (dv < 0.0) + dv = 0.0; + else if (dv > 1.0) + dv = 1.0; } - if (dif < bdif) { - bdif = dif; - bix = m; + if (j < 10 && chpow[j] != 1.0) { + dv = pow(dv, chpow[j]); } + sep[j] = dv; } - /* Copy best value over */ - if (!dosep) /* Doesn't make sense for separation ??? */ - for (j = 0; j < nchan; j++) { - dev[j] = *((double *)ti3->t[0].fdata[bix][ti3_chix[j]]) / 100.0; + + /* Do color conversion */ + if (icc_luo != NULL) { + if (bt1886) { + icmLuMatrix *lu = (icmLuMatrix *)icc_luo; /* Safe to coerce */ + //printf("Matrix dev in: %s\n",icmPdv(inn, sep)); + bt1886_fwd_curve(&bt, sep, sep); + //printf("Matrix after bt1886 curve: %s\n",icmPdv(inn, sep)); + lu->fwd_matrix(lu, PCS, sep); + //printf("Matrix after matrix XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); + bt1886_wp_adjust(&bt, PCS, PCS); + //printf("Matrix after bt1186 wp adj. XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); + lu->fwd_abs(lu, PCS, PCS); + //printf("Matrix after abs %s\n",icmPdv(3, PCS)); + } else { + if (icc_luo->lookup(icc_luo, PCS, sep) > 1) + error ("%d, %s",icc_icco->errc,icc_icco->err); + } + + if (tbp[0] >= 0) { /* Doing black point scaling */ + + for (j = 0; j < 3; j++) + PCS[j] -= wp[j]; + icmMulBy3x3(PCS, bpt, PCS); + for (j = 0; j < 3; j++) + PCS[j] += wp[j]; + } + + } else if (mlu != NULL) { + mlu->lookup(mlu, PCS, sep); + if (dospec && spec_n > 0) { + mlu->lookup_spec(mlu, &out, sep); + } + } else if (ti3 != NULL) { + int m; + double bdif = 1e6; + int bix = -1; + + /* Search for the closest device values in TI3 file */ + for (m = 0; m < ti3_npat; m++) { + double dif; + + for (dif = 0.0, j = 0; j < nchan; j++) { + double xx; + + xx = (*((double *)ti3->t[0].fdata[m][ti3_chix[j]]) / 100.0) - sep[j]; + dif += xx * xx; + } + if (dif < bdif) { + bdif = dif; + bix = m; + } + } + /* Copy best value over */ + if (!dosep) /* Doesn't make sense for separation ??? */ + for (j = 0; j < nchan; j++) { + dev[j] = *((double *)ti3->t[0].fdata[bix][ti3_chix[j]]) / 100.0; + } + for (j = 0; j < 3; j++) { + PCS[j] = *((double *)ti3->t[0].fdata[bix][ti3_pcsix[j]]); + } + if (ti3_isLab && !dolab) { /* Convert Lab to XYZ */ + icmLab2XYZ(&icmD50, PCS, PCS); + } else if (!ti3_isLab && dolab) { /* Convert XYZ to Lab */ + icmXYZ2Lab(&icmD50, PCS, PCS); + } else if (!ti3_isLab) { /* Convert XYZ100 to XYZ1 */ + PCS[0] /= 100.0; + PCS[1] /= 100.0; + PCS[2] /= 100.0; + } + if (dospec && spec_n > 0) { + for (j = 0; j < spec_n; j++) { + out.spec[j] = *((double *)ti3->t[0].fdata[bix][ti3_spi[j]]); + } } - for (j = 0; j < 3; j++) { - PCS[j] = *((double *)ti3->t[0].fdata[bix][ti3_pcsix[j]]); } - if (ti3_isLab && !dolab) { /* Convert Lab to XYZ */ - icmLab2XYZ(&icmD50, PCS, PCS); - } else if (!ti3_isLab && dolab) { /* Convert XYZ to Lab */ - icmXYZ2Lab(&icmD50, PCS, PCS); - } else if (!ti3_isLab) { /* Convert XYZ100 to XYZ1 */ - PCS[0] /= 100.0; - PCS[1] /= 100.0; - PCS[2] /= 100.0; + + id = ((char *)icg->t[0].fdata[i][si]); + setel[k++].c = id; + + for (j = 0; j < nchan; j++) + setel[k++].d = 100.0 * odev[j]; + + if (dolab == 0) { + PCS[0] *= 100.0; + PCS[1] *= 100.0; + PCS[2] *= 100.0; } + + /* Add randomness. rplevel is avg. dev. */ + /* Note PCS is 0..100 XYZ or Lab at this point */ + /* Adding uniform error to XYZ produces unreasonable */ + /* bumpiness near black, so scale it by Y */ + if (rplevel > 0.0) { + double opcs[3], ll = 1.0; + if (!dolab) + ll = 0.01 * PCS[1]; + for (j = 0; j < 3; j++) { + double dv = PCS[j]; + double rr; + opcs[j] = dv; + if (unidist) + rr = 100.0 * d_rand(-2.0 * rplevel, 2.0 * rplevel); + else + rr = 100.0 * 1.2533 * rplevel * norm_rand(); + dv += ll * rr; + + /* Don't let L*, X, Y or Z go negative */ + if ((!dolab || j == 0) && dv < 0.0) + dv = 0.0; + PCS[j] = dv; + } + //printf("~1 pcs %f %f %f -> %f %f %f\n", opcs[0], opcs[1], opcs[2], PCS[0], PCS[1], PCS[2]); + } + + setel[k++].d = PCS[0]; + setel[k++].d = PCS[1]; + setel[k++].d = PCS[2]; + if (dospec && spec_n > 0) { for (j = 0; j < spec_n; j++) { - out.spec[j] = *((double *)ti3->t[0].fdata[bix][ti3_spi[j]]); + setel[k++].d = 100.0 * out.spec[j]; } } + + ocg->add_setarr(ocg, 0, setel); } - setel[k++].c = id; - - for (j = 0; j < nchan; j++) - setel[k++].d = 100.0 * odev[j]; - - if (dolab == 0) { - PCS[0] *= 100.0; - PCS[1] *= 100.0; - PCS[2] *= 100.0; - } + /* Do reverse (PCS -> device) lookup */ + } else { - /* Add randomness. rplevel is avg. dev. */ - /* Note PCS is 0..100 XYZ or Lab at this point */ - /* Adding uniform error to XYZ produces unreasonable */ - /* bumpiness near black, so scale it by Y */ - if (rplevel > 0.0) { - double opcs[3], ll = 1.0; - if (!dolab) - ll = 0.01 * PCS[1]; + /* Read all the PCS test patches in, convert them to device values, */ + /* and write them out. */ + for (i = 0; i < npat; i++) { + int k = 0; + char *id; + double pcs[3]; + double dev[ICX_MXINKS]; + + /* read PCS values */ for (j = 0; j < 3; j++) { - double dv = PCS[j]; - double rr; - opcs[j] = dv; - if (unidist) - rr = 100.0 * d_rand(-2.0 * rplevel, 2.0 * rplevel); - else - rr = 100.0 * 1.2533 * rplevel * norm_rand(); - dv += ll * rr; - - /* Don't let L*, X, Y or Z go negative */ - if ((!dolab || j == 0) && dv < 0.0) - dv = 0.0; - PCS[j] = dv; + double pv = *((double *)icg->t[0].fdata[i][pcsix[j]]); + if (!islab) + pv /= 100.0; + pcs[j] = pv; } -//printf("~1 pcs %f %f %f -> %f %f %f\n", opcs[0], opcs[1], opcs[2], PCS[0], PCS[1], PCS[2]); - } - - setel[k++].d = PCS[0]; - setel[k++].d = PCS[1]; - setel[k++].d = PCS[2]; - - if (dospec && spec_n > 0) { - for (j = 0; j < spec_n; j++) { - setel[k++].d = 100.0 * out.spec[j]; + + /* Do PCS to device color conversion */ + if (icc_luo->lookup(icc_luo, dev, pcs) > 1) + error ("%d, %s",icc_icco->errc,icc_icco->err); + + /* Write the values */ + id = ((char *)icg->t[0].fdata[i][si]); + setel[k++].c = id; + + for (j = 0; j < nchan; j++) + setel[k++].d = 100.0 * dev[j]; + + for (j = 0; j < 3; j++) { + double pv = pcs[j]; + if (!islab) + pv *= 100.0; + setel[k++].d = pv; } + + ocg->add_setarr(ocg, 0, setel); } - - ocg->add_setarr(ocg, 0, setel); } free(setel); diff --git a/spectro/hcfr.c b/spectro/hcfr.c index c67a419..e5e1288 100644 --- a/spectro/hcfr.c +++ b/spectro/hcfr.c @@ -32,6 +32,13 @@ and agreed to support. */ +/* + TTBD: + Should really switch this to be high speed serial type of coms, + now that this is supported by inst API. + + */ + #include <stdio.h> #include <stdlib.h> #include <ctype.h> @@ -78,7 +85,7 @@ hcfr_command( ) { int rv, se; - if ((se = p->icom->write_read(p->icom, in, out, bsize, "\n", 1, to)) != 0) { + if ((se = p->icom->write_read(p->icom, in, 0, out, bsize, NULL, "\n", 1, to)) != 0) { int ec; a1logd(p->log, 1, "hcfr_command: serial i/o failure on write_read '%s'\n",icoms_fix(in)); return hcfr_interp_code((inst *)p, icoms2hcfr_err(se)); @@ -89,7 +96,7 @@ hcfr_command( } /* Do a break to check coms is working */ -inst_code +static inst_code hcfr_break( hcfr *p ) { @@ -108,7 +115,7 @@ hcfr_break( } /* Flush an pending messages from the device */ -inst_code +static inst_code hcfr_flush( hcfr *p ) { @@ -118,7 +125,7 @@ hcfr_flush( int rv; for (rv = ICOM_OK;;) { - rv = c->read(c, buf, MAX_MES_SIZE, '\000', 100000, 0.05); + rv = c->read(c, buf, MAX_MES_SIZE, NULL, '\000', 100000, 0.05); if (rv != ICOM_OK) break; /* Expect timeout with nothing to read */ } @@ -128,7 +135,7 @@ hcfr_flush( } /* Get and check the firmware version */ -inst_code +static inst_code hcfr_get_check_version( hcfr *p, int *pmaj, @@ -175,7 +182,7 @@ hcfr_get_check_version( } /* Get a raw measurement value */ -inst_code +static inst_code hcfr_get_rgb( hcfr *p, double rgb[3] /* return value */ @@ -263,7 +270,7 @@ hcfr_get_rgb( /* The basic calibration data is from my particular HCFR, measured */ /* against one of my CRT and LCD displays, with the reference XYZ */ /* derived from my i1pro. */ -inst_code +static inst_code hcfr_comp_matrix( hcfr *p ) { @@ -370,9 +377,6 @@ hcfr_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { if (p->icom->port_type(p->icom) != icomt_usb) { a1logd(p->log, 1, "hcfr_init_coms: expect hcfr to be USB\n"); return hcfr_interp_code((inst *)p, HCFR_UNKNOWN_MODEL); - } else { - a1logd(p->log, 1, "hcfr_init_coms: wrong communications type for device!\n"); - return inst_coms_fail; } /* Set config, interface, "Serial" write & read end points */ @@ -515,30 +519,46 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ return inst_ok; } +static inst_code set_base_disp_type(hcfr *p, int cbid); + /* Insert a colorimetric correction matrix in the instrument XYZ readings */ /* This is only valid for colorimetric instruments. */ /* To remove the matrix, pass NULL for the filter filename */ -inst_code hcfr_col_cor_mat( +static inst_code hcfr_col_cor_mat( inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ \ +int cbid, /* Calibration display type base ID, 1 if unknown */\ double mtx[3][3] ) { hcfr *p = (hcfr *)pp; + inst_code ev; if (!p->gotcoms) return inst_no_coms; if (!p->inited) return inst_no_init; - if (mtx == NULL) { + if ((ev = set_base_disp_type(p, cbid)) != inst_ok) + return ev; + if (mtx == NULL) icmSetUnity3x3(p->ccmat); - } else { - if (p->cbid == 0) { - a1loge(p->log, 1, "hcfr: can't set col_cor_mat over non-base display type\n"); - return inst_wrong_setup; - } + else icmCpy3x3(p->ccmat, mtx); + p->dtech = dtech; + p->refrmode = disptech_get_id(dtech)->refr; + p->cbid = 0; /* Can't be base type now */ + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); } - + return inst_ok; } @@ -621,7 +641,7 @@ hcfr_del(inst *pp) { } /* Return the instrument mode capabilities */ -void hcfr_capabilities(inst *pp, +static void hcfr_capabilities(inst *pp, inst_mode *pcap1, inst2_capability *pcap2, inst3_capability *pcap3) { @@ -648,7 +668,7 @@ inst3_capability *pcap3) { } /* Check device measurement mode */ -inst_code hcfr_check_mode(inst *pp, inst_mode m) { +static inst_code hcfr_check_mode(inst *pp, inst_mode m) { inst_mode cap; if (!pp->gotcoms) @@ -671,7 +691,7 @@ inst_code hcfr_check_mode(inst *pp, inst_mode m) { } /* Set device measurement mode */ -inst_code hcfr_set_mode(inst *pp, inst_mode m) { +static inst_code hcfr_set_mode(inst *pp, inst_mode m) { inst_code ev; if ((ev = hcfr_check_mode(pp, m)) != inst_ok) @@ -680,21 +700,23 @@ inst_code hcfr_set_mode(inst *pp, inst_mode m) { return inst_ok; } -inst_disptypesel hcfr_disptypesel[4] = { +static inst_disptypesel hcfr_disptypesel[4] = { { inst_dtflags_default, 0, "l", "LCD display", 0, + disptech_lcd, 0 }, { inst_dtflags_none, /* flags */ - 0, /* cbix */ + 0, /* cbid */ "c", /* sel */ "CRT display", /* desc */ 0, /* refr */ + disptech_crt, /* disptype */ 1 /* ix */ }, { @@ -703,6 +725,7 @@ inst_disptypesel hcfr_disptypesel[4] = { "R", "Raw Reading", 0, + disptech_unknown, 2 }, { @@ -711,6 +734,7 @@ inst_disptypesel hcfr_disptypesel[4] = { "", "", 0, + disptech_none, 0 } }; @@ -745,15 +769,58 @@ int recreate /* nz to re-check for new ccmx & ccss files */ /* Given a display type entry, setup for that type */ static inst_code set_disp_type(hcfr *p, inst_disptypesel *dentry) { - p->ix = dentry->ix; - p->refrmode = dentry->refr; - p->cbid = dentry->cbid; - if (dentry->flags & inst_dtflags_ccmx) { + inst_code ev; + if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok) + return ev; icmCpy3x3(p->ccmat, dentry->mat); + p->dtech = dentry->dtech; + p->cbid = 0; /* Can't be a base type */ + } else { + + p->ix = dentry->ix; + p->dtech = dentry->dtech; + p->cbid = dentry->cbid; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ icmSetUnity3x3(p->ccmat); } + p->refrmode = dentry->refr; + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + + return inst_ok; +} + +/* Set the display type */ +static inst_code hcfr_set_disptype(inst *pp, int ix) { + hcfr *p = (hcfr *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + hcfr_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + if (ix < 0 || ix >= p->ndtlist) + return inst_unsupported; + + dentry = &p->dtlist[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; + } return inst_ok; } @@ -784,30 +851,55 @@ static inst_code set_default_disp_type(hcfr *p) { return inst_ok; } -/* Set the display type */ -static inst_code hcfr_set_disptype(inst *pp, int ix) { - hcfr *p = (hcfr *)pp; +/* Setup the display type to the given base type */ +static inst_code set_base_disp_type(hcfr *p, int cbid) { inst_code ev; - inst_disptypesel *dentry; + int i; + if (cbid == 0) { + a1loge(p->log, 1, "hcfr set_base_disp_type: can't set base display type of 0\n"); + return inst_wrong_setup; + } if (p->dtlist == NULL) { - if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, hcfr_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) return ev; } - if (ix < 0 || ix >= p->ndtlist) - return inst_unsupported; - - dentry = &p->dtlist[ix]; - - if ((ev = set_disp_type(p, dentry)) != inst_ok) { + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (p->dtlist[i].cbid == cbid) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid); + return inst_wrong_setup; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { return ev; } return inst_ok; } +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code hcfr_get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + hcfr *p = (hcfr *)pp; + if (dtech != NULL) + *dtech = p->dtech; + if (refrmode != NULL) + *refrmode = p->refrmode; + if (cbid != NULL) + *cbid = p->cbid; + return inst_ok; +} + /* * set or reset an optional mode * @@ -826,24 +918,6 @@ hcfr_get_set_opt(inst *pp, inst_opt_type m, ...) { return inst_ok; } - /* Get the display type information */ - if (m == inst_opt_get_dtinfo) { - va_list args; - int *refrmode, *cbid; - - va_start(args, m); - refrmode = va_arg(args, int *); - cbid = va_arg(args, int *); - va_end(args); - - if (refrmode != NULL) - *refrmode = p->refrmode; - if (cbid != NULL) - *cbid = p->cbid; - - return inst_ok; - } - return inst_unsupported; } @@ -864,6 +938,7 @@ extern hcfr *new_hcfr(icoms *icom, instType itype) { p->set_mode = hcfr_set_mode; p->get_disptypesel = hcfr_get_disptypesel; p->set_disptype = hcfr_set_disptype; + p->get_disptechi = hcfr_get_disptechi; p->get_set_opt = hcfr_get_set_opt; p->read_sample = hcfr_read_sample; p->col_cor_mat = hcfr_col_cor_mat; @@ -874,6 +949,7 @@ extern hcfr *new_hcfr(icoms *icom, instType itype) { p->itype = icom->itype; icmSetUnity3x3(p->ccmat); /* Set the colorimeter correction matrix to do nothing */ + p->dtech = disptech_unknown; return p; } diff --git a/spectro/hcfr.h b/spectro/hcfr.h index efbbd6c..8910cec 100644 --- a/spectro/hcfr.h +++ b/spectro/hcfr.h @@ -86,7 +86,9 @@ struct _hcfr { inst_disptypesel *dtlist; /* Display Type list */ int ndtlist; /* Number of valid dtlist entries */ int ix; /* 0 = CRT, 1 = LCD, 2 = raw RGB from sensors */ - int cbid; /* calibration base ID, 0 if not a base */ + disptech dtech; /* Display technology enum */ + int cbid; /* current calibration base ID, 0 if not a base */ + int ucbid; /* Underlying base ID if being used for matrix, 0 othewise */ int refrmode; /* Refresh mode (always 0) */ double ccmat[3][3]; /* Colorimeter correction matrix */ diff --git a/spectro/hidio.c b/spectro/hidio.c index 918bb83..c930755 100644 --- a/spectro/hidio.c +++ b/spectro/hidio.c @@ -30,6 +30,9 @@ New 10.5 and later code is not completely developed/debugged - the read and write routines simply error out. + ~~ It turns out that doing CFRetain() on the CFRunLoopRef and CFRunLoopSourceRef + is important (see usbio_ox.c). Make sure that is correct. + Perhaps we should try using IOHIDDeviceRegisterInputReportCallback() @@ -85,7 +88,7 @@ #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) #include <sys/types.h> #include <usbhid.h> #else /* assume Linux */ @@ -239,10 +242,16 @@ int hid_get_paths(icompaths *p) { a1loge(p->log, ICOM_SYS, "hid_get_paths() calloc failed!\n"); return ICOM_SYS; } - if ((hidd->dpath = strdup(pdidd->DevicePath)) == NULL) { + if ((hidd->dpath = calloc(1, strlen(pdidd->DevicePath)+2)) == NULL) { a1loge(p->log, ICOM_SYS, "hid_get_paths() calloc failed!\n"); return ICOM_SYS; } + /* Windows 10 seems to return paths without the leading '\\' */ + /* (Or are the structure layouts not matching ?) */ + if (pdidd->DevicePath[0] == '\\' && + pdidd->DevicePath[1] != '\\') + strcpy(hidd->dpath, "\\"); + strcat(hidd->dpath, pdidd->DevicePath); /* Add the path to the list */ p->add_hid(p, pname, VendorID, ProductID, 0, hidd, itype); @@ -523,7 +532,11 @@ char **pnames /* List of process names to try and kill before opening */ ) { /* Make sure the port is open */ if (!p->is_open) { +#if defined(NT) + a1logd(p->log, 8, "hid_open_port: about to open HID port '%s' path '%s'\n",p->name,p->hidd->dpath); +#else a1logd(p->log, 8, "hid_open_port: about to open HID port '%s'\n",p->name); +#endif p->uflags = hidflags; @@ -538,21 +551,21 @@ char **pnames /* List of process names to try and kill before opening */ != INVALID_HANDLE_VALUE) { memset(&p->hidd->ols,0,sizeof(OVERLAPPED)); if ((p->hidd->ols.hEvent = CreateEvent(NULL, 0, 0, NULL)) == NULL) { - a1loge(p->log, ICOM_SYS, "hid_open_port: Failed to create HID " - "Event with %d'\n",GetLastError()); + a1loge(p->log, ICOM_SYS, "hid_open_port: Failed to create HID Event " + "with %d'\n",GetLastError()); return ICOM_SYS; } break; } if (tries > 0 && pnames != NULL) { /* Open failed. This could be the i1ProfileTray.exe */ - kill_nprocess(pnames, p->log); + kill_nprocess(pnames, p->log); /* Try and kill it once */ msec_sleep(100); } } if (p->hidd->fh == INVALID_HANDLE_VALUE) { a1loge(p->log, ICOM_SYS, "hid_open_port: Failed to open " - "HID '%s' with %d\n",GetLastError()); + "path '%s' with err %d\n",p->hidd->dpath, GetLastError()); return ICOM_SYS; } } diff --git a/spectro/hidio.h b/spectro/hidio.h index a7e94c8..90a741b 100644 --- a/spectro/hidio.h +++ b/spectro/hidio.h @@ -66,7 +66,7 @@ struct hid_idevice { /* Stuff setup when device is open: */ CFRunLoopRef rlr; #else - int lid; /* Location ID */ + int lid; /* Location ID */ io_object_t ioob; /* Object to open */ /* Stuff setup when device is open: */ IOHIDDeviceInterface122 **device; /* OS X HID device we've opened */ @@ -75,13 +75,10 @@ struct hid_idevice { CFRunLoopRef rlr; IOReturn result; # define HID_RBUF_SIZE 1024 - unsigned char rbuf[HID_RBUF_SIZE]; /* Buffer for read callback */ + unsigned char rbuf[HID_RBUF_SIZE]; /* Buffer for read callback */ int bread; /* Bytes read by callback */ #endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ #endif -#if defined (UNIX) && !defined(__APPLE__) - int temp; /* Shut the compiler up */ -#endif }; /* Cleanup and then free an hidd entry */ diff --git a/spectro/huey.c b/spectro/huey.c index 8c307ac..79d0284 100644 --- a/spectro/huey.c +++ b/spectro/huey.c @@ -414,6 +414,7 @@ huey_wrreg_byte( int rsize; inst_code ev; + memset(ibuf, 0, 7); ibuf[0] = addr; ibuf[1] = inv; @@ -480,6 +481,7 @@ huey_rd_int_time( int rsize; inst_code ev; + memset(buf, 0, 7); if ((ev = huey_command(p, i1d_getintgt, buf, buf, 1.0, 1.0)) != inst_ok) return ev; @@ -499,6 +501,7 @@ huey_wr_int_time( int rsize; inst_code ev; + memset(buf, 0, 7); int2buf(buf, inv); if ((ev = huey_command(p, i1d_setintgt, buf, buf, 1.0, 1.0)) != inst_ok) return ev; @@ -522,17 +525,20 @@ huey_freq_measure( inst_code ev; /* Do the measurement, and return the Red value */ + memset(ibuf, 0, 7); ibuf[0] = 2; /* Sync mode 2 for CRT */ if ((ev = huey_command(p, i1d_m_red_2, ibuf, obuf, 1.0, 10.0)) != inst_ok) return ev; rgb[0] = (double)buf2int(obuf); /* Get the green value */ + memset(ibuf, 0, 7); if ((ev = huey_command(p, i1d_rd_green, ibuf, obuf, 1.0, 1.0)) != inst_ok) return ev; rgb[1] = (double)buf2int(obuf); /* Get the blue value */ + memset(ibuf, 0, 7); if ((ev = huey_command(p, i1d_rd_blue, ibuf, obuf, 1.0, 1.0)) != inst_ok) return ev; rgb[2] = (double)buf2int(obuf); @@ -556,6 +562,7 @@ huey_period_measure( inst_code ev; /* Set the edge count */ + memset(ibuf, 0, 7); short2buf(ibuf + 0, edgec[0]); short2buf(ibuf + 2, edgec[1]); short2buf(ibuf + 4, edgec[2]); @@ -566,11 +573,13 @@ huey_period_measure( rgb[0] = (double)buf2int(obuf); /* Get the green value */ + memset(ibuf, 0, 7); if ((ev = huey_command(p, i1d_rd_green, ibuf, obuf, 1.0, 1.0)) != inst_ok) return ev; rgb[1] = (double)buf2int(obuf); /* Get the blue value */ + memset(ibuf, 0, 7); if ((ev = huey_command(p, i1d_rd_blue, ibuf, obuf, 1.0, 1.0)) != inst_ok) return ev; rgb[2] = (double)buf2int(obuf); @@ -593,6 +602,7 @@ huey_take_raw_measurement_3( inst_code ev; /* Do the measurement, and return the Red value */ + memset(ibuf, 0, 7); short2buf(ibuf + 0, edgec[0]); ibuf[2] = 0; /* Channel */ if ((ev = huey_command(p, i1d_m_rgb_edge_2, ibuf, obuf, 1.0, 10.0)) != inst_ok) @@ -600,6 +610,7 @@ huey_take_raw_measurement_3( rgb[0] = (double)buf2int(obuf); /* Do the measurement, and return the Green value */ + memset(ibuf, 0, 7); short2buf(ibuf + 0, edgec[1]); ibuf[2] = 1; /* Channel */ if ((ev = huey_command(p, i1d_m_rgb_edge_2, ibuf, obuf, 1.0, 10.0)) != inst_ok) @@ -607,6 +618,7 @@ huey_take_raw_measurement_3( rgb[1] = (double)buf2int(obuf); /* Do the measurement, and return the Blue value */ + memset(ibuf, 0, 7); short2buf(ibuf + 0, edgec[2]); ibuf[2] = 2; /* Channel */ if ((ev = huey_command(p, i1d_m_rgb_edge_2, ibuf, obuf, 1.0, 10.0)) != inst_ok) @@ -742,6 +754,7 @@ huey_take_amb_measurement_1( int rsize; inst_code ev; + memset(ibuf, 0, 7); a1 &= 0xff; ibuf[0] = a1; ibuf[1] = syncmode; @@ -795,6 +808,7 @@ huey_set_LEDs( unsigned char obuf[8]; inst_code ev; + memset(ibuf, 0, 7); mask &= 0xf; p->led_state = mask; @@ -867,10 +881,12 @@ huey_check_unlock( a1logd(p->log,2,"huey_check_unlock: called\n"); /* Check the instrument status */ + memset(buf, 0, 7); if ((ev = huey_command(p, i1d_status, buf, buf, 1.0,1.0)) != inst_ok) return ev; if (strncmp((char *)buf, "Locked", 6) == 0) { + memset(buf, 0, 7); if (p->lenovo) strcpy((char *)buf,"huyL"); else @@ -879,11 +895,13 @@ huey_check_unlock( if ((ev = huey_command(p, i1d_unlock, buf, buf, 1.0,1.0)) != inst_ok) return ev; + memset(buf, 0, 7); if ((ev = huey_command(p, i1d_status, buf, buf, 1.0,1.0)) != inst_ok) return ev; } if (strncmp((char *)buf, "huL002", 6) != 0 /* Lenovo Huey ? */ + && strncmp((char *)buf, "ECCM2 ", 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); @@ -1059,6 +1077,7 @@ huey_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } /* Check instrument is responding */ + memset(buf, 0, 7); if ((ev = huey_command(p, i1d_status, buf, buf, 1.0, 1.0)) != inst_ok) { a1logd(p->log, 1, "huey_init_coms: instrument didn't respond 0x%x\n",ev); return ev; @@ -1188,6 +1207,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ if ((rv = huey_take_XYZ_measurement(p, val->XYZ)) != inst_ok) { return rv; } + /* This may not change anything since instrument may clamp */ if (clamp) icmClamp3(val->XYZ, val->XYZ); @@ -1206,30 +1226,47 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ return rv; } +static inst_code set_base_disp_type(huey *p, int cbid); + /* Insert a colorimetric correction matrix in the instrument XYZ readings */ /* This is only valid for colorimetric instruments. */ /* To remove the matrix, pass NULL for the filter filename */ inst_code huey_col_cor_mat( inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ \ +int cbid, /* Calibration display type base ID, 1 if unknown */\ double mtx[3][3] ) { huey *p = (huey *)pp; + inst_code ev; if (!p->gotcoms) return inst_no_coms; if (!p->inited) return inst_no_init; - if (mtx == NULL) { + if ((ev = set_base_disp_type(p, cbid)) != inst_ok) + return ev; + if (mtx == NULL) icmSetUnity3x3(p->ccmat); - } else { - if (p->cbid == 0) { - a1loge(p->log, 1, "huey: can't set col_cor_mat over non-base display type\n"); - return inst_wrong_setup; - } + else icmCpy3x3(p->ccmat, mtx); - } + + p->dtech = dtech; + p->refrmode = disptech_get_id(dtech)->refr; + p->cbid = 0; /* Can't be base type now */ + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + return inst_ok; } @@ -1367,7 +1404,7 @@ huey_del(inst *pp) { } /* Return the instrument mode capabilities */ -void huey_capabilities(inst *pp, +static void huey_capabilities(inst *pp, inst_mode *pcap1, inst2_capability *pcap2, inst3_capability *pcap3) { @@ -1397,7 +1434,7 @@ inst3_capability *pcap3) { } /* Check device measurement mode */ -inst_code huey_check_mode(inst *pp, inst_mode m) { +static inst_code huey_check_mode(inst *pp, inst_mode m) { huey *p = (huey *)pp; inst_mode cap; @@ -1422,7 +1459,7 @@ inst_code huey_check_mode(inst *pp, inst_mode m) { } /* Set device measurement mode */ -inst_code huey_set_mode(inst *pp, inst_mode m) { +static inst_code huey_set_mode(inst *pp, inst_mode m) { huey *p = (huey *)pp; inst_code ev; @@ -1434,21 +1471,23 @@ inst_code huey_set_mode(inst *pp, inst_mode m) { return inst_ok; } -inst_disptypesel huey_disptypesel[3] = { +static inst_disptypesel huey_disptypesel[3] = { { inst_dtflags_default, 1, "l", "LCD display", 0, + disptech_lcd, 0 }, { inst_dtflags_none, /* flags */ - 2, /* cbix */ + 2, /* cbid */ "c", /* sel */ "CRT display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 1 /* ix */ }, { @@ -1457,6 +1496,7 @@ inst_disptypesel huey_disptypesel[3] = { "", "", 0, + disptech_none, 0 } }; @@ -1491,16 +1531,59 @@ int recreate /* nz to re-check for new ccmx & ccss files */ /* Given a display type entry, setup for that type */ static inst_code set_disp_type(huey *p, inst_disptypesel *dentry) { - p->icx = dentry->ix; - p->refrmode = dentry->refr; - p->cbid = dentry->cbid; - if (dentry->flags & inst_dtflags_ccmx) { + inst_code ev; + if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok) + return ev; icmCpy3x3(p->ccmat, dentry->mat); + p->dtech = dentry->dtech; + p->cbid = 0; /* Can't be a base type */ + } else { + p->icx = dentry->ix; + p->dtech = dentry->dtech; + p->cbid = dentry->cbid; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ icmSetUnity3x3(p->ccmat); } + p->refrmode = dentry->refr; + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + + return inst_ok; +} + +/* Set the display type */ +static inst_code huey_set_disptype(inst *pp, int ix) { + huey *p = (huey *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + huey_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + if (ix < 0 || ix >= p->ndtlist) + return inst_unsupported; + + dentry = &p->dtlist[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; + } + return inst_ok; } @@ -1530,30 +1613,56 @@ static inst_code set_default_disp_type(huey *p) { return inst_ok; } -/* Set the display type */ -static inst_code huey_set_disptype(inst *pp, int ix) { - huey *p = (huey *)pp; +/* Setup the display type to the given base type */ +static inst_code set_base_disp_type(huey *p, int cbid) { inst_code ev; - inst_disptypesel *dentry; + int i; + if (cbid == 0) { + a1loge(p->log, 1, "huey set_base_disp_type: can't set base display type of 0\n"); + return inst_wrong_setup; + } if (p->dtlist == NULL) { - if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, huey_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) return ev; } - if (ix < 0 || ix >= p->ndtlist) - return inst_unsupported; - - dentry = &p->dtlist[ix]; - - if ((ev = set_disp_type(p, dentry)) != inst_ok) { + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (!(p->dtlist[i].flags & inst_dtflags_ccmx) /* Prevent infinite recursion */ + && p->dtlist[i].cbid == cbid) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid); + return inst_wrong_setup; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { return ev; } return inst_ok; } +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code huey_get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + huey *p = (huey *)pp; + if (dtech != NULL) + *dtech = p->dtech; + if (refrmode != NULL) + *refrmode = p->refrmode; + if (cbid != NULL) + *cbid = p->cbid; + return inst_ok; +} + /* * set or reset an optional mode * @@ -1573,24 +1682,6 @@ huey_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } - /* Get the display type information */ - if (m == inst_opt_get_dtinfo) { - va_list args; - int *refrmode, *cbid; - - va_start(args, m); - refrmode = va_arg(args, int *); - cbid = va_arg(args, int *); - va_end(args); - - if (refrmode != NULL) - *refrmode = p->refrmode; - if (cbid != NULL) - *cbid = p->cbid; - - return inst_ok; - } - if (!p->gotcoms) return inst_no_coms; if (!p->inited) @@ -1645,6 +1736,7 @@ extern huey *new_huey(icoms *icom, instType itype) { p->set_mode = huey_set_mode; p->get_disptypesel = huey_get_disptypesel; p->set_disptype = huey_set_disptype; + p->get_disptechi = huey_get_disptechi; p->get_set_opt = huey_get_set_opt; p->read_sample = huey_read_sample; p->col_cor_mat = huey_col_cor_mat; @@ -1655,6 +1747,7 @@ extern huey *new_huey(icoms *icom, instType itype) { p->itype = icom->itype; icmSetUnity3x3(p->ccmat); /* Set the colorimeter correction matrix to do nothing */ + p->dtech = disptech_unknown; return p; } diff --git a/spectro/huey.h b/spectro/huey.h index 6b8858a..aec96a0 100644 --- a/spectro/huey.h +++ b/spectro/huey.h @@ -117,7 +117,9 @@ struct _huey { inst_disptypesel *dtlist; /* Display Type list */ int ndtlist; /* Number of valid dtlist entries */ int icx; /* 0 = LCD, 1 = CRT matrix */ - int cbid; /* calibration base ID, 0 if not a base */ + disptech dtech; /* Display technology enum */ + int cbid; /* current calibration base ID, 0 if not a base */ + int ucbid; /* Underlying base ID if being used for matrix, 0 othewise */ int refrmode; /* Refresh mode (always 0) */ double ccmat[3][3]; /* Colorimeter correction matrix */ diff --git a/spectro/i1d3.c b/spectro/i1d3.c index cf0fc08..b088728 100644 --- a/spectro/i1d3.c +++ b/spectro/i1d3.c @@ -56,7 +56,10 @@ #include "i1d3.h" #undef PLOT_SPECTRA /* Plot the sensor senitivity spectra */ +#undef PLOT_XYZSPECTRA /* Plot the calibrated sensor senitivity spectra */ #undef SAVE_SPECTRA /* Save the sensor senitivity spectra to "sensors.cmf" */ +#undef SAVE_XYZSPECTRA /* Save the XYZ senitivity spectra to "sensorsxyz.cmf" (scale 1.4) */ +#undef SAVE_STDXYZ /* save 1931 2 degree to stdobsxyz.cmf */ #undef PLOT_REFRESH /* Plot data used to determine refresh rate */ #undef PLOT_UPDELAY /* Plot data used to determine display update delay */ @@ -560,12 +563,13 @@ i1d3_unlock( i1d3_dtype dtype; /* Base type enumerator */ i1d3_dtype stype; /* Sub type enumerator */ } codes[] = { - { "i1Display3 ", { 0xe9622e9f, 0x8d63e133 }, i1d3_disppro, i1d3_disppro }, + { "i1Display3 ", { 0xe9622e9f, 0x8d63e133 }, i1d3_disppro, i1d3_disppro }, { "Colormunki Display ", { 0xe01e6e0a, 0x257462de }, i1d3_munkdisp, i1d3_munkdisp }, - { "i1Display3 ", { 0xcaa62b2c, 0x30815b61 }, i1d3_disppro, i1d3_oem }, - { "i1Display3 ", { 0xa9119479, 0x5b168761 }, i1d3_disppro, i1d3_nec_ssp }, - { "i1Display3 ", { 0x160eb6ae, 0x14440e70 }, i1d3_disppro, i1d3_quato_sh3 }, - { "i1Display3 ", { 0x291e41d7, 0x51937bdd }, i1d3_disppro, i1d3_hp_dreamc }, + { "i1Display3 ", { 0xcaa62b2c, 0x30815b61 }, i1d3_disppro, i1d3_oem }, + { "i1Display3 ", { 0xa9119479, 0x5b168761 }, i1d3_disppro, i1d3_nec_ssp }, + { "i1Display3 ", { 0x160eb6ae, 0x14440e70 }, i1d3_disppro, i1d3_quato_sh3 }, + { "i1Display3 ", { 0x291e41d7, 0x51937bdd }, i1d3_disppro, i1d3_hp_dreamc }, + { "i1Display3 ", { 0xc9bfafe0, 0x02871166 }, i1d3_disppro, i1d3_sc_c6 }, { NULL } }; inst_code ev; @@ -1918,6 +1922,12 @@ i1d3_take_XYZ_measurement( int pos; inst_code ev; + i1d3_mmode mmode = i1d3_adaptive; + + if (IMODETST(p->mode, inst_mode_emis_nonadaptive)) { + mmode = i1d3_frequency; + } + if (IMODETST(p->mode, inst_mode_emis_ambient)) { /* Check that the ambient filter is in place */ @@ -1928,7 +1938,7 @@ i1d3_take_XYZ_measurement( return i1d3_interp_code((inst *)p, I1D3_SPOS_EMIS); /* Best type of reading, including refresh support */ - if ((ev = i1d3_take_emis_measurement(p, i1d3_adaptive, XYZ)) != inst_ok) + if ((ev = i1d3_take_emis_measurement(p, mmode, XYZ)) != inst_ok) return ev; /* Multiply by ambient calibration matrix */ @@ -1944,7 +1954,7 @@ i1d3_take_XYZ_measurement( return i1d3_interp_code((inst *)p, I1D3_SPOS_EMIS); /* Best type of reading, including refresh support */ - if ((ev = i1d3_take_emis_measurement(p, i1d3_adaptive, XYZ)) != inst_ok) + if ((ev = i1d3_take_emis_measurement(p, mmode, XYZ)) != inst_ok) return ev; /* Multiply by current emissive calibration matrix */ @@ -2173,6 +2183,7 @@ i1d3_comp_calmat( icmTranspose3x3(mat, mat); /* Otherwise we compute the least squares calibration matrix. */ + /* (Another possibility is to use a minimization algorithm) */ } else { /* Multiply the [3 x nsamp] XYZ matrix by the [nsamp x 3] RGB */ /* matrix to produce the [3 x 3] design matrix. */ @@ -2202,6 +2213,73 @@ i1d3_comp_calmat( free_dmatrix(sampXYZ, 0, nsamp-1, 0, 3-1); free_dmatrix(sampRGB, 0, nsamp-1, 0, 3-1); +#if defined(PLOT_XYZSPECTRA) || defined(SAVE_XYZSPECTRA) + + /* Compute the calibrated sensor spectra */ + { + int i, j, k; + xspect calcmfs[3]; + double scale; + xspect *xyz[3]; + standardObserver(xyz, icxOT_CIE_1931_2); + + for (j = 0; j < 3; j++) { + XSPECT_COPY_INFO(&calcmfs[j], &RGBcmfs[j]); + } + + /* For each wavelength */ + for (i = 0; i < RGBcmfs[0].spec_n; i++) { + /* Do matrix multiply */ + for (j = 0; j < 3; j++) { + calcmfs[j].spec[i] = 0.0; + for (k = 0; k < 3; k++) { + calcmfs[j].spec[i] += mat[j][k] * RGBcmfs[k].spec[i]; + } + } + } + + /* Scale the X to be 1.0 */ + scale = value_xspect(xyz[1], 555.0)/value_xspect(&calcmfs[1], 555.0); + for (i = 0; i < RGBcmfs[0].spec_n; i++) { + for (j = 0; j < 3; j++) { + calcmfs[j].spec[i] *= scale; + } + } + + +#ifdef PLOT_XYZSPECTRA + { + double xx[XSPECT_MAX_BANDS]; + double y1[XSPECT_MAX_BANDS]; + double y2[XSPECT_MAX_BANDS]; + double y3[XSPECT_MAX_BANDS]; + + for (i = 0; i < calcmfs[0].spec_n; i++) { + xx[i] = XSPECT_XWL(&calcmfs[0], i); + y1[i] = calcmfs[0].spec[i]; + y2[i] = calcmfs[1].spec[i]; + y3[i] = calcmfs[2].spec[i]; + } + printf("The calibrated sensor sensitivities\n"); + do_plot(xx, y1, y2, y3, calcmfs[0].spec_n); + } +#endif /* PLOT_XYZSPECTRA */ +#ifdef SAVE_XYZSPECTRA /* Save the default XYZ senitivity spectra to "sensorsxyz.cmf" */ + write_nxspect("sensorsxyz.cmf", calcmfs, 3, 0); +#endif + } +#endif /* PLOT_XYZSPECTRA || SAVE_XYZSPECTRA */ +#ifdef SAVE_STDXYZ + { + xspect *xyz[3], xyzl[3]; + standardObserver(xyz, icxOT_CIE_1931_2); + xyzl[0] = *xyz[0]; + xyzl[1] = *xyz[1]; + xyzl[2] = *xyz[2]; + write_nxspect("stdobsxyz.cmf", xyzl, 3, 0); + } +#endif /* SAVE_STDXYZ */ + return inst_ok; } @@ -2232,23 +2310,23 @@ i1d3_set_speccal( return inst_ok; } - /* Preset the calibration to a matrix. The spectral type is set to none */ static inst_code i1d3_set_matcal(i1d3 *p, double mtx[3][3]) { + + if (p->samples != NULL) + free(p->samples); + p->samples = NULL; + p->nsamp = 0; + if (mtx == NULL) icmSetUnity3x3(p->ccmat); - else { - if (p->cbid == 0) { - a1loge(p->log, 1, "i1d3: can't set col_cor_mat over non-base display type\n"); - return inst_wrong_setup; - } + else icmCpy3x3(p->ccmat, mtx); - } + return inst_ok; } - /* Set the calibration to the currently preset type */ static inst_code i1d3_set_cal(i1d3 *p) { @@ -2269,39 +2347,45 @@ i1d3_set_cal(i1d3 *p) { icmSetUnity3x3(p->ccmat); /* to be sure to be sure... */ - } else { /* Assume matrix */ + } else { /* Assume default spectral samples, + possible matrix */ /* Create the default MIbLSr calibration matrix */ if ((ev = i1d3_comp_calmat(p, p->emis_cal, p->obType, p->custObserver, p->sens, p->sens, 3)) != inst_ok) { - a1logd(p->log, 1, "i1d3_set_disp_type: comp_calmat dflt failed with rv = 0x%x\n",ev); + a1logd(p->log, 1, "i1d3_set_cal: comp_calmat dflt failed with rv = 0x%x\n",ev); return ev; } /* Use MIbLSr for ambient */ if ((ev = i1d3_comp_calmat(p, p->ambi_cal, p->obType, p->custObserver, p->ambi, p->ambi, 3)) != inst_ok) return ev; + + /* We assume any ccmat has/will be set by caller */ } if (p->log->debug >= 4) { - a1logd(p->log,4,"Emissive matrix = %f %f %f\n", - p->emis_cal[0][0], p->emis_cal[0][1], p->emis_cal[0][2]); - a1logd(p->log,4," %f %f %f\n", - p->emis_cal[1][0], p->emis_cal[1][1], p->emis_cal[1][2]); - a1logd(p->log,4," %f %f %f\n\n", - p->emis_cal[2][0], p->emis_cal[2][1], p->emis_cal[2][2]); - a1logd(p->log,4,"Ambient matrix = %f %f %f\n", - p->ambi_cal[0][0], p->ambi_cal[0][1], p->ambi_cal[0][2]); - a1logd(p->log,4," %f %f %f\n", - p->ambi_cal[1][0], p->ambi_cal[1][1], p->ambi_cal[1][2]); - a1logd(p->log,4," %f %f %f\n\n", - p->ambi_cal[2][0], p->ambi_cal[2][1], p->ambi_cal[2][2]); + if (IMODETST(p->mode, inst_mode_emis_ambient)) { + a1logd(p->log,4,"Ambient matrix = %f %f %f\n", + p->ambi_cal[0][0], p->ambi_cal[0][1], p->ambi_cal[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ambi_cal[1][0], p->ambi_cal[1][1], p->ambi_cal[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ambi_cal[2][0], p->ambi_cal[2][1], p->ambi_cal[2][2]); + } else { + a1logd(p->log,4,"Emissive matrix = %f %f %f\n", + p->emis_cal[0][0], p->emis_cal[0][1], p->emis_cal[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->emis_cal[1][0], p->emis_cal[1][1], p->emis_cal[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->emis_cal[2][0], p->emis_cal[2][1], p->emis_cal[2][2]); + } a1logd(p->log,4,"ccmat = %f %f %f\n", p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); a1logd(p->log,4," %f %f %f\n", p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); a1logd(p->log,4," %f %f %f\n\n", p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); a1logd(p->log,4,"\n"); } @@ -2381,39 +2465,16 @@ i1d3_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { return inst_ok; } -// Print bytes as hex to debug log */ -static void dump_bytes(a1log *log, char *pfx, unsigned char *buf, int len) { - int i, j, ii; - char oline[200] = { '\000' }, *bp = oline; - for (i = j = 0; i < len; i++) { - if ((i % 16) == 0) - bp += sprintf(bp,"%s%04x:",pfx,i); - bp += sprintf(bp," %02x",buf[i]); - if ((i+1) >= len || ((i+1) % 16) == 0) { - for (ii = i; ((ii+1) % 16) != 0; ii++) - bp += sprintf(bp," "); - bp += sprintf(bp," "); - for (; j <= i; j++) { - if (!(buf[j] & 0x80) && isprint(buf[j])) - bp += sprintf(bp,"%c",buf[j]); - else - bp += sprintf(bp,"."); - } - bp += sprintf(bp,"\n"); - a1logd(log,0, "%s", oline); - bp = oline; - } - } -} - /* Diffuser position thread. */ /* Poll the instrument at 100msec intervals */ -int i1d3_diff_thread(void *pp) { +static int i1d3_diff_thread(void *pp) { int nfailed = 0; i1d3 *p = (i1d3 *)pp; inst_code rv = inst_ok; a1logd(p->log,3,"Diffuser thread started\n"); - for (nfailed = 0; nfailed < 5;) { +// for (nfailed = 0; nfailed < 5;) + /* Try indefinitely, in case instrument is put to sleep */ + for (;;) { int pos; /* Don't get diffpos if we're doing something else that */ @@ -2503,7 +2564,7 @@ i1d3_init_inst(inst *pp) { return ev; if (p->log->debug >= 8) { a1logd(p->log, 8, "Internal EEPROM:\n"); - dump_bytes(p->log, " ", buf, 256); + adump_bytes(p->log, " ", buf, 0, 256); } /* Decode the Internal EEPRom */ if ((ev = i1d3_decode_intEE(p, buf)) != inst_ok) @@ -2513,7 +2574,7 @@ i1d3_init_inst(inst *pp) { return ev; if (p->log->debug >= 8) { a1logd(p->log, 8, "External EEPROM:\n"); - dump_bytes(p->log, " ", buf, 8192); + adump_bytes(p->log, " ", buf, 0, 8192); } /* Decode the External EEPRom */ if ((ev = i1d3_decode_extEE(p, buf)) != inst_ok) @@ -2656,10 +2717,14 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } } + /* Read the XYZ value */ - if ((rv = i1d3_take_XYZ_measurement(p, val->XYZ)) != inst_ok) + rv = i1d3_take_XYZ_measurement(p, val->XYZ); + + if (rv != inst_ok) return rv; + /* This may not change anything since instrument may clamp */ if (clamp) icmClamp3(val->XYZ, val->XYZ); @@ -2696,21 +2761,55 @@ double *ref_rate) { if (p->dtype == i1d3_munkdisp) return inst_unsupported; + if (ref_rate != NULL) + *ref_rate = 0.0; + if ((rv = i1d3_imp_measure_refresh(p, ref_rate, NULL)) != inst_ok) return rv; - if (*ref_rate == 0.0) + + if (ref_rate != NULL && *ref_rate == 0.0) return inst_misread; return inst_ok; } +/* Make a possible change of the refresh mode */ +static void update_refmode(i1d3 *p, int refrmode) { + + if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */ + refrmode = 0; + } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) { + refrmode = 1; + } + + if (p->refrmode != refrmode) { + p->rrset = 0; /* This is a hint we may have swapped displays */ + p->refrvalid = 0; + } + p->refrmode = refrmode; + + /* default before any refresh rate calibration */ + if (p->refrmode) { + p->inttime = 2.0 * p->dinttime; /* Double integration time */ + } else { + p->inttime = p->dinttime; /* Normal integration time */ + } + if (p->omininttime != 0.0) + p->inttime = p->omininttime; /* Override */ + p->mininttime = p->inttime; /* Current value */ +} + +static inst_code set_base_disp_type(i1d3 *p, int cbid); + /* Insert a colorimetric correction matrix in the instrument XYZ readings */ /* This is only valid for colorimetric instruments. */ /* To remove the matrix, pass NULL for the matrix. */ -inst_code i1d3_col_cor_mat( +static inst_code i1d3_col_cor_mat( inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ \ +int cbid, /* Calibration display type base ID, 1 if unknown */\ double mtx[3][3] ) { i1d3 *p = (i1d3 *)pp; @@ -2723,9 +2822,18 @@ double mtx[3][3] if (!p->inited) return inst_no_init; + if ((ev = set_base_disp_type(p, cbid)) != inst_ok) + return ev; + if ((ev = i1d3_set_matcal(p, mtx)) != inst_ok) return ev; + p->dtech = dtech; + p->cbid = 0; + + /* Effective refresh mode may change */ + update_refmode(p, disptech_get_id(dtech)->refr); + return i1d3_set_cal(p); } @@ -2733,8 +2841,9 @@ double mtx[3][3] /* instrumen calibration. */ /* This is only valid for colorimetric instruments. */ /* To set calibration back to default, pass NULL for sets. */ -inst_code i1d3_col_cal_spec_set( +static inst_code i1d3_col_cal_spec_set( inst *pp, +disptech dtech, xspect *sets, int no_sets ) { @@ -2748,6 +2857,9 @@ int no_sets if (!p->inited) return inst_no_init; + p->dtech = dtech; + p->cbid = 0; + if (sets == NULL || no_sets <= 0) { if ((ev = set_default_disp_type(p)) != inst_ok) { return ev; @@ -2756,8 +2868,10 @@ int no_sets if ((ev = i1d3_set_speccal(p, sets, no_sets)) != inst_ok) return ev; + p->ucbid = 0; /* We're using external samples */ ev = i1d3_set_cal(p); } + update_refmode(p, disptech_get_id(dtech)->refr); return ev; } @@ -2784,7 +2898,7 @@ static inst_code i1d3_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_ty } /* Request an instrument calibration. */ -inst_code i1d3_calibrate( +static inst_code i1d3_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ @@ -2866,22 +2980,27 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ return inst_ok; } -/* Measure a display update delay. It is assumed that a */ -/* white to black change has been made to the displayed color, */ +/* Measure a display update delay. It is assumed that */ +/* white_stamp(init) has been called, and then a */ +/* black to white change has been made to the displayed color, */ /* and this will measure the time it took for the update to */ -/* be noticed by the instrument, up to 0.6 seconds. */ +/* be noticed by the instrument, up to 2.0 seconds. */ +/* (It is assumed that white_change() will be called at the time the patch */ +/* changes color.) */ /* inst_misread will be returned on failure to find a transition to black. */ -#define NDSAMPS 200 +#define NDSAMPS 400 #define DINTT 0.005 /* Too short hits blanking */ -#define NDMXTIME 1.0 /* Maximum time to take */ +#define NDMXTIME 2.0 /* Maximum time to take */ -inst_code i1d3_meas_delay( +static inst_code i1d3_meas_delay( inst *pp, -int *msecdelay) { /* Return the number of msec */ +int *pdispmsec, /* Return display update delay in msec */ +int *pinstmsec) { /* Return instrument reaction time in msec */ i1d3 *p = (i1d3 *)pp; inst_code ev; int i, j, k; - double sutime, putime, cutime, eutime; + double putime, cutime; + int mtachdel; struct { double sec; double rgb[3]; @@ -2890,9 +3009,19 @@ int *msecdelay) { /* Return the number of msec */ int ndsamps; double inttime = DINTT; double stot, etot, del, thr; - double etime; + double stime, etime; int isdeb; int isth; + int dispmsec, instmsec; + + if (pinstmsec != NULL) + *pinstmsec = 0; + + if (!p->gotcoms) + return inst_no_coms; + + if (!p->inited) + return inst_no_init; if (usec_time() < 0.0) { a1loge(p->log, inst_internal_error, "i1d3_meas_delay: No high resolution timers\n"); @@ -2906,8 +3035,7 @@ int *msecdelay) { /* Return the number of msec */ p->th_en = 0; /* Read the samples */ - sutime = usec_time(); - putime = (usec_time() - sutime) / 1000000.0; + putime = usec_time() / 1000000.0; for (i = 0; i < NDSAMPS; i++) { if ((ev = i1d3_freq_measure(p, &inttime, samp[i].rgb)) != inst_ok) { a1logd(p->log, 1, "i1d3_meas_delay: measurement failed\n"); @@ -2915,7 +3043,7 @@ int *msecdelay) { /* Return the number of msec */ p->th_en = isth; return ev; } - cutime = (usec_time() - sutime) / 1000000.0; + cutime = usec_time() / 1000000.0; samp[i].sec = 0.5 * (putime + cutime); /* Mean of before and after stamp */ putime = cutime; samp[i].tot = samp[i].rgb[0] + samp[i].rgb[1] + samp[i].rgb[2]; @@ -2932,36 +3060,23 @@ int *msecdelay) { /* Return the number of msec */ a1logd(p->log, 1, "i1d3_meas_delay: No measurement samples returned in time\n"); return inst_internal_error; } - -#ifdef PLOT_UPDELAY - /* Plot the raw sensor values */ - { - double xx[NDSAMPS]; - double y1[NDSAMPS]; - double y2[NDSAMPS]; - double y3[NDSAMPS]; - double y4[NDSAMPS]; - for (i = 0; i < ndsamps; i++) { - xx[i] = samp[i].sec; - y1[i] = samp[i].rgb[0]; - y2[i] = samp[i].rgb[1]; - y3[i] = samp[i].rgb[2]; - y4[i] = samp[i].tot; - //printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].tot); - } - printf("Display update delay measure sensor values and time (sec)\n"); - do_plot6(xx, y1, y2, y3, y4, NULL, NULL, ndsamps); + if (p->whitestamp < 0.0) { + a1logd(p->log, 1, "i1d3_meas_delay: White transition wasn't timestamped\n"); + return inst_internal_error; } -#endif + + /* Set the times to be white transition relative */ + for (i = 0; i < ndsamps; i++) + samp[i].sec -= p->whitestamp / 1000000.0; /* Over the first 100msec, locate the maximum value */ - etime = samp[ndsamps-1].sec; + stime = samp[0].sec; stot = -1e9; for (i = 0; i < ndsamps; i++) { if (samp[i].tot > stot) stot = samp[i].tot; - if (samp[i].sec > 0.1) + if ((samp[i].sec - stime) > 0.1) break; } @@ -2975,33 +3090,72 @@ int *msecdelay) { /* Return the number of msec */ break; } - del = stot - etot; - thr = etot + 0.2 * del; /* 20% of transition threshold */ + del = etot - stot; + thr = stot + 0.2 * del; /* 20% of transition threshold */ #ifdef PLOT_UPDELAY a1logd(p->log, 0, "i1d3_meas_delay: start tot %f end tot %f del %f, thr %f\n", stot, etot, del, thr); #endif +#ifdef PLOT_UPDELAY + /* Plot the raw sensor values */ + { + double xx[NDSAMPS]; + double y1[NDSAMPS]; + double y2[NDSAMPS]; + double y3[NDSAMPS]; + double y4[NDSAMPS]; + + for (i = 0; i < ndsamps; i++) { + xx[i] = samp[i].sec; + y1[i] = samp[i].rgb[0]; + y2[i] = samp[i].rgb[1]; + y3[i] = samp[i].rgb[2]; + y4[i] = samp[i].tot; + //printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].tot); + } + printf("Display update delay measure sensor values and time (sec)\n"); + do_plot6(xx, y1, y2, y3, y4, NULL, NULL, ndsamps); + } +#endif + /* Check that there has been a transition */ if (del < 10.0) { - a1logd(p->log, 1, "i1d3_meas_delay: can't detect change from white to black\n"); + a1logd(p->log, 1, "i1d3_meas_delay: can't detect change from black to white\n"); return inst_misread; } - /* Working from the end, locate the time at which the level was above the threshold */ - for (i = ndsamps-1; i >= 0; i--) { + /* Working from the start, locate the time at which the level was above the threshold */ + for (i = 0; i < (ndsamps-1); i++) { if (samp[i].tot > thr) break; } - if (i < 0) /* Assume the update was so fast that we missed it */ - i = 0; a1logd(p->log, 2, "i1d3_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec); - *msecdelay = (int)(samp[i].sec * 1000.0 + 0.5); + /* Compute overall delay */ + dispmsec = (int)(samp[i].sec * 1000.0 + 0.5); + instmsec = 0; + +#ifdef PLOT_UPDELAY + a1logd(p->log, 0, "i1d3_meas_delay: disp %d, inst %d msec\n",dispmsec,instmsec); +#else + a1logd(p->log, 2, "i1d3_meas_delay: disp %d, inst %d msec\n",dispmsec,instmsec); +#endif + + if (dispmsec < 0) /* This can happen if the patch generator delays it's return */ + dispmsec = 0; + + if (pdispmsec != NULL) + *pdispmsec = dispmsec; + + if (pinstmsec != NULL) + *pinstmsec = instmsec; #ifdef PLOT_UPDELAY - a1logd(p->log, 0, "i1d3_meas_delay: returning %d msec\n",*msecdelay); + a1logd(p->log, 0, "i1d3_meas_delay: returning %d & %d msec\n",dispmsec,instmsec); +#else + a1logd(p->log, 2, "i1d3_meas_delay: returning %d & %d msec\n",dispmsec,instmsec); #endif return inst_ok; @@ -3010,6 +3164,23 @@ int *msecdelay) { /* Return the number of msec */ #undef DINTT #undef NDMXTIME +/* Timestamp the white patch change during meas_delay() */ +/* Initialise the whitestap to invalid if init nz */ +static inst_code i1d3_white_change( +inst *pp, int init) { + i1d3 *p = (i1d3 *)pp; + + if (init) + p->whitestamp = -1.0; + else { + if ((p->whitestamp = usec_time()) < 0.0) { + a1loge(p->log, inst_internal_error, "i1d3_wite_change: No high resolution timers\n"); + return inst_internal_error; + } + } + return inst_ok; +} + /* Return the last calibrated refresh rate in Hz. Returns: */ static inst_code i1d3_get_refr_rate(inst *pp, double *ref_rate @@ -3149,6 +3320,9 @@ i1d3_interp_code(inst *pp, int ec) { case I1D3_OK: return inst_ok; + + case I1D3_INTERNAL_ERROR: + return inst_internal_error | ec; case I1D3_BAD_MEM_ADDRESS: case I1D3_BAD_MEM_LENGTH: @@ -3242,7 +3416,7 @@ i1d3_del(inst *pp) { } /* Return the instrument capabilities */ -void i1d3_capabilities(inst *pp, +static void i1d3_capabilities(inst *pp, inst_mode *pcap1, inst2_capability *pcap2, inst3_capability *pcap3) { @@ -3255,6 +3429,7 @@ inst3_capability *pcap3) { | inst_mode_emis_ambient | inst_mode_emis_refresh_ovd /* (allow override ccmx & ccss mode) */ | inst_mode_emis_norefresh_ovd + | inst_mode_emis_nonadaptive | inst_mode_colorimeter ; @@ -3284,6 +3459,7 @@ inst3_capability *pcap3) { } /* Return current or given configuration available measurement modes. */ +/* NOTE that conf_ix values shoudn't be changed, as it is used as a persistent key */ static inst_code i1d3_meas_config( inst *pp, inst_mode *mmodes, @@ -3334,7 +3510,7 @@ int *conf_ix } /* Check device measurement mode */ -inst_code i1d3_check_mode(inst *pp, inst_mode m) { +static inst_code i1d3_check_mode(inst *pp, inst_mode m) { i1d3 *p = (i1d3 *)pp; inst_mode cap; @@ -3360,7 +3536,7 @@ inst_code i1d3_check_mode(inst *pp, inst_mode m) { } /* Set device measurement mode */ -inst_code i1d3_set_mode(inst *pp, inst_mode m) { +static inst_code i1d3_set_mode(inst *pp, inst_mode m) { i1d3 *p = (i1d3 *)pp; int refrmode; inst_code ev; @@ -3371,39 +3547,19 @@ inst_code i1d3_set_mode(inst *pp, inst_mode m) { p->mode = m; /* Effective refresh mode may change */ - refrmode = p->refrmode; - if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */ - refrmode = 0; - } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) { - refrmode = 1; - } - - if (p->refrmode != refrmode) { - p->rrset = 0; /* This is a hint we may have swapped displays */ - p->refrvalid = 0; - } - p->refrmode = refrmode; - - /* default before any refresh rate calibration */ - if (p->refrmode) { - p->inttime = 2.0 * p->dinttime; /* Double default integration time */ - } else { - p->inttime = p->dinttime; /* Normal integration time */ - } - if (p->omininttime != 0.0) - p->inttime = p->omininttime; /* Override */ - p->mininttime = p->inttime; /* Current value */ + update_refmode(p, p->refrmode); return inst_ok; } -inst_disptypesel i1d3_disptypesel[3] = { +static inst_disptypesel i1d3_disptypesel[3] = { { inst_dtflags_default, 1, "nl", "Non-Refresh display", 0, + disptech_lcd, 0 }, { @@ -3412,6 +3568,7 @@ inst_disptypesel i1d3_disptypesel[3] = { "rc", /* sel */ "Refresh display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 1 /* ix */ }, { @@ -3420,6 +3577,7 @@ inst_disptypesel i1d3_disptypesel[3] = { "", "", 0, + disptech_none, 0 } }; @@ -3469,44 +3627,62 @@ static inst_code set_disp_type(i1d3 *p, inst_disptypesel *dentry) { int refrmode; p->icx = dentry->ix; + p->dtech = dentry->dtech; p->cbid = dentry->cbid; - refrmode = dentry->refr; - if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */ - refrmode = 0; - } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) { - refrmode = 1; - } - - if (p->refrmode != refrmode) - p->rrset = 0; /* This is a hint we may have swapped displays */ - p->refrmode = refrmode; - - if (p->refrmode) { - p->inttime = 2.0 * p->dinttime; /* Double integration time */ - } else { - p->inttime = p->dinttime; /* Normal integration time */ - } - if (p->omininttime != 0.0) - p->inttime = p->omininttime; /* Override */ - p->mininttime = p->inttime; /* Current value */ + update_refmode(p, dentry->refr); if (dentry->flags & inst_dtflags_ccss) { /* Spectral sample */ if ((ev = i1d3_set_speccal(p, dentry->sets, dentry->no_sets)) != inst_ok) return ev; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ - } else { /* Matrix */ + } else { - if (dentry->flags & inst_dtflags_ccmx) { + if (dentry->flags & inst_dtflags_ccmx) { /* Matrix */ + if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok) + return ev; if ((ev = i1d3_set_matcal(p, dentry->mat)) != inst_ok) return ev; - } else { + p->cbid = 0; /* Matrix will be an override of cbid set in i1d3_set_cal() */ + + } else { /* Native */ if ((ev = i1d3_set_matcal(p, NULL)) != inst_ok) /* Noop */ return ev; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ } } - return i1d3_set_cal(p); + return i1d3_set_cal(p); /* Make it happen */ +} + +/* Set the display type */ +static inst_code i1d3_set_disptype(inst *pp, int ix) { + i1d3 *p = (i1d3 *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + i1d3_disptypesel, 1 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + if (ix < 0 || ix >= p->ndtlist) + return inst_unsupported; + + dentry = &p->dtlist[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; + } + + return inst_ok; } /* Setup the default display type */ @@ -3535,35 +3711,55 @@ static inst_code set_default_disp_type(i1d3 *p) { return inst_ok; } -/* Set the display type */ -static inst_code i1d3_set_disptype(inst *pp, int ix) { - i1d3 *p = (i1d3 *)pp; +/* Setup the display type to the given base type */ +static inst_code set_base_disp_type(i1d3 *p, int cbid) { inst_code ev; - inst_disptypesel *dentry; - - if (!p->gotcoms) - return inst_no_coms; - if (!p->inited) - return inst_no_init; + int i; + if (cbid == 0) { + a1loge(p->log, 1, "i1d3 set_base_disp_type: can't set base display type of 0\n"); + return inst_wrong_setup; + } if (p->dtlist == NULL) { - if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, - i1d3_disptypesel, 1 /* doccss*/, 1 /* doccmx */)) != inst_ok) + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, + i1d3_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) return ev; } - if (ix < 0 || ix >= p->ndtlist) - return inst_unsupported; - - dentry = &p->dtlist[ix]; - - if ((ev = set_disp_type(p, dentry)) != inst_ok) { + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (!(p->dtlist[i].flags & inst_dtflags_ccmx) /* Prevent infinite recursion */ + && p->dtlist[i].cbid == cbid) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid); + return inst_wrong_setup; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { return ev; } return inst_ok; } +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code i1d3_get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + i1d3 *p = (i1d3 *)pp; + if (dtech != NULL) + *dtech = p->dtech; + if (refrmode != NULL) + *refrmode = p->refrmode; + if (cbid != NULL) + *cbid = p->cbid; + return inst_ok; +} /* * set or reset an optional mode @@ -3589,24 +3785,6 @@ i1d3_get_set_opt(inst *pp, inst_opt_type m, ...) if (!p->inited) return inst_no_init; - /* Get the display type information */ - if (m == inst_opt_get_dtinfo) { - va_list args; - int *refrmode, *cbid; - - va_start(args, m); - refrmode = va_arg(args, int *); - cbid = va_arg(args, int *); - va_end(args); - - if (refrmode != NULL) - *refrmode = p->refrmode; - if (cbid != NULL) - *cbid = p->cbid; - - return inst_ok; - } - /* Get the current minimum integration time */ if (m == inst_opt_get_min_int_time) { va_list args; @@ -3689,7 +3867,7 @@ i1d3_get_set_opt(inst *pp, inst_opt_type m, ...) a1logd(p->log, 4, "inst_opt_set_ccss_obs\n"); - return i1d3_set_cal(p); /* Recompute calibration */ + return i1d3_set_cal(p); /* Recompute calibration if spectral sample */ } /* Operate the LEDS */ @@ -3814,6 +3992,7 @@ extern i1d3 *new_i1d3(icoms *icom, instType itype) { p->set_mode = i1d3_set_mode; p->get_disptypesel = i1d3_get_disptypesel; p->set_disptype = i1d3_set_disptype; + p->get_disptechi = i1d3_get_disptechi; p->get_set_opt = i1d3_get_set_opt; p->read_sample = i1d3_read_sample; p->read_refrate = i1d3_read_refrate; @@ -3822,6 +4001,7 @@ extern i1d3 *new_i1d3(icoms *icom, instType itype) { p->get_n_a_cals = i1d3_get_n_a_cals; p->calibrate = i1d3_calibrate; p->meas_delay = i1d3_meas_delay; + p->white_change = i1d3_white_change; p->get_refr_rate = i1d3_get_refr_rate; p->set_refr_rate = i1d3_set_refr_rate; p->interp_error = i1d3_interp_error; @@ -3833,6 +4013,7 @@ extern i1d3 *new_i1d3(icoms *icom, instType itype) { amutex_init(p->lock); icmSetUnity3x3(p->ccmat); + p->dtech = disptech_unknown; return p; } diff --git a/spectro/i1d3.h b/spectro/i1d3.h index 6d0eb85..47f3858 100644 --- a/spectro/i1d3.h +++ b/spectro/i1d3.h @@ -81,12 +81,13 @@ typedef enum { i1d3_oem = 2, /* OEM */ i1d3_nec_ssp = 3, /* NEC SpectraSensor Pro */ i1d3_quato_sh3 = 4, /* Quato Silver Haze 3 */ - i1d3_hp_dreamc = 5 /* HP DreameColor */ + i1d3_hp_dreamc = 5, /* HP DreameColor */ + i1d3_sc_c6 = 6 /* SpectraCal C6 */ } i1d3_dtype; /* Measurement mode */ typedef enum { - i1d3_adaptive = 0, /* Frequency over fixed period then adaptive period measurement */ + i1d3_adaptive = 0, /* Frequency over fixed period then adaptive period measurement (def) */ i1d3_frequency = 1, /* Frequency over fixed period measurement */ i1d3_period = 2 /* Adaptive period measurement */ } i1d3_mmode; @@ -126,7 +127,9 @@ struct _i1d3 { inst_disptypesel *dtlist; /* Display Type list */ int ndtlist; /* Number of valid dtlist entries */ int icx; /* Internal calibration matrix index, 11 = Raw */ - int cbid; /* calibration base ID, 0 if not a base */ + disptech dtech; /* Display technology enum */ + int cbid; /* current calibration base ID, 0 if not a base */ + int ucbid; /* Underlying base ID if being used for matrix, 0 othewise */ int refrmode; /* nz if in refresh display mode/double int. time */ icxObserverType obType; /* ccss observer to use */ xspect custObserver[3]; /* Custom ccss observer to use */ @@ -158,6 +161,8 @@ struct _i1d3 { volatile int th_termed; /* nz when thread terminated */ int dpos; /* Diffuser position, 0 = display, 1 = ambient */ + volatile double whitestamp; /* meas_delay() white timestamp */ + }; typedef struct _i1d3 i1d3; /* Constructor */ diff --git a/spectro/i1disp.c b/spectro/i1disp.c index cb5472b..e9a375e 100644 --- a/spectro/i1disp.c +++ b/spectro/i1disp.c @@ -53,7 +53,6 @@ #include "icoms.h" #include "i1disp.h" -static void dump_bytes(a1log *log, char *pfx, unsigned char *buf, int base, int len); static inst_code i1disp_interp_code(inst *pp, int ec); static inst_code i1disp_do_fcal_setit(i1disp *p); static inst_code i1disp_check_unlock(i1disp *p); @@ -108,6 +107,7 @@ typedef enum { i1d_m_rgb_edge_2 = 0x16, /* -:W Measure RGB Edge (16 bit) */ + /* Different meanings for different devices ? */ i1d_set_pll_p = 0x11, /* SS:- Set PLL period */ i1d_get_pll_p = 0x12, /* -:W Get PLL period */ @@ -118,9 +118,11 @@ typedef enum { i1d_wrxreg = 0x13, /* SB:- Write an extra register value */ i1d_rdxreg = 0x14, /* S:B Read an extra register value */ - - i1d_rdexreg = 0x19 /* BS:B Smile: Read an extended register value */ /* The address range overlapps i1d_rdreg */ + /* Smile */ +// i1d_xxxxxxx = 0x18, /* XXX:X Unknown */ + i1d_rdexreg = 0x19 /* BS:BBBB Read an extended register value */ + } i1DispCC; /* Do a command/response exchange with the i1disp. */ @@ -1324,6 +1326,9 @@ i1disp_read_refrate( if (p->dtype != 1) return inst_unsupported; + if (ref_rate != NULL) + *ref_rate = 0.0; + /* Average a few refresh period readings */ for (i = 0; i < p->nmeasprds; i++) { int mp; @@ -1406,6 +1411,7 @@ i1disp_check_unlock( { { '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 }, /* */ @@ -1491,11 +1497,13 @@ i1disp_check_unlock( } else if (ver >= 6.0 && ver <= 6.29 && vv == 'L') { p->dtype = 1; /* Eye-One Display 2 */ - } else if (ver >= 6.0 && ver <= 6.29 && vv == 'M') { + } else if (ver >= 6.0 && ver <= 6.29 + && (vv = 0xff || vv == 'M')) { // Faulty Smile's have vv = 0xff /* ColorMunki Create ? */ /* ColorMunki Smile */ if (p->dtype == 0) /* Not sure if this is set by Create */ p->dtype = 1; + } 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); @@ -1785,41 +1793,42 @@ i1disp_init_inst(inst *pp) { if ((ev = i1disp_check_unlock(p)) != inst_ok) return ev; - /* Read all the registers and store their contents */ - if ((ev = i1disp_read_all_regs(p)) != inst_ok) - return ev; + if (p->log->debug >= 3) { -#ifdef NEVER - /* Dump all the register space */ - if (p->dtype < 2) { - unsigned char buf[0x200]; - int i, len; - - len = 128; - if (p->dtype != 0) - len = 160; - - for (i = 0; i < len; i++) { - int v; - if ((ev = i1disp_rdreg_byte(p, &v, i)) != inst_ok) { - return ev; + /* Dump all the register space */ + if (p->dtype < 2) { + unsigned char buf[0x200]; + int i, len; + + len = 128; + if (p->dtype != 0) + len = 160; + + for (i = 0; i < len; i++) { + int v; + if ((ev = i1disp_rdreg_byte(p, &v, i)) != inst_ok) { + return ev; + } + buf[i] = v; } - buf[i] = v; - } - dump_bytes(p->log, "dump:", buf, 0, len); - - /* Dump ColorMunki Smile extended range */ - /* Main difference is Ascii serial number + other minor unknown */ - } else { - unsigned char buf[0x200]; - + adump_bytes(p->log, "dump:", buf, 0, len); - if ((ev = i1disp_rdexreg_bytes(p, buf, 0, 0x200)) != inst_ok) { - return ev; + /* Dump ColorMunki Smile extended range */ + /* Main difference is Ascii serial number + other minor unknown */ + } else { + unsigned char buf[0x200]; + + + if ((ev = i1disp_rdexreg_bytes(p, buf, 0, 0x200)) != inst_ok) { + return ev; + } + adump_bytes(p->log, "dump:", buf, 0, 0x200); } - dump_bytes(p->log, "dump:", buf, 0, 0x200); } -#endif /* NEVER */ + + /* Read all the registers and store their contents */ + if ((ev = i1disp_read_all_regs(p)) != inst_ok) + return ev; if ((ev = i1disp_compute_factors(p)) != inst_ok) return ev; @@ -1884,9 +1893,14 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ return rv; /* Abort */ } + /* Read the XYZ value */ - if ((rv = i1disp_take_XYZ_measurement(p, val->XYZ)) != inst_ok) + rv = i1disp_take_XYZ_measurement(p, val->XYZ); + + if (rv != inst_ok) return rv; + + /* This may not change anything since instrument may clamp */ if (clamp) icmClamp3(val->XYZ, val->XYZ); @@ -1905,28 +1919,44 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ return rv; } +static inst_code set_base_disp_type(i1disp *p, int cbid); + /* Insert a colorimetric correction matrix in the instrument XYZ readings */ /* This is only valid for colorimetric instruments. */ /* To remove the matrix, pass NULL for the filter filename */ inst_code i1disp_col_cor_mat( inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ \ +int cbid, /* Calibration display type base ID, 1 if unknown */\ double mtx[3][3] ) { i1disp *p = (i1disp *)pp; + inst_code ev; if (!p->gotcoms) return inst_no_coms; if (!p->inited) return inst_no_init; - if (mtx == NULL) { + if ((ev = set_base_disp_type(p, cbid)) != inst_ok) + return ev; + if (mtx == NULL) icmSetUnity3x3(p->ccmat); - } else { - if (p->cbid == 0) { - a1loge(p->log, 1, "spyd2: can't set col_cor_mat over non-base display type\n"); - return inst_wrong_setup; - } + else icmCpy3x3(p->ccmat, mtx); + p->dtech = dtech; + p->refrmode = disptech_get_id(dtech)->refr; + p->cbid = 0; /* Can't be base type now */ + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); } return inst_ok; @@ -2059,6 +2089,7 @@ double *ref_rate i1disp *p = (i1disp *)pp; if (p->refrvalid) { *ref_rate = p->refrate; + return inst_ok; } else if (p->rrset) { *ref_rate = 0.0; @@ -2213,7 +2244,7 @@ i1disp_del(inst *pp) { } /* Return the instrument capabilities */ -void i1disp_capabilities(inst *pp, +static void i1disp_capabilities(inst *pp, inst_mode *pcap1, inst2_capability *pcap2, inst3_capability *pcap3) { @@ -2254,7 +2285,7 @@ inst3_capability *pcap3) { } /* Check device measurement mode */ -inst_code i1disp_check_mode(inst *pp, inst_mode m) { +static inst_code i1disp_check_mode(inst *pp, inst_mode m) { i1disp *p = (i1disp *)pp; inst_mode cap; @@ -2279,7 +2310,7 @@ inst_code i1disp_check_mode(inst *pp, inst_mode m) { } /* Set device measurement mode */ -inst_code i1disp_set_mode(inst *pp, inst_mode m) { +static inst_code i1disp_set_mode(inst *pp, inst_mode m) { i1disp *p = (i1disp *)pp; inst_code ev; @@ -2296,21 +2327,23 @@ inst_code i1disp_set_mode(inst *pp, inst_mode m) { return inst_ok; } -inst_disptypesel i1disp_disptypesel[3] = { +static inst_disptypesel i1disp_disptypesel[3] = { { inst_dtflags_default, 1, "l", "LCD display", 0, + disptech_lcd, 0 }, { inst_dtflags_none, /* flags */ - 2, /* cbix */ + 2, /* cbid */ "c", /* sel */ "CRT display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 1 /* ix */ }, { @@ -2319,18 +2352,20 @@ inst_disptypesel i1disp_disptypesel[3] = { "", "", 0, + disptech_none, 0 } }; -inst_disptypesel smile_disptypesel[3] = { +static inst_disptypesel smile_disptypesel[3] = { { inst_dtflags_default, /* flags */ - 1, /* cbix */ + 1, /* cbid */ "fl", /* sel */ "LCD with CCFL backlight", /* desc */ 0, /* refr */ + disptech_lcd_ccfl, /* disptype */ 1 /* ix */ }, { @@ -2339,6 +2374,7 @@ inst_disptypesel smile_disptypesel[3] = { "e", "LCD with White LED backlight", 0, + disptech_lcd_wled, 0 }, { @@ -2347,6 +2383,7 @@ inst_disptypesel smile_disptypesel[3] = { "", "", 0, + disptech_none, 0 } }; @@ -2371,7 +2408,7 @@ int recreate /* nz to re-check for new ccmx & ccss files */ i1disp *p = (i1disp *)pp; inst_code rv = inst_ok; - /* Create/Re-create a current list of abailable display types */ + /* Create/Re-create a current list of available display types */ if (p->dtlist == NULL || recreate) { if ((rv = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) @@ -2391,8 +2428,24 @@ int recreate /* nz to re-check for new ccmx & ccss files */ static inst_code set_disp_type(i1disp *p, inst_disptypesel *dentry) { int refrmode; - p->icx = dentry->ix; - p->cbid = dentry->cbid; + if (dentry->flags & inst_dtflags_ccmx) { + inst_code ev; + if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok) + return ev; + icmCpy3x3(p->ccmat, dentry->mat); + p->dtech = dentry->dtech; + p->cbid = 0; /* Can't be a base type now */ + + } else { /* Native */ + + p->icx = dentry->ix; + p->dtech = dentry->dtech; + p->cbid = dentry->cbid; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ + icmSetUnity3x3(p->ccmat); + } + + /* Implement any refresh mode change */ refrmode = dentry->refr; if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */ @@ -2407,15 +2460,50 @@ static inst_code set_disp_type(i1disp *p, inst_disptypesel *dentry) { } p->refrmode = refrmode; - if (dentry->flags & inst_dtflags_ccmx) { - icmCpy3x3(p->ccmat, dentry->mat); - } else { - icmSetUnity3x3(p->ccmat); + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + + return inst_ok; +} + +/* Set the display type */ +static inst_code i1disp_set_disptype(inst *pp, int ix) { + i1disp *p = (i1disp *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + p->_dtlist, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + if (ix < 0 || ix >= p->ndtlist) + return inst_unsupported; + + dentry = &p->dtlist[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; } return inst_ok; } +/* Get the disptech corresponding to the current */ /* Setup the default display type */ static inst_code set_default_disp_type(i1disp *p) { inst_code ev; @@ -2442,35 +2530,55 @@ static inst_code set_default_disp_type(i1disp *p) { return inst_ok; } -/* Set the display type */ -static inst_code i1disp_set_disptype(inst *pp, int ix) { - i1disp *p = (i1disp *)pp; +/* Setup the display type to the given base type */ +static inst_code set_base_disp_type(i1disp *p, int cbid) { inst_code ev; - inst_disptypesel *dentry; - - if (!p->gotcoms) - return inst_no_coms; - if (!p->inited) - return inst_no_init; + int i; + if (cbid == 0) { + a1loge(p->log, 1, "i1disp set_base_disp_type: can't set base display type of 0\n"); + return inst_wrong_setup; + } if (p->dtlist == NULL) { - if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, - p->_dtlist, 1 /* doccss*/, 1 /* doccmx */)) != inst_ok) + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, + i1disp_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) return ev; } - if (ix < 0 || ix >= p->ndtlist) - return inst_unsupported; - - dentry = &p->dtlist[ix]; - - if ((ev = set_disp_type(p, dentry)) != inst_ok) { + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (!(p->dtlist[i].flags & inst_dtflags_ccmx) /* Prevent infinite recursion */ + && p->dtlist[i].cbid == cbid) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid); + return inst_wrong_setup; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { return ev; } return inst_ok; } +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code i1disp_get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + i1disp *p = (i1disp *)pp; + if (dtech != NULL) + *dtech = p->dtech; + if (refrmode != NULL) + *refrmode = p->refrmode; + if (cbid != NULL) + *cbid = p->cbid; + return inst_ok; +} /* * set or reset an optional mode @@ -2484,24 +2592,6 @@ i1disp_get_set_opt(inst *pp, inst_opt_type m, ...) i1disp *p = (i1disp *)pp; inst_code ev; - /* Get the display type information */ - if (m == inst_opt_get_dtinfo) { - va_list args; - int *refrmode, *cbid; - - va_start(args, m); - refrmode = va_arg(args, int *); - cbid = va_arg(args, int *); - va_end(args); - - if (refrmode != NULL) - *refrmode = p->refrmode; - if (cbid != NULL) - *cbid = p->cbid; - - return inst_ok; - } - /* Record the trigger mode */ if (m == inst_opt_trig_prog || m == inst_opt_trig_user) { @@ -2527,6 +2617,7 @@ extern i1disp *new_i1disp(icoms *icom, instType itype) { p->capabilities = i1disp_capabilities; p->check_mode = i1disp_check_mode; p->set_mode = i1disp_set_mode; + p->get_disptechi = i1disp_get_disptechi; p->get_disptypesel = i1disp_get_disptypesel; p->set_disptype = i1disp_set_disptype; p->get_set_opt = i1disp_get_set_opt; @@ -2552,37 +2643,13 @@ extern i1disp *new_i1disp(icoms *icom, instType itype) { icmSetUnity3x3(p->ccmat); /* Set the colorimeter correction matrix to do nothing */ set_base_disptype_list(p); + p->dtech = disptech_unknown; return p; } /* ---------------------------------------------------------------- */ -// Print bytes as hex to debug log */ -static void dump_bytes(a1log *log, char *pfx, unsigned char *buf, int base, int len) { - int i, j, ii; - char oline[200] = { '\000' }, *bp = oline; - for (i = j = 0; i < len; i++) { - if ((i % 16) == 0) - bp += sprintf(bp,"%s%04x:",pfx,base+i); - bp += sprintf(bp," %02x",buf[i]); - if ((i+1) >= len || ((i+1) % 16) == 0) { - for (ii = i; ((ii+1) % 16) != 0; ii++) - bp += sprintf(bp," "); - bp += sprintf(bp," "); - for (; j <= i; j++) { - if (!(buf[j] & 0x80) && isprint(buf[j])) - bp += sprintf(bp,"%c",buf[j]); - else - bp += sprintf(bp,"."); - } - bp += sprintf(bp,"\n"); - a1logd(log,0,"%s",oline); - bp = oline; - } - } -} - diff --git a/spectro/i1disp.h b/spectro/i1disp.h index 893cad2..45e21db 100644 --- a/spectro/i1disp.h +++ b/spectro/i1disp.h @@ -140,7 +140,9 @@ struct _i1disp { inst_disptypesel *dtlist; /* Display Type list */ int ndtlist; /* Number of valid dtlist entries */ int icx; /* 0 = LCD, 1 = CRT/CCFL matrix */ - int cbid; /* calibration base ID, 0 if not a base */ + disptech dtech; /* Display technology enum */ + int cbid; /* current calibration base ID, 0 if not a base */ + int ucbid; /* Underlying base ID if being used for matrix, 0 othewise */ double ccmat[3][3]; /* Colorimeter correction matrix */ /* For dtype == 1 (Eye-One Display2) */ diff --git a/spectro/i1pro.c b/spectro/i1pro.c index 2cac518..cc2d7f2 100644 --- a/spectro/i1pro.c +++ b/spectro/i1pro.c @@ -35,6 +35,9 @@ /* TTBD + There may be a bug in HiRes emissive calibration - if you do an initial + cal. in normal, then switch to HiRes, no white plate cal is performed. + Is it using a previous cal result for this ? Should add extra filter compensation support. @@ -273,9 +276,13 @@ double *ref_rate) { if (!p->inited) return inst_no_init; + if (ref_rate != NULL) + *ref_rate = 0.0; + rv = i1pro_imp_meas_refrate(p, ref_rate); + return i1pro_interp_code(p, rv); } @@ -283,7 +290,8 @@ double *ref_rate) { static inst_code i1pro_meas_delay( inst *pp, -int *msecdelay) { +int *pdispmsec, /* Return display update delay in msec */ +int *pinstmsec) { /* Return instrument reaction time in msec */ i1pro *p = (i1pro *)pp; i1pro_code rv; @@ -292,11 +300,20 @@ int *msecdelay) { if (!p->inited) return inst_no_init; - rv = i1pro_imp_meas_delay(p, msecdelay); + rv = i1pro_imp_meas_delay(p, pdispmsec, pinstmsec); return i1pro_interp_code(p, rv); } +/* Timestamp the white patch change during meas_delay() */ +static inst_code i1pro_white_change( +inst *pp, +int init) { + i1pro *p = (i1pro *)pp; + + return i1pro_imp_white_change(p, init); +} + /* Return needed and available inst_cal_type's */ static inst_code i1pro_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) { i1pro *p = (i1pro *)pp; @@ -307,7 +324,7 @@ static inst_code i1pro_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_t } /* Request an instrument calibration. */ -inst_code i1pro_calibrate( +static inst_code i1pro_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ @@ -514,9 +531,11 @@ i1pro_interp_code(i1pro *p, i1pro_code ec) { switch (ec) { case I1PRO_OK: - return inst_ok; + case I1PRO_INTERNAL_ERROR: + return inst_internal_error | ec; + case I1PRO_COMS_FAIL: return inst_coms_fail | ec; @@ -601,7 +620,7 @@ i1pro_interp_code(i1pro *p, i1pro_code ec) { } /* Return the instrument capabilities */ -void i1pro_capabilities(inst *pp, +static void i1pro_capabilities(inst *pp, inst_mode *pcap1, inst2_capability *pcap2, inst3_capability *pcap3) { @@ -655,7 +674,7 @@ static i1p_mode i1pro_convert_mode(i1pro *p, inst_mode m) { } /* Check device measurement mode */ -inst_code i1pro_check_mode(inst *pp, inst_mode m) { +static inst_code i1pro_check_mode(inst *pp, inst_mode m) { i1pro *p = (i1pro *)pp; i1p_mode mmode = 0; /* Instrument measurement mode */ @@ -671,7 +690,7 @@ inst_code i1pro_check_mode(inst *pp, inst_mode m) { } /* Set device measurement mode */ -inst_code i1pro_set_mode(inst *pp, inst_mode m) { +static inst_code i1pro_set_mode(inst *pp, inst_mode m) { i1pro *p = (i1pro *)pp; i1p_mode mmode; /* Instrument measurement mode */ inst_code rv; @@ -703,7 +722,11 @@ i1pro_get_set_opt(inst *pp, inst_opt_type m, ...) { i1pro *p = (i1pro *)pp; - if (m == inst_opt_noinitcalib) { + if (m == inst_opt_initcalib) { /* default */ + i1pro_set_noinitcalib(p, 0, 0); + return inst_ok; + + } if (m == inst_opt_noinitcalib) { va_list args; int losecs = 0; @@ -714,10 +737,6 @@ i1pro_get_set_opt(inst *pp, inst_opt_type m, ...) i1pro_set_noinitcalib(p, 1, losecs); return inst_ok; - } else if (m == inst_opt_initcalib) { - i1pro_set_noinitcalib(p, 0, 0); - return inst_ok; - /* Record the trigger mode */ } else if (m == inst_opt_trig_prog || m == inst_opt_trig_user @@ -815,6 +834,7 @@ extern i1pro *new_i1pro(icoms *icom, instType itype) { p->get_n_a_cals = i1pro_get_n_a_cals; p->calibrate = i1pro_calibrate; p->meas_delay = i1pro_meas_delay; + p->white_change = i1pro_white_change; p->interp_error = i1pro_interp_error; p->del = i1pro_del; diff --git a/spectro/i1pro_imp.c b/spectro/i1pro_imp.c index b6d6747..9a888f9 100644 --- a/spectro/i1pro_imp.c +++ b/spectro/i1pro_imp.c @@ -102,10 +102,12 @@ #define ENABLE_NONLINCOR /* [Def] Enable non-linear correction */ #define ENABLE_BKDRIFTC /* [Def] Enable Emis. Black drift compensation using sheilded cell values */ #define HEURISTIC_BKDRIFTC /* [Def] Enable heusristic black drift correction */ + #define WLCALTOUT (24 * 60 * 60) /* [24 Hrs] Wavelength calibration timeout in seconds */ -#define DCALTOUT ( 60 * 60) /* [60 Minuites] Dark Calibration timeout in seconds */ +#define DCALTOUT ( 30 * 60) /* [30 Minutes] Dark Calibration timeout in seconds */ #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 SW_THREAD_TIMEOUT (10 * 60.0) /* [10 Min] Switch read thread timeout */ @@ -118,7 +120,7 @@ #undef DEBUG /* Turn on debug printfs */ #undef PLOT_DEBUG /* Use plot to show readings & processing */ #undef PLOT_REFRESH /* Plot refresh rate measurement info */ -#undef PLOT_UPDELAY /* Plot data used to determine display update delay */ +#undef PLOT_UPDELAY /* Plot data used to determine display update delay */ #undef DUMP_SCANV /* Dump scan readings to a file "i1pdump.txt" */ #undef DUMP_DARKM /* Append raw dark readings to file "i1pddump.txt" */ #undef APPEND_MEAN_EMMIS_VAL /* Append averaged uncalibrated reading to file "i1pdump.txt" */ @@ -377,8 +379,13 @@ void del_i1proimp(i1pro *p) { m->th->del(m->th); usb_uninit_cancel(&m->sw_cancel); /* Don't need cancel token now */ usb_uninit_cancel(&m->rd_sync); /* Don't need sync token now */ + a1logd(p->log,5,"i1pro switch thread terminated\n"); + } + + if (m->trig_thread != NULL) { + m->trig_thread->del(m->trig_thread); + a1logd(p->log,5,"i1pro trigger thread terminated\n"); } - a1logd(p->log,5,"i1pro switch thread terminated\n"); /* Free any per mode data */ for (i = 0; i < i1p_no_modes; i++) { @@ -1312,20 +1319,24 @@ i1pro_code i1pro_imp_set_mode( case i1p_refl_spot: case i1p_refl_scan: if (p->itype == instI1Monitor) - return I1PRO_INT_ILLEGALMODE; /* i1Monitor */ - /* Fall through */ + return I1PRO_INT_ILLEGALMODE; /* i1Monitor can't do reflection */ + break; case i1p_emiss_spot_na: case i1p_emiss_spot: case i1p_emiss_scan: + break; case i1p_amb_spot: case i1p_amb_flash: + if (!i1pro_imp_ambient(p)) + return I1PRO_INT_ILLEGALMODE; + break; case i1p_trans_spot: case i1p_trans_scan: - m->mmode = mmode; break; default: return I1PRO_INT_ILLEGALMODE; } + m->mmode = mmode; m->spec_en = (mode & inst_mode_spectral) != 0; if ((mode & inst_mode_highres) != 0) { @@ -1352,6 +1363,10 @@ i1pro_code i1pro_imp_get_n_a_cals(i1pro *p, inst_cal_type *pn_cals, inst_cal_typ time_t curtime = time(NULL); inst_cal_type n_cals = inst_calt_none; inst_cal_type a_cals = inst_calt_none; + int wl_valid = cs->wl_valid; /* Locally timed out versions of valid state */ + int idark_valid = cs->idark_valid; + int dark_valid = cs->dark_valid; + int cal_valid = cs->cal_valid; a1logd(p->log,2,"i1pro_imp_get_n_a_cals: checking mode %d\n",m->mmode); @@ -1359,63 +1374,63 @@ i1pro_code i1pro_imp_get_n_a_cals(i1pro *p, inst_cal_type *pn_cals, inst_cal_typ if (m->capabilities2 & I1PRO_CAP2_WL_LED) { if ((curtime - cs->wldate) > WLCALTOUT) { a1logd(p->log,2,"Invalidating wavelength cal as %d secs from last cal\n",curtime - cs->wldate); - cs->wl_valid = 0; + wl_valid = 0; } } - if ((curtime - cs->iddate) > ((p->itype == instI1Pro) ? DCALTOUT2 : DCALTOUT)) { + if ((curtime - cs->iddate) > ((p->itype == instI1Pro2) ? DCALTOUT2 : DCALTOUT)) { a1logd(p->log,2,"Invalidating adaptive dark cal as %d secs from last cal\n",curtime - cs->iddate); - cs->idark_valid = 0; + idark_valid = 0; } - if ((curtime - cs->ddate) > ((p->itype == instI1Pro) ? DCALTOUT2 : DCALTOUT)) { + if ((curtime - cs->ddate) > ((p->itype == instI1Pro2) ? DCALTOUT2 : DCALTOUT)) { a1logd(p->log,2,"Invalidating dark cal as %d secs from last cal\n",curtime - cs->ddate); - cs->dark_valid = 0; + dark_valid = 0; } if (!cs->emiss && (curtime - cs->cfdate) > WCALTOUT) { a1logd(p->log,2,"Invalidating white cal as %d secs from last cal\n",curtime - cs->cfdate); - cs->cal_valid = 0; + cal_valid = 0; } #ifdef NEVER printf("~1 reflective = %d, adaptive = %d, emiss = %d, trans = %d, scan = %d\n", cs->reflective, cs->adaptive, cs->emiss, cs->trans, cs->scan); printf("~1 idark_valid = %d, dark_valid = %d, cal_valid = %d\n", - cs->idark_valid,cs->dark_valid,cs->cal_valid); + idark_valid,dark_valid,cal_valid); printf("~1 want_calib = %d, want_dcalib = %d, noinitcalib = %d\n", cs->want_calib,cs->want_dcalib, m->noinitcalib); #endif /* NEVER */ if (m->capabilities2 & I1PRO_CAP2_WL_LED) { - if (!cs->wl_valid + if (!wl_valid || (cs->want_dcalib && !m->noinitcalib)) // ?? want_dcalib ?? n_cals |= inst_calt_wavelength; a_cals |= inst_calt_wavelength; } if (cs->reflective) { - if (!cs->dark_valid + if (!dark_valid || (cs->want_dcalib && !m->noinitcalib)) n_cals |= inst_calt_ref_dark; a_cals |= inst_calt_ref_dark; - if (!cs->cal_valid + if (!cal_valid || (cs->want_calib && !m->noinitcalib)) n_cals |= inst_calt_ref_white; a_cals |= inst_calt_ref_white; } if (cs->emiss) { - if ((!cs->adaptive && !cs->dark_valid) - || (cs->adaptive && !cs->idark_valid) + if ((!cs->adaptive && !dark_valid) + || (cs->adaptive && !idark_valid) || (cs->want_dcalib && !m->noinitcalib)) n_cals |= inst_calt_em_dark; a_cals |= inst_calt_em_dark; } if (cs->trans) { - if ((!cs->adaptive && !cs->dark_valid) - || (cs->adaptive && !cs->idark_valid) + if ((!cs->adaptive && !dark_valid) + || (cs->adaptive && !idark_valid) || (cs->want_dcalib && !m->noinitcalib)) n_cals |= inst_calt_trans_dark; a_cals |= inst_calt_trans_dark; - if (!cs->cal_valid + if (!cal_valid || (cs->want_calib && !m->noinitcalib)) n_cals |= inst_calt_trans_vwhite; a_cals |= inst_calt_trans_vwhite; @@ -2208,8 +2223,9 @@ 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 */ - - if (ev != I1PRO_RD_TRANSWHITEWARN && ev != I1PRO_OK) { + if (ev == I1PRO_RD_TRANSWHITEWARN) /* Shouldn't happen ? */ + ev = I1PRO_OK; + if (ev != I1PRO_OK) { m->mmode = mmode; /* Restore actual mode */ return ev; } @@ -2395,12 +2411,15 @@ int icoms2i1pro_err(int se) { } /* - - - - - - - - - - - - - - - - */ -/* Measure a display update delay. It is assumed that a */ +/* 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, */ /* and this will measure the time it took for the update to */ -/* be noticed by the instrument, up to 0.6 seconds. */ +/* be noticed by the instrument, up to 2.0 seconds. */ +/* (It is assumed that white_change() will be called at the time the patch */ +/* changes color.) */ /* inst_misread will be returned on failure to find a transition to black. */ -#define NDMXTIME 0.7 /* Maximum time to take */ +#define NDMXTIME 2.0 /* Maximum time to take */ #define NDSAMPS 500 /* Debug samples */ typedef struct { @@ -2411,7 +2430,8 @@ typedef struct { i1pro_code i1pro_imp_meas_delay( i1pro *p, -int *msecdelay) { /* Return the number of msec */ +int *pdispmsec, /* Return display update delay in msec */ +int *pinstmsec) { /* Return instrument latency in msec */ i1pro_code ev = I1PRO_OK; i1proimp *m = (i1proimp *)p->m; i1pro_state *s = &m->ms[m->mmode]; @@ -2419,12 +2439,20 @@ int *msecdelay) { /* Return the number of msec */ double **multimeas; /* Spectral measurements */ int nummeas; double rgbw[3] = { 610.0, 520.0, 460.0 }; - double ucalf = 1.0; /* usec_time calibration factor */ double inttime; + double rstart; i1rgbdsamp *samp; double stot, etot, del, thr; - double etime; - int isdeb; + double stime, etime; + int dispmsec, instmsec; + + if (pinstmsec != NULL) + *pinstmsec = 0; + + if ((rstart = usec_time()) < 0.0) { + a1loge(p->log, inst_internal_error, "i1pro_imp_meas_delay: No high resolution timers\n"); + return inst_internal_error; + } /* Read the samples */ inttime = m->min_int_time; @@ -2435,16 +2463,23 @@ int *msecdelay) { /* Return the number of msec */ return I1PRO_INT_MALLOC; } -//printf("~1 %d samples at %f int\n",nummeas,inttime); + /* We rely on the measurement code setting m->trigstamp when the */ + /* trigger packet is sent to the instrument */ if ((ev = i1pro_read_patches_all(p, multimeas, nummeas, &inttime, 0)) != inst_ok) { free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav[m->highres]-1); free(samp); return ev; - } + } + + if (m->whitestamp < 0.0) { + a1logd(p->log, 1, "i1d3_meas_delay: White transition wasn't timestamped\n"); + return inst_internal_error; + } /* Convert the samples to RGB */ + /* Add 10 msec fudge factor */ for (i = 0; i < nummeas; i++) { - samp[i].sec = i * inttime; + samp[i].sec = i * inttime + (m->trigstamp - m->whitestamp)/1000000.0 + 0.01; samp[i].rgb[0] = samp[i].rgb[1] = samp[i].rgb[2] = 0.0; for (j = 0; j < m->nwav[m->highres]; j++) { double wl = XSPECT_WL(m->wl_short[m->highres], m->wl_long[m->highres], m->nwav[m->highres], j); @@ -2462,37 +2497,15 @@ int *msecdelay) { /* Return the number of msec */ } free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav[m->highres]-1); - a1logd(p->log, 3, "i1pro_measure_refresh: Read %d samples for refresh calibration\n",nummeas); - -#ifdef PLOT_UPDELAY - /* Plot the raw sensor values */ - { - double xx[NDSAMPS]; - double y1[NDSAMPS]; - double y2[NDSAMPS]; - double y3[NDSAMPS]; - double y4[NDSAMPS]; - - for (i = 0; i < nummeas && i < NDSAMPS; i++) { - xx[i] = samp[i].sec; - y1[i] = samp[i].rgb[0]; - y2[i] = samp[i].rgb[1]; - y3[i] = samp[i].rgb[2]; - y4[i] = samp[i].tot; -//printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].tot); - } - printf("Display update delay measure sensor values and time (sec)\n"); - do_plot6(xx, y1, y2, y3, y4, NULL, NULL, nummeas); - } -#endif + a1logd(p->log, 3, "i1pro_meas_delay: Read %d samples for refresh calibration\n",nummeas); /* Over the first 100msec, locate the maximum value */ - etime = samp[nummeas-1].sec; + stime = samp[0].sec; stot = -1e9; for (i = 0; i < nummeas; i++) { if (samp[i].tot > stot) stot = samp[i].tot; - if (samp[i].sec > 0.1) + if ((samp[i].sec - stime) > 0.1) break; } @@ -2506,34 +2519,73 @@ int *msecdelay) { /* Return the number of msec */ break; } - del = stot - etot; - thr = etot + 0.30 * del; /* 30% of transition threshold */ + del = etot - stot; + thr = stot + 0.30 * del; /* 30% of transition threshold */ #ifdef PLOT_UPDELAY a1logd(p->log, 0, "i1pro_meas_delay: start tot %f end tot %f del %f, thr %f\n", stot, etot, del, thr); #endif +#ifdef PLOT_UPDELAY + /* Plot the raw sensor values */ + { + double xx[NDSAMPS]; + double y1[NDSAMPS]; + double y2[NDSAMPS]; + double y3[NDSAMPS]; + double y4[NDSAMPS]; + + for (i = 0; i < nummeas && i < NDSAMPS; i++) { + xx[i] = samp[i].sec; + y1[i] = samp[i].rgb[0]; + y2[i] = samp[i].rgb[1]; + y3[i] = samp[i].rgb[2]; + y4[i] = samp[i].tot; +//printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].tot); + } + printf("Display update delay measure sensor values and time (sec)\n"); + do_plot6(xx, y1, y2, y3, y4, NULL, NULL, nummeas); + } +#endif + /* Check that there has been a transition */ if (del < 5.0) { free(samp); - a1logd(p->log, 1, "i1pro_meas_delay: can't detect change from white to black\n"); + a1logd(p->log, 1, "i1pro_meas_delay: can't detect change from black to white\n"); return I1PRO_RD_NOTRANS_FOUND; } - /* Locate the time at which the values are above the end values */ - for (i = nummeas-1; i >= 0; i--) { + /* Working from the start, locate the time at which the level was above the threshold */ + for (i = 0; i < (nummeas-1); i++) { if (samp[i].tot > thr) break; } - if (i < 0) /* Assume the update was so fast that we missed it */ - i = 0; a1logd(p->log, 2, "i1pro_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec); - *msecdelay = (int)(samp[i].sec * 1000.0 + 0.5); + /* Compute overall delay */ + dispmsec = (int)(samp[i].sec * 1000.0 + 0.5); /* Display update time */ + instmsec = (int)((m->trigstamp - rstart)/1000.0 + 0.5); /* Reaction time */ + +#ifdef PLOT_UPDELAY + a1logd(p->log, 0, "i1pro_meas_delay: disp %d, trig %d msec\n",dispmsec,instmsec); +#else + a1logd(p->log, 2, "i1pro_meas_delay: disp %d, trig %d msec\n",dispmsec,instmsec); +#endif + + if (dispmsec < 0) /* This can happen if the patch generator delays it's return */ + dispmsec = 0; + + if (pdispmsec != NULL) + *pdispmsec = dispmsec; + + if (pinstmsec != NULL) + *pinstmsec = instmsec; #ifdef PLOT_UPDELAY - a1logd(p->log, 0, "i1pro_meas_delay: returning %d msec\n",*msecdelay); + a1logd(p->log, 0, "i1pro_meas_delay: returning %d & %d msec\n",dispmsec,instmsec); +#else + a1logd(p->log, 2, "i1pro_meas_delay: returning %d & %d msec\n",dispmsec,instmsec); #endif free(samp); @@ -2542,6 +2594,22 @@ int *msecdelay) { /* Return the number of msec */ #undef NDSAMPS #undef NDMXTIME +/* Timestamp the white patch change during meas_delay() */ +inst_code i1pro_imp_white_change(i1pro *p, int init) { + i1proimp *m = (i1proimp *)p->m; + + if (init) + m->whitestamp = -1.0; + else { + if ((m->whitestamp = usec_time()) < 0.0) { + a1loge(p->log, inst_internal_error, "i1pro_imp_wite_change: No high resolution timers\n"); + return inst_internal_error; + } + } + + return inst_ok; +} + /* - - - - - - - - - - - - - - - - */ /* Measure a patch or strip in the current mode. */ /* To try and speed up the reaction time between */ @@ -2592,7 +2660,7 @@ i1pro_code i1pro_imp_measure( return I1PRO_INT_WRONGPATCHES; } - /* Notional number of measurements, befor adaptive and not counting scan */ + /* Notional number of measurements, before adaptive and not counting scan */ nummeas = i1pro_comp_nummeas(p, s->wreadtime, s->inttime); /* Allocate buf for pre-measurement dark calibration */ @@ -2984,7 +3052,6 @@ i1pro_code i1pro_imp_meas_refrate( double **multimeas; /* Spectral measurements */ int nummeas; double rgbw[3] = { 610.0, 520.0, 460.0 }; - double ucalf = 1.0; /* usec_time calibration factor */ double inttime; static unsigned int randn = 0x12345678; struct { @@ -3015,6 +3082,9 @@ i1pro_code i1pro_imp_meas_refrate( a1logd(p->log,2,"i1pro_imp_meas_refrate called\n"); + if (ref_rate != NULL) + *ref_rate = 0.0; + if (!s->emiss) { a1logd(p->log,2,"i1pro_imp_meas_refrate not in emissive mode\n"); return I1PRO_UNSUPPORTED; @@ -3063,7 +3133,7 @@ i1pro_code i1pro_imp_meas_refrate( free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav[m->highres]-1); nfsamps = i; - a1logd(p->log, 3, "i1pro_measure_refresh: Read %d samples for refresh calibration\n",nfsamps); + a1logd(p->log, 3, "i1pro_meas_refrate: Read %d samples for refresh calibration\n",nfsamps); #ifdef NEVER /* Plot the raw sensor values */ @@ -3102,7 +3172,6 @@ i1pro_code i1pro_imp_meas_refrate( /* Re-zero the sample times, and normalise the readings */ for (i = nfsamps-1; i >= 0; i--) { samp[i].sec -= samp[0].sec; - samp[i].sec *= ucalf; if (samp[i].sec > maxt) maxt = samp[i].sec; for (j = 0; j < 3; j++) { @@ -3116,7 +3185,7 @@ i1pro_code i1pro_imp_meas_refrate( nbins = 1 + (int)(maxt * 1000.0 * PBPMS + 0.5); for (j = 0; j < 3; j++) { if ((bins[j] = (double *)calloc(sizeof(double), nbins)) == NULL) { - a1loge(p->log, inst_internal_error, "i1pro_measure_refresh: malloc failed\n"); + a1loge(p->log, inst_internal_error, "i1pro_meas_refrate: malloc failed\n"); return I1PRO_INT_MALLOC; } } @@ -3153,7 +3222,7 @@ i1pro_code i1pro_imp_meas_refrate( y3 = malloc(sizeof(double) * nbins); if (xx == NULL || y1 == NULL || y2 == NULL || y3 == NULL) { - a1loge(p->log, inst_internal_error, "i1pro_measure_refresh: malloc failed\n"); + a1loge(p->log, inst_internal_error, "i1pro_meas_refrate: malloc failed\n"); for (j = 0; j < 3; j++) free(bins[j]); return I1PRO_INT_MALLOC; @@ -3596,9 +3665,6 @@ i1pro_code i1pro_imp_meas_refrate( a1logd(p->log, 3, "Not enough tries suceeded to determine refresh rate\n"); } - if (ref_rate != NULL) - *ref_rate = 0.0; - return I1PRO_RD_NOREFR_FOUND; } #undef NFSAMPS @@ -4719,6 +4785,8 @@ i1pro_code i1pro_whitemeasure( /* absolute linearised sensor values. */ if ((ev = i1pro_sens_to_absraw(p, multimes, buf, nummeas, *inttime, gainmode, &darkthresh)) != I1PRO_OK) { + free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); + free(buf); return ev; } @@ -4734,8 +4802,8 @@ i1pro_code i1pro_whitemeasure( ev = i1pro_whitemeasure_3(p, abswav0, abswav1, absraw, optscale, nummeas, *inttime, gainmode, targoscale, multimes, darkthresh); - free(buf); free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); + free(buf); return ev; } @@ -4954,6 +5022,9 @@ i1pro_code i1pro2_wl_measure( /* absolute linearised sensor values. */ if ((ev = i1pro_sens_to_absraw(p, multimes, buf, nummeas, *inttime, gainmode, &darkthresh)) != I1PRO_OK) { + free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); + free_dvector(dark, -1, m->nraw-1); + free(buf); return ev; } @@ -4993,11 +5064,17 @@ i1pro_code i1pro2_wl_measure( #ifndef IGNORE_WHITE_INCONS if (rv & 1) { + free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); + free_dvector(dark, -1, m->nraw-1); + free(buf); return I1PRO_RD_WHITEREADINCONS; } #endif /* IGNORE_WHITE_INCONS */ if (rv & 2) { + free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); + free_dvector(dark, -1, m->nraw-1); + free(buf); return I1PRO_RD_SENSORSATURATED; } @@ -5017,9 +5094,9 @@ i1pro_code i1pro2_wl_measure( *optscale = opttarget/lhighest; } - free(buf); free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); free_dvector(dark, -1, m->nraw-1); + free(buf); return ev; } @@ -5102,7 +5179,9 @@ i1pro_code i1pro_read_patches_2( /* Take a buffer full of raw readings, and convert them to */ /* absolute linearised sensor values. */ if ((ev = i1pro_sens_to_absraw(p, multimes, buf, nmeasuered, inttime, gainmode, &darkthresh)) - != I1PRO_OK) { + != I1PRO_OK) { + free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1); + free_dmatrix(multimes, 0, nmeasuered-1, -1, m->nraw-1); return ev; } @@ -5184,6 +5263,7 @@ i1pro_code i1pro_read_patches_2( } } } + free_dmatrix(multimes, 0, nmeasuered-1, -1, m->nraw-1); if (rv & 1) { free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1); @@ -5470,6 +5550,9 @@ i1pro_code i1pro_trialmeasure( /* absolute linearised sensor values. */ if ((ev = i1pro_sens_to_absraw(p, multimes, buf, nmeasuered, *inttime, gainmode, &darkthresh)) != I1PRO_OK) { + free_dvector(absraw, -1, m->nraw-1); + free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); + free(buf); return ev; } @@ -6504,6 +6587,9 @@ i1pro_code i1pro_extract_patches_multimeas( /* Now threshold the measurements into possible patches */ apat = 2 * nummeas; if ((pat = (i1pro_patch *)malloc(sizeof(i1pro_patch) * apat)) == NULL) { + free_ivector(sizepop, 0, nummeas-1); + free_dvector(slope, 0, nummeas-1); + free_dvector(maxval, -1, m->nraw-1); a1logd(p->log, 1, "i1pro: malloc of patch structures failed!\n"); return I1PRO_INT_MALLOC; } @@ -6517,6 +6603,9 @@ i1pro_code i1pro_extract_patches_multimeas( if (npat >= apat) { apat *= 2; if ((pat = (i1pro_patch *)realloc(pat, sizeof(i1pro_patch) * apat)) == NULL) { + free_ivector(sizepop, 0, nummeas-1); + free_dvector(slope, 0, nummeas-1); + free_dvector(maxval, -1, m->nraw-1); a1logd(p->log, 1, "i1pro: reallloc of patch structures failed!\n"); return I1PRO_INT_MALLOC; } @@ -6969,7 +7058,7 @@ i1pro_code i1pro_extract_patches_flash( nsampl++; } - /* Average all the values over the threshold, */ + /* Integrate all the values over the threshold, */ /* and also one either side of flash */ for (j = 0; j < m->nraw-1; j++) pavg[j] = 0.0; @@ -7370,7 +7459,7 @@ static double wlcal_opt1(void *vcx, double tp[]) { ix = ((int)xv) - 1; /* Reference index of Lagrange for this xv */ if (ix < 0) continue; - if ((ix + 3) > cx->wl_ref_n) + if ((ix + 4) > cx->wl_ref_n) break; /* Compute interpolated value of reference using Lagrange: */ @@ -8385,7 +8474,9 @@ i1pro_code i1pro_create_hr_calfactors(i1pro *p, int eonly) { s->cal_factor[0], m->white_ref[0], s->cal_factor[0], s->cal_factor[1], m->white_ref[1], s->cal_factor[1], i == i1p_refl_spot); - if (ev != I1PRO_RD_TRANSWHITEWARN && ev != I1PRO_OK) { + if (ev == I1PRO_RD_TRANSWHITEWARN) /* Shouldn't happen ? */ + ev = I1PRO_OK; + if (ev != I1PRO_OK) { return ev; } #ifdef NEVER @@ -8429,7 +8520,9 @@ i1pro_code i1pro_create_hr_calfactors(i1pro *p, int eonly) { i1pro_absraw_to_abswav(p, 1, s->reflective, 1, &s->cal_factor[1], &s->white_data); ev = i1pro_compute_white_cal(p, s->cal_factor[0], NULL, s->cal_factor[0], s->cal_factor[1], NULL, s->cal_factor[1], 0); - if (ev != I1PRO_RD_TRANSWHITEWARN && ev != I1PRO_OK) { + if (ev == I1PRO_RD_TRANSWHITEWARN) /* Ignore this ? */ + ev = I1PRO_OK; + if (ev != I1PRO_OK) { return ev; } } @@ -8975,7 +9068,8 @@ i1pro_code i1pro_create_hr(i1pro *p) { /* Our upsampling is OK for reflective and ambient cal's, */ /* but isn't so good for the emissive cal., especially */ - /* on the i1pro2. We'll get an opportunity to fix it */ + /* on the i1pro2 which has a rather bumpy diffraction */ + /* grating/sensor. We'll get an opportunity to fix it */ /* when we do a reflective calibration, by using the */ /* smoothness of the lamp as a reference. */ @@ -9947,20 +10041,23 @@ i1pro_code i1pro_check_white_reference1( free_dvector(emiswav, -1, m->nraw-1); + /* And check them against tolerance for the illuminant. */ if (m->physfilt == 0x82) { /* UV filter */ + a1logd(p->log,2,"Checking white reference (UV): 0.0 < avg01 %f < 0.05, 1.2 < avg2227 %f < 1.76\n",avg01,avg2227); if (0.0 < avg01 && avg01 < 0.05 && 1.2 < avg2227 && avg2227 < 1.76) { return I1PRO_OK; } } else { /* No filter */ + a1logd(p->log,2,"Checking white reference: 0.11 < avg01 %f < 0.22, 1.35 < avg2227 %f < 1.6\n",avg01,avg2227); if (0.11 < avg01 && avg01 < 0.22 && 1.35 < avg2227 && avg2227 < 1.6) { return I1PRO_OK; } } - a1logd(p->log,2,"Checking white reference failed, 0.11 < avg01 %f < 0.22, 1.35 < avg2227 %f < 1.6\n",avg01,avg2227); + a1logd(p->log,2,"Checking white reference failed - out of tollerance"); return I1PRO_RD_WHITEREFERROR; } @@ -10504,7 +10601,9 @@ int i1pro_switch_thread(void *pp) { i1proimp *m = (i1proimp *)p->m; i1pro_code rv = I1PRO_OK; a1logd(p->log,3,"Switch thread started\n"); - for (nfailed = 0;nfailed < 5;) { +// for (nfailed = 0;nfailed < 5;) + /* Try indefinitely, in case instrument is put to sleep */ + for (;;) { rv = i1pro_waitfor_switch_th(p, SW_THREAD_TIMEOUT); a1logd(p->log,8,"Switch handler triggered with rv %d, th_term %d\n",rv,m->th_term); if (m->th_term) { @@ -10928,7 +11027,7 @@ i1pro_delayed_trigger(void *pp) { se = p->icom->usb_control(p->icom, IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE, 0xC0, 0, 0, NULL, 0, 2.0); - + m->trigstamp = usec_time(); m->tr_t2 = msec_time(); /* Diagnostic */ m->trig_se = se; @@ -10954,8 +11053,10 @@ i1pro_triggermeasure(i1pro *p, int delay) { /* NOTE := would be better here to create thread once, and then trigger it */ /* using a condition variable. */ - if (m->trig_thread != NULL) + if (m->trig_thread != NULL) { m->trig_thread->del(m->trig_thread); + m->trig_thread = NULL; + } m->tr_t1 = m->tr_t2 = m->tr_t3 = m->tr_t4 = m->tr_t5 = m->tr_t6 = m->tr_t7 = 0; m->trig_delay = delay; @@ -10981,7 +11082,7 @@ i1pro_triggermeasure(i1pro *p, int delay) { /* between triggering the measurement and starting this read. */ /* It appears that the read can be pending before triggering though. */ /* Scan reads will also terminate if there is too great a delay beteween each read.) */ -i1pro_code +static i1pro_code i1pro_readmeasurement( i1pro *p, int inummeas, /* Initial number of measurements to expect */ @@ -11525,6 +11626,7 @@ i1pro2_delayed_trigger(void *pp) { a1logd(p->log,2,"i1pro2_delayed_trigger: trigger Rev E @ %d msec\n", (stime = msec_time()) - m->msec); + m->trigstamp = usec_time(); se = p->icom->usb_control(p->icom, IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE, 0xD4, 0, 0, pbuf, 14, 2.0); diff --git a/spectro/i1pro_imp.h b/spectro/i1pro_imp.h index c0d0708..6bf8de4 100644 --- a/spectro/i1pro_imp.h +++ b/spectro/i1pro_imp.h @@ -319,6 +319,9 @@ struct _i1proimp { int trig_se; /* Delayed trigger icoms error */ i1pro_code trig_rv; /* Delayed trigger result */ + volatile double whitestamp; /* meas_delay() white timestamp */ + volatile double trigstamp; /* meas_delay() trigger timestamp */ + }; typedef struct _i1proimp i1proimp; /* Add an implementation structure */ @@ -344,6 +347,7 @@ void del_i1proimp(i1pro *p); #define I1PRO_UNSUPPORTED 0x79 /* Unsupported function */ #define I1PRO_CAL_SETUP 0x7A /* Cal. retry with correct setup is needed */ +#define I1PRO_RD_TRANSWHITEWARN 0x7B /* Transmission white ref wl are low */ /* Real error code */ #define I1PRO_OK 0x00 @@ -395,10 +399,6 @@ void del_i1proimp(i1pro *p); #define I1PRO_RD_NOREFR_FOUND 0x40 /* Unable to measure refresh rate */ #define I1PRO_RD_NOTRANS_FOUND 0x41 /* Unable to measure delay transition */ -#define I1PRO_CAL_SETUP 0x7A /* Cal. retry with correct setup is needed */ -#define I1PRO_RD_TRANSWHITEWARN 0x7B /* Transmission white ref wl are low */ - - /* Internal errors */ #define I1PRO_INT_NO_COMS 0x50 #define I1PRO_INT_EETOOBIG 0x51 /* EEProm read size is too big */ @@ -480,8 +480,11 @@ i1pro_code i1pro_imp_meas_refrate( /* Measure the display update delay */ i1pro_code i1pro_imp_meas_delay( i1pro *p, - int *msecdelay -); + int *pdispmsec, + int *pinstmsec); + +/* Timestamp the white patch change during meas_delay() */ +inst_code i1pro_imp_white_change(i1pro *p, int init); /* Given a raw measurement of the wavelength LED, */ /* Compute the base offset that best fits it to the reference */ @@ -979,7 +982,7 @@ i1pro_triggermeasure(i1pro *p, int delay); /* Read a measurements results */ -i1pro_code +static i1pro_code i1pro_readmeasurement( i1pro *p, int inummeas, /* Initial number of measurements to expect */ diff --git a/spectro/icoms.c b/spectro/icoms.c index 0166e21..fb0fa20 100644 --- a/spectro/icoms.c +++ b/spectro/icoms.c @@ -369,10 +369,12 @@ static int icoms_write_read( icoms *p, char *wbuf, /* Write puffer */ +int nwch, /* if > 0, number of characters to write */ char *rbuf, /* Read buffer */ int bsize, /* Buffer size */ -char *tc, /* Terminating characers, NULL for none */ -int ntc, /* Number of terminating characters needed to terminate */ +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 ) { int rv = ICOM_OK; @@ -392,14 +394,14 @@ double tout if (debug < 8) p->log->debug = 0; for (; rv == ICOM_OK;) /* Until we get a timeout */ - rv = p->read(p, rbuf, bsize, '\000', 100000, 0.01); + rv = p->read(p, rbuf, bsize, NULL, NULL, bsize, 0.01); p->log->debug = debug; rv = ICOM_OK; } #endif /* Write the write data */ - rv = p->write(p, wbuf, tout); + rv = p->write(p, wbuf, nwch, tout); /* Return error if coms */ if (rv != ICOM_OK) { @@ -408,7 +410,7 @@ double tout } /* Read response */ - rv = p->read(p, rbuf, bsize, tc, ntc, tout); + rv = p->read(p, rbuf, bsize, bread, tc, ntc, tout); a1logd(p->log, 8, "icoms_write_read: returning 0x%x\n",rv); @@ -504,21 +506,56 @@ void bad_beep() { msec_beep(350, 800, 200); } +char *baud_rate_to_str(baud_rate br) { + switch (br) { + case baud_nc: + return "Not Configured"; + case baud_110: + return "110"; + case baud_300: + return "300"; + case baud_600: + return "600"; + case baud_1200: + return "1400"; + case baud_2400: + return "2400"; + case baud_4800: + return "4800"; + case baud_9600: + return "9600"; + case baud_14400: + return "14400"; + case baud_19200: + return "19200"; + case baud_38400: + return "38400"; + case baud_57600: + return "57600"; + case baud_115200: + return "115200"; + case baud_921600: + return "921600"; + } + return "Unknown"; +} + /* Convert control chars to ^[A-Z] notation in a string */ -/* Can be called 5 times without reusing the static buffer */ +/* Can be called 3 times without reusing the static buffer */ +/* Returns a maximum of 5000 characters */ char * icoms_fix(char *ss) { - static unsigned char buf[5][1005]; + static unsigned char buf[3][10005]; static int ix = 0; unsigned char *d; unsigned char *s = (unsigned char *)ss; - if (++ix >= 5) + if (++ix >= 3) ix = 0; if (ss == NULL) { strcpy((char *)buf[ix],"(null)"); return (char *)buf[ix]; } - for(d = buf[ix]; (d - buf[ix]) < 1000;) { + for(d = buf[ix]; (d - buf[ix]) < 10000;) { if (*s < ' ' && *s > '\000') { *d++ = '^'; *d++ = *s++ + '@'; diff --git a/spectro/icoms.h b/spectro/icoms.h index f219312..a9d8d34 100644 --- a/spectro/icoms.h +++ b/spectro/icoms.h @@ -152,7 +152,8 @@ typedef enum { fc_nc = 0, /* not configured/default */ fc_none, fc_XonXOff, - fc_Hardware + fc_Hardware, /* RTS CTS flow control */ + fc_HardwareDTR /* DTR DSR flow control */ } flow_control; /* baud rate available on all systems */ @@ -173,6 +174,8 @@ typedef enum { baud_921600 = 13 } baud_rate; +char *baud_rate_to_str(baud_rate br); + /* Possible parity */ typedef enum { parity_nc, @@ -209,7 +212,7 @@ typedef enum { /* Type of port */ typedef enum { icomt_serial, /* Serial port */ - icomt_usbserial, /* USB Serial port */ + icomt_usbserial, /* USB (fast) Serial port, i.e. FTDI */ icomt_usb, /* USB port */ icomt_hid /* HID (USB) port */ } icom_type; @@ -295,8 +298,10 @@ struct _icoms { int nep; /* Number of end points */ int wr_ep, rd_ep; /* Default end points to use for "serial" read/write */ int rd_qa; /* Read quanta size */ - int ms_bytes; /* No. Modem status bytes to strip from each read */ + int ms_bytes; /* No. of Modem status bytes to strip from each read */ int latmsec; /* Latency timeout in msec for modem status bytes */ + int (*interp_ms)(struct _icoms *p, unsigned char *msbytes); + /* return icom error from ms bytes, NULL if none */ usb_ep ep[32]; /* Information about each end point for general usb i/o */ @@ -364,7 +369,8 @@ struct _icoms { /* return icom error */ int (*write)( struct _icoms *p, - char *buf, + char *buf, /* null terminated unless nch > 0 */ + int nch, /* if > 0, number of characters to write */ double tout); /* Timeout in seconds */ /* "Serial" read characters into the buffer */ @@ -374,8 +380,10 @@ struct _icoms { struct _icoms *p, char *buf, /* Buffer to store characters read */ int bsize, /* Buffer size */ - char *tc, /* Terminating characters */ - int ntc, /* Number of terminating characters seen */ + int *bread, /* Bytes read (not including forced '\000') */ + char *tc, /* Terminating characters, NULL for none or char count mode */ + int ntc, /* Number of terminating characters or char count needed, */ + /* if 0 use bsize. */ double tout); /* Timeout in seconds */ /* "Serial" write and read */ @@ -383,10 +391,12 @@ struct _icoms { int (*write_read)( struct _icoms *p, char *wbuf, /* Write puffer */ + int nwch, /* if > 0, number of characters to write */ char *rbuf, /* Read buffer */ int bsize, /* Buffer size */ - char *tc, /* Terminating characers */ - int ntc, /* Number of terminating characters seen */ + 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 */ /* For a USB device, do a control message */ @@ -467,7 +477,8 @@ extern icoms *new_icoms(icompath *ipath, a1log *log); /* - - - - - - - - - - - - - - - - - - -- */ /* Utilities */ -/* Convert control chars to ^[A-Z] notation in a string */ +/* Convert control chars to ^[A-Z] notation in a string. */ +/* Returns a maximum of 1000 characters in static buffer. */ char *icoms_fix(char *s); /* Convert a limited binary buffer to a list of hex */ diff --git a/spectro/icoms_nt.c b/spectro/icoms_nt.c index 3caa8e0..6e2741d 100644 --- a/spectro/icoms_nt.c +++ b/spectro/icoms_nt.c @@ -229,9 +229,15 @@ int icompaths_refresh_paths(icompaths *p) { fast = 1; } +#ifndef ENABLE_SERIAL + if (fast) { /* Only add fast ports if !ENABLE_SERIAL */ +#endif /* Add the port to the list */ p->add_serial(p, value, value, fast); a1logd(p->log, 8, "icoms_get_paths: Added path '%s' fast %d\n",value,fast); +#ifndef ENABLE_SERIAL + } +#endif /* If fast, try and identify it */ if (fast) { @@ -249,7 +255,7 @@ int icompaths_refresh_paths(icompaths *p) { if ((stat = RegCloseKey(sch)) != ERROR_SUCCESS) { a1logw(p->log, "icoms_get_paths: RegCloseKey failed with %d\n",stat); } -#endif /* ENABLE_SERIAL */ +#endif /* ENABLE_SERIAL || ENABLE_FAST_SERIAL */ /* Sort the COM keys so people don't get confused... */ /* Sort identified instruments ahead of unknown serial ports */ @@ -300,8 +306,9 @@ static void icoms_close_port(icoms *p) { #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) -static int icoms_ser_write(icoms *p, char *wbuf, double tout); -static int icoms_ser_read(icoms *p, char *rbuf, int bsize, char *tc, int ntc, double tout); +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 @@ -383,7 +390,7 @@ word_length word) dcb.fOutxCtsFlow = FALSE; /* Not using Cts flow control */ dcb.fOutxDsrFlow = FALSE; /* Not using Dsr Flow control */ dcb.fDtrControl = DTR_CONTROL_ENABLE; /* Enable DTR during connection */ - dcb.fDsrSensitivity = FALSE; /* Not using Dsr Flow control */ + dcb.fDsrSensitivity = FALSE; /* Not using Dsr to ignore characters */ dcb.fTXContinueOnXoff = TRUE; /* */ dcb.fOutX = FALSE; /* No default Xon/Xoff flow control */ dcb.fInX = FALSE; /* No default Xon/Xoff flow control */ @@ -411,6 +418,11 @@ word_length word) dcb.fOutxCtsFlow = TRUE; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; break; + case fc_HardwareDTR: + /* Use DTR/DSR bi-directional flow control */ + dcb.fOutxDsrFlow = TRUE; + dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; + break; default: break; } @@ -544,7 +556,8 @@ word_length word) static int icoms_ser_write( icoms *p, -char *wbuf, +char *wbuf, /* null terminated unless nwch > 0 */ +int nwch, /* if > 0, number of characters to write */ double tout) { COMMTIMEOUTS tmo; @@ -560,7 +573,10 @@ double tout) return rv; } - len = strlen(wbuf); + if (nwch != 0) + len = nwch; + else + len = strlen(wbuf); tout *= 1000.0; /* Timout in msec */ top = 20; /* Timeout period in msecs */ @@ -620,8 +636,9 @@ icoms_ser_read( icoms *p, char *rbuf, /* Buffer to store characters read */ int bsize, /* Buffer size */ -char *tc, /* Terminating characers, NULL for none */ -int ntc, /* Number of terminating characters seen */ +int *pbread, /* Bytes read (not including forced '\000') */ +char *tc, /* Terminating characers, NULL for none or char count mode */ +int ntc, /* Number of terminating characters or char count needed, if 0 use bsize */ double tout /* Time out in seconds */ ) { COMMTIMEOUTS tmo; @@ -630,6 +647,7 @@ double tout /* Time out in seconds */ long toc, i, top; /* Timout count, counter, timeout period */ char *rrbuf = rbuf; /* Start of return buffer */ DCB dcb; + int bread = 0; int rv = ICOM_OK; if (p->phandle == NULL) { @@ -639,13 +657,13 @@ double tout /* Time out in seconds */ } if (bsize < 3) { - a1loge(p->log, ICOM_SYS, "icoms_read: given too small a bufferi (%d)\n",bsize); + a1loge(p->log, ICOM_SYS, "icoms_read: given too small a buffer (%d)\n",bsize); p->lserr = rv = ICOM_SYS; return rv; } tout *= 1000.0; /* Timout in msec */ - bsize--; /* Allow space for null */ + bsize--; /* Allow space for forced final null */ top = 20; /* Timeout period in msecs */ toc = (int)(tout/top + 0.5); /* Number of timout periods in timeout */ @@ -664,8 +682,17 @@ double tout /* Time out in seconds */ return rv; } + if (tc == NULL) { /* no tc or char count mode */ + j = -1; + if (ntc > 0 && ntc < bsize) + bsize = ntc; /* Don't read more than ntc */ + } else { + j = 0; + } + j = (tc == NULL && ntc <= 0) ? -1 : 0; + /* Until data is all read or we time out */ - for (i = toc, j = 0; i > 0 && bsize > 1 && j < ntc ;) { + for (i = toc; i > 0 && bsize > 0 && j < ntc ;) { if (!ReadFile(p->phandle, rbuf, bsize, &rbytes, NULL)) { DWORD errs; if (!ClearCommError(p->phandle,&errs,NULL)) @@ -684,6 +711,7 @@ double tout /* Time out in seconds */ } else if (rbytes > 0) { /* Account for bytes done */ i = toc; bsize -= rbytes; + bread += rbytes; if (tc != NULL) { while(rbytes--) { /* Count termination characters */ char ch = *rbuf++, *tcp = tc; @@ -694,14 +722,20 @@ double tout /* Time out in seconds */ tcp++; } } - } else + } else { + if (ntc > 0) + j += rbytes; rbuf += rbytes; + } } } if (i <= 0) { /* timed out */ rv |= ICOM_TO; } *rbuf = '\000'; + if (pbread != NULL) + *pbread = bread; + a1logd(p->log, 8, "icoms_ser_read: returning '%s' ICOM err 0x%x\n",icoms_fix(rrbuf),rv); p->lserr = rv; diff --git a/spectro/icoms_ux.c b/spectro/icoms_ux.c index 3e49f3e..8fd1b3e 100644 --- a/spectro/icoms_ux.c +++ b/spectro/icoms_ux.c @@ -124,10 +124,15 @@ int icompaths_refresh_paths(icompaths *p) { if (strstr(pname, "usbserial") != NULL) fast = 1; +#ifndef ENABLE_SERIAL + if (fast) { /* Only add fast ports if !ENABLE_SERIAL */ +#endif /* Add the port to the list */ p->add_serial(p, pname, pname, fast); a1logd(p->log, 8, "icoms_get_paths: Added path '%s' fast %d\n",pname, fast); - +#ifndef ENABLE_SERIAL + } +#endif /* If fast, try and identify it */ if (fast) { icompath *path; @@ -218,7 +223,7 @@ int icompaths_refresh_paths(icompaths *p) { break; if (!( -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__OpenBSD__) /* This should match uart & USB devs. */ ( strncmp (de->d_name, "cua", 3) == 0 && strlen (de->d_name) < 7) @@ -268,9 +273,15 @@ int icompaths_refresh_paths(icompaths *p) { if (strncmp(de->d_name, "ttyUSB", 5) == 0) fast = 1; +#ifndef ENABLE_SERIAL + if (fast) { /* 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' fast %d\n",dpath,fast); +#ifndef ENABLE_SERIAL + } +#endif free(dpath); /* If fast, try and identify it */ @@ -332,14 +343,18 @@ static void icoms_close_port(icoms *p) { #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) -static int icoms_ser_write(icoms *p, char *wbuf, double tout); -static int icoms_ser_read(icoms *p, char *rbuf, int bsize, char *tc, int ntc, double tout); +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); #ifdef __APPLE__ # ifndef IOSSIOSPEED # define IOSSIOSPEED _IOW('T', 2, speed_t) # endif +#endif + +#if defined(__APPLE__) || defined(__OpenBSD__) # ifndef B921600 # define B921600 921600 # endif @@ -451,6 +466,17 @@ word_length word tio.c_cflag |= CRTSCTS; #endif break; + case fc_HardwareDTR: + /* Use DTR/DSR bi-directional flow control */ +#ifdef __APPLE__ + tio.c_cflag |= CDSR_OFLOW; + tio.c_cflag |= CDTR_IFLOW; +#else +#ifdef CDTRDSR /* Hmm. Not all Linux's support this... */ + tio.c_cflag |= CDTRDSR; +#endif +#endif + break; default: break; } @@ -600,7 +626,8 @@ word_length word static int icoms_ser_write( icoms *p, -char *wbuf, +char *wbuf, /* null terminated unless nwch > 0 */ +int nwch, /* if > 0, number of characters to write */ double tout ) { int rv, retrv = ICOM_OK; @@ -610,7 +637,6 @@ double tout int nfd = 1; /* Number of fd's to poll */ struct termios origs, news; - a1logd(p->log, 8, "icoms_ser_write: About to write '%s' ",icoms_fix(wbuf)); if (!p->is_open) { a1loge(p->log, ICOM_SYS, "icoms_ser_write: device not initialised\n"); p->lserr = rv = ICOM_SYS; @@ -623,8 +649,12 @@ double tout pa[0].revents = 0; /* Until timed out, aborted, or transmitted */ - len = strlen(wbuf); + if (nwch != 0) + len = nwch; + else + len = strlen(wbuf); tout *= 1000.0; /* Timout in msec */ + a1logd(p->log, 8, "icoms_ser_write: About to write %d bytes",len); top = 100; /* Timeout period in msecs */ toc = (int)(tout/top + 0.5); /* Number of timout periods in timeout */ @@ -674,8 +704,9 @@ icoms_ser_read( icoms *p, char *rbuf, /* Buffer to store characters read */ int bsize, /* Buffer size */ -char *tc, /* Terminating characers, NULL for none */ -int ntc, /* Number of terminating characters */ +int *pbread, /* Bytes read (not including forced '\000') */ +char *tc, /* Terminating characers, NULL for none or char count mode */ +int ntc, /* Number of terminating characters or char count needed, if 0 use bsize */ double tout /* Time out in seconds */ ) { int rv, retrv = ICOM_OK; @@ -685,6 +716,7 @@ double tout /* Time out in seconds */ int nfd = 1; /* Number of fd's to poll */ struct termios origs, news; char *rrbuf = rbuf; /* Start of return buffer */ + int bread = 0; if (!p->is_open) { a1loge(p->log, ICOM_SYS, "icoms_ser_read: device not initialised\n"); @@ -698,12 +730,14 @@ double tout /* Time out in seconds */ return rv; } + a1logd(p->log, 8, "icoms_ser_read: About to read buf %d, tc %p ntc %d tout %f",bsize,tc,ntc,tout); + /* Wait for serial input to have data */ pa[0].fd = p->fd; pa[0].events = POLLIN | POLLPRI; pa[0].revents = 0; - bsize--; /* Allow space for null */ + bsize--; /* Allow space for forced null */ tout *= 1000.0; /* Timout in msec */ top = 100; /* Timeout period in msecs */ @@ -711,11 +745,18 @@ double tout /* Time out in seconds */ if (toc < 1) toc = 1; + if (tc == NULL) { /* no tc or char count mode */ + j = -1; + if (ntc > 0 && ntc < bsize) + bsize = ntc; /* Don't read more than ntc */ + } else { + j = 0; + } /* Until data is all read, we time out, or the user aborts */ - for(i = toc, j = 0; i > 0 && bsize > 1 && j < ntc ;) { - + for (i = toc; i > 0 && bsize > 0 && j < ntc ;) { if (poll_x(pa, nfd, top) > 0) { if (pa[0].revents != 0) { + int btr; if (pa[0].revents != POLLIN && pa[0].revents != POLLPRI) { a1loge(p->log, ICOM_SYS, "icoms_ser_read: poll on serin returned " "unexpected value 0x%x",pa[0].revents); @@ -730,6 +771,7 @@ double tout /* Time out in seconds */ } else if (rbytes > 0) { i = toc; /* Reset time */ bsize -= rbytes; + bread += rbytes; if (tc != NULL) { while(rbytes--) { /* Count termination characters */ char ch = *rbuf++, *tcp = tc; @@ -740,8 +782,11 @@ double tout /* Time out in seconds */ tcp++; } } - } else + } else { + if (ntc > 0) + j += rbytes; rbuf += rbytes; + } } } } else { @@ -749,10 +794,12 @@ double tout /* Time out in seconds */ } } - *rbuf = '\000'; - if (i <= 0) { /* timed out */ + if (i <= 0) /* timed out */ retrv |= ICOM_TO; - } + + *rbuf = '\000'; + if (pbread != NULL) + *pbread = bread; a1logd(p->log, 8, "icoms_ser_read: returning '%s' ICOM err 0x%x\n",icoms_fix(rrbuf),retrv); diff --git a/spectro/ifiles b/spectro/ifiles index 717fcca..b2806f5 100644 --- a/spectro/ifiles +++ b/spectro/ifiles @@ -16,3 +16,4 @@ munki_imp.c hcfr.c colorhug.c specbos.c +kleink10.c diff --git a/spectro/illumread.c b/spectro/illumread.c index 7cc7eae..381f6b6 100644 --- a/spectro/illumread.c +++ b/spectro/illumread.c @@ -30,17 +30,21 @@ */ -#define DEBUG /* Save measurements and restore them to outname_X.sp */ - /* +/* + Test mode files restored/saved: + + outname_i.sp Illuminant spectrum + outname_r.sp Illuminant off paper spectrum + outname_p.sp Instrument measured paper reflectance spectrum - outname_i.sp Illuminant spectrum - outname_r.sp Illuminant off paper spectrum - outname_p.sp Instrument measured paper reflectance spectrum - outname_mpir.sp Measured paper under illuminant spectrum - outname_cpir.sp Computed paper under illuminant spectrum + Just saved: - */ + outname_mpir.sp Measured paper under illuminant spectrum + outname_cpir.sp Computed paper under illuminant spectrum + */ +#undef DEBUG /* Debug messages */ +#undef PLOT_FITTING /* Plot the fitting itterations */ #undef SHOWDXX /* Plot the best matched daylight as well */ #include <stdio.h> @@ -59,7 +63,10 @@ #include "icoms.h" #include "instappsup.h" #include "plot.h" -#include "spyd2setup.h" /* Enable Spyder 2 access */ +#include "ui.h" +#ifdef ENABLE_USB +# include "spyd2.h" +#endif #include <stdarg.h> @@ -123,10 +130,15 @@ static double bfindfunc(void *adata, double pv[]) { double rv = 0.0; /* Add UV level to illuminant */ - b->ill = *b->i_sp; + b->ill = *b->i_sp; /* Structure copy */ xsp_setUV(&b->ill, b->i_sp, pv[0]); /* Extends ill range into UV */ - /* Update the conversion */ +#ifdef NEVER /* Plot the two reflectance spectra */ + printf("Black = Measured illum, Red = illum + %f UV\n",pv[0]); + xspect_plot(b->i_sp, &b->ill, NULL); +#endif + + /* Update the conversion to use observer illuminant with UV */ if (b->pap->update_fwa_custillum(b->pap, NULL, &b->ill) != 0) error ("Updating FWA compensation failed"); @@ -161,7 +173,13 @@ static double bfindfunc(void *adata, double pv[]) { /* if the case is unconstrained. */ rv += 0.1 * fabs(pv[0]); -//printf("~1 rev = %f (%f %f %f - %f %f %f) from %f %f\n",rv, b->lab0[0], b->lab0[1], b->lab0[2], b->lab1[0], b->lab1[1], b->lab1[2], pv[0], pv[1]); +#ifdef PLOT_FITTING /* Plot the two reflectance spectra */ + printf("rev = %f (%f %f %f - %f %f %f) from UV %f GA %f\n",rv, b->lab0[0], b->lab0[1], b->lab0[2], b->lab1[0], b->lab1[1], b->lab1[2], pv[0], pv[1]); + + printf("Black = Measured, Red = trial\n"); + xspect_plot(&b->srop, &b->cpsp, NULL); +#endif + return rv; } @@ -216,8 +234,6 @@ usage(char *diag, ...) { icompaths *icmps; fprintf(stderr,"Measure an illuminant, Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); - if (setup_spyd2() == 2) - fprintf(stderr,"WARNING: This file contains a proprietary firmware image, and may not be freely distributed !\n"); if (diag != NULL) { va_list args; fprintf(stderr,"Diagnostic: "); @@ -237,7 +253,8 @@ usage(char *diag, ...) { for (i = 0; ; i++) { if (paths[i] == NULL) break; - if (paths[i]->itype == instSpyder2 && setup_spyd2() == 0) + 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 fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name); @@ -250,6 +267,8 @@ usage(char *diag, ...) { fprintf(stderr," -Y r Set refresh measurement mode\n"); fprintf(stderr," -Y R:rate Override measured refresh rate with rate Hz\n"); fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n"); + fprintf(stderr," -T Test mode - restore & save measurements to\n"); + fprintf(stderr," *_i.sp, *_r.sp, *_p.sp, *_mpir.sp, *_cpir.sp files\n"); fprintf(stderr," -D [level] Print debug diagnostics to stderr\n"); fprintf(stderr," illuminant.sp File to save measurement to\n"); @@ -270,10 +289,10 @@ int main(int argc, char *argv[]) int refrmode = -1; /* -1 = default, = non-refresh mode, 1 = non-refresh mode */ double refrate = 0.0; /* 0.0 = default, > 0.0 = override refresh rate */ char outname[MAXNAMEL+1] = "\000"; /* Spectral output file name */ -#ifdef DEBUG - char tname[MAXNAMEL+11] = "\000", *tnp; - int rd_i = 0, rd_r = 0, rd_p = 0; -#endif + + int tmode = 0; /* Test mode */ + char tname[MAXNAMEL+11] = "\000", *tnp; /* Test mode file names */ + int rd_i = 0, rd_r = 0, rd_p = 0; /* Test mode flags */ icompaths *icmps = NULL; /* Ports to choose from */ int comno = 1; /* Specific port suggested by user */ @@ -300,7 +319,6 @@ int main(int argc, char *argv[]) set_exe_path(argv[0]); /* Set global exe_path and error_program */ check_if_not_interactive(); - setup_spyd2(); /* Load firware if available */ i_sp.spec_n = 0; r_sp.spec_n = 0; @@ -383,6 +401,9 @@ int main(int argc, char *argv[]) else usage(NULL); + } else if (argv[fa][1] == 'T') { + tmode = 1; + } else if (argv[fa][1] == 'D') { debug = 1; if (na != NULL && na[0] >= '0' && na[0] <= '9') { @@ -403,31 +424,31 @@ int main(int argc, char *argv[]) strncpy(outname,argv[fa++],MAXNAMEL-1); outname[MAXNAMEL-1] = '\000'; -#ifdef DEBUG - strcpy(tname, outname); - if ((tnp = strrchr(tname, '.')) == NULL) - tnp = tname + strlen(tname); - - /* Special debug */ - strcpy(tnp, "_i.sp"); - if (read_xspect(&i_sp, 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) { - rd_r = 1; - printf("(Found '%s' file and loaded it)\n",tname); - } - strcpy(tnp, "_p.sp"); - if (read_xspect(&p_sp, tname) == 0) { - rd_p = 1; - /* Should read instrument type from debug_p.sp !! */ - if (inst_illuminant(&insp, instI1Pro) != 0) /* Hack !! */ - error ("Instrument doesn't have an FWA illuminent"); - printf("(Found '%s' file and loaded it)\n",tname); + if (tmode) { + strcpy(tname, outname); + if ((tnp = strrchr(tname, '.')) == NULL) + tnp = tname + strlen(tname); + + /* Special debug */ + strcpy(tnp, "_i.sp"); + if (read_xspect(&i_sp, 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) { + rd_r = 1; + printf("(Found '%s' file and loaded it)\n",tname); + } + strcpy(tnp, "_p.sp"); + if (read_xspect(&p_sp, tname) == 0) { + rd_p = 1; + /* Should read instrument type from debug_p.sp !! */ + if (inst_illuminant(&insp, instI1Pro) != 0) /* Hack !! */ + error ("Instrument doesn't have an FWA illuminent"); + printf("(Found '%s' file and loaded it)\n",tname); + } } -#endif /* DEBUG */ /* Until the measurements are done, or we give up */ for (;;) { @@ -474,11 +495,13 @@ int main(int argc, char *argv[]) int ch; if (it == NULL) { + icompath *ipath; if (icmps == NULL) icmps = new_icompaths(g_log); /* Open the instrument */ - if ((it = new_inst(icmps->get_path(icmps, comno), 0, g_log, DUIH_FUNC_AND_CONTEXT)) == NULL) { + ipath = icmps->get_path(icmps, comno); + if ((it = new_inst(ipath, 0, g_log, DUIH_FUNC_AND_CONTEXT)) == NULL) { printf("!!! Unknown, inappropriate or no instrument detected !!!\n"); continue; } @@ -694,7 +717,7 @@ int main(int argc, char *argv[]) } } - ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL); + ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0); if (ev != inst_ok) { /* Abort or fatal error */ printf("!!! Calibration failed with error :'%s' (%s) !!!\n", it->inst_interp_error(it, rv), it->interp_error(it, rv)); @@ -871,7 +894,7 @@ int main(int argc, char *argv[]) } else if ((rv & inst_mask) == inst_needs_cal) { inst_code ev; printf("\nIlluminant measure failed because instruments needs calibration.\n"); - ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL); + ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, 0); continue; /* Deal with a bad sensor position */ @@ -900,20 +923,16 @@ int main(int argc, char *argv[]) if (c == '1') { i_sp = val.sp; -#ifdef DEBUG - if (rd_i == 0) { + if (tmode && rd_i == 0) { strcpy(tnp, "_i.sp"); write_xspect(tname,&i_sp); } -#endif } else if (c == '2') { r_sp = val.sp; -#ifdef DEBUG - if (rd_r == 0) { + if (tmode && rd_r == 0) { strcpy(tnp, "_r.sp"); write_xspect(tname,&r_sp); } -#endif } else if (c == '3') { p_sp = val.sp; @@ -921,13 +940,11 @@ int main(int argc, char *argv[]) if (inst_illuminant(&insp, itype) != 0) error ("Instrument doesn't have an FWA illuminent"); -#ifdef DEBUG - if (rd_p == 0) { + if (tmode && rd_p == 0) { /* Should save instrument type/instrument illuminant spectrum !!! */ strcpy(tnp, "_p.sp"); write_xspect(tname,&p_sp); } -#endif } if (pspec) { @@ -982,7 +999,8 @@ int main(int argc, char *argv[]) for (i = 0; ; i++) { if (paths[i] == NULL) break; - if (paths[i]->itype == instSpyder2 && setup_spyd2() == 0) + 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 fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name); @@ -1111,12 +1129,13 @@ int main(int argc, char *argv[]) printf("\nWriting file '%s' failed\n",outname); else printf("\nWriting file '%s' succeeded\n",outname); -#ifdef DEBUG - strcpy(tnp, "_mpir.sp"); // Measured paper under illuminant spectrum - write_xspect(tname,&bf.srop); - strcpy(tnp, "_cpir.sp"); // Computed paper under illuminant spectrum - write_xspect(tname,&bf.cpsp); -#endif + + if (tmode) { + strcpy(tnp, "_mpir.sp"); // Measured paper under illuminant spectrum + write_xspect(tname,&bf.srop); + strcpy(tnp, "_cpir.sp"); // Computed paper under illuminant spectrum + write_xspect(tname,&bf.cpsp); + } } /* Plot the result */ diff --git a/spectro/inst.c b/spectro/inst.c index 2d3adc3..9aba1bd 100644 --- a/spectro/inst.c +++ b/spectro/inst.c @@ -46,14 +46,14 @@ #endif /* !SALONEINSTLIB */ #include "numsup.h" #include "xspect.h" -#include "ccmx.h" -#include "ccss.h" #include "conv.h" #include "insttypes.h" #include "icoms.h" #include "inst.h" #include "insttypeinst.h" +#include "ccmx.h" +#include "ccss.h" #include "sort.h" #if defined(ENABLE_FAST_SERIAL) @@ -167,6 +167,24 @@ int ix /* index into the inst_disptypesel[] */ return inst_unsupported; } +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + if (dtech != NULL) + *dtech = disptech_unknown; + if (refrmode != NULL) + *refrmode = disptech_get_id(disptech_unknown)->refr; + if (cbid != NULL) + *cbid = 0; + return inst_ok; +} + /* Get a status or set or get an option (default implementation) */ inst_code inst_get_set_opt_def( inst *p, @@ -359,16 +377,25 @@ char id[CALIDLEN]) { /* Condition identifier (ie. white reference ID, filter ID) } /* Measure a display update delay. It is assumed that a */ -/* White to black change has been made to the displayed color, */ +/* black to white change has been made to the displayed color, */ /* and this will measure the time it took for the update to */ +/* (It is assumed that white_change() will be called at the time the patch */ +/* changes color.) */ /* be noticed by the instrument, up to 1.0 seconds. */ /* inst_misread will be returned on failure to find a transition. */ static inst_code meas_delay( inst *p, -int *msecdelay) { /* Return the number of msec */ +int *pdispmsec, +int *pinstmsec) { return inst_unsupported; } +/* Call used by other thread to timestamp the transition. */ +static inst_code white_change( +struct _inst *p, int init) { + return inst_unsupported; +} + \ /* Return the last calibrated refresh rate in Hz. Returns: */ /* inst_unsupported - if this instrument doesn't suport a refresh mode */ /* or is unable to retrieve the refresh rate */ @@ -407,6 +434,8 @@ char *filtername) { /* File containing compensating filter */ /* To remove the matrix, pass NULL for the matrix */ static inst_code col_cor_mat( struct _inst *p, +disptech dtech, /* Use disptech_unknown if not known */ +int cbid, /* Calibration display type base ID needed, 1 if unknown */ double mtx[3][3]) { /* XYZ matrix */ return inst_unsupported; } @@ -417,6 +446,7 @@ double mtx[3][3]) { /* XYZ matrix */ /* To set calibration back to default, pass NULL for ccss. */ static inst_code col_cal_spec_set( inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ xspect *sets, int no_sets) { return inst_unsupported; @@ -487,6 +517,8 @@ static char *inst_interp_error(inst *p, inst_code ec) { return "Bad Parameter Value"; case inst_hardware_fail: return "Hardware Failure"; + case inst_system_error: + return "Operating System Error"; case inst_other_error: return "Non-specific error"; } @@ -524,6 +556,11 @@ void *cntx /* Context for callback */ icoms *icom; inst *p = NULL; + if (path == NULL) { + a1logd(log, 2, "new_inst: got NULL path\n"); + return NULL; + } + a1logd(log, 2, "new_inst: called with path '%s'\n",path->name); if ((icom = new_icoms(path, log)) == NULL) { @@ -536,8 +573,9 @@ 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->fast) + if (itype == instUnknown && !nocoms && icom->fast) { itype = fast_ser_inst_type(icom, 1, uicallback, cntx); /* Else type from serial */ + } #endif /* ENABLE_FAST_SERIAL */ #if defined(ENABLE_SERIAL) @@ -569,6 +607,8 @@ void *cntx /* Context for callback */ if (itype == instSpecbos1201 || itype == instSpecbos) p = (inst *)new_specbos(icom, itype); + if (itype == instKleinK10) + p = (inst *)new_kleink10(icom, itype); #endif /* ENABLE_SERIAL */ #ifdef ENABLE_USB @@ -588,19 +628,26 @@ void *cntx /* Context for callback */ p = (inst *)new_i1pro(icom, itype); else if (itype == instColorMunki) p = (inst *)new_munki(icom, itype); - else if (itype == instHCFR) - p = (inst *)new_hcfr(icom, itype); + else if (itype == instSpyder1) + p = (inst *)new_spyd2(icom, itype); else if (itype == instSpyder2) p = (inst *)new_spyd2(icom, itype); else if (itype == instSpyder3) p = (inst *)new_spyd2(icom, itype); else if (itype == instSpyder4) p = (inst *)new_spyd2(icom, itype); + else if (itype == instSpyder5) + p = (inst *)new_spyd2(icom, itype); else if (itype == instHuey) p = (inst *)new_huey(icom, itype); else if (itype == instSmile) p = (inst *)new_i1disp(icom, itype); - else if (itype == instColorHug) + else if (itype == instEX1) + p = (inst *)new_ex1(icom, itype); + else if (itype == instHCFR) + p = (inst *)new_hcfr(icom, itype); + else if (itype == instColorHug + || itype == instColorHug2) p = (inst *)new_colorhug(icom, itype); #endif /* ENABLE_USB */ @@ -632,6 +679,8 @@ void *cntx /* Context for callback */ p->get_disptypesel = get_disptypesel; if (p->set_disptype == NULL) p->set_disptype = set_disptype; + if (p->get_disptechi == NULL) + p->get_disptechi = get_disptechi; if (p->get_set_opt == NULL) p->get_set_opt = get_set_opt; if (p->read_chart == NULL) @@ -666,6 +715,8 @@ void *cntx /* Context for callback */ p->calibrate = calibrate; if (p->meas_delay == NULL) p->meas_delay = meas_delay; + if (p->white_change == NULL) + p->white_change = white_change; if (p->get_refr_rate == NULL) p->get_refr_rate = get_refr_rate; if (p->set_refr_rate == NULL) @@ -696,59 +747,6 @@ void *cntx /* Context for callback */ } /* --------------------------------------------------- */ -/* Display type list support */ - -/* Default display type UI selector characters: - - g Generic - n Non-refresh (Generic) - r Refresh (Generic) - - c CRT - l LCD (Generic or CCFL Backlight) - f LCD, CCFL Backlight - b LCD, RGB LED Backlight - L LCD, Wide Gamut, CCFL Backlight - B LCD, Wide Gamut, RGB LED Backlight - e LCD, White LED Backlight - o OLED - a AMOLED - p Projector (Generic) - m Plasma - - F Factory base calibration - R Raw sensor values - */ - -/* Default display technology strings: - - "Color Matching Function" - "Custom" - "CRT" - "LCD CCFL IPS" - "LCD CCFL VPA" - "LCD CCFL TFT" - "LCD CCFL Wide Gamut IPS" - "LCD CCFL Wide Gamut VPA" - "LCD CCFL Wide Gamut TFT" - "LCD White LED IPS" - "LCD White LED VPA" - "LCD White LED TFT" - "LCD RGB LED IPS" - "LCD RGB LED VPA" - "LCD RGB LED TFT" - "LED OLED" - "LED AMOLED" - "Plasma" - "LCD RG Phosphor" - "Projector RGB Filter Wheel" - "Projector RGBW Filter Wheel" - "Projector RGBCMY Filter Wheel" - "Projector" - "Unknown" - - */ - /* Free a display type list */ void inst_del_disptype_list(inst_disptypesel *list, int no) { @@ -789,6 +787,7 @@ static inst_disptypesel *expand_dlist(inst_disptypesel *list, int nlist, int *na list[nlist].desc[0] = '\000'; list[nlist].refr = 0; list[nlist].ix = 0; + list[nlist].cc_cbid = 0; list[nlist].path = NULL; list[nlist].sets = NULL; list[nlist].no_sets = 0; @@ -808,49 +807,9 @@ static inst_disptypesel *expand_dlist(inst_disptypesel *list, int nlist, int *na We treat the first selector as more important than any aliases that come after it, so we need to do two passes to resolve what gets used. -*/ - -/* Set the selection characters */ -/* return NZ if we have run out */ -/* If flag == 0, deal with all selectors */ -/* If flag == 1, deal with just primary selectors */ -/* If flag == 2, deal with just secondary selectors */ -static int set_sel(int flag, char *sel, char *usels, int *k, char *asels) { - char *d, *s, i; - - /* First remove any used chars from selector */ - for (i = 0, d = s = sel; *s != '\000'; s++, i++) { - if ((flag == 2 && i == 0) /* Ignore and keep primary selector */ - || (flag == 1 && i == 1)) { /* Ignore and keep secondary selectors */ - *d++ = *s; - continue; - } - if (usels[*s] == 0) { /* If selector is not currently used */ - *d++ = *s; - usels[*s] = 1; - } - } - *d = '\000'; - - /* Add a selector if we need one */ - if (sel[0] == '\000') { - - /* Locate the next unused selector */ - for (;;) { - if (asels[*k] == '\000') /* Run out of selectors */ - return 1; - if (usels[*k] == 0) - break; - (*k)++; - } - sel[0] = asels[*k]; - sel[1] = '\000'; - usels[sel[0]] = 1; - (*k)++; - } - return 0; -} + Use disptechs_set_sel() utility function to some of the work. +*/ /* Create the display type list */ inst_code inst_creat_disptype_list(inst *p, @@ -883,7 +842,7 @@ int doccmx /* Add matching installed ccmx files */ list[nlist-1] = sdtlist[i]; /* Struct copy */ - if (set_sel(1, list[nlist-1].sel, usels, &k, asels)) { + if (disptechs_set_sel(2, list[nlist-1].sel, usels, &k, asels)) { a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n"); break; } @@ -902,22 +861,24 @@ int doccmx /* Add matching installed ccmx files */ if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) return inst_internal_error; - list[nlist-1].flags = inst_dtflags_ccss; + list[nlist-1].flags = inst_dtflags_ccss | inst_dtflags_ld | inst_dtflags_wr; if (ss_list[i].sel != NULL) { strncpy(list[nlist-1].sel, ss_list[i].sel, INST_DTYPE_SEL_LEN); list[nlist-1].sel[INST_DTYPE_SEL_LEN-1] = '\000'; } - if (set_sel(1, list[nlist-1].sel, usels, &k, asels)) { + if (disptechs_set_sel(2, list[nlist-1].sel, usels, &k, asels)) { a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n"); break; } strncpy(list[nlist-1].desc, ss_list[i].desc, INST_DTYPE_DESC_LEN); list[nlist-1].desc[INST_DTYPE_DESC_LEN-1] = '\000'; + list[nlist-1].dtech = ss_list[i].dtech; list[nlist-1].refr = ss_list[i].refr; list[nlist-1].ix = 0; list[nlist-1].path = ss_list[i].path; ss_list[i].path = NULL; + list[nlist-1].cbid = 0; 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; } @@ -928,49 +889,56 @@ int doccmx /* Add matching installed ccmx files */ iccmx *ss_list; /* Just ccmx's for this instrument */ - if ((ss_list = list_iccmx(inst_name(p->itype), NULL)) == NULL) { + if ((ss_list = list_iccmx(p->itype, NULL)) == NULL) { free(list); return inst_internal_error; } for (i = 0; ss_list[i].path != NULL; i++) { - /* Check that we can find the matching base calibation */ + /* Check that there is a matching base calibation */ for (j = 0; j < nlist; j++) { - if (list[j].cbid == ss_list[i].cbid) + if (ss_list[i].cc_cbid != 0 + && list[j].cbid == ss_list[i].cc_cbid) break; } if (j >= nlist) { - a1loge(p->log, 1, "inst_creat_disptype_list can't find cbid %d for '%s'\n",list[j].cbid, list[j].path); + a1loge(p->log, 1, "inst_creat_disptype_list can't find cbid %d for '%s'\n",ss_list[i].cc_cbid, ss_list[i].path); continue; } if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) return inst_internal_error; - list[nlist-1].flags = inst_dtflags_ccmx; + list[nlist-1].flags = inst_dtflags_ccmx | inst_dtflags_ld | inst_dtflags_wr; if (ss_list[i].sel != NULL) { strncpy(list[nlist-1].sel, ss_list[i].sel, INST_DTYPE_SEL_LEN); list[nlist-1].sel[INST_DTYPE_SEL_LEN-1] = '\000'; } - if (set_sel(1, list[nlist-1].sel, usels, &k, asels)) { + if (disptechs_set_sel(2, list[nlist-1].sel, usels, &k, asels)) { a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n"); break; } strncpy(list[nlist-1].desc, ss_list[i].desc, INST_DTYPE_DESC_LEN); list[nlist-1].desc[INST_DTYPE_DESC_LEN-1] = '\000'; + list[nlist-1].dtech = ss_list[i].dtech; list[nlist-1].refr = ss_list[i].refr; list[nlist-1].ix = list[j].ix; /* Copy underlying cal selection from base */ list[nlist-1].path = ss_list[i].path; ss_list[i].path = NULL; + list[nlist-1].cbid = 0; + list[nlist-1].cc_cbid = ss_list[i].cc_cbid; icmCpy3x3(list[nlist-1].mat, ss_list[i].mat); } } - /* Verify or delete any secondary selectors from the list */ - for (i = 0; i < nlist; i++) { - set_sel(2, list[i].sel, usels, &k, asels); - } + /* Create needed selectors */ + for (i = 0; i < nlist; i++) + disptechs_set_sel(4, list[i].sel, usels, &k, asels); + + /* Verify or delete any secondary selectors */ + for (i = 0; i < nlist; i++) + disptechs_set_sel(3, list[i].sel, usels, &k, asels); if (pndtlist != NULL) *pndtlist = nlist; @@ -984,11 +952,11 @@ int doccmx /* Add matching installed ccmx files */ /* CCMX location support */ /* return a list of installed ccmx files. */ -/* if inst != NULL, return those that match the given instrument. */ +/* if itype != instUnknown, return those that match the given instrument. */ /* The list is sorted by description and terminated by a NULL entry. */ /* If no is != NULL, return the number in the list */ /* Return NULL and -1 if there is a malloc error */ -iccmx *list_iccmx(char *inst, int *no) { +iccmx *list_iccmx(instType itype, int *no) { int i, j; iccmx *rv; @@ -1011,8 +979,9 @@ iccmx *list_iccmx(char *inst, int *no) { ccmx *cs; int len; char *pp; + disptech dtech; char *tech, *disp; - int cbid, refr; + int cc_cbid, refr; if ((cs = new_ccmx()) == NULL) { a1loge(g_log, 1, "list_iccmx: new_ccmx failed\n"); @@ -1030,7 +999,7 @@ iccmx *list_iccmx(char *inst, int *no) { } /* Skip any that don't match */ - if (inst != NULL && cs->inst != NULL && strcmp(inst, cs->inst) != 0) + if (itype != instUnknown && cs->inst != NULL && inst_enum(cs->inst) != itype) continue; a1logd(g_log, 5, "Reading '%s'\n",paths[i]); @@ -1038,7 +1007,8 @@ iccmx *list_iccmx(char *inst, int *no) { tech = ""; if ((disp = cs->disp) == NULL) disp = ""; - cbid = cs->cbid; + cc_cbid = cs->cc_cbid; + dtech = cs->dtech; refr = cs->refrmode; len = strlen(tech) + strlen(disp) + 4; if ((pp = malloc(len)) == NULL) { @@ -1071,7 +1041,8 @@ iccmx *list_iccmx(char *inst, int *no) { strcat(pp, disp); strcat(pp, ")"); rv[j].desc = pp; - rv[j].cbid = cbid; + rv[j].cc_cbid = cc_cbid; + rv[j].dtech = dtech; rv[j].refr = refr; rv[j].sel = cs->sel; cs->sel = NULL; icmCpy3x3(rv[j].mat, cs->matrix); @@ -1081,7 +1052,8 @@ iccmx *list_iccmx(char *inst, int *no) { xdg_free(paths, npaths); rv[j].path = NULL; rv[j].desc = NULL; - rv[j].cbid = 0; + rv[j].cc_cbid = 0; + rv[j].dtech = disptech_unknown; rv[j].refr = -1; rv[j].sel = NULL; if (no != NULL) @@ -1142,6 +1114,7 @@ iccss *list_iccss(int *no) { ccss *cs; int len; char *pp; + disptech dtech; char *tech, *disp; int refr; @@ -1165,6 +1138,7 @@ iccss *list_iccss(int *no) { tech = ""; if ((disp = cs->disp) == NULL) disp = ""; + dtech = cs->dtech; refr = cs->refrmode; len = strlen(tech) + strlen(disp) + 4; if ((pp = malloc(len)) == NULL) { @@ -1197,6 +1171,7 @@ iccss *list_iccss(int *no) { strcat(pp, disp); strcat(pp, ")"); rv[j].desc = pp; + rv[j].dtech = dtech; rv[j].refr = refr; rv[j].sel = cs->sel; cs->sel = NULL; rv[j].sets = cs->samples; cs->samples = NULL; @@ -1207,6 +1182,7 @@ iccss *list_iccss(int *no) { xdg_free(paths, npaths); rv[j].path = NULL; rv[j].desc = NULL; + rv[j].dtech = disptech_unknown; rv[j].refr = -1; rv[j].sel = NULL; rv[j].sets = NULL; @@ -1258,7 +1234,7 @@ instType fast_ser_inst_type( ) { instType rv = instUnknown; char buf[100]; - baud_rate brt[] = { baud_921600, baud_115200, baud_38400, baud_nc }; + baud_rate brt[] = { baud_921600, baud_115200, baud_38400, baud_9600, baud_nc }; unsigned int etime; unsigned int i; int se, len; @@ -1287,36 +1263,64 @@ instType fast_ser_inst_type( // a1logd(p->log, 5, "brt = %d\n",brt[i]); - /* See if it's a JETI specbos */ - if ((se = p->write_read(p, "*idn?\r", buf, 100, "\r", 1, 0.050)) != 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 (brt[i] == baud_9600) { + /* See if it's a Klein K10 */ + + if ((se = p->write_read(p, "P0\r", 0, buf, 100, 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) { + a1logd(p->log, 5, "fser_inst_type: User aborted\n"); + return instUnknown; + } } + continue; + } + len = strlen(buf); + + a1logd(p->log, 5, "len = %d\n",len); + + /* 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) { + rv = instKleinK10; + break; + } + } else { + /* See if it's a JETI specbos */ + if ((se = p->write_read(p, "*idn?\r", 0, buf, 100, 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; + } + } + continue; + } + len = strlen(buf); + + a1logd(p->log, 5, "len = %d\n",len); + + /* 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, "specbos1201\n"); + rv = instSpecbos1201; + break; + } + /* Is this a JETI specbos XXXX response ? */ + if (len >= 11 && strncmp(buf, "JETI_SB", 7) == 0) { +// a1logd(p->log, 5, "specbos\n"); + rv = instSpecbos; + break; } - continue; - } - len = strlen(buf); - - a1logd(p->log, 5, "len = %d\n",len); - - /* 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, "specbos1201\n"); - rv = instSpecbos1201; - break; - } - /* Is this a JETI specbos XXXX response ? */ - if (len >= 11 && strncmp(buf, "JETI_SB", 7) == 0) { -// a1logd(p->log, 5, "specbos\n"); - rv = instSpecbos; - break; } } @@ -1357,7 +1361,7 @@ static instType ser_inst_type( baud_600, baud_300, baud_110, baud_nc }; unsigned int etime; unsigned int bi, i; - int se, len; + int se, len, bread; int xrite = 0; int ss = 0; int so = 0; @@ -1385,7 +1389,8 @@ static instType ser_inst_type( } // a1logd(p->log, 5, "brt = %d\n",brt[i]); - if ((se = p->write_read(p, ";D024\r\n", buf, 100, "\r", 1, 0.5)) != inst_ok) { + bread = 0; + if ((se = p->write_read(p, ";D024\r\n", 0, buf, 100, &bread, "\r", 1, 0.5)) != inst_ok) { /* Check for user abort */ if (uicallback != NULL) { inst_code ev; @@ -1394,7 +1399,8 @@ static instType ser_inst_type( return instUnknown; } } - continue; + if (bread == 0) + continue; } len = strlen(buf); @@ -1439,7 +1445,7 @@ static instType ser_inst_type( /* SpectroScan */ if (ss) { rv = instSpectroScan; - if ((se = p->write_read(p, ";D030\r\n", buf, 100, "\n", 1, 1.5)) == 0) { + if ((se = p->write_read(p, ";D030\r\n", 0, buf, 100, NULL, "\n", 1, 1.5)) == 0) { if (strlen(buf) >= 41) { hex2bin(&buf[5], 12); // a1logd(p->log, 5, "spectroscan type = '%s'\n",buf); @@ -1451,7 +1457,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", buf, 100, ">", 1, 2.5)) != 0) + if ((se = p->write_read(p, "SV\r\n", 0, buf, 100, NULL, ">", 1, 2.5)) != 0) return instUnknown; if (strlen(buf) >= 12) { @@ -1509,6 +1515,87 @@ static void hex2bin(char *buf, int len) { #endif /* ENABLE_SERIAL */ +/* ============================================================= */ +/* inst_mode persistent storage support */ + +/* Table listing and relating masks to symbols. */ +/* Need to keep this in sync with inst.h */ +struct { + int mode; /* Mode bits */ + char *sym; /* 4 character symbol */ +} inst_mode_sym[] = { + { inst_mode_reflection, inst_mode_reflection_sym }, + { inst_mode_s_reflection, inst_mode_s_reflection_sym }, + { inst_mode_transmission, inst_mode_transmission_sym }, + { inst_mode_emission, inst_mode_emission_sym }, + + { inst_mode_spot, inst_mode_spot_sym }, + { inst_mode_strip, inst_mode_strip_sym }, + { inst_mode_xy, inst_mode_xy_sym }, + { inst_mode_chart, inst_mode_chart_sym }, + { inst_mode_ambient, inst_mode_ambient_sym }, + { inst_mode_ambient_flash, inst_mode_ambient_flash_sym }, + { inst_mode_tele, inst_mode_tele_sym }, + + { inst_mode_emis_nonadaptive, inst_mode_emis_nonadaptive_sys }, + { inst_mode_ref_uv, inst_mode_ref_uv_sym }, + { inst_mode_emis_refresh_ovd, inst_mode_emis_refresh_ovd_sym }, + { inst_mode_emis_norefresh_ovd, inst_mode_emis_norefresh_ovd_sym }, + + { inst_mode_colorimeter, inst_mode_colorimeter_sym }, + { inst_mode_spectral, inst_mode_spectral_sym }, + { inst_mode_highres, inst_mode_highres_sym }, + + { inst_mode_calibration, inst_mode_calibration_sym }, + + { 0, NULL } +}; + +/* Return a string with a symbolic encoding of the mode flags */ +void inst_mode_to_sym(char sym[MAX_INST_MODE_SYM_SZ], inst_mode mode) { + int i; + char *cp = sym; + + for (i = 0; inst_mode_sym[i].mode != 0; i++) { + if (mode & inst_mode_sym[i].mode) { + if (cp != sym) + *cp++ = '_'; + strncpy(cp, inst_mode_sym[i].sym, 4); + cp += 4; + } + } + *cp++ = '\000'; +} + +/* Return a set of mode flags that correspondf to the symbolic encoding */ +/* Return nz if a symbol wasn't recognized */ +int sym_to_inst_mode(inst_mode *mode, const char *sym) { + int i; + const char *cp = sym; + int rv = 0; + + for (*mode = 0;;) { + if (cp[0] == '\000' || cp[1] == '\000' || cp[2] == '\000' || cp[3] == '\000') + break; + + for (i = 0; inst_mode_sym[i].mode != 0; i++) { + if (strncmp(inst_mode_sym[i].sym, cp, 4) == 0) { + *mode |= inst_mode_sym[i].mode; + break; + } + } + if (inst_mode_sym[i].mode == 0) + rv = 1; + + cp += 4; + + if (*cp == '_') + cp++; + } + + return rv; +} + diff --git a/spectro/inst.h b/spectro/inst.h index d69a37f..a4184d4 100644 --- a/spectro/inst.h +++ b/spectro/inst.h @@ -42,6 +42,7 @@ */ #include "insttypes.h" /* libinst Includes this functionality */ +#include "disptechs.h" /* libinst Includes this functionality */ #include "icoms.h" /* libinst Includes this functionality */ #include "conv.h" @@ -70,8 +71,8 @@ typedef enum { /* XYZ units, Spectral units */ inst_mrt_none = 0, /* Not set */ inst_mrt_emission = 1, /* cd/m^2, mW/(m^2.sr.nm) */ inst_mrt_ambient = 2, /* Lux mW/(m^2.nm) */ - inst_mrt_emission_flash = 3, /* cd/(m^2.s), mW/(m^2.sr.nm.s) */ - inst_mrt_ambient_flash = 4, /* Lux/s mW/(m^2.nm.s) */ + inst_mrt_emission_flash = 3, /* cd.s/m^2, mW.s/(m^2.sr.nm) */ + inst_mrt_ambient_flash = 4, /* Lux.s mW.s/(m^2.nm) */ inst_mrt_reflective = 5, /* %, %/nm */ inst_mrt_transmissive = 6, /* %, %/nm */ inst_mrt_frequency = 7 /* Hz */ @@ -123,77 +124,100 @@ typedef enum { inst_unexpected_reply = 0x140000, /* Unexpected Reply */ inst_wrong_setup = 0x150000, /* Setup is wrong or conflicting */ inst_hardware_fail = 0x160000, /* Hardware failure */ - inst_bad_parameter = 0x170000, /* Bad parameter value */ - inst_other_error = 0x180000, /* Some other error */ + inst_system_error = 0x170000, /* System call (ie malloc) fail */ + inst_bad_parameter = 0x180000, /* Bad parameter value */ + inst_other_error = 0x190000, /* Some other error */ inst_mask = 0xff0000, /* inst_code mask value */ inst_imask = 0x00ffff /* instrument specific mask value */ } inst_code; + +/* + possible UV modes: + + Do the reflective measurement with UV rather than normal illuminant + [ Should this be reflectivity against 'A', or absolute ?? ] + (ie. spot, strip, xy or chart). inst_mode_ref_uv + + Do a white & UV measurement at the start of each strip reading. + Return result with special call after each strip read. + inst_mode_ref_uv_strip_1 + + Do a dual white & UV measurement + [ Can do in one hit for spot, but how should two strip passes be handled ? + ie. two separate strip reads of phase 1 & then 2 ? ] + (ie. spot, strip, xy or chart). inst_mode_ref_uv_2pass + + Get normal illuminant spectrum. + + Get UV spectrum. + + get_meas_illum_spectrum(mode); + */ + /* Instrument capabilities & modes */ /* Note that due to the binary combinations, capabilities is not definititive */ /* as to valid modes. check_mode() is definitive. */ +/* #defines are for saving modes in a version independent way. */ +/* Note :- update inst_mode_sym[] table in inst.c if anything here is changed. */ typedef enum { inst_mode_none = 0x00000000, /* No capability or mode */ /* Mode of light measurement */ inst_mode_reflection = 0x00000001, /* General reflection mode */ +# define inst_mode_reflection_sym "REFL" inst_mode_s_reflection = 0x00000002, /* General saved reflection mode */ +# define inst_mode_s_reflection_sym "SRFL" inst_mode_transmission = 0x00000004, /* General transmission mode */ +# define inst_mode_transmission_sym "TRAN" inst_mode_emission = 0x00000008, /* General emission mode */ +# define inst_mode_emission_sym "EMIS" inst_mode_illum_mask = 0x0000000f, /* Mask of sample illumination sub mode */ /* Access mode of measurement */ inst_mode_spot = 0x00000010, /* General spot measurement mode */ +# define inst_mode_spot_sym "SPOT" inst_mode_strip = 0x00000020, /* General strip measurement mode */ +# define inst_mode_strip_sym "STRP" inst_mode_xy = 0x00000040, /* General X-Y measurement mode */ +# define inst_mode_xy_sym "CHXY" inst_mode_chart = 0x00000080, /* General chart measurement mode */ +# define inst_mode_chart_sym "CHRT" inst_mode_ambient = 0x00000100, /* General ambient measurement mode */ +# define inst_mode_ambient_sym "AMBI" inst_mode_ambient_flash = 0x00000200, /* General ambient flash measurement mode */ +# define inst_mode_ambient_flash_sym "ABFL" inst_mode_tele = 0x00000400, /* General telephoto measurement mode */ +# define inst_mode_tele_sym "TELE" // Hmm. Should there be a tele_flash mode ???? inst_mode_sub_mask = 0x000007f0, /* Mask of sub-mode */ /* Basic mode */ inst_mode_basic_mask = inst_mode_illum_mask | inst_mode_sub_mask, -/* - possible UV modes: - - Do the reflective measurement with UV rather than normal illuminant - [ Should this be reflectivity against 'A', or absolute ?? ] - (ie. spot, strip, xy or chart). inst_mode_ref_uv - - Do a white & UV measurement at the start of each strip reading. - Return result with special call after each strip read. - inst_mode_ref_uv_strip_1 - - Do a dual white & UV measurement - [ Can do in one hit for spot, but how should two strip passes be handled ? - ie. two separate strip reads of phase 1 & then 2 ? ] - (ie. spot, strip, xy or chart). inst_mode_ref_uv_2pass - - Get normal illuminant spectrum. - - Get UV spectrum. - - get_meas_illum_spectrum(mode); - */ - /* Extra dependent modes */ inst_mode_emis_nonadaptive = 0x00000800, /* Emissom Non-adaptive mode */ +# define inst_mode_emis_nonadaptive_sys "EMNA" inst_mode_ref_uv = 0x00001000, /* Ultra Violet measurement mode */ +# define inst_mode_ref_uv_sym "REUV" inst_mode_emis_refresh_ovd = 0x00002000, /* Emissom Refresh mode override */ +# define inst_mode_emis_refresh_ovd_sym "EMRO" inst_mode_emis_norefresh_ovd = 0x00006000, /* Emissom Non-refresh mode override */ +# define inst_mode_emis_norefresh_ovd_sym "ENRO" inst_mode_dep_extra_mask = 0x00007800, /* Mask of measurement modifiers */ /* Extra independent modes */ inst_mode_colorimeter = 0x00004000, /* Colorimetric mode */ +# define inst_mode_colorimeter_sym "COLI" inst_mode_spectral = 0x00008000, /* Spectral mode */ +# define inst_mode_spectral_sym "SPEC" inst_mode_highres = 0x00010000, /* High Resolution Spectral mode */ +# define inst_mode_highres_sym "HIRZ" inst_mode_extra_mask = 0x0001c000, /* Mask of extra modes */ /* Configured for calibration & capable of returning it from inst_mode_calibration */ inst_mode_calibration = 0x80000000, /* Configured for calibration */ +# define inst_mode_calibration_sym "CALB" /* Combined operating modes (from above): */ /* These mode capabilities are also use to set the mode */ @@ -248,6 +272,16 @@ typedef enum { /* Test for a specific mode in capability and mode */ #define IMODETST2(mcap, mbits, mode) (IMODETST(mcap, mode) && IMODETST(mbits, mode)) +#define MAX_INST_MODE_SYM_SZ (32 * (4 + 1)) /* Each bit sym is 4 chars */ + +/* Return a string with a symbolic encoding of the mode flags */ +void inst_mode_to_sym(char sym[MAX_INST_MODE_SYM_SZ], inst_mode mode); + +/* Return a set of mode flags that correspondf to the symbolic encoding */ +/* Return nz if a symbol wasn't recognized */ +int sym_to_inst_mode(inst_mode *mode, const char *sym); + + /* Instrument capabilities 2 */ /* (Available capabilities may be mode dependent) */ typedef enum { @@ -281,6 +315,8 @@ typedef enum { inst2_has_battery = 0x01000000, /* Instrument is battery powered */ inst2_disptype = 0x02000000, /* Has a display type selector */ + /* (ie. get_disptypesel(), set_disptype */ + inst2_ccmx = 0x04000000, /* Colorimeter Correction Matrix capability */ inst2_ccss = 0x08000000, /* Colorimeter Cal. Spectral Set capability */ @@ -300,18 +336,43 @@ typedef enum { } inst3_capability; +/* - - - - - - - - - - - - - - - - - - - */ + +/* API ideas for supporting calibration management: + + functions to: + + return number of used and free builtin slots, + password required flag. + + write builtin calibration or ccss/ccmx file + + write builtin calibration or ccss/ccmx file + + delete builtin calibration or ccss/ccmx file + + Things to be allowed for: + + optional password + configuration specific calibrations. How are they marked ? +*/ + typedef enum { - inst_dtflags_none = 0x0000, /* no flags */ - inst_dtflags_default = 0x0001, /* default display type */ - inst_dtflags_ccss = 0x0002, /* ccss */ - inst_dtflags_ccmx = 0x0004, /* ccmx */ + inst_dtflags_none = 0x0000, /* no flags - assume builtin calibration */ + inst_dtflags_mtx = 0x0001, /* matrix read from instrument */ + inst_dtflags_ccss = 0x0002, /* ccss file */ + inst_dtflags_ccmx = 0x0004, /* ccmx file */ + inst_dtflags_wr = 0x0010, /* Writable slot */ + inst_dtflags_ld = 0x0020, /* mtx/ccss/ccmx is loaded */ + inst_dtflags_default = 0x1000, /* Dafault calibration to use */ inst_dtflags_end = 0x8000 /* end marker */ } inst_dtflags; #define INST_DTYPE_SEL_LEN 10 #define INST_DTYPE_DESC_LEN 100 -/* Structure used to return display type selection information */ +/* Structure used to return display type selection information. */ +/* Calibrations may be implicit (based on ix and driver code), or */ +/* explicit mtx/ccss/ccmx information contained in structure. */ typedef struct _inst_disptypesel { /* Public: */ @@ -322,17 +383,22 @@ typedef struct _inst_disptypesel { char desc[INST_DTYPE_DESC_LEN]; /* Textural description */ int refr; /* Refresh mode flag */ + disptech dtech; /* display techology */ + /* Private: */ int ix; /* Internal index, */ // Stuff for ccss & ccmx - char *path; /* Path to ccss or ccmx */ + char *path; /* Path to ccss or ccmx. NULL if not valid */ + int cc_cbid; /* cbid that matrix requires */ double mat[3][3]; /* ccmx matrix */ - xspect *sets; /* ccss set of sample spectra */ - int no_sets; /* ccs number of sets */ + xspect *sets; /* ccss set of sample spectra. NULL if not valid */ + int no_sets; /* ccs number of sets. 0 if not valid */ } inst_disptypesel; +/* - - - - - - - - - - - - - - - - - - - */ + /* Instrument options for get_set_opt() */ typedef enum { inst_opt_unknown = 0x0000, /* Option not specified */ @@ -361,15 +427,16 @@ typedef enum { inst_opt_initcalib = 0x0008, /* Enable initial calibration (default) [No args] */ inst_opt_noinitcalib = 0x0009, /* Disable initial calibration if < losecs since last */ /* opened, or losecs == 0 [int losecs] */ - inst_opt_set_ccss_obs = 0x000A, /* Set the observer used with ccss device types - */ + inst_opt_askcalib = 0x000A, /* Ask before proceeding with calibration (default) */ + /* [ No args] */ + inst_opt_noaskcalib = 0x000B, /* Proceed with calibration immediately if possible */ + /* [ No args] */ + + inst_opt_set_ccss_obs = 0x000C, /* Set the observer used with ccss device types - */ /* Not applicable to any other type of instrument. */ /* [args: icxObserverType obType,*/ /* xspect custObserver[3] */ - inst_opt_get_dtinfo = 0x000C, /* Get current display type information */ - /* [args: int *refrmode,*/ - /* int *cbid] */ - inst_opt_set_filter = 0x000D, /* Set a filter configuration */ /* [1 argument type inst_opt_filter] */ @@ -391,10 +458,11 @@ typedef enum { inst_opt_set_led_pulse_state= 0x0019, /* Set the current LED state. [double period_in_secs, */ /* double on_time_prop, double trans_time_prop] */ inst_opt_get_led_pulse_state= 0x001A, /* Get the current pulse LED state. [*double period] */ - inst_opt_set_target_state = 0x001B, /* Set the aiming target state 0 = off, 1 == on, 2 = toggle [int] */ + inst_opt_get_target_state = 0x001B, /* Get the aiming target state 0 = off, 1 == on [*int] */ + inst_opt_set_target_state = 0x001C, /* Set the aiming target state 0 = off, 1 == on, 2 = toggle [int] */ - inst_opt_get_min_int_time = 0x001C, /* Get the minimum integration time [*double time] */ - inst_opt_set_min_int_time = 0x001D /* Set the minimum integration time [double time] */ + 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_type; @@ -624,11 +692,22 @@ typedef enum { int recreate); /* nz to re-check for new ccmx & ccss files */ \ \ /* Set the display type. index is into the inst_disptypesel[] returned */ \ - /* returned by get_disptypesel(). clears col_cor_mat() */ \ + /* returned by get_disptypesel(). clears col_cor_mat() and */ \ + /* col_cal_spec_set(). */ \ inst_code (*set_disptype)( \ struct _inst *p, \ int index); \ \ + /* Get the disptech and other corresponding info for the current */ \ + /* selected display type. Returns disptype_unknown by default. */ \ + /* Because refrmode can be overridden, it may not match the refrmode */ \ + /* of the dtech. (Pointers may be NULL if not needed) */ \ + inst_code (*get_disptechi)( \ + struct _inst *p, \ + disptech *dtech, \ + int *refrmode, \ + int *cbid); \ + \ /* Get a status or get or set an option */ \ /* option state. */ \ /* Some options can be set before init */ \ @@ -806,14 +885,27 @@ typedef enum { char id[CALIDLEN]); /* Condition identifier (ie. white */ \ /* reference ID, filter ID) */ \ \ - /* Measure a display update delay. It is assumed that a */ \ - /* White to black change has been made to the displayed color, */ \ - /* and this will measure the time it took for the update to */ \ - /* be noticed by the instrument, up to 1.0 seconds. */ \ - /* inst_misread will be returned on failure to find a transition to black. */ \ + /* Measure a display update, and instrument reaction time. It is */ \ + /* assumed that a white to black change will be made to the */ \ + /* displayed color during this call, and this is used to measure */ \ + /* the time it took for the update to be noticed by the instrument, */ \ + /* up to 1.0 second. */ \ + /* The instrument reaction time accounts for the time between */ \ + /* when the measure function is called and the samples actually being */ \ + /* taken. This value may be negative if there is a filter delay. */ \ + /* The method white_change() should be called with init=1 before */ \ + /* calling meas_delay, and then with init=0 during the meas_delay() */ \ + /* call to timestamp the transition. */ \ + /* Note that a default instmsec will be returned even on error. */ \ inst_code (*meas_delay)( \ struct _inst *p, \ - int *msecdelay); /* Return the number of msec */ \ + int *dispmsec, /* Return display update delay in msec */ \ + int *instmsec); /* Return instrument reaction time in msec */ \ + \ + /* Call used by other thread to timestamp the patch transition. */ \ + inst_code (*white_change)( \ + struct _inst *p, \ + int init); /* nz to init time stamp, z to mark transition */ \ \ /* Return the last calibrated refresh rate in Hz. Returns: */ \ /* (Available if cap2 & inst2_get_refresh_rate) */ \ @@ -852,7 +944,9 @@ typedef enum { /* To clear the matrix, pass NULL for the matrix */ \ inst_code (*col_cor_mat)( \ struct _inst *p, \ - double mtx[3][3]); /* XYZ matrix */ \ + disptech dtech, /* Use disptech_unknown if not known */ \ + int cbid, /* Calibration display type base ID, needed 1 if unknown */\ + double mtx[3][3]); /* XYZ matrix */ \ \ /* Use a Colorimeter Calibration Spectral Set (ccss) to set the */ \ /* instrumen calibration. This will affect emissive readings. */ \ @@ -863,19 +957,20 @@ typedef enum { /* icxOT_default for the observer. */ \ inst_code (*col_cal_spec_set)( \ struct _inst *p, \ - xspect *sets, /* Set of sample spectra */ \ - int no_sets); /* Number on set */ \ + disptech dtech, /* Use disptech_unknown if not known */ \ + xspect *sets, /* Set of sample spectra */ \ + int no_sets); /* Number on set */ \ \ /* Supply a user interaction callback function. \ * This is called for one of three different purposes: \ * To signal that the instrument measurement has been triggered. \ - * To poll for a abort while waiting to trigger. \ + * To poll for an abort while waiting to trigger. \ * To poll for a user abort during measurement. \ * \ * The callback function will have the purpose paramater appropriately. \ * \ * For inst_negcoms, the return value of inst_user_abort \ - * will abort the communication negotiation \ + * will abort the communication negotiation. \ * \ * For inst_triggered, the return value of the callback is ignored. \ * \ @@ -963,33 +1058,35 @@ void inst_del_disptype_list(inst_disptypesel *list, int no); /* - - - - - - - - - - - - - - - - - - -- */ -/* CCMX support */ +/* CCMX support - ccmx instrument proxy */ typedef struct { char *path; /* Path to the file */ char *desc; /* Technology + display description */ - int cbid; /* Calibration display type base ID */ + disptech dtech; /* Display Technology enumeration (optional if disp) */ + int cc_cbid; /* Calibration display type base ID required */ int refr; /* Refresh mode flag */ char *sel; /* UI selector characters (may be NULL) */ double mat[3][3]; /* The matrix values */ } iccmx; /* return a list of installed ccmx files. */ -/* if inst != NULL, return those that match the given instrument. */ +/* if itype != instUnknown, return those that match the given instrument. */ /* The list is sorted by description and terminated by a NULL entry. */ /* If no is != NULL, return the number in the list */ /* Return NULL and -1 if there is a malloc error */ -iccmx *list_iccmx(char *inst, int *no); +iccmx *list_iccmx(instType itype, int *no); /* Free up a iccmx list */ void free_iccmx(iccmx *list); /* - - - - - - - - - - - - - - - - - - -- */ -/* CCSS support */ +/* CCSS support - ccss instrument proxy */ typedef struct { char *path; /* Path to the file */ char *desc; /* Technology + display description */ + disptech dtech; /* Display Technology enumeration (optional if disp) */ int refr; /* Refresh mode flag */ char *sel; /* UI selector characters (may be NULL) */ xspect *sets; /* Set of sample spectra */ @@ -1011,5 +1108,6 @@ void free_iccss(iccss *list); } #endif + #define INST_H #endif /* INST_H */ diff --git a/spectro/instappsup.c b/spectro/instappsup.c index c83c953..112bab5 100644 --- a/spectro/instappsup.c +++ b/spectro/instappsup.c @@ -168,7 +168,8 @@ inst_code inst_handle_calibrate( inst_cal_cond calc, /* Current current condition */ inst_code (*disp_setup) (inst *p,inst_cal_cond calc, disp_win_info *dwi), /* Callback for handling a display calibration - May be NULL */ - disp_win_info *dwi /* Information to be able to open a display test patch - May be NULL */ + disp_win_info *dwi, /* Information to be able to open a display test patch - May be NULL */ + 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 */ @@ -210,6 +211,10 @@ inst_code inst_handle_calibrate( printf("Calibration failed with '%s' (%s)\n", p->inst_interp_error(p, ev), p->interp_error(p, ev)); + + if (doimmediately) + return inst_user_abort; + printf("Hit any key to retry, or Esc or Q to abort:\n"); empty_con_chars(); @@ -262,14 +267,14 @@ inst_code inst_handle_calibrate( case inst_calc_man_em_dark: printf("Place cap on the instrument, or place on a dark surface,\n"); - printf("or place on the white calibration reference,\n"); + printf("or place on the calibration reference,\n"); printf(" and then hit any key to continue,\n"); printf(" or hit Esc or Q to abort: "); break; case inst_calc_man_am_dark: printf("Place ambient adapter and cap on the instrument,\n"); - printf("or place on the white calibration reference,\n"); + printf("or place on the calibration reference,\n"); printf(" and then hit any key to continue,\n"); printf(" or hit Esc or Q to abort: "); break; @@ -389,7 +394,8 @@ inst_code inst_handle_calibrate( usermes = 1; - if (calc != inst_calc_man_ref_whitek) { + if (!doimmediately + && calc != inst_calc_man_ref_whitek) { empty_con_chars(); ch = next_con_char(); printf("\n"); diff --git a/spectro/instappsup.h b/spectro/instappsup.h index 24da6db..8da4325 100644 --- a/spectro/instappsup.h +++ b/spectro/instappsup.h @@ -77,7 +77,8 @@ inst_code inst_handle_calibrate( inst_cal_cond calc, /* Current calibration condition */ inst_code (*disp_setup) (inst *p, inst_cal_cond calc, disp_win_info *dwi), /* Callback for handling a display calibration - May be NULL */ - disp_win_info *dwi /* Information to be able to open a display test patch - May be NULL */ + disp_win_info *dwi, /* Information to be able to open a display test patch - May be NULL */ + int doimmediately /* If nz, don't wait for user, calibrate immediatley */ ); /* ============================================================================= */ diff --git a/spectro/instlib.ksh b/spectro/instlib.ksh index e112b3c..88796f8 100644 --- a/spectro/instlib.ksh +++ b/spectro/instlib.ksh @@ -56,6 +56,8 @@ SPECTRO_FILES=" icoms.h inst.h inst.c + disptechs.h + disptechs.c insttypes.c insttypes.h insttypeinst.h @@ -95,10 +97,10 @@ SPECTRO_FILES=" colorhug.h spyd2.c spyd2.h - spyd2setup.h - spyd2PLD.h specbos.h specbos.c + kleink10.h + kleink10.c oemarch.c oemarch.h oeminst.c diff --git a/spectro/insttypeinst.h b/spectro/insttypeinst.h index 8ed93d8..a55c3d4 100644 --- a/spectro/insttypeinst.h +++ b/spectro/insttypeinst.h @@ -9,18 +9,31 @@ * see the License2.txt file for licencing details. */ -#include "dtp20.h" -#include "dtp22.h" -#include "dtp41.h" -#include "dtp51.h" -#include "dtp92.h" -#include "ss.h" -#include "i1disp.h" -#include "i1d3.h" -#include "i1pro.h" -#include "munki.h" -#include "hcfr.h" -#include "spyd2.h" -#include "huey.h" -#include "specbos.h" -#include "colorhug.h" + +#ifdef ENABLE_SERIAL +# include "dtp22.h" +# include "dtp41.h" +# include "dtp51.h" +# include "ss.h" +#endif + +#ifdef ENABLE_FAST_SERIAL +# include "specbos.h" +# include "kleink10.h" +#endif + +#ifdef ENABLE_USB +# include "dtp20.h" +# include "dtp92.h" +# include "i1disp.h" +# include "i1d3.h" +# include "i1pro.h" +# include "munki.h" +# include "spyd2.h" +# include "huey.h" +# include "ex1.h" +# include "hcfr.h" +# include "colorhug.h" +#endif + + diff --git a/spectro/insttypes.c b/spectro/insttypes.c index d70bc71..729b328 100644 --- a/spectro/insttypes.c +++ b/spectro/insttypes.c @@ -76,12 +76,16 @@ char *inst_sname(instType itype) { return "ColorMunki"; case instHCFR: return "HCFR"; + case instSpyder1: + return "Spyder1"; case instSpyder2: return "Spyder2"; case instSpyder3: return "Spyder3"; case instSpyder4: return "Spyder4"; + case instSpyder5: + return "Spyder5"; case instHuey: return "Huey"; case instSmile: @@ -90,8 +94,14 @@ char *inst_sname(instType itype) { return "specbos 1201"; case instSpecbos: return "specbos"; + case instKleinK10: + return "K-10"; + case instEX1: + return "EX1"; case instColorHug: return "ColorHug"; + case instColorHug2: + return "ColorHug2"; default: break; } @@ -102,17 +112,17 @@ char *inst_sname(instType itype) { char *inst_name(instType itype) { switch (itype) { case instDTP20: - return "Xrite DTP20"; + return "X-Rite DTP20"; case instDTP22: - return "Xrite DTP22"; + return "X-Rite DTP22"; case instDTP41: - return "Xrite DTP41"; + return "X-Rite DTP41"; case instDTP51: - return "Xrite DTP51"; + return "X-Rite DTP51"; case instDTP92: - return "Xrite DTP92"; + return "X-Rite DTP92"; case instDTP94: - return "Xrite DTP94"; + return "X-Rite DTP94"; case instSpectrolino: return "GretagMacbeth Spectrolino"; case instSpectroScan: @@ -126,7 +136,7 @@ char *inst_name(instType itype) { case instI1Disp2: return "GretagMacbeth i1 Display 2"; case instI1Disp3: - return "Xrite i1 DisplayPro, ColorMunki Display"; + return "X-Rite i1 DisplayPro, ColorMunki Display"; case instI1Monitor: return "GretagMacbeth i1 Monitor"; case instI1Pro: @@ -137,12 +147,16 @@ char *inst_name(instType itype) { return "X-Rite ColorMunki"; case instHCFR: return "Colorimtre HCFR"; + case instSpyder1: + return "ColorVision Spyder1"; case instSpyder2: return "ColorVision Spyder2"; case instSpyder3: return "Datacolor Spyder3"; case instSpyder4: return "Datacolor Spyder4"; + case instSpyder5: + return "Datacolor Spyder5"; case instHuey: return "GretagMacbeth Huey"; case instSmile: @@ -151,8 +165,14 @@ char *inst_name(instType itype) { return "JETI specbos 1201"; case instSpecbos: return "JETI specbos"; + case instKleinK10: + return "Klein K-10"; + case instEX1: + return "Image Engineering EX1"; case instColorHug: return "Hughski ColorHug"; + case instColorHug2: + return "Hughski ColorHug2"; default: break; } @@ -163,17 +183,23 @@ char *inst_name(instType itype) { /* instType, or instUnknown if not matched */ instType inst_enum(char *name) { - if (strcmp(name, "Xrite DTP20") == 0) + if (strcmp(name, "Xrite DTP20") == 0 + || strcmp(name, "X-Rite DTP20") == 0) return instDTP20; - else if (strcmp(name, "Xrite DTP22") == 0) + else if (strcmp(name, "Xrite DTP22") == 0 + || strcmp(name, "X-Rite DTP22") == 0) return instDTP22; - else if (strcmp(name, "Xrite DTP41") == 0) + else if (strcmp(name, "Xrite DTP41") == 0 + || strcmp(name, "X-Rite DTP41") == 0) return instDTP41; - else if (strcmp(name, "Xrite DTP51") == 0) + else if (strcmp(name, "Xrite DTP51") == 0 + || strcmp(name, "X-Rite DTP51") == 0) return instDTP51; - else if (strcmp(name, "Xrite DTP92") == 0) + else if (strcmp(name, "Xrite DTP92") == 0 + || strcmp(name, "X-Rite DTP92") == 0) return instDTP92; - else if (strcmp(name, "Xrite DTP94") == 0) + else if (strcmp(name, "Xrite DTP94") == 0 + || strcmp(name, "X-Rite DTP94") == 0) return instDTP94; else if (strcmp(name, "GretagMacbeth Spectrolino") == 0) return instSpectrolino; @@ -187,28 +213,39 @@ instType inst_enum(char *name) { return instI1Disp1; else if (strcmp(name, "GretagMacbeth i1 Display 2") == 0 || strcmp(name, "GretagMacbeth i1 Display") == 0 - || strcmp(name, "Xrite i1 Display") == 0) + || strcmp(name, "Xrite i1 Display") == 0 + || strcmp(name, "X-Rite i1 Display") == 0) return instI1Disp2; else if (strcmp(name, "Xrite i1 DisplayPro") == 0 - || strcmp(name, "ColorMunki Display") == 0) + || strcmp(name, "X-Rite i1 DisplayPro") == 0 + || strcmp(name, "ColorMunki Display") == 0 + || strcmp(name, "X-Rite i1 DisplayPro, ColorMunki Display") == 0 + || strcmp(name, "Xrite i1 DisplayPro, ColorMunki Display") == 0) return instI1Disp3; else if (strcmp(name, "GretagMacbeth i1 Monitor") == 0) return instI1Monitor; else if (strcmp(name, "GretagMacbeth i1 Pro") == 0 - || strcmp(name, "Xrite i1 Pro") == 0) + || strcmp(name, "Xrite i1 Pro") == 0 + || strcmp(name, "X-Rite i1 Pro") == 0) return instI1Pro; - else if (strcmp(name, "Xrite i1 Pro 2") == 0) + else if (strcmp(name, "Xrite i1 Pro 2") == 0 + || strcmp(name, "X-Rite i1 Pro 2") == 0) return instI1Pro2; - else if (strcmp(name, "X-Rite ColorMunki") == 0) + else if (strcmp(name, "XRite ColorMunki") == 0 + || strcmp(name, "X-Rite ColorMunki") == 0) return instColorMunki; else if (strcmp(name, "Colorimtre HCFR") == 0) return instHCFR; + else if (strcmp(name, "ColorVision Spyder1") == 0) + return instSpyder1; else if (strcmp(name, "ColorVision Spyder2") == 0) return instSpyder2; else if (strcmp(name, "Datacolor Spyder3") == 0) return instSpyder3; else if (strcmp(name, "Datacolor Spyder4") == 0) return instSpyder4; + else if (strcmp(name, "Datacolor Spyder5") == 0) + return instSpyder5; else if (strcmp(name, "GretagMacbeth Huey") == 0) return instHuey; else if (strcmp(name, "ColorMunki Smile") == 0) @@ -217,8 +254,14 @@ instType inst_enum(char *name) { return instSpecbos1201; else if (strcmp(name, "JETI specbos") == 0) return instSpecbos; + else if (strcmp(name, "Klein K-10") == 0) + return instKleinK10; + else if (strcmp(name, "Image Engineering EX1") == 0) + return instEX1; else if (strcmp(name, "Hughski ColorHug") == 0) return instColorHug; + else if (strcmp(name, "Hughski ColorHug2") == 0) + return instColorHug2; return instUnknown; @@ -265,13 +308,15 @@ int nep) { /* Number of end points */ if (idVendor == 0x085C) { /* ColorVision */ if (idProduct == 0x0100) /* ColorVision Spyder1 */ - return instSpyder2; /* Alias to Spyder 2 */ + return instSpyder1; if (idProduct == 0x0200) /* ColorVision Spyder2 */ return instSpyder2; - if (idProduct == 0x0300) /* ColorVision Spyder3 */ + if (idProduct == 0x0300) /* DataColor Spyder3 */ return instSpyder3; - if (idProduct == 0x0400) /* ColorVision Spyder4 */ + if (idProduct == 0x0400) /* DataColor Spyder4 */ return instSpyder4; + if (idProduct == 0x0500) /* DataColor Spyder5 */ + return instSpyder5; } if (idVendor == 0x0971) { /* Gretag Macbeth */ @@ -293,10 +338,18 @@ int nep) { /* Number of end points */ return instColorMunki; } + if (idVendor == 0x2457) { /* Image Engineering */ + if (idProduct == 0x4000) /* EX1 */ + return instEX1; + } + if ((idVendor == 0x04d8 && idProduct == 0xf8da) /* Microchip & Hughski ColorHug (old) */ || (idVendor == 0x273f && idProduct == 0x1001)) { /* Hughski & ColorHug Fmw. >= 0.1.20 */ return instColorHug; } + if (idVendor == 0x273f && idProduct == 0x1004) { /* Hughski & ColorHug2 */ + return instColorHug2; + } /* Add other instruments here */ @@ -371,13 +424,11 @@ int inst_illuminant(xspect *sp, instType itype) { case instHCFR: return 1; /* Not applicable */ + case instSpyder1: case instSpyder2: - return 1; /* Not applicable */ - case instSpyder3: - return 1; /* Not applicable */ - case instSpyder4: + case instSpyder5: return 1; /* Not applicable */ case instHuey: @@ -390,7 +441,14 @@ int inst_illuminant(xspect *sp, instType itype) { case instSpecbos: return 1; /* Not applicable */ + case instKleinK10: + return 1; /* Not applicable */ + + case instEX1: + return 1; /* Not applicable */ + case instColorHug: + case instColorHug2: return 1; /* Not applicable */ diff --git a/spectro/insttypes.h b/spectro/insttypes.h index ff86cc5..58c5bfe 100644 --- a/spectro/insttypes.h +++ b/spectro/insttypes.h @@ -44,14 +44,19 @@ typedef enum { instI1Pro2, /* X-Rite i1 Pro2 */ instColorMunki, /* X-Rite ColorMunki */ instHCFR, /* Colorimtre HCFR */ + instSpyder1, /* Datacolor/ColorVision Spyder1 */ instSpyder2, /* Datacolor/ColorVision Spyder2 */ instSpyder3, /* Datacolor Spyder3 */ instSpyder4, /* Datacolor Spyder4 */ + instSpyder5, /* Datacolor Spyder5 */ instHuey, /* GretagMacbeth Huey */ instSmile, /* X-rite Colormunki Smile */ instSpecbos1201, /* JETI specbos 1201 */ instSpecbos, /* JETI specbos XXXX */ + instKleinK10, /* Klein K10-A */ + instEX1, /* Image Engineering EX1 */ instColorHug, /* Hughski ColorHug */ + instColorHug2, /* Hughski ColorHug2 */ instFakeDisp = 9998, /* Fake display & instrument device id */ diff --git a/spectro/kleink10.c b/spectro/kleink10.c new file mode 100644 index 0000000..d632bdb --- /dev/null +++ b/spectro/kleink10.c @@ -0,0 +1,2810 @@ + +/* + * Argyll Color Correction System + * + * JETI kleink10 1211/1201 related functions + * + * Author: Graeme W. Gill + * Date: 29/4/2014 + * + * Copyright 1996 - 2014, 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. + * + * Based on DTP92.c & specbos.c + */ + +/* + If you make use of the instrument driver code here, please note + that it is the author(s) of the code who take responsibility + for its operation. Any problems or queries regarding driving + instruments with the Argyll drivers, should be directed to + the Argyll's author(s), and not to any other party. + + If there is some instrument feature or function that you + would like supported here, it is recommended that you + contact Argyll's author(s) first, rather than attempt to + modify the software yourself, if you don't have firm knowledge + of the instrument communicate protocols. There is a chance + that an instrument could be damaged by an incautious command + sequence, and the instrument companies generally cannot and + will not support developers that they have not qualified + and agreed to support. + */ + +/* + + TTBD: + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <time.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 */ +#include "xspect.h" +#include "insttypes.h" +#include "conv.h" +#include "icoms.h" +#include "kleink10.h" + +#undef HIGH_SPEED /* [und] Use high speed flicker measure for refresh rate etc. */ +#define AUTO_AVERAGE /* [def] Automatically average more readings for low light */ +#define RETRY_RANGE_ERROR 3 /* [3] Retry range error readings 3 times */ + +#undef PLOT_REFRESH /* [und] Plot refresh rate measurement info */ +#undef PLOT_UPDELAY /* [und] Plot update delay measurement info */ + +#undef TEST_BAUD_CHANGE /* Torture test baud rate change on non high speed K10 */ + +static inst_disptypesel k10_disptypesel[98]; +static inst_code k10_interp_code(kleink10 *p, int ec); +static inst_code k10_read_cal_list(kleink10 *p); +static inst_code set_default_disp_type(kleink10 *p); +static inst_code k10_read_flicker_samples(kleink10 *p, double duration, double *srate, + double **pvals, int *pnsamp, int usefast); + +#define MAX_MES_SIZE 500 /* Maximum normal message reply size */ +#define MAX_RD_SIZE 8000 /* Maximum reading message reply size */ + +/* Decode a K10 error letter */ +static int decodeK10err(char c) { + if (c == '0') { + return K10_OK; + } else if (c == 'B') { + return K10_FIRMWARE; + } else if (c == 'X') { + return K10_FIRMWARE; + } else if (c == 'b') { + return K10_BLACK_EXCESS; + } else if (c == 's') { + return K10_BLACK_OVERDRIVE; + } else if (c == 't') { + return K10_BLACK_ZERO; + } else if (c == 'w') { + return K10_OVER_HIGH_RANGE; + } else if (c == 'v') { + return K10_TOP_OVER_RANGE; + } else if (c == 'u') { + return K10_BOT_UNDER_RANGE; + } else if (c == 'L') { + return K10_AIMING_LIGHTS; + } else { + return K10_UNKNOWN; + } +} + +/* Extract an error code from a reply string */ +/* Remove the error code from the string and return the */ +/* new length in *nlength */ +/* Return K10_BAD_RETVAL if no error code can be found */ +static int +extract_ec(char *s, int *nlength, int bread) { +#define MAXECHARS 1 + char *f, *p; + char tt[MAXECHARS+1]; + int rv; + p = s + bread; + +//printf("Got '%s' bread %d\n",s,bread); + + /* Find the trailing '>' */ + for (p--; p >= s; p--) { + if (*p == '>') + break; + } + if (p < s) + return K10_BAD_RETVAL; +//printf("trailing is at %d '%s'\n",p - s, p); + + /* Find the leading '<' */ + for (f = p-1; f >= (p-MAXECHARS-1) && f >= s; f--) { + if (*f == '<') + break; + if ((*f < '0' || *f > '9') + && (*f < 'a' || *f > 'z') + && (*f < 'A' || *f > 'Z')) + return K10_BAD_RETVAL; + } + if (f < s || f < (p-MAXECHARS-1) || (p-f) <= 1) { +//printf("f < s ? %d, f < (p-MAXECHARS-1) ? %d, (p-f) <= 1 ? %d\n", +//f < s, f < (p-10), (p-f) <= 1); + return K10_BAD_RETVAL; + } +//printf("leading is at %d '%s'\n",f - s, f); + + if (p-f-1 <= 0) + return K10_BAD_RETVAL; + + strncpy(tt, f+1, p-f-1); + tt[p-f-1] = '\000'; +//printf("error code is '%s'\n",tt); + + /* Interpret the error character(s) */ + /* It's not clear if more than one error can be returned. */ + /* We are only looking at the first character - we should */ + /* really prioritize them id more than one can occur. */ + for (p = tt; *p != '\000'; p++) { + rv = decodeK10err(*p); + break; + } + + /* Remove the error code from the reply */ + if (nlength != NULL) + *nlength = f - s; + *f = '\000'; + return rv; +} + +/* Interpret an icoms error into a KLEINK10 error */ +static int icoms2k10_err(int se) { + if (se != ICOM_OK) { + if (se & ICOM_TO) + return K10_TIMEOUT; + return K10_COMS_FAIL; + } + return K10_OK; +} + +typedef enum { + ec_n = 0, /* No error code or command echo */ + ec_e = 1, /* Error code */ + ec_c = 2, /* Command echo */ + ec_ec = 3 /* Both error code and command echo */ +} ichecks; + +/* Do a full command/response echange with the kleink10 */ +/* (This level is not multi-thread safe) */ +/* Return the kleink10 error code. */ +static int +k10_fcommand( +struct _kleink10 *p, +char *in, /* In string */ +char *out, /* Out string buffer */ +int bsize, /* Out buffer size */ +int *pbread, /* Bytes read (including '\000') */ +int nchar, /* Number of characters to expect */ +double to, /* Timeout in seconds */ +ichecks xec, /* Error check */ +int nd /* nz to disable debug messages */ +) { + int se, rv = K10_OK; + int bwrite, bread = 0; + char cmd[10]; + + bwrite = strlen((char *)in); + strncpy((char *)cmd, (char *)in, 2); + cmd[2] = '\000'; + + if ((se = p->icom->write_read(p->icom, in, 0, out, bsize, &bread, NULL, nchar, to)) + != ICOM_OK) { + rv = icoms2k10_err(se); + + } else { + + if (!nd && p->log->debug >= 6) { + a1logd(p->log, 6, "k10_fcommand: command sent\n"); + adump_bytes(p->log, " ", (unsigned char *)in, 0, bwrite); + a1logd(p->log, 6, " returned %d bytes:\n",bread); + adump_bytes(p->log, " ", (unsigned char *)out, 0, bread); + } + + if (xec & ec_e) { + rv = extract_ec(out, &bread, bread); + } + + if ((xec & ec_c) && rv == K10_OK && strncmp(cmd, out, 2) != 0) { + rv = K10_CMD_VERIFY; + } + } + if (!nd) a1logd(p->log, 6, " error code 0x%x\n",rv); + + if (pbread != NULL) + *pbread = bread; + + return rv; +} + +/* Do a normal command/response echange with the kleink10. */ +/* (This level is not multi-thread safe) */ +/* Return the inst code */ +static inst_code +k10_command( +kleink10 *p, +char *in, /* In string */ +char *out, /* Out string buffer */ +int bsize, /* Out buffer size */ +int *bread, /* Bytes read */ +int nchar, /* Number of characters to expect */ +ichecks xec, /* Error check */ +double to) { /* Timout in seconds */ + int rv = k10_fcommand(p, in, out, bsize, bread, nchar, to, xec, 0); + return k10_interp_code(p, rv); +} + +/* Do a write to the kleink10 */ +/* (This level is not multi-thread safe) */ +/* Return the kleink10 error code. */ +static int +k10_write( +struct _kleink10 *p, +char *in, /* In string */ +double to /* Timeout in seconds */ +) { + int rv = K10_OK; + int se; + + if ((se = p->icom->write(p->icom, in, 0, to)) != ICOM_OK) { + rv = icoms2k10_err(se); + + } else { + + if (p->log->debug >= 6) { + a1logd(p->log, 6, "k10_write: command sent\n"); + adump_bytes(p->log, " ", (unsigned char *)in, 0, strlen((char *)in)); + } + } + a1logd(p->log, 6, " error code 0x%x\n",rv); + + return rv; +} + +/* Do a read from the kleink10 */ +/* (This level is not multi-thread safe) */ +/* Return the kleink10 error code. */ +static int +k10_read( +struct _kleink10 *p, +char *out, /* Out string buffer */ +int bsize, /* Out buffer size */ +int *pbread, /* Bytes read (including '\000') */ +char *tc, /* Terminating characters, NULL for none or char count mode */ +int nchar, /* Number of terminating characters needed, or char count needed */ +double to /* Timeout in seconds */ +) { + int se, rv = K10_OK; + int bread = 0; + + if ((se = p->icom->read(p->icom, out, bsize, &bread, tc, nchar, to)) != ICOM_OK) { + rv = icoms2k10_err(se); + } else { + + if (p->log->debug >= 6) { + a1logd(p->log, 6, "k10_read: read %d bytes\n",bread); + adump_bytes(p->log, " ", (unsigned char *)out, 0, bread); + } + } + a1logd(p->log, 6, " error code 0x%x\n",rv); + + if (pbread != NULL) + *pbread = bread; + + return rv; +} + +/* Change baud rates */ +/* (This level is not multi-thread safe) */ +/* Return the kleink10 error code. */ +static int +k10_set_baud( +struct _kleink10 *p, +baud_rate br +) { + int se, rv = K10_OK; + if ((se = p->icom->set_ser_port(p->icom, fc_HardwareDTR, br, parity_none, + stop_1, length_8)) != ICOM_OK) { + rv = icoms2k10_err(se); + } else { + if (p->log->debug >= 6) { + a1logd(p->log, 6, "k10_set_baud: %d\n",br); + } + } + a1logd(p->log, 6, " error code 0x%x\n",rv); + + return rv; +} + +/* ------------------------------------------------------------ */ + +/* Establish communications with a kleink10 */ +/* Return K10_COMS_FAIL on failure to establish communications */ +static inst_code +k10_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { + kleink10 *p = (kleink10 *) pp; + char buf[MAX_MES_SIZE]; + baud_rate brt[] = { baud_9600, baud_nc }; + unsigned int etime; + unsigned int i; + instType itype = pp->itype; + int se; + char *cp; + + inst_code ev = inst_ok; + + a1logd(p->log, 2, "k10_init_coms: About to init Serial I/O\n"); + + if (p->gotcoms) { + a1logd(p->log, 2, "k10_init_coms: already inited\n"); + return inst_ok; + } + + 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, "k10_init_coms: wrong communications type for device!\n"); + return inst_coms_fail; + } + + /* The tick to give up on */ + etime = msec_time() + (long)(500.0 + 0.5); + + a1logd(p->log, 1, "k10_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 = 0; msec_time() < etime; i++) { + if (brt[i] == baud_nc) { + i = 0; + } + a1logd(p->log, 5, "k10_init_coms: trying baud ix %d\n",brt[i]); + 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); + a1logd(p->log, 5, "k10_init_coms: set_ser_port failed with 0x%x\n",se); + return k10_interp_code(p, icoms2k10_err(se));; /* Give up */ + } + + /* 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 */ + } + + /* 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, "k10_init_coms: user aborted\n"); + return inst_user_abort; + } + } + } + + 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; + } + + /* Check the response */ + if (ev != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 2, "k10_init_coms: status command failed\n"); + return ev; + } + + if (strncmp (buf+2, "K-10 ", 7) == 0) + p->model = k10_k10; + else if (strncmp (buf+2, "K-10-A ", 7) == 0) + p->model = k10_k10a; + else if (strncmp (buf+2, "KV-10-A", 7) == 0) + p->model = k10_kv10a; + else { + amutex_unlock(p->lock); + a1logd(p->log, 2, "k10_init_coms: unrecognised model '%s'\n",buf); + return inst_unknown_model; + } + + /* Extract the serial number */ + strncpy(p->serial_no, buf+9, 9); + p->serial_no[20] = '\000'; + + a1logd(p->log, 2, "k10_init_coms: coms established\n"); + + p->gotcoms = 1; + + amutex_unlock(p->lock); + + /* Get the list of calibrations */ + if ((ev = k10_read_cal_list(p)) != inst_ok) { + return ev; + } + + a1logd(p->log, 2, "k10_init_coms: init coms is returning\n"); + return inst_ok; +} + +/* Initialise the KLEINK10 */ +/* return non-zero on an error, with dtp error code */ +static inst_code +k10_init_inst(inst *pp) { + kleink10 *p = (kleink10 *)pp; + char mes[100]; + char buf[MAX_MES_SIZE]; + unsigned int stime; + int se; + inst_code ev = inst_ok; + + a1logd(p->log, 2, "k10_init_inst: called\n"); + + if (p->gotcoms == 0) + return inst_internal_error; /* Must establish coms before calling init */ + + amutex_lock(p->lock); + + /* Make sure the target lights are off */ + if ((ev = k10_command(p, "L0\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + p->lights = 0; + + /* Make sure we are auto ranging by default */ + if ((ev = k10_command(p, "J8\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + p->autor = 1; + + /* Grab the firware version */ + stime = msec_time(); + if ((ev = k10_command(p, "P2\r", buf, MAX_MES_SIZE, NULL, 2+8+3, ec_ec, 2.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + p->comdel = (msec_time() - stime)/2; /* Or is this the FD232 update latency ? */ + strncpy(p->firm_ver, buf+2, 8); + p->firm_ver[8] = '\000'; + + amutex_unlock(p->lock); + + /* Set a default calibration */ + if ((ev = set_default_disp_type(p)) != inst_ok) { + return ev; + } + + p->inited = 1; + + /* Do a flicker read to work around glitch at the 0.4 second mark of the */ + /* first one after power up. We ignore any error. */ + if ((ev = k10_read_flicker_samples(p, 0.5, NULL, NULL, NULL, 0)) != inst_ok) { + a1logd(p->log, 1, "k10_init_inst: warning - startup k10_read_flicker_samples failed with 0x%x - ignored\n",ev); + } + + a1logd(p->log, 2, "k10_init_inst: instrument inited OK\n"); + + if (p->log->verb) { + char *model = "Unknown"; + switch (p->model) { + case k10_k10: + model = "K10"; + break; + case k10_k10a: + model = "K10-A"; + break; + case k10_kv10a: + model = "KV10-A"; + break; + } + a1logv(p->log, 1, " Model: '%s'\n",model); + a1logv(p->log, 1, " Serial number: '%s'\n",p->serial_no); + a1logv(p->log, 1, " Firmware version: '%s'\n",p->firm_ver); + } + + return inst_ok; +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Convert a Klein Measurement encoded 24 bit value to a double */ +double KleinMeas2double(char *ibuf) { + unsigned char *buf = (unsigned char *)ibuf; + unsigned int ip; + double op; + int sn = 0, ma; + int ep; + + ip = (buf[0] << 8) + buf[1]; + sn = (ip >> 15) & 0x1; + ma = ip & ((1 << 15)-1); + ep = buf[2]; + if (ep >= 128) + ep -= 256; + + op = (double)ma; + op *= pow(2.0, (double)ep-16); + if (sn) + op = -op; + return op; +} + +/* Decode measurement RGB range into 3 x 1..6 */ +static void decodeRange(int *out, char iin) { + unsigned char in = (unsigned char)iin; + int t, r0, r1, r2, r3; + int tt; + + out[0] = (in >> 7) & 1; + out[1] = (in >> 6) & 1; + out[2] = (in >> 5) & 1; + + in &= 0x1F; + + out[0] += 1 + 2 * ((in / 9) % 3); + out[1] += 1 + 2 * ((in / 3) % 3); + out[2] += 1 + 2 * (in % 3); +} + + +/* Convert a Klein Calibration encoded 24 bit value to a double */ +double KleinCal2double(char *ibuf) { + unsigned char *buf = (unsigned char *)ibuf; + ORD32 ip; + double op; + ORD32 sn = 0, ma; + int ep; + + ip = (buf[0] << 8) + buf[1]; + sn = (ip >> 15) & 0x1; + ma = ip & ((1 << 15)-1); + ep = buf[2]; + if (ep >= 128) + ep -= 256; + + op = (double)ma; + op *= pow(2.0, (double)ep-15); + if (sn) + op = -op; + return op; +} + +/* Convert a native double to an Klein Calibration encoded 24 bit value, */ +void double2KleinCal(char *ibuf, double d) { + unsigned char *buf = (unsigned char *)ibuf; + ORD32 sn = 0, ma; + int ep; + double n; + + if (d < 0.0) { + sn = 1; + d = -d; + } + if (d != 0.0) { + ep = (int)floor(log(d)/log(2.0)) + 1; + + n = pow(0.5, (double)(ep - 15)); /* Normalisation factor */ + + /* If rounding would cause an overflow, adjust exponent */ + if (floor(d * n + 0.5) >= (double)(1 << 15)) { + n *= 0.5; + ep++; + } + + if (ep < -128) { /* Alow denormalised */ + ep = -128; + n = pow(0.5, (double)(ep - 15)); /* Normalisation factor */ + } + + if (ep > 127) { /* Saturate maximum */ + ep = 127; + d = (double)(1 << 15)-1; + } else { + d *= n; + if (d < 0.5) + ep = 0; + } + } else { + ep = 0; /* Zero */ + } + ma = (((ORD32)floor(d + 0.5)) & ((1 << 16)-1)) | (sn << 15); + buf[0] = ((ma >> 8) & 0xff); + buf[1] = (ma & 0xff); + + buf[2] = ep; +} + +double CalMan2double(char *ibuf) { + unsigned char *buf = (unsigned char *)ibuf; + ORD64 val; + + /* Load LE into 64 bit */ + val = buf[7]; + val = ((val << 8) + (0xff & buf[6])); + val = ((val << 8) + (0xff & buf[5])); + val = ((val << 8) + (0xff & buf[4])); + val = ((val << 8) + (0xff & buf[3])); + val = ((val << 8) + (0xff & buf[2])); + val = ((val << 8) + (0xff & buf[1])); + val = ((val << 8) + (0xff & buf[0])); + + return IEEE754_64todouble(val); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Decode an N5 measre command response */ +static inst_code decodeN5(kleink10 *p, double *XYZ, int *range, char *buf, int blen) { + + if (blen < (2 + 3 * 3 + 1)) { + a1logd(p->log, 1, "decodeN5: failed to parse '%s'\n",icoms_fix(buf)); + return inst_protocol_error; + } + + if (XYZ != NULL) { + XYZ[0] = KleinMeas2double(buf+2); + XYZ[1] = KleinMeas2double(buf+5); + XYZ[2] = KleinMeas2double(buf+8); + } + + if (range != NULL) + decodeRange(range, buf[11]); + + return inst_ok; +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Read a given calibration matrix */ +static inst_code +k10_read_cal_matrix( +kleink10 *p, +inst_disptypesel *m, /* Matrix calibration to write */ +int ix /* Klein calibration index 1 - 96 */ +) { + inst_code ev = inst_protocol_error; + int se; + char cmd[3]; + char buf[MAX_MES_SIZE]; + int bread, i, j, k; + + if (!p->gotcoms) + return inst_no_coms; + + amutex_lock(p->lock); + + /* Trigger cal matrix read */ + if ((ev = k10_command(p, "D1\r", buf, MAX_MES_SIZE, &bread, 2, ec_c, 2.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + if (buf[0] != 'D' || buf[1] != '1') { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_read_cal_matrix: didn't get echo'd commad D1\n"); + return inst_protocol_error; + } + + /* Send the cal index and read matrix */ + cmd[0] = ix; + cmd[1] = '\r'; + cmd[2] = '\000'; + + if ((ev = k10_command(p, cmd, buf, MAX_MES_SIZE, &bread, 128+3, ec_e, 2.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + if (bread < 128) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_read_cal_matrix: not enough bytes returned (%d)\n",bread); + return inst_protocol_error; + } + + a1logd(p->log, 6, "Cal '%s':\n",m->desc); + + /* CalMan format matrix */ + if (buf[21] == 'C') { + for (k = 24, i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + if ((bread-k) < 8) { + amutex_unlock(p->lock); + return inst_protocol_error; + } + m->mat[i][j] = CalMan2double(buf + k); + k += 8; + a1logd(p->log, 6, " Mat[%d][%d] = %f\n",i,j,m->mat[i][j]); + } + } + + /* Klein format matrix */ + } else { + for (k = 101, i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + if ((bread-k) < 3) { + amutex_unlock(p->lock); + return inst_protocol_error; + } + m->mat[i][j] = KleinCal2double(buf + k); + k += 3; + a1logd(p->log, 6, " Mat[%d][%d] = %f\n",i,j,m->mat[i][j]); + } + } + } + m->flags |= inst_dtflags_ld; /* It's now loaded */ + amutex_unlock(p->lock); + + return inst_ok; +} + +/* Guess appropriate disptype and selector letters for standard calibrations */ +static void guess_disptype(inst_disptypesel *s, char *desc) { + disptech dtype; + disptech_info *i; + char *sel = NULL; + + if (strcmp(desc, "Default CRT File") == 0) { + dtype = disptech_crt; + } else if (strcmp(desc, "Klein DLP Lux") == 0) { + dtype = disptech_dlp; + sel = "P"; + } else if (strcmp(desc, "Klein SMPTE C") == 0) { + dtype = disptech_crt; + sel = "E"; + } else if (strcmp(desc, "TVL XVM245") == 0) { /* RGB LED LCD Video display */ + dtype = disptech_lcd_rgbled; + } else if (strcmp(desc, "Klein LED Bk LCD") == 0) { + dtype = disptech_lcd_rgbled; + sel = "d"; + } else if (strcmp(desc, "Klein Plasma") == 0) { + dtype = disptech_plasma; + } else if (strcmp(desc, "DLP Screen") == 0) { + dtype = disptech_dlp; + } else if (strcmp(desc, "TVL LEM150") == 0) { /* OLED */ + dtype = disptech_oled; + } else if (strcmp(desc, "Sony EL OLED") == 0) { /* OLED */ + dtype = disptech_oled; + sel = "O"; + } else if (strcmp(desc, "Eizo CG LCD") == 0) { /* Wide gamut IPS LCD RGB ? (or RG+P ?)*/ + dtype = disptech_lcd_rgbled_ips; + sel = "z"; + } else if (strcmp(desc, "FSI 2461W") == 0) { /* Wide gamut IPS ? LCD CCFL */ + dtype = disptech_lcd_ccfl_wg; + } else if (strcmp(desc, "HP DreamColor 2") == 0) { /* Wide gamut IPS ? LCD RG+P */ + dtype = disptech_lcd_rgledp; + } else { + dtype = disptech_unknown; + } + + i = disptech_get_id(dtype); + s->dtech = dtype; + if (sel != NULL) + strcpy(s->sel, sel); + else + strcpy(s->sel, i->sel); +} + +/* Read the list of calibrations available */ +static inst_code +k10_read_cal_list( +kleink10 *p) { + inst_code ev = inst_protocol_error; + char buf[MAX_RD_SIZE]; + int bread, i, j, ix, n; + char name[21]; + + if (!p->gotcoms) + return inst_no_coms; + + /* Make sure factory matrix values is in the first entry */ + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + if (i == j) + k10_disptypesel[0].mat[i][j] = 1.0; + else + k10_disptypesel[0].mat[i][j] = 0.0; + } + } + k10_disptypesel[0].flags |= inst_dtflags_ld; + + amutex_lock(p->lock); + + /* Grab the raw info */ + if ((ev = k10_command(p, "D7\r", buf, MAX_RD_SIZE, &bread, 1925, ec_ec, 6.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_read_cal_list D7 returning error 0x%x\n",ev); + return ev; + } + + /* Parse it. There should be 96 calibrations */ + name[20] = '\000'; + for (i = 2, ix = 1, n = 1; ix <= 96 && (bread-i) >= 20; i += 20, ix++) { + + for (j = 0; j < 20; j++) + name[j] = buf[i + j]; + + if (((unsigned char *)name)[0] == 0xff) { + continue; + } + for (j = 19; j >= 0; j--) { + if (name[j] != ' ') { + name[j+1] = '\000'; + break; + } + } + +// printf("Adding Cal %d is '%s'\n",ix,name); + + /* Add it to the list */ + memset((void *)&k10_disptypesel[n], 0, sizeof(inst_disptypesel)); + k10_disptypesel[n].flags = inst_dtflags_mtx | inst_dtflags_wr; /* Not loaded yet */ + k10_disptypesel[n].cbid = 0; + strcpy(k10_disptypesel[n].desc, name); + k10_disptypesel[n].refr = 0; + k10_disptypesel[n].ix = ix; + guess_disptype(&k10_disptypesel[n], name); + n++; + } + + /* Put marker at end */ + k10_disptypesel[n].flags = inst_dtflags_end; + k10_disptypesel[n].cbid = 0; + k10_disptypesel[n].sel[0] = '\000'; + k10_disptypesel[n].desc[0] = '\000'; + k10_disptypesel[n].refr = 0; + k10_disptypesel[n].ix = 0; + + amutex_unlock(p->lock); + + return inst_ok; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +static abort_flicker(kleink10 *p, int isnew, double *retbuf) { + char buf[MAX_MES_SIZE]; + int bread; + + /* Abort flicker transfer */ + k10_write(p, "N5\r", 2.0); + + /* Flush the buffer of any remaining characters. */ + k10_read(p, buf, MAX_MES_SIZE, &bread, "<0>", 3, 1.0); + + /* Return the baud rate to normal */ + if (isnew) + k10_set_baud(p, baud_9600); + +#ifdef TEST_BAUD_CHANGE + else { + k10_set_baud(p, baud_19200); + k10_set_baud(p, baud_9600); + } +#endif + + /* Clean up everything else */ + amutex_unlock(p->lock); + + if (retbuf != NULL) + free(retbuf); +} + +/* Read flicker samples */ +/* Free *pvals after use */ +static inst_code +k10_read_flicker_samples( +kleink10 *p, +double duration, /* duration to take samples */ +double *srate, /* Return the sampel rate */ +double **pvals, /* Return the sample values */ +int *pnsamp, /* Return the number of samples */ +int usefast /* If nz use fast rate is possible */ +) { + int se = K10_OK; + inst_code ev = inst_ok; + int isnew = 0; + double rate = 256; + double *retbuf; + int tsamp, nsamp; + char mes[4] = "JX\r"; + char buf[MAX_MES_SIZE]; + int boff, bread; + int range[3]; + unsigned int stime; + int derr = 0, rerr = 0; + int i; + + stime = msec_time(); + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + amutex_lock(p->lock); + +#ifdef HIGH_SPEED + /* This isn't reliable, because there is no way to ensure that */ + /* the T1 command has been sent before we change the baud rate, */ + /* anf if we wait too long will will loose the measurements. */ + if (usefast && strcmp(p->firm_ver, "v01.09fh") > 0) { + isnew = 1; /* We can use faster T1 command */ + rate = 384; + a1logd(p->log, 1, "k10_read_flicker: using faster T1\n"); + } +#endif /* HIGH_SPEED */ + + /* Target number of samples */ + tsamp = (int)(duration * (double)rate + 0.5); + + if (tsamp < 1) + tsamp = 1; + + a1logd(p->log, 1, "k10_read_flicker: taking %d samples\n",tsamp); + + if ((retbuf = (double *)malloc(sizeof(double) * tsamp)) == NULL) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_read_flicker: malloc of %d bytes failed\n",sizeof(double) * tsamp); + return k10_interp_code(p, K10_INT_MALLOC); + } + + /* Make sure the target lights are off */ + if (p->lights) { + int se; + if ((ev = k10_command(p, "L0\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 0.5)) != inst_ok) { + amutex_unlock(p->lock); + free(retbuf); + a1logd(p->log, 1, "k10_read_flicker: L0 failed\n"); + return ev; + } + p->lights = 0; + } + + /* Make sure we are auto ranging */ + if (!p->autor) { + if ((ev = k10_command(p, "J8\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_read_flicker: J8 failed with 0x%x\n",ev); + return ev; + } + p->autor = 1; + } + + /* Take a measurement to get ranges ? */ + if ((ev = k10_command(p, "N5\r", buf, MAX_MES_SIZE, &bread, 15, ec_ec, 2.0)) != inst_ok) { + amutex_unlock(p->lock); + free(retbuf); + a1logd(p->log, 1, "k10_read_flicker: N5 failed with 0x%x\n",ev); + return ev; + } + + if ((ev = decodeN5(p, NULL, range, buf, bread)) != inst_ok) { + a1logd(p->log, 1, "k10_read_flicker: decodeN5 failed with 0x%x\n",ev); + amutex_unlock(p->lock); + return ev; + } + + /* Set a fixed range to avoid a range change error */ + p->autor = 0; + if ((ev = k10_command(p, "J7\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_read_flicker: J7 failed with 0x%x\n",ev); + return ev; + } + mes[1] = '0' + range[1]; /* Green range */ + if ((ev = k10_command(p, mes, buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_read_flicker: %s failed with 0x%x\n",buf,ev); + return ev; + } + + /* Issue an T2 for normal speed flicker measure, or T1 for fast */ + a1logd(p->log, 6, "k10_read_flicker: issuing T1/T2 command\n"); + if ((se = k10_write(p, isnew ? "T1\r" : "T2\r", 2.0)) != K10_OK) { + amutex_unlock(p->lock); + free(retbuf); + a1logd(p->log, 1, "k10_read_flicker: T1/T2 failed with 0x%x\n",icoms2k10_err(se)); + return k10_interp_code(p, se); + } + + stime = msec_time() - stime; + stime -= p->comdel; + + /* Switch to 19200 baud if using fast */ + if (isnew) { + /* Allow the T1/T2 to flow out before changing the baud rate */ + msec_sleep(2); + + if ((se = k10_set_baud(p, baud_19200)) != K10_OK) { + abort_flicker(p, isnew, retbuf); + a1logd(p->log, 1, "k10_read_flicker: T1 19200 baud failed with 0x%x\n", + icoms2k10_err(se)); + return k10_interp_code(p, se); + } + } + +#ifdef TEST_BAUD_CHANGE + else { + msec_sleep(2); + k10_set_baud(p, baud_19200); + k10_set_baud(p, baud_9600); + } +#endif + + /* Capture flicker packets until we've got enough samples */ + for (boff = nsamp = 0; nsamp < tsamp; ) { + if ((se = k10_read(p, buf + boff, MAX_MES_SIZE - boff, &bread, + NULL, 96, 2.0)) != K10_OK) { + abort_flicker(p, isnew, retbuf); + a1logd(p->log, 1, "k10_read_flicker: reading packet failed with 0x%x\n",icoms2k10_err(se)); + return k10_interp_code(p, se); + } + + boff += bread; + + /* Extract the values we want */ + /* (We could get XYZ, range & error value too) */ + if (boff >= 96) { + int trange[3]; + unsigned char *ubuf = (unsigned char *)buf; + + for (i = 0; i < 32 && nsamp < tsamp; i++, nsamp++) + retbuf[nsamp] = ubuf[i * 3 + 1] * 256.0 + ubuf[i * 3 + 2]; + + /* Check the error and range */ + if ((se = decodeK10err(buf[3 * 13])) != K10_OK) { + a1logd(p->log, 1, "k10_read_flicker: decode error 0x%x\n",se); + derr = se; + + } else { + + decodeRange(trange, buf[3 * 11]); + + if (trange[0] != range[0] + || trange[1] != range[1] + || trange[2] != range[2]) { + a1logd(p->log, 1, "k10_read_flicker: range changed\n"); + rerr = 1; + } + } + + /* Shuffle any remaining bytes down */ + if (boff > 96) + memmove(buf, buf + 96, boff - 96); + boff -= 96; + +#ifdef NEVER + { /* Dump */ + char xtra[32]; + double XYZ[3]; + int range[3]; + char err; + + for (i = 0; i < 32; i++) + xtra[i] = buf[i * 3 + 0]; + + adump_bytes(p->log, " ", (unsigned char *)buf, 0, 96); + printf("Extra bytes:\n"); + adump_bytes(p->log, " ", (unsigned char *)xtra, 0, 32); + + XYZ[0] = KleinMeas2double(xtra+2); + XYZ[1] = KleinMeas2double(xtra+5); + XYZ[2] = KleinMeas2double(xtra+8); + + decodeRange(range, xtra[11]); + err = xtra[13]; + printf("XYZ %f %f %f range %d %d %d err '%c'\n\n", + XYZ[0], XYZ[1], XYZ[2], range[0], range[1], range[2],err); + } +#endif + + } + } + + a1logd(p->log, 6, "k10_read_flicker: read %d samples\n",nsamp); + + /* Then issue an N5 to cancel, and clean up */ + abort_flicker(p, isnew, NULL); + + if (derr != 0) { + free(retbuf); + a1logd(p->log, 1, "k10_read_flicker: got error 0x%x during readings\n",derr); + return icoms2k10_err(derr); + } + + if (rerr != 0) { + free(retbuf); + a1logd(p->log, 1, "k10_read_flicker: range changed during readings\n"); + return icoms2k10_err(K10_RANGE_CHANGE); + } + +#ifdef NEVER + { /* Plot */ + double *xx; + + xx = (double *)malloc(sizeof(double) * tsamp); + for (i = 0; i < tsamp; i++) + xx[i] = (double)i/(double)rate; + + do_plot(xx, retbuf, NULL, NULL, tsamp); + free(xx); + } +#endif + + if (pvals != NULL) + *pvals = retbuf; + else + free(retbuf); + if (pnsamp != NULL) + *pnsamp = nsamp; + if (srate != NULL) + *srate = (double)rate; + + return inst_ok; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Read a single sample */ +static inst_code +k10_read_sample( +inst *pp, +char *name, /* Strip name (7 chars) */ +ipatch *val, /* Pointer to instrument patch value */ +instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ + kleink10 *p = (kleink10 *)pp; + char buf[MAX_RD_SIZE]; + int user_trig = 0; + int bsize; + inst_code rv = inst_protocol_error; + int range[3]; /* Range for RGB sensor values */ + int i, tries,ntav = 1; /* Number of readings to average */ + double v, vv, XYZ[3]; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + amutex_lock(p->lock); + + if (p->trig == inst_opt_trig_user) { + amutex_unlock(p->lock); + + if (p->uicallback == NULL) { + a1logd(p->log, 1, "kleink10: inst_opt_trig_user but no uicallback function set!\n"); + return inst_unsupported; + } + + for (;;) { + if ((rv = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) { + if (rv == inst_user_abort) { + return rv; /* Abort */ + } + if (rv == inst_user_trig) { + user_trig = 1; + break; /* Trigger */ + } + } + msec_sleep(200); + } + /* Notify of trigger */ + if (p->uicallback) + p->uicallback(p->uic_cntx, inst_triggered); + amutex_lock(p->lock); + + /* Progromatic Trigger */ + } else { + /* Check for abort */ + if (p->uicallback != NULL + && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) { + amutex_unlock(p->lock); + return rv; /* Abort */ + } + } + + /* Make sure the target lights are off */ + if (p->lights) { + if ((rv = k10_command(p, "L0\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_read_sample: L0 failed\n"); + return rv; + } + p->lights = 0; + } + + /* Make sure we are auto ranging */ + if (!p->autor) { + if ((rv = k10_command(p, "J8\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + p->autor = 1; + } + + + for (tries = 0; tries < RETRY_RANGE_ERROR; tries++) { + + /* Take a measurement */ + rv = k10_command(p, "N5\r", buf, MAX_MES_SIZE, &bsize, 15, ec_ec, 2.0); + + if (rv == inst_ok + || ( (rv & inst_imask) != K10_TOP_OVER_RANGE + && (rv & inst_imask) != K10_BOT_UNDER_RANGE)) + break; + } + + if (rv == inst_ok) + rv = decodeN5(p, val->XYZ, range, buf, bsize); + + if (rv == inst_ok) { + double thr[4] = { 0.2, 2.0, 20.0, 50.0 }; /* Threshold */ + double nav[4] = { 20, 10, 4, 2 }; /* Count */ + + /* Make v the largest */ + v = val->XYZ[1]; + if (val->XYZ[0] > v) + v = val->XYZ[0]; + if (val->XYZ[2] > v) + v = val->XYZ[2]; + + ntav = 1; +#ifdef AUTO_AVERAGE + if (!IMODETST(p->mode, inst_mode_emis_nonadaptive)) { + /* Decide how many extra readings to average into result. */ + /* Interpolate between the thresholds */ + if (v < 0.2) { + ntav = nav[0]; + } else if (v < thr[1]) { + vv = 1.0 - (v - thr[0]) / (thr[1] - thr[0]); + vv = vv * vv * vv; + ntav = (int)(vv * (nav[0] - 10) + 10.0 + 0.5); + } else if (v < thr[2]) { + vv = 1.0 - (v - thr[1]) / (thr[2] - thr[1]); + vv = vv * vv * vv; + ntav = (int)(vv * (nav[1] - nav[2]) + nav[2] + 0.5); + } else if (v < thr[3]) { + vv = 1.0 - (v - thr[2]) / (thr[3] - thr[2]); + vv = vv * vv * vv; + ntav = (int)(vv * (nav[2] - nav[3]) + nav[3] + 0.5); + } + } +#endif + + for (i = 1; i < ntav; i++) { + if ((rv = k10_command(p, "N5\r", buf, MAX_MES_SIZE, &bsize, 15, ec_ec, 2.0)) != inst_ok) + break; + if ((rv = decodeN5(p, XYZ, range, buf, bsize)) != inst_ok) + break; + + val->XYZ[0] += XYZ[0]; + val->XYZ[1] += XYZ[1]; + val->XYZ[2] += XYZ[2]; + } + } + + + if (rv != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + + val->XYZ[0] /= (double)ntav; + val->XYZ[1] /= (double)ntav; + val->XYZ[2] /= (double)ntav; + + + amutex_unlock(p->lock); + + if ((rv = decodeN5(p, val->XYZ, range, buf, bsize)) != inst_ok) { + return rv; + } + + /* Apply the calibration correction matrix */ + icmMulBy3x3(val->XYZ, p->ccmat, val->XYZ); + +//printf("matrix = %f %f %f\n", p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); +//printf(" %f %f %f\n", p->ccmat[1][0], p->ccmat[1][1], p->ccmat[2][2]); +//printf(" %f %f %f\n", p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); +//printf("XYZ = %f %f %f\n", val->XYZ[0], val->XYZ[1], val->XYZ[2]); +//printf("range = %d %d %d\n", range[0], range[1], range[2]); + + /* This may not change anything since instrument may clamp */ + if (clamp) + icmClamp3(val->XYZ, val->XYZ); + + val->loc[0] = '\000'; + + /* Check if the matrix seems to be an Ambient matrix */ + if ((p->ccmat[0][0] + p->ccmat[1][1] + p->ccmat[2][2])/3.0 > 5.0) + val->mtype = inst_mrt_ambient; + else + val->mtype = inst_mrt_emission; + val->XYZ_v = 1; /* These are absolute XYZ readings */ + val->sp.spec_n = 0; + val->duration = 0.0; + rv = inst_ok; + + + if (user_trig) + return inst_user_trig; + return rv; +} + +/* - - - - - - - - - - - - - - - - */ +/* + + Determining the refresh rate for a refresh type display. + + This is easy because the sample rate of the Kleoin + is well above the refresh rates we ant to measure. + + If there is no aparent refresh, or the refresh rate is not determinable, + return a period of 0.0 and inst_ok; +*/ + +#undef FREQ_SLOW_PRECISE /* [und] Interpolate then autocorrelate, else autc & filter */ + +#define NFSAMPS 450 /* Maximum number of samples to read (= 1.0sec) */ +#define NFMXTIME 0.5 /* Time to take measurements over */ +#define PBPMS 20 /* bins per msec */ +#define PERMIN ((1000 * PBPMS)/40) /* 40 Hz */ +#define PERMAX ((1000 * PBPMS)/4) /* 4 Hz*/ +#define NPER (PERMAX - PERMIN + 1) +#define PWIDTH (8 * PBPMS) /* 8 msec bin spread to look for peak in */ +#define MAXPKS 20 /* Number of peaks to find */ + +static inst_code k10_imp_measure_refresh( + kleink10 *p, + double *ref_rate +) { + inst_code ev; + int i, j, k, mm; + + int nfsamps; /* Actual samples read */ + double *samp; /* Samples */ + double srate; /* Sampling rate used to measure frequency */ + double rsamp; /* Sampling time */ + + double minv; /* Minimum reading */ + double maxv; /* Maximum reading */ + double maxt; /* Time range */ + +#ifdef FREQ_SLOW_PRECISE + int nbins; + double *bins; /* PBPMS sample bins */ +#else + double tcorr[NPER]; /* Temp for initial autocorrelation */ + int ntcorr[NPER]; /* Number accumulated */ +#endif + double corr[NPER]; /* Filtered correlation for each period value */ + double mincv, maxcv; /* Max and min correlation values */ + double crange; /* Correlation range */ + double peaks[MAXPKS]; /* Peak wavelength */ + double peakh[MAXPKS]; /* Peak heighheight */ + int npeaks; /* Number of peaks */ + double pval; /* Period value */ + double rfreq; /* Computed refresh frequency for each try */ + int tix = 0; /* try index */ + + a1logd(p->log,2,"k10_imp_meas_refrate called\n"); + + if (ref_rate != NULL) + *ref_rate = 0.0; /* Define refresh rate on error */ + + rfreq = 0.0; + npeaks = 0; /* Number of peaks */ + + if ((ev = k10_read_flicker_samples(p, NFMXTIME, &srate, &samp, &nfsamps, 1)) != inst_ok) { + return ev; + } + + rsamp = 1.0/srate; + +#ifdef PLOT_REFRESH + /* Plot the raw sensor values */ + { + double xx[NFSAMPS]; + + for (i = 0; i < nfsamps; i++) + xx[i] = i * rsamp; + printf("Fast scan sensor values and time (sec)\n"); + do_plot(xx, samp, NULL, NULL, nfsamps); + } +#endif /* PLOT_REFRESH */ + + /* Locate the smallest values and maximum time */ + maxt = -1e6; + minv = minv = minv = 1e20; + maxv = maxv = maxv = -11e20; + for (i = nfsamps-1; i >= 0; i--) { + if (samp[i] < minv) + minv = samp[i]; + if (samp[i] > maxv) + maxv = samp[i]; + } + maxt = (nfsamps-1) * rsamp; + + /* Zero offset the readings */ + for (i = nfsamps-1; i >= 0; i--) + samp[i] -= minv; + +#ifdef FREQ_SLOW_PRECISE /* Interp then autocorrelate */ + + /* Create PBPMS bins and interpolate readings into them */ + nbins = 1 + (int)(maxt * 1000.0 * PBPMS + 0.5); + if ((bins = (double *)calloc(sizeof(double), nbins)) == NULL) { + a1loge(p->log, inst_internal_error, "k10_imp_measure_refresh: malloc nbins %d failed\n",nbins); + free(samp); + return k10_interp_code(p, K10_INT_MALLOC); + } + + /* Do the interpolation */ + for (k = 0; k < (nfsamps-1); k++) { + int sbin, ebin; + double ksec = k * rsamp; + double ksecp1 = (k+1) * rsamp; + sbin = (int)(ksec * 1000.0 * PBPMS + 0.5); + ebin = (int)(ksecp1 * 1000.0 * PBPMS + 0.5); + for (i = sbin; i <= ebin; i++) { + double bl; +#if defined(__APPLE__) && defined(__POWERPC__) + gcc_bug_fix(i); +#endif + bl = (i - sbin)/(double)(ebin - sbin); /* 0.0 to 1.0 */ + bins[i] = (1.0 - bl) * samp[k] + bl * samp[k+1]; + } + } + +#ifdef NEVER + + /* Plot interpolated values */ + { + double *xx = malloc(sizeof(double) * nbins); + + if (xx == NULL) { + a1loge(p->log, inst_internal_error, "k10_imp_measure_refresh: malloc plot nbins %d failed\n",nbins); + free(samp); + return k10_interp_code(p, K10_INT_MALLOC); + } + for (i = 0; i < nbins; i++) + xx[i] = i / (double)PBPMS; /* msec */ + printf("Interpolated fast scan sensor values and time (msec)\n"); + do_plot(xx, bins, NULL, NULL, nbins); + free(xx); + } +#endif /* NEVER */ + + /* Compute auto-correlation at 1/PBPMS msec intervals */ + /* from 25 msec (40Hz) to 100msec (10 Hz) */ + mincv = 1e48, maxcv = -1e48; + for (i = 0; i < NPER; i++) { + int poff = PERMIN + i; /* Offset to corresponding sample */ + + corr[i] = 0; + for (k = 0; (k + poff) < nbins; k++) + corr[i] += bins[k] * bins[k + poff]; + corr[i] /= (double)k; /* Normalize */ + + if (corr[i] > maxcv) + maxcv = corr[i]; + if (corr[i] < mincv) + mincv = corr[i]; + } + /* Free the bins */ + free(bins); + +#else /* !FREQ_SLOW_PRECISE Fast - autocorrellate then filter */ + + /* Do point by point correllation of samples */ + for (i = 0; i < NPER; i++) { + tcorr[i] = 0.0; + ntcorr[i] = 0; + } + + for (j = 0; j < (nfsamps-1); j++) { + + for (k = j+1; k < nfsamps; k++) { + double del, cor; + int bix; + + del = (k - j) * rsamp; /* Sample time delta */ + bix = (int)(del * 1000.0 * PBPMS + 0.5); + if (bix < PERMIN) + continue; + if (bix > PERMAX) + break; + bix -= PERMIN; + + cor = samp[j] * samp[k]; + +//printf("~1 j %d k %d, del %f bix %d cor %f\n",j,k,del,bix,cor); + tcorr[bix] += cor; + ntcorr[bix]++; + } + } + /* Divide out count and linearly interpolate */ + j = 0; + for (i = 0; i < NPER; i++) { + if (ntcorr[i] > 0) { + tcorr[i] /= ntcorr[i]; + if ((i - j) > 1) { + if (j == 0) { + for (k = j; k < i; k++) + tcorr[k] = tcorr[i]; + + } else { /* Linearly interpolate from last value */ + double ww = (double)i-j; + for (k = j+1; k < i; k++) { + double bl = (k-j)/ww; + tcorr[k] = (1.0 - bl) * tcorr[j] + bl * tcorr[i]; + } + } + } + j = i; + } + } + if (j < (NPER-1)) { + for (k = j+1; k < NPER; k++) { + tcorr[k] = tcorr[j]; + } + } + +#ifdef PLOT_REFRESH + /* Plot unfiltered auto correlation */ + { + double xx[NPER]; + double y1[NPER]; + + for (i = 0; i < NPER; i++) { + xx[i] = (i + PERMIN) / (double)PBPMS; /* msec */ + y1[i] = tcorr[i]; + } + printf("Unfiltered auto correlation (msec)\n"); + do_plot(xx, y1, NULL, NULL, NPER); + } +#endif /* PLOT_REFRESH */ + + /* Apply a gausian filter */ +#define FWIDTH 100 + { + double gaus_[2 * FWIDTH * PBPMS + 1]; + double *gaus = &gaus_[FWIDTH * PBPMS]; + double bb = 1.0/pow(2, 5.0); + double fw = rsamp * 1000.0; + int ifw; + +//printf("~1 sc = %f = %f msec\n",1.0/rsamp, fw); +//printf("~1 fw = %f, ifw = %d\n",fw,ifw); + + fw *= 0.9; + ifw = (int)ceil(fw * PBPMS); + if (ifw > FWIDTH * PBPMS) + error("k10: Not enough space for lanczos 2 filter"); + for (j = -ifw; j <= ifw; j++) { + double x, y; + x = j/(PBPMS * fw); + if (fabs(x) > 1.0) + y = 0.0; + else + y = 1.0/pow(2, 5.0 * x * x) - bb; + gaus[j] = y; +//printf("~1 gaus[%d] = %f\n",j,y); + } + + for (i = 0; i < NPER; i++) { + double sum = 0.0; + double wght = 0.0; + + for (j = -ifw; j <= ifw; j++) { + double w; + int ix = i + j; + if (ix < 0) + ix = -ix; + if (ix > (NPER-1)) + ix = 2 * NPER-1 - ix; + w = gaus[j]; + sum += w * tcorr[ix]; + wght += w; + } +//printf("~1 corr[%d] wgt = %f\n",i,wght); + corr[i] = sum / wght; + } + } + + /* Compute min & max */ + mincv = 1e48, maxcv = -1e48; + for (i = 0; i < NPER; i++) { + if (corr[i] > maxcv) + maxcv = corr[i]; + if (corr[i] < mincv) + mincv = corr[i]; + } + +#endif /* !FREQ_SLOW_PRECISE Fast - autocorrellate then filter */ + + crange = maxcv - mincv; + a1logd(p->log,3,"Correlation value range %f - %f = %f = %f%%\n",mincv, maxcv,crange, 100.0 * (maxcv-mincv)/maxcv); + +#ifdef PLOT_REFRESH + /* Plot this measuremnts auto correlation */ + { + double xx[NPER]; + double y1[NPER]; + + for (i = 0; i < NPER; i++) { + xx[i] = (i + PERMIN) / (double)PBPMS; /* msec */ + y1[i] = corr[i]; + } + printf("Auto correlation (msec)\n"); + do_plot6(xx, y1, NULL, NULL, NULL, NULL, NULL, NPER); + } +#endif /* PLOT_REFRESH */ + +#define PFDB 4 // normally debug level 4 + /* If there is sufficient level and distict correlations */ + if (crange/maxcv >= 0.1) { + + a1logd(p->log,PFDB,"Searching for peaks\n"); + + /* Locate all the peaks starting at the longest correllation */ + for (i = (NPER-1-PWIDTH); i >= 0 && npeaks < MAXPKS; i--) { + double v1, v2, v3; + v1 = corr[i]; + v2 = corr[i + PWIDTH/2]; /* Peak */ + v3 = corr[i + PWIDTH]; + + if (fabs(v3 - v1)/crange < 0.05 + && (v2 - v1)/crange > 0.025 + && (v2 - v3)/crange > 0.025 + && (v2 - mincv)/crange > 0.5) { + double pkv; /* Peak value */ + int pki; /* Peak index */ + double ii, bl; + +#ifdef PLOT_REFRESH + a1logd(p->log,PFDB,"Max between %f and %f msec\n", + (i + PERMIN)/(double)PBPMS,(i + PWIDTH + PERMIN)/(double)PBPMS); +#endif + + /* Locate the actual peak */ + pkv = -1.0; + pki = 0; + for (j = i; j < (i + PWIDTH); j++) { + if (corr[j] > pkv) { + pkv = corr[j]; + pki = j; + } + } +#ifdef PLOT_REFRESH + a1logd(p->log,PFDB,"Peak is at %f msec, %f corr\n", (pki + PERMIN)/(double)PBPMS, pkv); +#endif + + /* Interpolate the peak value for higher precision */ + /* j = bigest */ + if (corr[pki-1] > corr[pki+1]) { + j = pki-1; + k = pki+1; + } else { + j = pki+1; + k = pki-1; + } + bl = (corr[pki] - corr[j])/(corr[pki] - corr[k]); + bl = (bl + 1.0)/2.0; + ii = bl * pki + (1.0 - bl) * j; + pval = (ii + PERMIN)/(double)PBPMS; +#ifdef PLOT_REFRESH + a1logd(p->log,PFDB,"Interpolated peak is at %f msec\n", pval); +#endif + peaks[npeaks] = pval; + peakh[npeaks] = corr[pki]; + npeaks++; + + i -= PWIDTH; + } +#ifdef NEVER + if (v2 > v1 && v2 > v3) { + printf("Peak rejected:\n"); + printf("(v3 - v1)/crange = %f < 0.05 ?\n",fabs(v3 - v1)/crange); + printf("(v2 - v1)/crange = %f > 0.025 ?\n",(v2 - v1)/crange); + printf("(v2 - v3)/crange = %f > 0.025 ?\n",(v2 - v3)/crange); + printf("(v2 - mincv)/crange = %f > 0.5 ?\n",(v2 - mincv)/crange); + } +#endif + } + a1logd(p->log,3,"Number of peaks located = %d\n",npeaks); + + } else { + a1logd(p->log,3,"All rejected, crange/maxcv = %f < 0.06\n",crange/maxcv); + } +#undef PFDB + + a1logd(p->log,3,"Number of peaks located = %d\n",npeaks); + + if (npeaks > 1) { /* Compute aparent refresh rate */ + int nfails; + double div, avg, ano; + + /* Try and locate a common divisor amongst all the peaks. */ + /* This is likely to be the underlying refresh rate. */ + for (k = 0; k < npeaks; k++) { + for (j = 1; j < 25; j++) { + avg = ano = 0.0; + div = peaks[k]/(double)j; + if (div < 5.0) + continue; /* Skip anything higher than 200Hz */ +//printf("~1 trying %f Hz\n",1000.0/div); + for (nfails = i = 0; i < npeaks; i++) { + double rem, cnt; + + rem = peaks[i]/div; + cnt = floor(rem + 0.5); + rem = fabs(rem - cnt); + +#ifdef PLOT_REFRESH + a1logd(p->log, 3, "remainder for peak %d = %f\n",i,rem); +#endif + if (rem > 0.06) { + if (++nfails > 2) + break; /* Fail this divisor */ + } else { + avg += peaks[i]; /* Already weighted by cnt */ + ano += cnt; + } + } + + if (nfails == 0 || (nfails <= 2 && npeaks >= 6)) + break; /* Sucess */ + /* else go and try a different divisor */ + } + if (j < 25) + break; /* Success - found common divisor */ + } + if (k >= npeaks) { + a1logd(p->log,3,"Failed to locate common divisor\n"); + + } else { + pval = 1000.0 * ano/avg; + if (pval > srate) { + a1logd(p->log,3,"Discarding frequency %f > sample rate %f\n",pval, srate); + } else { + rfreq = pval; + a1logd(p->log,3,"Located frequency %f sum %f dif %f\n",pval, pval + srate, fabs(pval - srate)); + tix++; + } + } + } + + if (tix) { + + /* The Klein samples so fast, we don't have to deal with */ + /* sub Nyquist aliases. */ + + if (ref_rate != NULL) + *ref_rate = rfreq; + + /* Error against my 85Hz CRT - GWG */ + a1logd(p->log, 1, "Refresh rate %f Hz, error = %.4f%%\n",rfreq,100.0 * fabs(rfreq - 85.0)/(85.0)); + free(samp); + return k10_interp_code(p, K10_OK); + + } else { + a1logd(p->log, 3, "Refresh rate was unclear\n"); + } + + free(samp); + + return k10_interp_code(p, K10_NOREFR_FOUND); +} +#undef NFSAMPS +#undef PBPMS +#undef PERMIN +#undef PERMAX +#undef NPER +#undef PWIDTH + +/* Read an emissive refresh rate */ +static inst_code +k10_read_refrate( +inst *pp, +double *ref_rate +) { + kleink10 *p = (kleink10 *)pp; + char buf[MAX_MES_SIZE]; + double refrate; + inst_code rv; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if (ref_rate != NULL) + *ref_rate = 0.0; + + if ((rv = k10_imp_measure_refresh(p, &refrate)) != inst_ok) { + return rv; + } + + if (refrate == 0.0) + return inst_misread; + + if (ref_rate != NULL) + *ref_rate = refrate; + + return inst_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, */ +/* and this will measure the time it took for the update to */ +/* be noticed by the instrument, up to 2.0 seconds. */ +/* (It is assumed that white_change() will be called at the time the patch */ +/* changes color.) */ +/* inst_misread will be returned on failure to find a transition to black. */ + +#define NDSAMPS 40 /* Maximum samples */ +#define NDMXTIME 2.0 /* Maximum time to take */ + +static inst_code k10_meas_delay( +inst *pp, +int *pdispmsec, /* Return display update delay in msec */ +int *pinstmsec) { /* Return instrument reaction time in msec */ + kleink10 *p = (kleink10 *)pp; + inst_code ev; + char mes[MAX_MES_SIZE]; + int bread; + int i, j, k; + double sutime, putime, cutime, eutime; + struct { + double sec; + double xyz[3]; + } samp[NDSAMPS]; + int ndsamps; + double stot, etot, del, thr; + double stime, etime; + int isdeb; + int avgsampsp; + int dispmsec, instmsec; + + if (pinstmsec != NULL) + *pinstmsec = -230; + + if (!p->gotcoms) + return inst_no_coms; + + if (!p->inited) + return inst_no_init; + + if (usec_time() < 0.0) { + a1loge(p->log, inst_internal_error, "k10_imp_meas_delay: No high resolution timers\n"); + return inst_internal_error; + } + + /* Turn debug off so that they doesn't intefere with measurement timing */ + isdeb = p->log->debug; + p->icom->log->debug = 0; + + /* Read the samples */ + putime = usec_time() / 1000000.0; + amutex_lock(p->lock); + for (i = 0; i < NDSAMPS; i++) { + + /* Take a measurement to get ranges ? */ + if ((ev = k10_command(p, "N5\r", mes, MAX_MES_SIZE, &bread, 15, ec_ec, 2.0)) != inst_ok) { + amutex_unlock(p->lock); + p->log->debug = isdeb; + a1logd(p->log, 1, "k10_meas_delay: measurement failed\n"); + return ev; + } + + if ((ev = decodeN5(p, samp[i].xyz, NULL, mes, bread)) != inst_ok) { + amutex_unlock(p->lock); + p->log->debug = isdeb; + a1logd(p->log, 1, "k10_meas_delay: measurement decode failed\n"); + return ev; + } + + cutime = usec_time() / 1000000.0; +// samp[i].sec = 0.5 * (putime + cutime); /* Mean of before and after stamp ? */ + samp[i].sec = cutime; /* Assume took until measure was received */ +// samp[i].sec = putime; /* Assume sampled at time triggered */ + putime = cutime; + if (cutime > NDMXTIME) + break; + } + ndsamps = i; + amutex_unlock(p->lock); + + /* Average sample spacing in msec */ + avgsampsp = (int)(1000.0 * (samp[i-1].sec - samp[0].sec)/(i-1.0) + 0.5); + + /* Restore debugging */ + p->log->debug = isdeb; + + if (ndsamps == 0) { + a1logd(p->log, 1, "k10_meas_delay: No measurement samples returned in time\n"); + return inst_internal_error; + } + + if (p->whitestamp < 0.0) { + a1logd(p->log, 1, "k10_meas_delay: White transition wasn't timestamped\n"); + return inst_internal_error; + } + + /* Set the times to be white transition relative */ + for (i = 0; i < ndsamps; i++) + samp[i].sec -= p->whitestamp / 1000000.0; + + /* Over the first 100msec, locate the maximum value */ + stime = samp[0].sec; + stot = -1e9; + for (i = 0; i < ndsamps; i++) { + if (samp[i].xyz[1] > stot) + stot = samp[i].xyz[1]; + if ((samp[i].sec - stime) > 0.1) + break; + } + + /* Over the last 100msec, locate the maximum value */ + etime = samp[ndsamps-1].sec; + etot = -1e9; + for (i = ndsamps-1; i >= 0; i--) { + if (samp[i].xyz[1] > etot) + etot = samp[i].xyz[1]; + if ((etime - samp[i].sec) > 0.1) + break; + } + + del = etot - stot; + thr = etot - 0.10 * del; /* 10% of transition threshold */ + +#ifdef PLOT_UPDELAY + a1logd(p->log, 0, "k10_meas_delay: start tot %f end tot %f del %f, thr %f\n", stot, etot, del, thr); +#endif + +#ifdef PLOT_UPDELAY + /* Plot the raw sensor values */ + { + double xx[NDSAMPS]; + double y1[NDSAMPS]; + double y2[NDSAMPS]; + double y3[NDSAMPS]; + + for (i = 0; i < ndsamps; i++) { + xx[i] = samp[i].sec; + y1[i] = samp[i].xyz[0]; + y2[i] = samp[i].xyz[1]; + y3[i] = samp[i].xyz[2]; + } + printf("Display update delay measure sensor values and time (sec)\n"); + do_plot(xx, y1, y2, y3, ndsamps); + } +#endif + + /* Check that there has been a transition */ + if (del < (0.7 * etot)) { + a1logd(p->log, 1, "k10_meas_delay: can't detect change from black to white\n"); + return inst_misread; + } + + /* Working from the start, locate the time at which the level was above the threshold */ + for (i = 0; i < (ndsamps-1); i++) { + if (samp[i].xyz[1] > thr) + break; + } + + a1logd(p->log, 2, "k10_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec); + + /* Compute overall delay */ + dispmsec = (int)(samp[i].sec * 1000.0 + 0.5); + + /* The 20 Hz filter is probably a FIR which introduces a delay in */ + /* the samples being measured, creating both a settling delay and */ + /* a look ahead. A negative inst. reaction time value will cause the */ + /* patch_delay to be extended by that amount of time. */ + /* We assume 2 samples times to settle, but round up the patch */ + /* delay conservatively. */ + instmsec = -2 * avgsampsp; + +#ifdef PLOT_UPDELAY + a1logd(p->log, 0, "k10_meas_delay: raw %d & %d msec\n",dispmsec,instmsec); +#endif + + dispmsec += instmsec; /* Account for lookahead */ + + if (dispmsec < 0) /* This can happen if the patch generator delays it's return */ + dispmsec = 0; + + /* Round the patch delay to to next highest avgsampsp */ + dispmsec = (int)((1.0 + floor((double)dispmsec/(double)avgsampsp)) * avgsampsp + 0.5); + + if (pdispmsec != NULL) + *pdispmsec = dispmsec; + + if (pinstmsec != NULL) + *pinstmsec = instmsec; + +#ifdef PLOT_UPDELAY + a1logd(p->log, 0, "k10_meas_delay: returning %d & %d msec\n",dispmsec,instmsec); +#endif + + return inst_ok; +} +#undef NDSAMPS +#undef DINTT +#undef NDMXTIME + + +/* Timestamp the white patch change during meas_delay() */ +static inst_code k10_white_change( +inst *pp, +int init) { + kleink10 *p = (kleink10 *)pp; + inst_code ev; + + if (init) + p->whitestamp = -1.0; + else { + if ((p->whitestamp = usec_time()) < 0.0) { + a1loge(p->log, inst_internal_error, "k10_wite_changeO: No high resolution timers\n"); + return inst_internal_error; + } + } + + return inst_ok; +} + +/* - - - - - - - - - - - - - - - - */ + +/* Do a black calibration */ +static inst_code +k10_do_black_cal( + kleink10 *p +) { + inst_code ev; + char mes[MAX_MES_SIZE]; + unsigned char *umes = (unsigned char *)mes; + int bread; + int i, j, k; + int val, th1, th2; + int bvals[6][3]; /* Black values for range 1 to 6 */ + int thermal; /* Thermal value */ + + amutex_lock(p->lock); + + /* First get the Measure Count to check that TH1 and TH2 are between 50 and 200 */ + /* (Don't know why or what these mean - something to do with temperature compensation */ + /* values not being setup ?) */ + if ((ev = k10_command(p, "M6\r", mes, MAX_MES_SIZE, &bread, 20, ec_e, 2.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_do_black_cal: M6 failed\n"); + return ev; + } + + if (bread < 17) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_do_black_cal: not enough bytes returned from M6 (%d)\n",bread); + return inst_protocol_error; + } + + th1 = umes[14]; + th2 = umes[15]; + + if (th1 < 50 || th1 > 200 + || th2 < 50 || th2 > 200) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "th1 %d or th2 %d is out of range 50-200\n",th1,th2); + return k10_interp_code(p, K10_BLACK_CAL_INIT); + } + + /* Do the black calibration */ + if ((ev = k10_command(p, "B9\r", mes, MAX_MES_SIZE, &bread, 43, ec_ec, 15.0)) != inst_ok) { + a1logd(p->log, 1, "k10_do_black_cal: B9 failed\n"); + amutex_unlock(p->lock); + return ev; + } + + if (bread < 40) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_do_black_cal: not enough bytes returned from B9 (%d)\n",bread); + return inst_protocol_error; + } + + /* Parse the black values that resulted */ + for (k = i = 0; i < 6; i++) { + for (j = 0; j < 3; j++, k++) { + val = umes[2 + 2 * k] * 256 + umes[2 + 2 * k + 1]; + if (val < 500 || val > 2500) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_do_black_cal: B9 black result value out of range\n"); + return k10_interp_code(p, K10_BLACK_CAL_FAIL); + } + bvals[i][j] = val; + } + } + val = umes[2 + 2 * k] * 256 + umes[2 + 2 * k + 1]; + if (val < 500 || val > 2500) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_do_black_cal: B9 black thermal result value out of range\n"); + return k10_interp_code(p, K10_BLACK_CAL_FAIL); + } + thermal = val; + + if (p->log->debug >= 4) { + for (i = 0; i < 6; i++) + a1logd(p->log, 4, "Black cal. Range %d XYZ = %d %d %d\n", + i+1, bvals[i][0], bvals[i][1], bvals[i][2]); + a1logd(p->log, 4, "Thermal %d\n",thermal); + } + + /* All looks well - copy into Flash ROM */ + if ((ev = k10_command(p, "B7\r", mes, MAX_MES_SIZE, &bread, 2, ec_c, 2.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_do_black_cal: B7 failed\n"); + return ev; + } + + /* Send verification code and get error code*/ + if ((ev = k10_command(p, "{00000000}@%#\r", mes, MAX_MES_SIZE, &bread, 3, ec_e, 2.0)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_do_black_cal: B7 followup failed\n"); + return ev; + } + amutex_unlock(p->lock); + + a1logd(p->log, 4, "k10_do_black_cal: Done\n"); + + return inst_ok; +} + +/* - - - - - - - - - - - - - - - - */ + +/* Return needed and available inst_cal_type's */ +static inst_code k10_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) { + kleink10 *p = (kleink10 *)pp; + inst_cal_type n_cals = inst_calt_none; + inst_cal_type a_cals = inst_calt_none; + + /* Can do a black cal, but not required */ + a_cals |= inst_calt_emis_offset; + + if (pn_cals != NULL) + *pn_cals = n_cals; + + if (pa_cals != NULL) + *pa_cals = a_cals; + + return inst_ok; +} + +/* Request an instrument calibration. */ +inst_code k10_calibrate( +inst *pp, +inst_cal_type *calt, /* Calibration type to do/remaining */ +inst_cal_cond *calc, /* Current condition/desired condition */ +char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ +) { + kleink10 *p = (kleink10 *)pp; + inst_code ev; + inst_cal_type needed, available; + + if (!p->gotcoms) + return inst_no_coms; + + if (!p->inited) + return inst_no_init; + + id[0] = '\000'; + + if ((ev = k10_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok) + return ev; + + /* Translate inst_calt_all/needed into something specific */ + if (*calt == inst_calt_all + || *calt == inst_calt_needed + || *calt == inst_calt_available) { + if (*calt == inst_calt_all) + *calt = (needed & inst_calt_n_dfrble_mask) | inst_calt_ap_flag; + else if (*calt == inst_calt_needed) + *calt = needed & inst_calt_n_dfrble_mask; + else if (*calt == inst_calt_available) + *calt = available & inst_calt_n_dfrble_mask; + + a1logd(p->log,4,"k10_calibrate: doing calt 0x%x\n",calt); + + if ((*calt & inst_calt_n_dfrble_mask) == 0) /* Nothing todo */ + return inst_ok; + } + + /* See if it's a calibration we understand */ + if (*calt & ~available & inst_calt_all_mask) { + return inst_unsupported; + } + + /* Do the appropriate calibration */ + if (*calt & inst_calt_emis_offset) { + + if (*calc != inst_calc_man_em_dark) { + *calc = inst_calc_man_em_dark; + return inst_cal_setup; + } + + /* Do black offset calibration */ + if ((ev = k10_do_black_cal(p)) != inst_ok) + return ev; + + *calt &= ~inst_calc_man_em_dark; + } + return inst_ok; +} + +/* Error codes interpretation */ +static char * +k10_interp_error(inst *pp, int ec) { + kleink10 *p = (kleink10 *)pp; + ec &= inst_imask; + switch (ec) { + case K10_INTERNAL_ERROR: + return "Internal software error"; + case K10_TIMEOUT: + return "Communications timeout"; + case K10_COMS_FAIL: + return "Communications failure"; + case K10_UNKNOWN_MODEL: + return "Not a Klein K10"; + case K10_DATA_PARSE_ERROR: + return "Data from kleink10 didn't parse as expected"; +// case K10_SPOS_EMIS: +// return "Ambient filter should be removed"; +// case K10_SPOS_AMB: +// return "Ambient filter should be used"; + + case K10_OK: + return "No device error"; + + case K10_CMD_VERIFY: + return "Instrument didn't echo command code"; + case K10_BAD_RETVAL: + return "Unable to parse return instruction return code"; + + case K10_FIRMWARE: + return "Firmware error"; + + case K10_BLACK_EXCESS: + return "Black Excessive"; + case K10_BLACK_OVERDRIVE: + return "Black Overdrive"; + case K10_BLACK_ZERO: + return "Black Zero"; + + case K10_OVER_HIGH_RANGE: + return "Over High Range"; + case K10_TOP_OVER_RANGE: + return "Top over range"; + case K10_BOT_UNDER_RANGE: + return "Bottom under range"; + case K10_AIMING_LIGHTS: + return "Aiming lights on when measuring"; + + case K10_UNKNOWN: + return "Unknown error from instrument"; + + case K10_INT_MALLOC: + return "Memory allocation failure"; + + case K10_NOREFR_FOUND: + return "No refresh rate detected or failed to measure it"; + + case K10_NOTRANS_FOUND: + return "No delay measurment transition found"; + + case K10_RANGE_CHANGE: + return "Range changed during measurement"; + + case K10_BLACK_CAL_INIT: + return "Instrument hasn't been setup for black calibration"; + case K10_BLACK_CAL_FAIL: + return "Black calibration failed"; + + default: + return "Unknown error code"; + } +} + + +/* Convert a machine specific error code into an abstract dtp code */ +static inst_code +k10_interp_code(kleink10 *p, int ec) { + + ec &= inst_imask; + switch (ec) { + + case K10_OK: + return inst_ok; + + case K10_INTERNAL_ERROR: + case K10_AIMING_LIGHTS: + case K10_UNKNOWN: + case K10_INT_MALLOC: + return inst_internal_error | ec; + + case K10_TIMEOUT: + case K10_COMS_FAIL: + return inst_coms_fail | ec; + + case K10_UNKNOWN_MODEL: + return inst_unknown_model | ec; + + case K10_CMD_VERIFY: + case K10_BAD_RETVAL: + case K10_DATA_PARSE_ERROR: + return inst_protocol_error | ec; + + case K10_FIRMWARE: + case K10_BLACK_EXCESS: // ? + case K10_BLACK_OVERDRIVE: // ? + case K10_BLACK_ZERO: // ? + case K10_BLACK_CAL_INIT: + return inst_hardware_fail | ec; + + case K10_OVER_HIGH_RANGE: + case K10_TOP_OVER_RANGE: + case K10_BOT_UNDER_RANGE: + case K10_NOREFR_FOUND: + case K10_NOTRANS_FOUND: + case K10_RANGE_CHANGE: + case K10_BLACK_CAL_FAIL: + return inst_misread | ec; + + } + return inst_other_error | ec; +} + +/* Destroy ourselves */ +static void +k10_del(inst *pp) { + if (pp != NULL) { + kleink10 *p = (kleink10 *)pp; + if (p->icom != NULL) + p->icom->del(p->icom); + amutex_del(p->lock); + free(p); + } +} + +/* Return the instrument mode capabilities */ +static void k10_capabilities(inst *pp, +inst_mode *pcap1, +inst2_capability *pcap2, +inst3_capability *pcap3) { + kleink10 *p = (kleink10 *)pp; + inst_mode cap1 = 0; + inst2_capability cap2 = 0; + + cap1 |= inst_mode_emis_tele + | inst_mode_emis_spot + | inst_mode_ambient /* But cc matrix is up to user */ + | inst_mode_emis_nonadaptive + | inst_mode_colorimeter + ; + + /* can inst2_has_sensmode, but not report it asynchronously */ + cap2 |= inst2_prog_trig + | inst2_user_trig + | inst2_disptype + | inst2_has_target /* Has target lights */ + | inst2_ccmx + | inst2_emis_refr_meas + | inst2_meas_disp_update + ; + + + if (pcap1 != NULL) + *pcap1 = cap1; + if (pcap2 != NULL) + *pcap2 = cap2; + if (pcap3 != NULL) + *pcap3 = inst3_none; +} + +/* Check device measurement mode */ +static inst_code k10_check_mode(inst *pp, inst_mode m) { + inst_mode cap; + + if (!pp->gotcoms) + return inst_no_coms; + if (!pp->inited) + return inst_no_init; + + pp->capabilities(pp, &cap, NULL, NULL); + + /* Simple test */ + if (m & ~cap) + return inst_unsupported; + + if (!IMODETST(m, inst_mode_emis_spot) + && !IMODETST(m, inst_mode_emis_tele) + && !IMODETST(m, inst_mode_emis_ambient)) { + return inst_unsupported; + } + + return inst_ok; +} + +/* Set device measurement mode */ +static inst_code k10_set_mode(inst *pp, inst_mode m) { + kleink10 *p = (kleink10 *)pp; + int refrmode; + inst_code ev; + + if ((ev = k10_check_mode(pp, m)) != inst_ok) + return ev; + + p->mode = m; + + return inst_ok; +} + +/* This table gets extended on initialisation */ +/* There is 1 factory + 96 programmable + end marker */ +static inst_disptypesel k10_disptypesel[98] = { + { + inst_dtflags_default | inst_dtflags_mtx, /* flags */ + 1, /* cbid */ + "F", /* sel */ + "Factory Default", /* desc */ + 0, /* refr */ + disptech_unknown, /* disptype */ + 0 /* ix */ + }, + { + inst_dtflags_end, + 0, + "", + "", + 0, + disptech_none, + 0 + } +}; + +/* Get mode and option details */ +static inst_code k10_get_disptypesel( +inst *pp, +int *pnsels, /* Return number of display types */ +inst_disptypesel **psels, /* Return the array of display types */ +int allconfig, /* nz to return list for all configs, not just current. */ +int recreate /* nz to re-check for new ccmx & ccss files */ +) { + kleink10 *p = (kleink10 *)pp; + inst_code rv = inst_ok; + + /* Create/Re-create a current list of available display types */ + if (p->dtlist == NULL || recreate) { + if ((rv = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + k10_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return rv; + } + + if (pnsels != NULL) + *pnsels = p->ndtlist; + + if (psels != NULL) + *psels = p->dtlist; + + return inst_ok; +} + +/* Given a display type entry, setup calibration from that type */ +static inst_code set_disp_type(kleink10 *p, inst_disptypesel *dentry) { + + /* If aninbuilt matrix hasn't been read from the instrument, */ + /* read it now. */ + if ((dentry->flags & inst_dtflags_mtx) + && (dentry->flags & inst_dtflags_ld) == 0) { + inst_code rv; + if ((rv = k10_read_cal_matrix(p, dentry, dentry->ix)) != inst_ok) + return rv; + } + + if (dentry->flags & inst_dtflags_ccmx) { + if (dentry->cc_cbid != 1) { + a1loge(p->log, 1, "k10: matrix must use cbid 1!\n",dentry->cc_cbid); + return inst_wrong_setup; + } + + p->dtech = dentry->dtech; + icmCpy3x3(p->ccmat, dentry->mat); + p->cbid = 0; /* Can't be a base type now */ + + } else { + p->dtech = dentry->dtech; + icmCpy3x3(p->ccmat, dentry->mat); + p->cbid = dentry->cbid; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ + } + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + + return inst_ok; +} + +/* Setup the default display type */ +static inst_code set_default_disp_type(kleink10 *p) { + inst_code ev; + int i; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, + k10_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (p->dtlist[i].flags & inst_dtflags_default) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_default_disp_type: failed to find type!\n"); + return inst_internal_error; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { + return ev; + } + + return inst_ok; +} + +/* Set the display type */ +static inst_code k10_set_disptype(inst *pp, int ix) { + kleink10 *p = (kleink10 *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, + k10_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + if (ix < 0 || ix >= p->ndtlist) + return inst_unsupported; + + dentry = &p->dtlist[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; + } + + return inst_ok; +} + +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code k10_get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + kleink10 *p = (kleink10 *)pp; + if (dtech != NULL) + *dtech = p->dtech; + if (refrmode != NULL) + *refrmode = disptech_get_id(disptech_unknown)->refr; + if (cbid != NULL) + *cbid = p->cbid; + return inst_ok; +} + +/* Insert a colorimetric correction matrix in the instrument XYZ readings */ +/* This is only valid for colorimetric instruments. */ +/* To remove the matrix, pass NULL for the filter filename */ +inst_code k10_col_cor_mat( +inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ \ +int cbid, /* Calibration display type base ID, 1 if unknown */\ +double mtx[3][3] +) { + kleink10 *p = (kleink10 *)pp; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + /* We don't have to set the base type since the instrument always returns factory */ + if (cbid != 1) { + a1loge(p->log, 1, "k10: matrix must use cbid 1!\n",cbid); + return inst_wrong_setup; + } + + if (mtx == NULL) { + icmSetUnity3x3(p->ccmat); + } else { + icmCpy3x3(p->ccmat, mtx); + } + + p->dtech = dtech; + p->cbid = 0; + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + + return inst_ok; +} + +/* + * set or reset an optional mode + * + * Some options talk to the instrument, and these will + * error if it hasn't been initialised. + */ +static inst_code +k10_get_set_opt(inst *pp, inst_opt_type m, ...) +{ + kleink10 *p = (kleink10 *)pp; + char buf[MAX_MES_SIZE]; + int se; + + a1logd(p->log, 5, "k10_get_set_opt: opt type 0x%x\n",m); + + /* Record the trigger mode */ + if (m == inst_opt_trig_prog + || m == inst_opt_trig_user) { + p->trig = m; + return inst_ok; + } + + /* Get target light state */ + if (m == inst_opt_get_target_state) { + va_list args; + int *pstate, lstate = 0; + + va_start(args, m); + pstate = va_arg(args, int *); + va_end(args); + + if (pstate != NULL) + *pstate = p->lights; + + return inst_ok; + + /* Set target light state */ + } else if (m == inst_opt_set_target_state) { + inst_code ev; + va_list args; + int state = 0; + + va_start(args, m); + state = va_arg(args, int); + va_end(args); + + amutex_lock(p->lock); + + if (state == 2) { /* Toggle */ + if (p->lights) + state = 0; + else + state = 1; + } + + if (state == 1) { /* Turn on */ + if ((ev = k10_command(p, "L1\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 0.5)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_get_set_opt: L1 failed\n"); + return ev; + } + p->lights = 1; + } else if (state == 0) { /* Turn off */ + if ((ev = k10_command(p, "L0\r", buf, MAX_MES_SIZE, NULL, 2+3, ec_ec, 0.5)) != inst_ok) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "k10_get_set_opt: L0 failed\n"); + return ev; + } + p->lights = 0; + } + amutex_unlock(p->lock); + return inst_ok; + } + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + return inst_unsupported; +} + +/* Constructor */ +extern kleink10 *new_kleink10(icoms *icom, instType itype) { + kleink10 *p; + if ((p = (kleink10 *)calloc(sizeof(kleink10),1)) == NULL) { + a1loge(icom->log, 1, "new_kleink10: malloc failed!\n"); + return NULL; + } + + p->log = new_a1log_d(icom->log); + + p->init_coms = k10_init_coms; + p->init_inst = k10_init_inst; + p->capabilities = k10_capabilities; + p->check_mode = k10_check_mode; + p->set_mode = k10_set_mode; + p->get_disptypesel = k10_get_disptypesel; + p->set_disptype = k10_set_disptype; + p->get_disptechi = k10_get_disptechi; + p->get_set_opt = k10_get_set_opt; + p->read_sample = k10_read_sample; + p->read_refrate = k10_read_refrate; + p->get_n_a_cals = k10_get_n_a_cals; + p->calibrate = k10_calibrate; + p->col_cor_mat = k10_col_cor_mat; + p->meas_delay = k10_meas_delay; + p->white_change = k10_white_change; + p->interp_error = k10_interp_error; + p->del = k10_del; + + p->icom = icom; + p->itype = icom->itype; + p->dtech = disptech_unknown; + + amutex_init(p->lock); + + /* Attempt to get the calibration list */ + k10_init_coms((inst *)p, baud_nc, fc_nc, 0.0); + + return p; +} + diff --git a/spectro/kleink10.h b/spectro/kleink10.h new file mode 100644 index 0000000..4dcc334 --- /dev/null +++ b/spectro/kleink10.h @@ -0,0 +1,121 @@ +#ifndef KLEINK10_H + +/* + * Argyll Color Correction System + * + * Klein K10 related defines + * + * Author: Graeme W. Gill + * Date: 29/4/2014 + * + * Copyright 2001 - 2014, 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. + * + * Based on DTP02.h & specbos.h + */ + +/* + If you make use of the instrument driver code here, please note + that it is the author(s) of the code who take responsibility + for its operation. Any problems or queries regarding driving + instruments with the Argyll drivers, should be directed to + the Argyll's author(s), and not to any other party. + + If there is some instrument feature or function that you + would like supported here, it is recommended that you + contact Argyll's author(s) first, rather than attempt to + modify the software yourself, if you don't have firm knowledge + of the instrument communicate protocols. There is a chance + that an instrument could be damaged by an incautious command + sequence, and the instrument companies generally cannot and + will not support developers that they have not qualified + and agreed to support. + */ + +#include "inst.h" + +/* Fake Error codes */ +#define K10_INTERNAL_ERROR 0xff01 /* Internal software error */ +#define K10_TIMEOUT 0xff02 /* Communication timeout */ +#define K10_COMS_FAIL 0xff03 /* Communication failure */ +#define K10_UNKNOWN_MODEL 0xff04 /* Not a JETI kleink10 */ +#define K10_DATA_PARSE_ERROR 0xff05 /* Read data parsing error */ + +//#define K10_SPOS_EMIS 0xff06 /* Needs to be in emsissive configuration */ +//#define K10_SPOS_AMB 0xff07 /* Needs to be in ambient configuration */ + +/* Real instrument error code */ +#define K10_OK 0 + +/* Other errors */ +#define K10_CMD_VERIFY 0x1000 /* Didn't echo the commamd */ +#define K10_BAD_RETVAL 0x1001 /* Error code return value can't be parsed */ + +#define K10_FIRMWARE 0x2001 /* Firmware error */ + +#define K10_BLACK_EXCESS 0x2010 /* Black Excessive */ +#define K10_BLACK_OVERDRIVE 0x2011 /* Black Overdrive */ +#define K10_BLACK_ZERO 0x2012 /* Black Zero */ + +#define K10_OVER_HIGH_RANGE 0x2020 /* Over High Range */ +#define K10_TOP_OVER_RANGE 0x2021 /* Top over range */ +#define K10_BOT_UNDER_RANGE 0x2022 /* Bottom under range */ +#define K10_AIMING_LIGHTS 0x2023 /* Aiming lights on when measuring */ + +#define K10_RANGE_CHANGE 0x2024 /* Range changed during measurment */ +#define K10_NOREFR_FOUND 0x2025 /* Unable to measure refresh rate */ +#define K10_NOTRANS_FOUND 0x2026 /* No delay measurment transition found */ + +#define K10_BLACK_CAL_INIT 0x2027 /* Instrument not setup for bcal */ +#define K10_BLACK_CAL_FAIL 0x2028 /* Black calibration failed */ + +#define K10_UNKNOWN 0x2030 /* Unknown error code */ + +#define K10_INT_MALLOC 0x3000 /* Malloc failed */ + +typedef enum { + k10_k1 = 0, /* K-1 */ + k10_k8 = 1, /* K-8 */ + k10_k10 = 2, /* K-10 */ + k10_k10a = 3, /* K-10-A */ + k10_kv10a = 4, /* KV-10-A */ +} k10_dtype; + +/* Klein K10 communication object */ +struct _kleink10 { + INST_OBJ_BASE + + amutex lock; /* Command lock */ + + k10_dtype model; /* Klein kleink10 model number */ + char serial_no[21]; /* Serial number */ + char firm_ver[21]; /* Firmware version number */ + int comdel; /* Estimated communication delay to the instrument */ + + inst_mode mode; /* Currently instrument mode */ + + inst_opt_type trig; /* Reading trigger mode */ + + int autor; /* Auto range state, nz = on */ + int lights; /* Target light state, nz = on */ + + inst_disptypesel *dtlist; /* Current display Type list */ + int ndtlist; /* Number of valid dtlist entries */ + int cbid; /* current calibration base ID, 0 if not a base */ + int ucbid; /* Underlying base ID if being used for matrix, 0 othewise */ + disptech dtech; /* Display technology enum */ + double ccmat[3][3]; /* Colorimeter correction matrix */ + + volatile double whitestamp; /* meas_delay() white timestamp */ + + }; typedef struct _kleink10 kleink10; + +/* Constructor */ +extern kleink10 *new_kleink10(icoms *icom, instType itype); + + +#define KLEINK10_H +#endif /* KLEINK10_H */ diff --git a/spectro/linear.cal b/spectro/linear.cal new file mode 100644 index 0000000..da60b9e --- /dev/null +++ b/spectro/linear.cal @@ -0,0 +1,275 @@ +CAL + +DESCRIPTOR "Argyll Device Calibration Curves" +ORIGINATOR "Argyll synthcal" +CREATED "Tue Apr 28 15:22:30 2015" +KEYWORD "DEVICE_CLASS" +DEVICE_CLASS "DISPLAY" +KEYWORD "COLOR_REP" +COLOR_REP "RGB" + +KEYWORD "RGB_I" +NUMBER_OF_FIELDS 4 +BEGIN_DATA_FORMAT +RGB_I RGB_R RGB_G RGB_B +END_DATA_FORMAT + +NUMBER_OF_SETS 256 +BEGIN_DATA +0.00000 0.00000 0.00000 0.00000 +3.92157e-003 3.92157e-003 3.92157e-003 3.92157e-003 +7.84314e-003 7.84314e-003 7.84314e-003 7.84314e-003 +0.0117647 0.0117647 0.0117647 0.0117647 +0.0156863 0.0156863 0.0156863 0.0156863 +0.0196078 0.0196078 0.0196078 0.0196078 +0.0235294 0.0235294 0.0235294 0.0235294 +0.0274510 0.0274510 0.0274510 0.0274510 +0.0313725 0.0313725 0.0313725 0.0313725 +0.0352941 0.0352941 0.0352941 0.0352941 +0.0392157 0.0392157 0.0392157 0.0392157 +0.0431373 0.0431373 0.0431373 0.0431373 +0.0470588 0.0470588 0.0470588 0.0470588 +0.0509804 0.0509804 0.0509804 0.0509804 +0.0549020 0.0549020 0.0549020 0.0549020 +0.0588235 0.0588235 0.0588235 0.0588235 +0.0627451 0.0627451 0.0627451 0.0627451 +0.0666667 0.0666667 0.0666667 0.0666667 +0.0705882 0.0705882 0.0705882 0.0705882 +0.0745098 0.0745098 0.0745098 0.0745098 +0.0784314 0.0784314 0.0784314 0.0784314 +0.0823529 0.0823529 0.0823529 0.0823529 +0.0862745 0.0862745 0.0862745 0.0862745 +0.0901961 0.0901961 0.0901961 0.0901961 +0.0941176 0.0941176 0.0941176 0.0941176 +0.0980392 0.0980392 0.0980392 0.0980392 +0.101961 0.101961 0.101961 0.101961 +0.105882 0.105882 0.105882 0.105882 +0.109804 0.109804 0.109804 0.109804 +0.113725 0.113725 0.113725 0.113725 +0.117647 0.117647 0.117647 0.117647 +0.121569 0.121569 0.121569 0.121569 +0.125490 0.125490 0.125490 0.125490 +0.129412 0.129412 0.129412 0.129412 +0.133333 0.133333 0.133333 0.133333 +0.137255 0.137255 0.137255 0.137255 +0.141176 0.141176 0.141176 0.141176 +0.145098 0.145098 0.145098 0.145098 +0.149020 0.149020 0.149020 0.149020 +0.152941 0.152941 0.152941 0.152941 +0.156863 0.156863 0.156863 0.156863 +0.160784 0.160784 0.160784 0.160784 +0.164706 0.164706 0.164706 0.164706 +0.168627 0.168627 0.168627 0.168627 +0.172549 0.172549 0.172549 0.172549 +0.176471 0.176471 0.176471 0.176471 +0.180392 0.180392 0.180392 0.180392 +0.184314 0.184314 0.184314 0.184314 +0.188235 0.188235 0.188235 0.188235 +0.192157 0.192157 0.192157 0.192157 +0.196078 0.196078 0.196078 0.196078 +0.200000 0.200000 0.200000 0.200000 +0.203922 0.203922 0.203922 0.203922 +0.207843 0.207843 0.207843 0.207843 +0.211765 0.211765 0.211765 0.211765 +0.215686 0.215686 0.215686 0.215686 +0.219608 0.219608 0.219608 0.219608 +0.223529 0.223529 0.223529 0.223529 +0.227451 0.227451 0.227451 0.227451 +0.231373 0.231373 0.231373 0.231373 +0.235294 0.235294 0.235294 0.235294 +0.239216 0.239216 0.239216 0.239216 +0.243137 0.243137 0.243137 0.243137 +0.247059 0.247059 0.247059 0.247059 +0.250980 0.250980 0.250980 0.250980 +0.254902 0.254902 0.254902 0.254902 +0.258824 0.258824 0.258824 0.258824 +0.262745 0.262745 0.262745 0.262745 +0.266667 0.266667 0.266667 0.266667 +0.270588 0.270588 0.270588 0.270588 +0.274510 0.274510 0.274510 0.274510 +0.278431 0.278431 0.278431 0.278431 +0.282353 0.282353 0.282353 0.282353 +0.286275 0.286275 0.286275 0.286275 +0.290196 0.290196 0.290196 0.290196 +0.294118 0.294118 0.294118 0.294118 +0.298039 0.298039 0.298039 0.298039 +0.301961 0.301961 0.301961 0.301961 +0.305882 0.305882 0.305882 0.305882 +0.309804 0.309804 0.309804 0.309804 +0.313725 0.313725 0.313725 0.313725 +0.317647 0.317647 0.317647 0.317647 +0.321569 0.321569 0.321569 0.321569 +0.325490 0.325490 0.325490 0.325490 +0.329412 0.329412 0.329412 0.329412 +0.333333 0.333333 0.333333 0.333333 +0.337255 0.337255 0.337255 0.337255 +0.341176 0.341176 0.341176 0.341176 +0.345098 0.345098 0.345098 0.345098 +0.349020 0.349020 0.349020 0.349020 +0.352941 0.352941 0.352941 0.352941 +0.356863 0.356863 0.356863 0.356863 +0.360784 0.360784 0.360784 0.360784 +0.364706 0.364706 0.364706 0.364706 +0.368627 0.368627 0.368627 0.368627 +0.372549 0.372549 0.372549 0.372549 +0.376471 0.376471 0.376471 0.376471 +0.380392 0.380392 0.380392 0.380392 +0.384314 0.384314 0.384314 0.384314 +0.388235 0.388235 0.388235 0.388235 +0.392157 0.392157 0.392157 0.392157 +0.396078 0.396078 0.396078 0.396078 +0.400000 0.400000 0.400000 0.400000 +0.403922 0.403922 0.403922 0.403922 +0.407843 0.407843 0.407843 0.407843 +0.411765 0.411765 0.411765 0.411765 +0.415686 0.415686 0.415686 0.415686 +0.419608 0.419608 0.419608 0.419608 +0.423529 0.423529 0.423529 0.423529 +0.427451 0.427451 0.427451 0.427451 +0.431373 0.431373 0.431373 0.431373 +0.435294 0.435294 0.435294 0.435294 +0.439216 0.439216 0.439216 0.439216 +0.443137 0.443137 0.443137 0.443137 +0.447059 0.447059 0.447059 0.447059 +0.450980 0.450980 0.450980 0.450980 +0.454902 0.454902 0.454902 0.454902 +0.458824 0.458824 0.458824 0.458824 +0.462745 0.462745 0.462745 0.462745 +0.466667 0.466667 0.466667 0.466667 +0.470588 0.470588 0.470588 0.470588 +0.474510 0.474510 0.474510 0.474510 +0.478431 0.478431 0.478431 0.478431 +0.482353 0.482353 0.482353 0.482353 +0.486275 0.486275 0.486275 0.486275 +0.490196 0.490196 0.490196 0.490196 +0.494118 0.494118 0.494118 0.494118 +0.498039 0.498039 0.498039 0.498039 +0.501961 0.501961 0.501961 0.501961 +0.505882 0.505882 0.505882 0.505882 +0.509804 0.509804 0.509804 0.509804 +0.513725 0.513725 0.513725 0.513725 +0.517647 0.517647 0.517647 0.517647 +0.521569 0.521569 0.521569 0.521569 +0.525490 0.525490 0.525490 0.525490 +0.529412 0.529412 0.529412 0.529412 +0.533333 0.533333 0.533333 0.533333 +0.537255 0.537255 0.537255 0.537255 +0.541176 0.541176 0.541176 0.541176 +0.545098 0.545098 0.545098 0.545098 +0.549020 0.549020 0.549020 0.549020 +0.552941 0.552941 0.552941 0.552941 +0.556863 0.556863 0.556863 0.556863 +0.560784 0.560784 0.560784 0.560784 +0.564706 0.564706 0.564706 0.564706 +0.568627 0.568627 0.568627 0.568627 +0.572549 0.572549 0.572549 0.572549 +0.576471 0.576471 0.576471 0.576471 +0.580392 0.580392 0.580392 0.580392 +0.584314 0.584314 0.584314 0.584314 +0.588235 0.588235 0.588235 0.588235 +0.592157 0.592157 0.592157 0.592157 +0.596078 0.596078 0.596078 0.596078 +0.600000 0.600000 0.600000 0.600000 +0.603922 0.603922 0.603922 0.603922 +0.607843 0.607843 0.607843 0.607843 +0.611765 0.611765 0.611765 0.611765 +0.615686 0.615686 0.615686 0.615686 +0.619608 0.619608 0.619608 0.619608 +0.623529 0.623529 0.623529 0.623529 +0.627451 0.627451 0.627451 0.627451 +0.631373 0.631373 0.631373 0.631373 +0.635294 0.635294 0.635294 0.635294 +0.639216 0.639216 0.639216 0.639216 +0.643137 0.643137 0.643137 0.643137 +0.647059 0.647059 0.647059 0.647059 +0.650980 0.650980 0.650980 0.650980 +0.654902 0.654902 0.654902 0.654902 +0.658824 0.658824 0.658824 0.658824 +0.662745 0.662745 0.662745 0.662745 +0.666667 0.666667 0.666667 0.666667 +0.670588 0.670588 0.670588 0.670588 +0.674510 0.674510 0.674510 0.674510 +0.678431 0.678431 0.678431 0.678431 +0.682353 0.682353 0.682353 0.682353 +0.686275 0.686275 0.686275 0.686275 +0.690196 0.690196 0.690196 0.690196 +0.694118 0.694118 0.694118 0.694118 +0.698039 0.698039 0.698039 0.698039 +0.701961 0.701961 0.701961 0.701961 +0.705882 0.705882 0.705882 0.705882 +0.709804 0.709804 0.709804 0.709804 +0.713725 0.713725 0.713725 0.713725 +0.717647 0.717647 0.717647 0.717647 +0.721569 0.721569 0.721569 0.721569 +0.725490 0.725490 0.725490 0.725490 +0.729412 0.729412 0.729412 0.729412 +0.733333 0.733333 0.733333 0.733333 +0.737255 0.737255 0.737255 0.737255 +0.741176 0.741176 0.741176 0.741176 +0.745098 0.745098 0.745098 0.745098 +0.749020 0.749020 0.749020 0.749020 +0.752941 0.752941 0.752941 0.752941 +0.756863 0.756863 0.756863 0.756863 +0.760784 0.760784 0.760784 0.760784 +0.764706 0.764706 0.764706 0.764706 +0.768627 0.768627 0.768627 0.768627 +0.772549 0.772549 0.772549 0.772549 +0.776471 0.776471 0.776471 0.776471 +0.780392 0.780392 0.780392 0.780392 +0.784314 0.784314 0.784314 0.784314 +0.788235 0.788235 0.788235 0.788235 +0.792157 0.792157 0.792157 0.792157 +0.796078 0.796078 0.796078 0.796078 +0.800000 0.800000 0.800000 0.800000 +0.803922 0.803922 0.803922 0.803922 +0.807843 0.807843 0.807843 0.807843 +0.811765 0.811765 0.811765 0.811765 +0.815686 0.815686 0.815686 0.815686 +0.819608 0.819608 0.819608 0.819608 +0.823529 0.823529 0.823529 0.823529 +0.827451 0.827451 0.827451 0.827451 +0.831373 0.831373 0.831373 0.831373 +0.835294 0.835294 0.835294 0.835294 +0.839216 0.839216 0.839216 0.839216 +0.843137 0.843137 0.843137 0.843137 +0.847059 0.847059 0.847059 0.847059 +0.850980 0.850980 0.850980 0.850980 +0.854902 0.854902 0.854902 0.854902 +0.858824 0.858824 0.858824 0.858824 +0.862745 0.862745 0.862745 0.862745 +0.866667 0.866667 0.866667 0.866667 +0.870588 0.870588 0.870588 0.870588 +0.874510 0.874510 0.874510 0.874510 +0.878431 0.878431 0.878431 0.878431 +0.882353 0.882353 0.882353 0.882353 +0.886275 0.886275 0.886275 0.886275 +0.890196 0.890196 0.890196 0.890196 +0.894118 0.894118 0.894118 0.894118 +0.898039 0.898039 0.898039 0.898039 +0.901961 0.901961 0.901961 0.901961 +0.905882 0.905882 0.905882 0.905882 +0.909804 0.909804 0.909804 0.909804 +0.913725 0.913725 0.913725 0.913725 +0.917647 0.917647 0.917647 0.917647 +0.921569 0.921569 0.921569 0.921569 +0.925490 0.925490 0.925490 0.925490 +0.929412 0.929412 0.929412 0.929412 +0.933333 0.933333 0.933333 0.933333 +0.937255 0.937255 0.937255 0.937255 +0.941176 0.941176 0.941176 0.941176 +0.945098 0.945098 0.945098 0.945098 +0.949020 0.949020 0.949020 0.949020 +0.952941 0.952941 0.952941 0.952941 +0.956863 0.956863 0.956863 0.956863 +0.960784 0.960784 0.960784 0.960784 +0.964706 0.964706 0.964706 0.964706 +0.968627 0.968627 0.968627 0.968627 +0.972549 0.972549 0.972549 0.972549 +0.976471 0.976471 0.976471 0.976471 +0.980392 0.980392 0.980392 0.980392 +0.984314 0.984314 0.984314 0.984314 +0.988235 0.988235 0.988235 0.988235 +0.992157 0.992157 0.992157 0.992157 +0.996078 0.996078 0.996078 0.996078 +1.00000 1.00000 1.00000 1.00000 +END_DATA diff --git a/spectro/madvrwin.c b/spectro/madvrwin.c index 9769b71..b96cd80 100644 --- a/spectro/madvrwin.c +++ b/spectro/madvrwin.c @@ -18,6 +18,7 @@ #include <string.h> #ifdef NT # include <winsock2.h> +# include <shlwapi.h> #endif #ifdef UNIX # include <sys/types.h> @@ -71,13 +72,27 @@ # define KEY_WOW64_32KEY (0x0200) #endif +/* Incase shlwapi.h doesn't declare this */ +#include <pshpack1.h> +typedef struct _ADLLVERSIONINFO2 +{ + DLLVERSIONINFO info1; + DWORD dwFlags; + ULONGLONG ullVersion; +} ADLLVERSIONINFO2; +#include <poppack.h> + HMODULE HcNetDll = NULL; BOOL (WINAPI *madVR_BlindConnect)(BOOL searchLan, DWORD timeOut) = NULL; +BOOL (WINAPI *madVR_GetVersion)(DWORD *version); BOOL (WINAPI *madVR_SetOsdText)(LPCWSTR text); BOOL (WINAPI *madVR_Disable3dlut)() = NULL; BOOL (WINAPI *madVR_GetDeviceGammaRamp)(LPVOID ramp) = NULL; BOOL (WINAPI *madVR_SetDeviceGammaRamp)(LPVOID ramp) = NULL; -BOOL (WINAPI *madVR_SetBackground)(int patternAreaInPercent, COLORREF backgroundColor) = NULL; +BOOL (WINAPI *madVR_GetPatternConfig)(int *patternAreaInPercent, int *backgroundLevelInPercent, + int *backgroundMode, int *blackBorderWidth) = NULL; +BOOL (WINAPI *madVR_SetPatternConfig)(int patternAreaInPercent, int backgroundLevelInPercent, + int backgroundMode, int blackBorderWidth) = NULL; BOOL (WINAPI *madVR_ShowRGB)(double r, double g, double b) = NULL; BOOL (WINAPI *madVR_SetProgressBarPos)(int currentPos, int maxPos); BOOL (WINAPI *madVR_Disconnect)() = NULL; @@ -86,6 +101,10 @@ BOOL (WINAPI *madVR_Disconnect)() = NULL; /* Return NZ on failure */ static int initMadVR(dispwin *p) { wchar_t *dllname; + WCHAR modname[MAX_PATH]; + DWORD hnd; + DWORD len = 0; + WORD v1 = 0, v2 = 0, v3 = 0, v4 = 0; /* MadVR version */ if (sizeof(dllname) > 4) /* Compiled as 64 bit */ dllname = L"madHcNet64.dll"; @@ -120,28 +139,59 @@ static int initMadVR(dispwin *p) { } } if (HcNetDll != NULL) { + HRESULT (WINAPI *dllgetver)(ADLLVERSIONINFO2 *) = NULL; + + *(FARPROC*)&dllgetver = GetProcAddress(HcNetDll, "DllGetVersion"); + + if (dllgetver != NULL) { + ADLLVERSIONINFO2 ver; + + ver.info1.cbSize = sizeof(ADLLVERSIONINFO2); + dllgetver(&ver); + + v1 = 0xffff & (ver.ullVersion >> 48); + v2 = 0xffff & (ver.ullVersion >> 32); + v3 = 0xffff & (ver.ullVersion >> 16); + v4 = 0xffff & ver.ullVersion; + + } else { + debugr2((errout,"MadVR DllGetVersion failed - can't determine DLL version\n")); + } + } + + if (HcNetDll != NULL) { *(FARPROC*)&madVR_BlindConnect = GetProcAddress(HcNetDll, "madVR_BlindConnect"); + *(FARPROC*)&madVR_GetVersion = GetProcAddress(HcNetDll, "madVR_GetVersion"); *(FARPROC*)&madVR_SetOsdText = GetProcAddress(HcNetDll, "madVR_SetOsdText"); *(FARPROC*)&madVR_Disable3dlut = GetProcAddress(HcNetDll, "madVR_Disable3dlut"); *(FARPROC*)&madVR_GetDeviceGammaRamp = GetProcAddress(HcNetDll, "madVR_GetDeviceGammaRamp"); *(FARPROC*)&madVR_SetDeviceGammaRamp = GetProcAddress(HcNetDll, "madVR_SetDeviceGammaRamp"); - *(FARPROC*)&madVR_SetBackground = GetProcAddress(HcNetDll, "madVR_SetBackground"); + *(FARPROC*)&madVR_GetPatternConfig = GetProcAddress(HcNetDll, "madVR_GetPatternConfig"); + *(FARPROC*)&madVR_SetPatternConfig = GetProcAddress(HcNetDll, "madVR_SetPatternConfig"); *(FARPROC*)&madVR_ShowRGB = GetProcAddress(HcNetDll, "madVR_ShowRGB"); *(FARPROC*)&madVR_SetProgressBarPos = GetProcAddress(HcNetDll, "madVR_SetProgressBarPos"); *(FARPROC*)&madVR_Disconnect = GetProcAddress(HcNetDll, "madVR_Disconnect"); if (madVR_BlindConnect + && madVR_GetVersion && madVR_SetOsdText && madVR_Disable3dlut && madVR_GetDeviceGammaRamp && madVR_SetDeviceGammaRamp - && madVR_SetBackground + && madVR_GetPatternConfig + && madVR_SetPatternConfig && madVR_ShowRGB && madVR_SetProgressBarPos && madVR_Disconnect) { + DWORD ver = 0; + /* Return value is unclear */ + if (!madVR_GetVersion(&ver)) + debugr2((errout,"MadVR_GetVersion failed - can't determine MadVR version\n")); + + debugr2((errout,"Found all required functions in %ls V%d.%d.%d.%d MadVR V%x.%x.%x.%x functions\n",dllname,v1,v2,v3,v4, 0xff & (ver >> 24), 0xff & (ver >> 16), 0xff & (ver >> 8), 0xff & ver)); return 0; } - debugr2((errout,"Failed to locate function in %ls\n",dllname)); + debugr2((errout,"Failed to locate MadVR function in %ls %d.%d.%d.%d\n",dllname,v1,v2,v3,v4)); FreeLibrary(HcNetDll); } else { debugr2((errout,"Failed to load %ls\n",dllname)); @@ -198,7 +248,7 @@ static ramdac *madvrwin_get_ramdac(dispwin *p) { return NULL; } - if (madVR_GetDeviceGammaRamp(vals) == 0) { + if (!madVR_GetDeviceGammaRamp(vals)) { free(r); debugr2((errout,"madvrwin_get_ramdac failed on madVR_GetDeviceGammaRamp()\n")); return NULL; @@ -237,7 +287,7 @@ static int madvrwin_set_ramdac(dispwin *p, ramdac *r, int persist) { } } - if (madVR_SetDeviceGammaRamp(vals) == 0) { + if (!madVR_SetDeviceGammaRamp(vals)) { debugr2((errout,"madvrwin_set_ramdac failed on madVR_SetDeviceGammaRamp()\n")); rv = 1; } else { @@ -282,8 +332,7 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ int j; double orgb[3]; /* Previous RGB value */ double kr, kf; - int update_delay = p->update_delay; - double xdelay = 0.0; /* Extra delay for response time */ + int update_delay = 0; debugr("madvrwin_set_color called\n"); @@ -299,44 +348,40 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ return 1; } - /* Don't want extra delay if we're measuring update delay */ - if (update_delay != 0 && p->do_resp_time_del) { - /* Compute am expected response time for the change in level */ - kr = DISPLAY_RISE_TIME/log(1 - 0.9); /* Exponent constant */ - kf = DISPLAY_FALL_TIME/log(1 - 0.9); /* Exponent constant */ -//printf("~1 k2 = %f\n",k2); - for (j = 0; j < 3; j++) { - double el, dl, n, t; - - el = pow(p->rgb[j], 2.2); - dl = el - pow(orgb[j], 2.2); /* Change in level */ - if (fabs(dl) > 0.01) { /* More than 1% change in level */ - n = DISPLAY_SETTLE_AIM * el; - if (n < DISPLAY_ABS_AIM) - n = DISPLAY_ABS_AIM; -//printf("~1 sl %f, el %f, log (%f / %f)\n",sl,el,n,fabs(sl - el)); - if (dl > 0.0) - t = kr * log(n/dl); - else - t = kf * log(n/-dl); - - if (t > xdelay) - xdelay = t; - } - } -//printf("~1 xdelay = %f secs\n",xdelay); - xdelay *= 1000.0; /* To msec */ - /* This is kind of a fudge since update delay is after latency, */ - /* but displays with long delay (ie. CRT) have short latency, and visa versa */ - if ((int)xdelay > update_delay) - update_delay = (int)xdelay; + /* Allow for display update & instrument delays */ + update_delay = dispwin_compute_delay(p, orgb); + debugr2((errout, "madvrwin_set_color delaying %d msec\n",update_delay)); + msec_sleep(update_delay); + + return 0; +} + +/* Set/unset the blackground color flag */ +/* Return nz on error */ +static int madvrwin_set_bg(dispwin *p, int blackbg) { + int perc, bgperc, bgmode, border; + + p->blackbg = blackbg; + + /* Parameters that shouldn't change can be set to -1, but this doesn't seem */ + /* to work for background level, so get current background level */ + if (!madVR_GetPatternConfig(&perc, &bgperc, &bgmode, &border)) { + debugr2((errout,"madVR_GetPatternConfig failed\n")); + return 1; } + debugr2((errout,"madvrwin_set_bg: got pattern config %i, %i, %i, %i\n", + perc, bgperc, bgmode, border)); - /* Allow some time for the display to update before */ - /* a measurement can take place. This allows for CRT */ - /* refresh, or LCD processing/update time, + */ - /* display settling time (quite long for smaller LCD changes). */ - msec_sleep(update_delay); + /* Default test window is 10% of the width/height = 1% of the area*/ + perc = (int)((p->width/100.0 * 0.1 * p->height/100.0 * 0.1) * 100.0 + 0.5); + + /* Background level is 1..100 in percent */ + debugr2((errout,"madvrwin_set_bg: setting pattern config %i, %i\n", + perc, blackbg ? 0 : bgperc)); + if (!madVR_SetPatternConfig(perc, blackbg ? 0 : bgperc, -1, -1)) { + debugr2((errout,"madVR_SetPatternConfig failed\n")); + return 1; + } return 0; } @@ -353,20 +398,6 @@ static int madvrwin_set_pinfo(dispwin *p, int pno, int tno) { } /* ----------------------------------------------- */ -/* Set an update delay, and return the previous value */ -/* Value can be set to zero, but othewise will be forced */ -/* to be >= min_update_delay */ -static int madvrwin_set_update_delay( -dispwin *p, -int update_delay) { - int cval = p->update_delay; - p->update_delay = update_delay; - if (update_delay != 0 && p->update_delay < p->min_update_delay) - p->update_delay = p->min_update_delay; - return cval; -} - -/* ----------------------------------------------- */ /* Set the shell set color callout */ void madvrwin_set_callout( dispwin *p, @@ -434,14 +465,15 @@ int ddebug /* >0 to print debug statements to stderr */ ) { dispwin *p = NULL; char *cp; - struct mg_context *mg; const char *options[3]; char port[50]; debug("new_madvrwin called with native = %d\n"); - if (out_tvenc) + if (out_tvenc) { + if (ddebug) fprintf(stderr,"new_madvrwin failed because out_tvenc set\n"); return NULL; + } if ((p = (dispwin *)calloc(sizeof(dispwin), 1)) == NULL) { if (ddebug) fprintf(stderr,"new_madvrwin failed because malloc failed\n"); @@ -450,21 +482,26 @@ int ddebug /* >0 to print debug statements to stderr */ /* !!!! Make changes in dispwin.c & webwin.c as well !!!! */ p->name = strdup("Web Window"); + p->width = width; + p->height = height; p->nowin = nowin; p->native = native; p->out_tvenc = 0; p->blackbg = blackbg; p->ddebug = ddebug; - p->get_ramdac = madvrwin_get_ramdac; - p->set_ramdac = madvrwin_set_ramdac; - p->install_profile = madvrwin_install_profile; - p->uninstall_profile = madvrwin_uninstall_profile; - p->get_profile = madvrwin_get_profile; - p->set_color = madvrwin_set_color; - p->set_pinfo = madvrwin_set_pinfo; - p->set_update_delay = madvrwin_set_update_delay; - p->set_callout = madvrwin_set_callout; - p->del = madvrwin_del; + p->get_ramdac = madvrwin_get_ramdac; + p->set_ramdac = madvrwin_set_ramdac; + p->install_profile = madvrwin_install_profile; + p->uninstall_profile = madvrwin_uninstall_profile; + p->get_profile = madvrwin_get_profile; + p->set_color = madvrwin_set_color; + p->set_bg = madvrwin_set_bg; + p->set_pinfo = madvrwin_set_pinfo; + p->set_update_delay = dispwin_set_update_delay; + p->set_settling_delay = dispwin_set_settling_delay; + p->enable_update_delay = dispwin_enable_update_delay; + p->set_callout = madvrwin_set_callout; + p->del = madvrwin_del; debugr2((errout, "new_madvrwin got native = %d\n",native)); @@ -476,31 +513,19 @@ int ddebug /* >0 to print debug statements to stderr */ p->rgb[0] = p->rgb[1] = p->rgb[2] = 0.5; /* Set Grey as the initial test color */ - p->min_update_delay = 20; - - if ((cp = getenv("ARGYLL_MIN_DISPLAY_UPDATE_DELAY_MS")) != NULL) { - p->min_update_delay = atoi(cp); - if (p->min_update_delay < 20) - p->min_update_delay = 20; - if (p->min_update_delay > 60000) - p->min_update_delay = 60000; - debugr2((errout, "new_madvrwin: Minimum display update delay set to %d msec\n",p->min_update_delay)); - } - - p->update_delay = DISPLAY_UPDATE_DELAY; /* Default update delay */ - if (p->update_delay < p->min_update_delay) - p->update_delay = p->min_update_delay; + dispwin_set_default_delays(p); p->pdepth = 8; /* Assume this */ p->edepth = 16; if (initMadVR(p)) { + debugr2((errout,"Failed to locate MadVR .dll or functions\n")); free(p); return NULL; } if (!madVR_BlindConnect(0, 1000)) { - debugr2((errout,"Failed to locate MadVR\n")); + debugr2((errout,"Failed to connect to MadVR\n")); free(p); return NULL; } @@ -510,12 +535,7 @@ int ddebug /* >0 to print debug statements to stderr */ madVR_Disable3dlut(); } - if (blackbg) { - int perc; - - perc = (int)(width * height * 100 + 0.5); - madVR_SetBackground(perc, 0x0000); - } + p->set_bg(p, blackbg); /* Create a suitable description */ { diff --git a/spectro/mongoose.c b/spectro/mongoose.c index e1df94a..019101e 100644 --- a/spectro/mongoose.c +++ b/spectro/mongoose.c @@ -18,6 +18,34 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +/* + Fedora 18+ and similar pose a problem with their use of FirewallD. + + Either the user has to enable tcp ports 8080 & 8081 in their zone using + firewall-config, or we have to add a DBUS function here to add and then + remove the port dynamically, which will probably require root access :-( + + There's no doco for the last - see: + gdbus introspect --system --dest org.fedoraproject.FirewallD1 + --object-path /org/fedoraproject/FirewallD1 + + + Maybe we could supply a firewalld.service configuration file for + the Argyll apps ? We'd have to use a fixed port no. though ? + + Something like: + + <?xml version="1.0" encoding="utf-8"?> + <service version="1.0"> + <short>ArgyllCMS</short> + <description>Allow web window and ChromeCast access</description> + <port port="8080" protocol="tcp"/> + <port port="8081" protocol="tcp"/> + </service> + + added to /usr/lib/firewalld/services + */ + #if defined(_WIN32) #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005 #else @@ -183,6 +211,7 @@ typedef struct DIR { #include <sys/wait.h> #include <sys/socket.h> #include <sys/select.h> +#include <ifaddrs.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/time.h> @@ -4026,10 +4055,110 @@ static int set_ports_option(struct mg_context *ctx) { if (!success) { close_all_listening_sockets(ctx); } - return success; } +// Get the allocated port number of the first port 0 listener, +// or the last non-0 port listener. +// Return 0 on error +int mg_get_listening_port(struct mg_context *ctx) { + struct socket *sp, *tmp; + union usa rsa; /* resolved socket address */ + socklen_t rsa_len = sizeof(rsa); + int rv = 0; + for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) { + if (ntohs(sp->lsa.sin.sin_port) == 0) { + if (!getsockname(ctx->listening_sockets->sock, (struct sockaddr *)&rsa, &rsa_len)) + rv = ntohs(rsa.sin.sin_port); + break; + } else { + rv = ntohs(sp->lsa.sin.sin_port); /* Return the last port found */ + } + } + return rv; +} + +// Get a URL for accessing the server. */ +// Return NULL on error. Free after use. +char *mg_get_url(struct mg_context *ctx) { + int portno = mg_get_listening_port(ctx); + char *rv = NULL; + + if (portno == 0) + return NULL; + + /* Create a suitable URL for accessing the server */ +#if _WIN32 + { + char szHostName[255]; + struct hostent *host_entry; + char *localIP; + char buf[100]; + + /* We assume WinSock has been started by mongoose */ + + // Get the local hostname + gethostname(szHostName, 255); + host_entry = gethostbyname(szHostName); + /* Get first entry */ + localIP = inet_ntoa(*(struct in_addr *)*host_entry->h_addr_list); + + sprintf(buf,"http://%s:%d/",localIP,portno); + rv = strdup(buf); + } +#else + { + struct ifaddrs *ifAddrStruct = NULL; + struct ifaddrs *ifa = NULL; + void *tmpAddrPtr = NULL; + char abuf[INET_ADDRSTRLEN] = ""; +#ifdef AF_INET6 + char abuf6[INET6_ADDRSTRLEN] = ""; +#endif + char buf[100]; + + getifaddrs(&ifAddrStruct); + + /* Look first for the first IPV4 address */ + for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { +#ifdef AF_INET6 + if (ifa->ifa_addr->sa_family==AF_INET) { /* IP4 ? */ +#endif + if (strncmp(ifa->ifa_name, "lo",2) == 0 || abuf[0] != '\000') + continue; /* Skip local */ + tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + inet_ntop(AF_INET, tmpAddrPtr, abuf, INET_ADDRSTRLEN); + sprintf(buf,"http://%s:%d/",abuf,portno); + break; + } + } + +#ifdef AF_INET6 + /* If that didn't work, look for IPV6 address */ + if (ifa == NULL) { + for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family==AF_INET6) { /* IP6 ? */ + if (strncmp(ifa->ifa_name, "lo",2) == 0 || abuf6[0] != '\000') + continue; /* Skip local */ + tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + inet_ntop(AF_INET6, tmpAddrPtr, abuf6, INET6_ADDRSTRLEN); + sprintf(buf,"http://[%s]:%d/",abuf6,portno); + break; + } + } + } +#endif + if (ifAddrStruct != NULL) + freeifaddrs(ifAddrStruct); + + rv = strdup(buf); + } +#endif /* _WIN32 */ + + return rv; +} + + static void log_header(const struct mg_connection *conn, const char *header, FILE *fp) { const char *header_value; diff --git a/spectro/mongoose.h b/spectro/mongoose.h index 708cf73..1ee0c47 100644 --- a/spectro/mongoose.h +++ b/spectro/mongoose.h @@ -107,11 +107,23 @@ typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn); // Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual // for the list of valid option and their possible values. // +// If one of the listening ports is "0", then it will be automatically +// allocated to a free port by the system, and the port that gets allocated +// may be retrieved by calling mg_get_listening_port() bellow. +// // Return: // web server context, or NULL on error. struct mg_context *mg_start(mg_callback_t callback, void *user_data, const char **options); +// Get the allocated port number of the first port 0 listener, +// or the last non-0 port listener. +// Return 0 on error +int mg_get_listening_port(struct mg_context *ctx); + +// Get a URL for accessing the server. */ +// Return NULL on error. Free after use. +char *mg_get_url(struct mg_context *ctx); // Stop the web server. // diff --git a/spectro/munki.c b/spectro/munki.c index 5d4de2d..cc875c9 100644 --- a/spectro/munki.c +++ b/spectro/munki.c @@ -157,6 +157,7 @@ munki_determine_capabilities(munki *p) { } /* Return current or given configuration available measurement modes. */ +/* NOTE that conf_ix values shoudn't be changed, as it is used as a persistent key */ static inst_code munki_meas_config( inst *pp, inst_mode *mmodes, @@ -277,6 +278,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ rv = munki_imp_measure(p, vals, npatch, 1); + return munki_interp_code(p, rv); } @@ -298,6 +300,7 @@ instClamping clamp) { /* Clamp XYZ/Lab to be +ve */ rv = munki_imp_measure(p, val, 1, clamp); + return munki_interp_code(p, rv); } @@ -314,6 +317,9 @@ double *ref_rate) { if (!p->inited) return inst_no_init; + if (ref_rate != NULL) + *ref_rate = 0.0; + rv = munki_imp_meas_refrate(p, ref_rate); @@ -324,7 +330,8 @@ double *ref_rate) { static inst_code munki_meas_delay( inst *pp, -int *msecdelay) { +int *pdispmsec, /* Return display update delay in msec */ +int *pinstmsec) { /* Return instrument reaction time in msec */ munki *p = (munki *)pp; munki_code rv; @@ -333,11 +340,20 @@ int *msecdelay) { if (!p->inited) return inst_no_init; - rv = munki_imp_meas_delay(p, msecdelay); + rv = munki_imp_meas_delay(p, pdispmsec, pinstmsec); return munki_interp_code(p, rv); } +/* Timestamp the white patch change during meas_delay() */ +static inst_code munki_white_change( +inst *pp, +int init) { + munki *p = (munki *)pp; + + return munki_imp_white_change(p, init); +} + /* Return needed and available inst_cal_type's */ static inst_code munki_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) { munki *p = (munki *)pp; @@ -348,7 +364,7 @@ static inst_code munki_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_t } /* Request an instrument calibration. */ -inst_code munki_calibrate( +static inst_code munki_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ @@ -522,7 +538,6 @@ munki_interp_code(munki *p, munki_code ec) { switch (ec) { case MUNKI_OK: - return inst_ok; case MUNKI_COMS_FAIL: @@ -637,7 +652,7 @@ static inst_config munki_config_enum(inst *pp, int ec) { } /* Return the instrument capabilities */ -void munki_capabilities(inst *pp, +static void munki_capabilities(inst *pp, inst_mode *pcap1, inst2_capability *pcap2, inst3_capability *pcap3) { @@ -694,7 +709,7 @@ static mk_mode munki_convert_mode(munki *p, inst_mode m) { } /* Check device measurement mode */ -inst_code munki_check_mode(inst *pp, inst_mode m) { +static inst_code munki_check_mode(inst *pp, inst_mode m) { munki *p = (munki *)pp; mk_mode mmode = 0; @@ -710,7 +725,7 @@ inst_code munki_check_mode(inst *pp, inst_mode m) { } /* Set device measurement mode */ -inst_code munki_set_mode(inst *pp, inst_mode m) { +static inst_code munki_set_mode(inst *pp, inst_mode m) { munki *p = (munki *)pp; mk_mode mmode = 0; inst_mode cap = p->cap; @@ -742,7 +757,11 @@ static inst_code munki_get_set_opt(inst *pp, inst_opt_type m, ...) { munki *p = (munki *)pp; - if (m == inst_opt_noinitcalib) { + if (m == inst_opt_initcalib) { /* default */ + munki_set_noinitcalib(p, 0, 0); + return inst_ok; + + } else if (m == inst_opt_noinitcalib) { va_list args; int losecs = 0; @@ -753,9 +772,12 @@ munki_get_set_opt(inst *pp, inst_opt_type m, ...) { munki_set_noinitcalib(p, 1, losecs); return inst_ok; - } else if (m == inst_opt_initcalib) { - munki_set_noinitcalib(p, 0, 0); - return inst_ok; + + } else if (m == inst_opt_askcalib) { /* default */ + munki_set_nocalibask(p, 0); + + } else if (m == inst_opt_noaskcalib) { + munki_set_nocalibask(p, 1); /* Record the trigger mode */ } else if (m == inst_opt_trig_prog @@ -944,6 +966,7 @@ extern munki *new_munki(icoms *icom, instType itype) { p->get_n_a_cals = munki_get_n_a_cals; p->calibrate = munki_calibrate; p->meas_delay = munki_meas_delay; + p->white_change = munki_white_change; p->interp_error = munki_interp_error; p->config_enum = munki_config_enum; p->del = munki_del; diff --git a/spectro/munki_imp.c b/spectro/munki_imp.c index 9f431cc..ee7b2b1 100644 --- a/spectro/munki_imp.c +++ b/spectro/munki_imp.c @@ -166,33 +166,6 @@ */ /* ============================================================ */ - -// Print bytes as hex to debug log */ -static void dump_bytes(a1log *log, char *pfx, unsigned char *buf, int base, int len) { - int i, j, ii; - char oline[200] = { '\000' }, *bp = oline; - for (i = j = 0; i < len; i++) { - if ((i % 16) == 0) - bp += sprintf(bp,"%s%04x:",pfx,base+i); - bp += sprintf(bp," %02x",buf[i]); - if ((i+1) >= len || ((i+1) % 16) == 0) { - for (ii = i; ((ii+1) % 16) != 0; ii++) - bp += sprintf(bp," "); - bp += sprintf(bp," "); - for (; j <= i; j++) { - if (!(buf[j] & 0x80) && isprint(buf[j])) - bp += sprintf(bp,"%c",buf[j]); - else - bp += sprintf(bp,"."); - } - bp += sprintf(bp,"\n"); - a1logd(log,0,"%s",oline); - bp = oline; - } - } -} - -/* ============================================================ */ /* Debugging plot support */ #if defined(DEBUG) || defined(PLOT_DEBUG) || defined(PLOT_PATREC) || defined(HIGH_RES_PLOT) || defined(HIGH_RES_PLOT_STRAYL) @@ -532,23 +505,6 @@ munki_code munki_imp_init(munki *p) { return MUNKI_INT_ASSERT; } - /* Dump the eeprom contents as a block */ - if (p->log->debug >= 7) { - int base, size; - - a1logd(p->log,7, "EEPROM contents:\n"); - - size = 8192; - for (base = 0; base < (2 * 8192); base += 8192) { - unsigned char eeprom[8192]; - - if ((ev = munki_readEEProm(p, eeprom, base, size)) != MUNKI_OK) - return ev; - - dump_bytes(p->log, " ", eeprom, base, size); - } - } - /* Tick in seconds */ m->intclkp = (double)m->tickdur * 1e-6; @@ -566,6 +522,23 @@ munki_code munki_imp_init(munki *p) { if ((ev = munki_getversionstring(p, m->vstring)) != MUNKI_OK) return ev; + /* Dump the eeprom contents as a block */ + if (p->log->debug >= 7) { + int base, size; + + a1logd(p->log,7, "EEPROM contents:\n"); + + size = 1024; /* read size == buffer size */ + for (base = 0; base < (2 * 8192); base += size) { + unsigned char eeprom[1024]; + + if ((ev = munki_readEEProm(p, eeprom, base, size)) != MUNKI_OK) + return ev; + + adump_bytes(p->log, " ", eeprom, base, size); + } + } + /* Read the calibration size */ if ((ev = munki_readEEProm(p, buf, 4, 4)) != MUNKI_OK) return ev; @@ -912,6 +885,9 @@ munki_code munki_imp_get_n_a_cals(munki *p, inst_cal_type *pn_cals, inst_cal_typ time_t curtime = time(NULL); inst_cal_type n_cals = inst_calt_none; inst_cal_type a_cals = inst_calt_none; + int idark_valid = cs->idark_valid; /* Current state of calib */ + int dark_valid = cs->dark_valid; + int cal_valid = cs->cal_valid; a1logd(p->log,3,"munki_imp_get_n_a_cals: checking mode %d\n",m->mmode); @@ -919,43 +895,43 @@ munki_code munki_imp_get_n_a_cals(munki *p, inst_cal_type *pn_cals, inst_cal_typ a1logd(p->log,4,"curtime %u, iddate %u, ddate %u, cfdate %u\n",curtime,cs->iddate,cs->ddate,cs->cfdate); if ((curtime - cs->iddate) > DCALTOUT) { a1logd(p->log,3,"Invalidating adaptive dark cal as %d secs from last cal\n",curtime - cs->iddate); - cs->idark_valid = 0; + idark_valid = 0; } if ((curtime - cs->ddate) > DCALTOUT) { a1logd(p->log,3,"Invalidating dark cal as %d secs from last cal\n",curtime - cs->ddate); - cs->dark_valid = 0; + dark_valid = 0; } if (!cs->emiss && (curtime - cs->cfdate) > WCALTOUT) { a1logd(p->log,3,"Invalidating white cal as %d secs from last cal\n",curtime - cs->cfdate); - cs->cal_valid = 0; + cal_valid = 0; } if (cs->reflective) { - if (!cs->dark_valid + if (!dark_valid || (cs->want_dcalib && !m->noinitcalib)) n_cals |= inst_calt_ref_dark; a_cals |= inst_calt_ref_dark; - if (!cs->cal_valid + if (!cal_valid || (cs->want_calib && !m->noinitcalib)) n_cals |= inst_calt_ref_white; a_cals |= inst_calt_ref_white; } if (cs->emiss) { - if ((!cs->adaptive && !cs->dark_valid) - || (cs->adaptive && !cs->idark_valid) + if ((!cs->adaptive && !dark_valid) + || (cs->adaptive && !idark_valid) || (cs->want_dcalib && !m->noinitcalib)) n_cals |= inst_calt_em_dark; a_cals |= inst_calt_em_dark; } if (cs->trans) { - if ((!cs->adaptive && !cs->dark_valid) - || (cs->adaptive && !cs->idark_valid) + if ((!cs->adaptive && !dark_valid) + || (cs->adaptive && !idark_valid) || (cs->want_dcalib && !m->noinitcalib)) n_cals |= inst_calt_trans_dark; a_cals |= inst_calt_trans_dark; - if (!cs->cal_valid + if (!cal_valid || (cs->want_calib && !m->noinitcalib)) n_cals |= inst_calt_trans_vwhite; a_cals |= inst_calt_trans_vwhite; @@ -1030,15 +1006,12 @@ munki_code munki_imp_calibrate( } a1logd(p->log,4,"munki sensor position = 0x%x\n",spos); -#ifdef NEVER /* We can set the *calc to the actual conditions, in which case */ /* the calibration will commence immediately. */ - if (!m->nosposcheck && spos == mk_spos_calib) { + if (m->nocalibask && !m->nosposcheck && spos == mk_spos_calib) { *calc = inst_calc_man_cal_smode; a1logd(p->log,4,"munki set calc to cal conditions\n",spos); - } -#endif /* Make sure that the instrument configuration matches the */ /* conditions */ @@ -1757,12 +1730,15 @@ int icoms2munki_err(int se) { } /* - - - - - - - - - - - - - - - - */ -/* Measure a display update delay. It is assumed that a */ +/* 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, */ /* and this will measure the time it took for the update to */ -/* be noticed by the instrument, up to 0.6 seconds. */ +/* be noticed by the instrument, up to 2.0 seconds. */ +/* (It is assumed that white_change() will be called at the time the patch */ +/* changes color.) */ /* inst_misread will be returned on failure to find a transition to black. */ -#define NDMXTIME 0.7 /* Maximum time to take */ +#define NDMXTIME 2.0 /* Maximum time to take */ #define NDSAMPS 500 /* Debug samples */ typedef struct { @@ -1773,7 +1749,8 @@ typedef struct { munki_code munki_imp_meas_delay( munki *p, -int *msecdelay) { /* Return the number of msec */ +int *pdispmsec, /* Return display update delay in msec */ +int *pinstmsec) { /* Return instrument reaction time in msec */ munki_code ev = MUNKI_OK; munkiimp *m = (munkiimp *)p->m; munki_state *s = &m->ms[m->mmode]; @@ -1783,10 +1760,20 @@ int *msecdelay) { /* Return the number of msec */ double rgbw[3] = { 610.0, 520.0, 460.0 }; double ucalf = 1.0; /* usec_time calibration factor */ double inttime; + double rstart; i1rgbdsamp *samp; double stot, etot, del, thr; - double etime; + double stime, etime; int isdeb; + int dispmsec, instmsec; + + if (pinstmsec != NULL) + *pinstmsec = 0; + + if ((rstart = usec_time()) < 0.0) { + a1loge(p->log, inst_internal_error, "munki_imp_meas_delay: No high resolution timers\n"); + return inst_internal_error; + } /* Read the samples */ inttime = m->min_int_time; @@ -1797,16 +1784,21 @@ int *msecdelay) { /* Return the number of msec */ return MUNKI_INT_MALLOC; } -//printf("~1 %d samples at %f int\n",nummeas,inttime); if ((ev = munki_read_patches_all(p, multimeas, nummeas, &inttime, 0)) != inst_ok) { free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav-1); free(samp); return ev; } + if (m->whitestamp < 0.0) { + a1logd(p->log, 1, "munki_meas_delay: White transition wasn't timestamped\n"); + return inst_internal_error; + } + /* Convert the samples to RGB */ + /* Add 10 msec fudge factor */ for (i = 0; i < nummeas; i++) { - samp[i].sec = i * inttime; + samp[i].sec = i * inttime + (m->trigstamp - m->whitestamp)/1000000.0 + 0.01; samp[i].rgb[0] = samp[i].rgb[1] = samp[i].rgb[2] = 0.0; for (j = 0; j < m->nwav; j++) { double wl = XSPECT_WL(m->wl_short, m->wl_long, m->nwav, j); @@ -1826,35 +1818,13 @@ int *msecdelay) { /* Return the number of msec */ a1logd(p->log, 3, "munki_measure_refresh: Read %d samples for refresh calibration\n",nummeas); -#ifdef PLOT_UPDELAY - /* Plot the raw sensor values */ - { - double xx[NDSAMPS]; - double y1[NDSAMPS]; - double y2[NDSAMPS]; - double y3[NDSAMPS]; - double y4[NDSAMPS]; - - for (i = 0; i < nummeas && i < NDSAMPS; i++) { - xx[i] = samp[i].sec; - y1[i] = samp[i].rgb[0]; - y2[i] = samp[i].rgb[1]; - y3[i] = samp[i].rgb[2]; - y4[i] = samp[i].tot; -//printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].tot); - } - printf("Display update delay measure sensor values and time (sec)\n"); - do_plot6(xx, y1, y2, y3, y4, NULL, NULL, nummeas); - } -#endif - /* Over the first 100msec, locate the maximum value */ - etime = samp[nummeas-1].sec; + stime = samp[0].sec; stot = -1e9; for (i = 0; i < nummeas; i++) { if (samp[i].tot > stot) stot = samp[i].tot; - if (samp[i].sec > 0.1) + if ((samp[i].sec - stime) > 0.1) break; } @@ -1868,34 +1838,73 @@ int *msecdelay) { /* Return the number of msec */ break; } - del = stot - etot; - thr = etot + 0.30 * del; /* 30% of transition threshold */ + del = etot - stot; + thr = stot + 0.30 * del; /* 30% of transition threshold */ #ifdef PLOT_UPDELAY a1logd(p->log, 0, "munki_meas_delay: start tot %f end tot %f del %f, thr %f\n", stot, etot, del, thr); #endif +#ifdef PLOT_UPDELAY + /* Plot the raw sensor values */ + { + double xx[NDSAMPS]; + double y1[NDSAMPS]; + double y2[NDSAMPS]; + double y3[NDSAMPS]; + double y4[NDSAMPS]; + + for (i = 0; i < nummeas && i < NDSAMPS; i++) { + xx[i] = samp[i].sec; + y1[i] = samp[i].rgb[0]; + y2[i] = samp[i].rgb[1]; + y3[i] = samp[i].rgb[2]; + y4[i] = samp[i].tot; +//printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].tot); + } + printf("Display update delay measure sensor values and time (sec)\n"); + do_plot6(xx, y1, y2, y3, y4, NULL, NULL, nummeas); + } +#endif + /* Check that there has been a transition */ if (del < 5.0) { free(samp); - a1logd(p->log, 1, "munki_meas_delay: can't detect change from white to black\n"); + a1logd(p->log, 1, "munki_meas_delay: can't detect change from black to white\n"); return MUNKI_RD_NOTRANS_FOUND; } - /* Locate the time at which the values are above the end values */ - for (i = nummeas-1; i >= 0; i--) { + /* Working from the start, locate the time at which the level was above the threshold */ + for (i = 0; i < (nummeas-1); i++) { if (samp[i].tot > thr) break; } - if (i < 0) /* Assume the update was so fast that we missed it */ - i = 0; a1logd(p->log, 2, "munki_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec); - *msecdelay = (int)(samp[i].sec * 1000.0 + 0.5); + /* Compute overall delay and subtract patch change delay */ + dispmsec = (int)(samp[i].sec * 1000.0 + 0.5); + instmsec = (int)((m->trigstamp - rstart)/1000.0 + 0.5); #ifdef PLOT_UPDELAY - a1logd(p->log, 0, "munki_meas_delay: returning %d msec\n",*msecdelay); + a1logd(p->log, 0, "munki_meas_delay: disp %d, inst %d msec\n",dispmsec,instmsec); +#else + a1logd(p->log, 2, "munki_meas_delay: disp %d, inst %d msec\n",dispmsec,instmsec); +#endif + + if (dispmsec < 0) /* This can happen if the patch generator delays it's return */ + dispmsec = 0; + + if (pdispmsec != NULL) + *pdispmsec = dispmsec; + + if (pinstmsec != NULL) + *pinstmsec = instmsec; + +#ifdef PLOT_UPDELAY + a1logd(p->log, 0, "munki_meas_delay: returning %d & %d msec\n",dispmsec,instmsec); +#else + a1logd(p->log, 2, "munki_meas_delay: returning %d & %d msec\n",dispmsec,instmsec); #endif free(samp); @@ -1904,6 +1913,21 @@ int *msecdelay) { /* Return the number of msec */ #undef NDSAMPS #undef NDMXTIME +/* Timestamp the white patch change during meas_delay() */ +inst_code munki_imp_white_change(munki *p, int init) { + munkiimp *m = (munkiimp *)p->m; + + if (init) + m->whitestamp = -1.0; + else { + if ((m->whitestamp = usec_time()) < 0.0) { + a1loge(p->log, inst_internal_error, "munki_imp_wite_change: No high resolution timers\n"); + return inst_internal_error; + } + } + + return inst_ok; +} /* - - - - - - - - - - - - - - - - */ /* Measure a patch or strip or flash in the current mode. */ @@ -2415,6 +2439,9 @@ munki_code munki_imp_meas_refrate( a1logd(p->log,2,"munki_imp_meas_refrate called\n"); + if (ref_rate != NULL) + *ref_rate = 0.0; + if (!s->emiss) { a1logd(p->log,2,"munki_imp_meas_refrate not in emissive mode\n"); return MUNKI_UNSUPPORTED; @@ -2993,9 +3020,6 @@ munki_code munki_imp_meas_refrate( a1logd(p->log, 3, "Not enough tries suceeded to determine refresh rate\n"); } - if (ref_rate != NULL) - *ref_rate = 0.0; - return MUNKI_RD_NOREFR_FOUND; } #undef NFSAMPS @@ -3983,10 +4007,12 @@ munki_code munki_ledtemp_whitemeasure( /* floating point sensor readings. Check for saturation */ if ((ev = munki_sens_to_raw(p, multimes, ledtemp, buf, ninvmeas, nummeas, m->satlimit, &darkthresh)) != MUNKI_OK) { + free(buf); free_dvector(ledtemp, 0, nummeas-1); free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); return ev; } + free(buf); /* Make the reference temperature nominal */ *reftemp = 0.5 * (ledtemp[0] + ledtemp[nummeas-1]); @@ -4002,7 +4028,6 @@ munki_code munki_ledtemp_whitemeasure( munki_sub_raw_to_absraw(p, nummeas, inttime, gainmode, multimes, s->dark_data, &darkthresh, 1, NULL); - free(buf); /* For each raw wavelength, compute a linear regression */ { @@ -4651,6 +4676,8 @@ munki_code munki_trialmeasure( if ((rv = munki_sens_to_raw(p, multimes, NULL, buf, 0, nmeasuered, m->satlimit, &darkthresh)) != MUNKI_OK) { if (rv != MUNKI_RD_SENSORSATURATED) { + free_dvector(absraw, -1, m->nraw-1); + free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); free(buf); return rv; } @@ -5360,7 +5387,7 @@ munki_code munki_extract_patches_multimeas( bdev = dev[k]; } -#ifndef NEVER +#ifndef NEVER /* Use this */ /* Weight the deviations with a triangular weighting */ /* to skew slightly towards the center */ for (k = 0; k < FW; k++) { @@ -5524,6 +5551,9 @@ munki_code munki_extract_patches_multimeas( apat = 2 * nummeas; if ((pat = (munki_patch *)malloc(sizeof(munki_patch) * apat)) == NULL) { a1logd(p->log,1,"munki: malloc of patch structures failed!\n"); + free_ivector(sizepop, 0, nummeas-1); + free_dvector(slope, 0, nummeas-1); + free_dvector(maxval, -1, m->nraw-1); return MUNKI_INT_MALLOC; } @@ -5536,6 +5566,9 @@ munki_code munki_extract_patches_multimeas( if (npat >= apat) { apat *= 2; if ((pat = (munki_patch *)realloc(pat, sizeof(munki_patch) * apat)) == NULL) { + free_ivector(sizepop, 0, nummeas-1); + free_dvector(slope, 0, nummeas-1); + free_dvector(maxval, -1, m->nraw-1); a1logd(p->log,1,"munki: reallloc of patch structures failed!\n"); return MUNKI_INT_MALLOC; } @@ -5672,7 +5705,7 @@ munki_code munki_extract_patches_multimeas( for (i = 1; i < (npat-1); i++) { if (pat[i].use == 0) continue; - printf("Patch %d, start %d, length %d:\n",i, pat[i].ss, pat[i].no, pat[i].use); + printf("Patch %d, start %d, length %d, use %d\n",i, pat[i].ss, pat[i].no, pat[i].use); } #endif @@ -5696,7 +5729,7 @@ munki_code munki_extract_patches_multimeas( for (i = 1; i < (npat-1); i++) { if (pat[i].use == 0) continue; - printf("Patch %d, start %d, length %d:\n",i, pat[i].ss, pat[i].no, pat[i].use); + printf("Patch %d, start %d, length %d, use %d\n",i, pat[i].ss, pat[i].no, pat[i].use); } /* Create fake "slope" value that marks patches */ @@ -5997,7 +6030,7 @@ munki_code munki_extract_patches_flash( nsampl++; } - /* Average all the values over the threshold, */ + /* Integrate all the values over the threshold, */ /* and also one either side of flash */ for (j = 0; j < m->nraw-1; j++) pavg[j] = 0.0; @@ -8169,6 +8202,15 @@ a1logd(p->log,3,"set_noinitcalib v = %d, ->lo_secs %d, losecs %d secs\n",v, m->l m->noinitcalib = v; } +/* Set the nocalibask mode */ +/* Don't ask user for confirmation of calibration */ +/* if the instrument is in the correct configuration for it. */ +void munki_set_nocalibask(munki *p, int v) { + munkiimp *m = (munkiimp *)p->m; + + m->nocalibask = v; +} + /* Set the trigger config */ void munki_set_trig(munki *p, inst_opt_type trig) { munkiimp *m = (munkiimp *)p->m; @@ -8188,7 +8230,9 @@ static int munki_switch_thread(void *pp) { munkiimp *m = (munkiimp *)p->m; munki_code rv = MUNKI_OK; a1logd(p->log,3,"Switch thread started\n"); - for (nfailed = 0;nfailed < 5;) { +// for (nfailed = 0;nfailed < 5;) + /* Try indefinitely, in case instrument is put to sleep */ + for (;;) { mk_eve ecode; rv = munki_waitfor_switch_th(p, &ecode, NULL, SW_THREAD_TIMEOUT); @@ -8291,7 +8335,7 @@ munki_readEEProm( } /* Now read the bytes */ - se = p->icom->usb_read(p->icom, NULL, 0x81, buf, size, &rwbytes, 5.0); + se = p->icom->usb_read(p->icom, NULL, 0x81, buf, size, &rwbytes, 6.0); if ((rv = icoms2munki_err(se)) != MUNKI_OK) { a1logd(p->log,1,"munki_readEEProm: read failed (2) with ICOM err 0x%x\n",se); return rv; @@ -8596,6 +8640,7 @@ munki_triggermeasure( se = p->icom->usb_control(p->icom, IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE, 0x80, 0, 0, pbuf, 12, 2.0); + m->trigstamp = usec_time(); m->tr_t2 = msec_time(); /* Diagnostic */ @@ -8611,7 +8656,7 @@ munki_triggermeasure( /* Read a measurements results. */ /* A buffer full of bytes is returned. */ -munki_code +static munki_code munki_readmeasurement( munki *p, int inummeas, /* Initial number of measurements to expect */ diff --git a/spectro/munki_imp.h b/spectro/munki_imp.h index 6af366e..ea9451f 100644 --- a/spectro/munki_imp.h +++ b/spectro/munki_imp.h @@ -164,6 +164,8 @@ struct _munkiimp { volatile int th_termed; /* Thread has terminated */ inst_opt_type trig; /* Reading trigger mode */ int noinitcalib; /* Disable initial calibration if not essential */ + int nocalibask; /* Disable asking user to proceed with calibration */ + /* even when the instrument is in correct configuration */ int nosposcheck; /* Disable checking the sensor position */ int highres; /* High resolution mode */ int hr_inited; /* High resolution has been initialized */ @@ -291,6 +293,9 @@ struct _munkiimp { volatile int spos_change; /* counter that increments on an spos event change */ unsigned int spos_msec; /* Time when spos last changes */ + volatile double whitestamp; /* meas_delay() white timestamp */ + volatile double trigstamp; /* meas_delay() trigger timestamp */ + }; typedef struct _munkiimp munkiimp; /* Add an implementation structure */ @@ -432,9 +437,12 @@ munki_code munki_imp_meas_refrate( /* Measure the display update delay */ munki_code munki_imp_meas_delay( - munki *p, - int *msecdelay -); +munki *p, +int *pdispmsec, +int *pinstmsec); + +/* Timestamp the white patch change during meas_delay() */ +inst_code munki_imp_white_change(munki *p, int init); /* return nz if high res is supported */ int munki_imp_highres(munki *p); @@ -622,7 +630,7 @@ munki_code munki_trialmeasure( /* level "calibrate" and "take reading" functions. */ /* The setup for the operation is in the current mode state. */ /* The called then needs to call munki_readmeasurement() */ -munki_code +static munki_code munki_trigger_one_measure( munki *p, int nummeas, /* Number of measurements to make */ @@ -813,6 +821,9 @@ munki_code munki_create_hr(munki *p, int ref); /* Set the noinitcalib mode */ void munki_set_noinitcalib(munki *p, int v, int losecs); +/* Set the nocalibask mode */ +void munki_set_nocalibask(munki *p, int v); + /* Set the trigger config */ void munki_set_trig(munki *p, inst_opt_type trig); @@ -933,7 +944,7 @@ munki_triggermeasure( ); /* Read a measurements results */ -munki_code +static munki_code munki_readmeasurement( munki *p, int inummeas, /* Initial number of measurements to expect */ diff --git a/spectro/oemarch.c b/spectro/oemarch.c index e27d25d..e3e262c 100644 --- a/spectro/oemarch.c +++ b/spectro/oemarch.c @@ -16,6 +16,13 @@ * */ +/* + Notes :- + Although we have made an allowance for a Spyder 1 PLD pattern, + we know nothing about it, or even if it exists... + + */ + #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -45,7 +52,9 @@ #include "xspect.h" #include "conv.h" #include "aglob.h" +#include "disptechs.h" #include "ccss.h" +#include "disptechs.h" #include "LzmaDec.h" #include "oemarch.h" @@ -56,25 +65,31 @@ oem_target oemtargs = { #ifdef NT { /* Installed files */ - { "/ColorVision/Spyder2express/CVSpyder.dll", targ_spyd_pld, file_dllcab }, - { "/ColorVision/Spyder2pro/CVSpyder.dll", targ_spyd_pld, file_dllcab }, - { "/PANTONE COLORVISION/ColorPlus/CVSpyder.dll", targ_spyd_pld, file_dllcab }, + { "/ColorVision/Spyder2express/CVSpyder.dll", targ_spyd2_pld, file_dllcab }, + { "/ColorVision/Spyder2pro/CVSpyder.dll", targ_spyd2_pld, file_dllcab }, + { "/PANTONE COLORVISION/ColorPlus/CVSpyder.dll", targ_spyd2_pld, file_dllcab }, { "/Datacolor/Spyder4Express/dccmtr.dll", targ_spyd_cal, file_dllcab }, { "/Datacolor/Spyder4Pro/dccmtr.dll", targ_spyd_cal, file_dllcab }, { "/Datacolor/Spyder4Elite/dccmtr.dll", targ_spyd_cal, file_dllcab }, { "/Datacolor/Spyder4TV HD/dccmtr.dll", targ_spyd_cal, file_dllcab }, + { "/Datacolor/Spyder5Express/dccmtr.dll", targ_spyd_cal, file_dllcab }, + { "/Datacolor/Spyder5Pro/dccmtr.dll", targ_spyd_cal, file_dllcab }, + { "/Datacolor/Spyder5Elite/dccmtr.dll", targ_spyd_cal, file_dllcab }, + { "/Datacolor/Spyder5TV HD/dccmtr.dll", targ_spyd_cal, file_dllcab }, { "/X-Rite/Devices/i1d3/Calibrations/*.edr", targ_i1d3_edr, file_data }, { NULL } }, { /* Volume names */ - { "ColorVision", targ_spyd_pld | targ_spyd_cal }, - { "Datacolor", targ_spyd_pld | targ_spyd_cal }, + { "ColorVision", targ_spyd2_pld | targ_spyd_cal }, + { "Datacolor", targ_spyd2_pld | targ_spyd_cal }, { "i1Profiler", targ_i1d3_edr }, { "ColorMunki Displ", targ_i1d3_edr }, { NULL } }, { /* Archive names */ - { "/setup/setup.exe", targ_spyd_pld }, + { "/PhotoCAL/PhotoCAL Setup.exe", targ_spyd2_pld }, + { "/OptiCAL/OptiCAL Setup.exe", targ_spyd2_pld }, + { "/setup/setup.exe", targ_spyd2_pld }, { "/Data/setup.exe", targ_spyd_cal }, { "/Installer/Setup.exe", targ_i1d3_edr }, { "/Installer/ColorMunkiDisplaySetup.exe", targ_i1d3_edr }, @@ -83,8 +98,8 @@ oem_target oemtargs = { #endif /* NT */ #ifdef __APPLE__ { /* Installed files */ - { "/Applications/Spyder2express 2.2/Spyder2express.app/Contents/MacOSClassic/Spyder.lib", targ_spyd_pld }, - { "/Applications/Spyder2pro 2.2/Spyder2pro.app/Contents/MacOSClassic/Spyder.lib", targ_spyd_pld }, + { "/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 }, { "/Library/Application Support/X-Rite/Devices/i1d3xrdevice/Contents/Resources/Calibrations/*.edr", targ_i1d3_edr }, { "/Library/Application Support/X-Rite/Frameworks/XRiteDevice.framework/PlugIns/i1d3.xrdevice/Contents/Resources/Calibrations/*.edr",targ_i1d3_edr }, @@ -92,14 +107,16 @@ oem_target oemtargs = { { NULL } }, { /* Volume names */ - { "/Volumes/ColorVision", targ_spyd_pld | targ_spyd_cal }, - { "/Volumes/Datacolor", targ_spyd_pld | targ_spyd_cal }, + { "/Volumes/ColorVision", targ_spyd2_pld | targ_spyd_cal }, + { "/Volumes/Datacolor", targ_spyd2_pld | targ_spyd_cal }, { "/Volumes/i1Profiler", targ_i1d3_edr }, { "/Volumes/ColorMunki Display", targ_i1d3_edr }, { NULL } }, { /* Archive names */ - { "/setup/setup.exe", targ_spyd_pld }, + { "/PhotoCAL/PhotoCAL Setup.exe", targ_spyd2_pld }, + { "/OptiCAL/OptiCAL Setup.exe", targ_spyd2_pld }, + { "/setup/setup.exe", targ_spyd2_pld }, { "/Data/setup.exe", targ_spyd_cal }, { "/Installer/Setup.exe", targ_i1d3_edr }, { "/Installer/ColorMunkiDisplaySetup.exe", targ_i1d3_edr }, @@ -111,20 +128,22 @@ oem_target oemtargs = { { NULL } }, { /* Volume names the CDROM may have */ - { "/media/ColorVision", targ_spyd_pld | targ_spyd_cal }, - { "/media/Datacolor", targ_spyd_pld | targ_spyd_cal }, + { "/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_spyd_pld | targ_spyd_cal | targ_i1d3_edr }, - { "/mnt/cdrecorder", targ_spyd_pld | targ_spyd_cal | targ_i1d3_edr }, - { "/media/cdrom", targ_spyd_pld | targ_spyd_cal | targ_i1d3_edr }, - { "/media/cdrecorder", targ_spyd_pld | targ_spyd_cal | targ_i1d3_edr }, - { "/cdrom", targ_spyd_pld | targ_spyd_cal | targ_i1d3_edr }, - { "/cdrecorder", targ_spyd_pld | targ_spyd_cal | 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 } }, { /* Archive names */ - { "/setup/setup.exe", targ_spyd_pld }, + { "/PhotoCAL/PhotoCAL Setup.exe", targ_spyd2_pld }, + { "/OptiCAL/OptiCAL Setup.exe", targ_spyd2_pld }, + { "/setup/setup.exe", targ_spyd2_pld }, { "/Data/setup.exe", targ_spyd_cal }, { "/Installer/Setup.exe", targ_i1d3_edr }, { "/Installer/ColorMunkiDisplaySetup.exe", targ_i1d3_edr }, @@ -163,6 +182,7 @@ int is_dll(xfile *xf); static xfile *vise_extract(xfile **pxf, xfile *xi, char *tfilename, int verb); static xfile *spyd2pld_extract(xfile **pxf, xfile *dll, int verb); static xfile *spyd4cal_extract(xfile **pxf, xfile *dll, int verb); +int is_s1pld(xfile *xf); int is_s2pld(xfile *xf); int is_s4cal(xfile *xf); @@ -270,15 +290,15 @@ xfile *oemarch_get_ifiles(xfile *files, int verb) { continue; } - /* If this could be spyder 2 PLD pattern: */ - if (arch->ttype & targ_spyd_pld) { + /* If this could be spyder 1/2 PLD pattern: */ + if (arch->ttype & (targ_spyd1_pld | targ_spyd2_pld)) { xfile *dll = NULL; /* dccmtr.dll */ if (vise_extract(&nfiles, arch, "CVSpyder.dll", verb) != NULL) continue; } - /* If this could be spyder 4 calibration file: */ + /* If this could be spyder 4/5 calibration file: */ if (arch->ttype & targ_spyd_cal) { xfile *dll = NULL; /* dccmtr.dll */ @@ -356,14 +376,14 @@ xfile *oemarch_get_ifiles(xfile *files, int verb) { continue; } - /* If this could be spyder 2 PLD pattern: */ - if (dllcab->ttype & targ_spyd_pld) { + /* If this could be spyder 1/2 PLD pattern: */ + if (dllcab->ttype & (targ_spyd1_pld | targ_spyd2_pld)) { if (spyd2pld_extract(&nfiles, dllcab, verb) != NULL) continue; } - /* If this could be spyder 4 calibration file: */ + /* If this could be spyder 4/5 calibration file: */ if (dllcab->ttype & targ_spyd_cal) { if (spyd4cal_extract(&nfiles, dllcab, verb) != NULL) @@ -415,6 +435,12 @@ xfile *oemarch_get_ifiles(xfile *files, int verb) { del_xf(ofiles); nfiles = NULL; + /* Mark any files that wern't recognized as unknown */ + for (i = 0; files[i].name != NULL; i++) { + if (files[i].ttype & targ_unknown) + files[i].ttype = targ_unknown; + } + #ifdef DEBUG list_files("Returning", files); printf("\n"); @@ -429,13 +455,13 @@ xfile *oemarch_get_ifiles(xfile *files, int verb) { void classify_file(xfile *xf, int verb) { if (is_dll(xf)) { xf->ftype = file_dllcab; - xf->ttype &= (targ_spyd_pld | targ_spyd_cal); + xf->ttype &= (targ_spyd1_pld | targ_spyd2_pld | targ_spyd_cal); if (verb) printf("'%s' seems to be a .dll file\n",xf->name); return; } if (is_vise(xf)) { xf->ftype = file_arch; - xf->ttype &= (targ_spyd_pld | targ_spyd_cal); + xf->ttype &= (targ_spyd1_pld | targ_spyd2_pld | targ_spyd_cal); if (verb) printf("'%s' seems to be a VISE archive\n",xf->name); return; } @@ -463,9 +489,15 @@ void classify_file(xfile *xf, int verb) { if (verb) printf("'%s' seems to be a .ccmx\n",xf->name); return; } + if (is_s1pld(xf)) { + xf->ftype = file_data; + xf->ttype &= targ_spyd1_pld; + if (verb) printf("'%s' seems to be a Spyder 1 PLD file\n",xf->name); + return; + } if (is_s2pld(xf)) { xf->ftype = file_data; - xf->ttype &= targ_spyd_pld; + xf->ttype &= targ_spyd2_pld; if (verb) printf("'%s' seems to be a Spyder 2 PLD file\n",xf->name); return; } @@ -476,8 +508,9 @@ void classify_file(xfile *xf, int verb) { return; } /* Hmm. */ - if (verb) printf("'%s' is unknown\n",xf->name); + if (verb) printf("'%s' is unknown - assume it's an archive\n",xf->name); xf->ftype = file_arch | file_dllcab | file_data; + xf->ttype |= targ_unknown; } /* ============================================================================= */ @@ -638,6 +671,7 @@ static xfile *locate_read_archive(xfile *vol, int verb) { } strcpy(ap, oemtargs.archnames[j].path); + if (verb > 1) printf("Looking for archive '%s'\n",buf); if (sys_access(buf, 0) == 0) { if (verb) printf("found\n"); new_add_xf(&xf, buf, NULL, 0, file_arch, ttype); @@ -726,7 +760,7 @@ xfile *add_xf(xfile **l) { ; if ((*l = (xfile *)realloc(*l, (n+2) * sizeof(xfile))) == NULL) - error("new_xf: Failed to realloc xfile structure"); + error("new_xf: Failed to realloc xfile structure of %d x %d bytes",(n+2), sizeof(xfile)); (*l)[n+1].name = NULL; /* End marker */ (*l)[n+1].buf = NULL; (*l)[n+1].len = 0; @@ -1098,7 +1132,7 @@ static xfile *vise_extract(xfile **pxf, xfile *arch, char *tfilename, int verb) printf("Input file '%s' is a VISE archive file base 0x%x\n",arch->name,vi->vbase); if (vi->locate_file(vi, tfilename)) { - if (verb) printf("Failed to locate file '%s' in VISE archive",tfilename); + if (verb) printf("Failed to locate file '%s' in VISE archive\n",tfilename); return NULL; } @@ -1141,7 +1175,22 @@ static xfile *vise_extract(xfile **pxf, xfile *arch, char *tfilename, int verb) } /* ============================================================================= */ -/* Spyder 2 PLD pattern extraction */ +/* Spyder 1/2 PLD pattern extraction */ + +int is_s1pld(xfile *xf) { + + if (xf->len != 6817) + return 0; + + /* Deobscured signature */ + if (xf->buf[0] == 0xff + && xf->buf[1] == 0x04 + && xf->buf[2] == 0xb0 + && xf->buf[3] == 0x0a + && xf->buf[7] == 0x57) + return 1; + return 0; +} int is_s2pld(xfile *xf) { @@ -1151,7 +1200,8 @@ int is_s2pld(xfile *xf) { if (xf->buf[0] == 0xff && xf->buf[1] == 0x04 && xf->buf[2] == 0xb0 - && xf->buf[3] == 0x0a) + && xf->buf[3] == 0x0a + && xf->buf[7] == 0xd7) return 1; return 0; } @@ -1164,7 +1214,9 @@ static xfile *spyd2pld_extract(xfile **pxf, xfile *dll, int verb) { unsigned char *firmware; unsigned int firmwaresize; /* First few bytes of the standard Xilinx XCS05XL PLD pattern */ - unsigned char magic[4] = { 0xff, 0x04, 0xb0, 0x0a }; + unsigned char magic1[4] = { 0xff, 0x94, 0xCB, 0x02 }; + unsigned char magic2[4] = { 0xff, 0x04, 0xb0, 0x0a }; + int isOld = 0; /* Old pattern - SpyderPro Optical etc. */ xfile *xf = NULL; buf = dll->buf; @@ -1173,22 +1225,38 @@ static xfile *spyd2pld_extract(xfile **pxf, xfile *dll, int verb) { firmwaresize = 6817; rfsize = (firmwaresize + 7) & ~7; - /* Search for start of PLD pattern */ - for(i = 0; (i + rfsize) < size ;i++) { - if (buf[i] == magic[0]) { + /* Search for start of Spyder 1 PLD pattern */ + for (i = 0; (i + rfsize) < size ;i++) { + if (buf[i] == magic1[0]) { for (j = 0; j < 4; j++) { - if (buf[i + j] != magic[j]) + if (buf[i + j] != magic1[j]) break; } if (j >= 4) break; /* found it */ } } - if ((i + rfsize) >= size) { - if (verb) printf("Failed to locate Spyder 2 firmware in '%s'\n",dll->name); - return NULL; - } + if ((i + rfsize) < size) { /* Found Spyder 1 PLD pattern */ + isOld = 1; + } else { + + /* Search for start of Spyder 2 PLD pattern */ + for (i = 0; (i + rfsize) < size ;i++) { + if (buf[i] == magic2[0]) { + for (j = 0; j < 4; j++) { + if (buf[i + j] != magic2[j]) + break; + } + if (j >= 4) + break; /* found it */ + } + } + if ((i + rfsize) >= size) { + if (verb) printf("Failed to locate Spyder 2 firmware in '%s'\n",dll->name); + return NULL; + } + } firmware = buf + i; xf = add_xf(pxf); @@ -1201,10 +1269,17 @@ static xfile *spyd2pld_extract(xfile **pxf, xfile *dll, int verb) { fprintf(stderr,"malloc failed for Spyder 2 PLD pattern (%d bytes)\n",firmwaresize); exit(-1); } - memcpy(xf->buf, firmware, firmwaresize); + if (isOld) { /* Old Spyder w PLD pattern is obscured */ + for (i = 0; i < firmwaresize; i++) { + int j = (3347 * i) % 0x1AA2; + xf->buf[i] = i ^ firmware[j]; + } + } else { + memcpy(xf->buf, firmware, firmwaresize); + } xf->len = firmwaresize; xf->ftype = file_data; - xf->ttype = targ_spyd_pld; + xf->ttype = targ_spyd2_pld; if (verb) printf("Returning '%s' length %ld from '%s'\n",xf->name, xf->len, dll->name); @@ -1286,7 +1361,8 @@ static xfile *spyd4cal_extract(xfile **pxf, xfile *dll, int verb) { return NULL; } - if (nocals != 6) { + if (nocals != 6 /* Spyder 4 */ + && nocals != 7) { /* Spyder 5 */ if (verb) printf("Spyder 4 calibration data has an unexpected number of entries (%d)\n",nocals); return NULL; } @@ -1298,7 +1374,7 @@ static xfile *spyd4cal_extract(xfile **pxf, xfile *dll, int verb) { exit(-1); } if ((xf->buf = malloc(rfsize)) == NULL) { - fprintf(stderr,"malloc failed for Spyder 4 calibration data (%d bytes)\n",rfsize); + fprintf(stderr,"malloc failed for Spyder 4/5 calibration data (%d bytes)\n",rfsize); exit(-1); } memcpy(xf->buf, caldata, rfsize); @@ -1363,29 +1439,6 @@ static xfile *edr_convert(xfile **pxf, xfile *xi, int verb) { /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Lower level */ -// Print bytes as hex to fp -static void dump_bytes(FILE *fp, char *pfx, unsigned char *buf, int len) { - int i, j, ii; - for (i = j = 0; i < len; i++) { - if ((i % 16) == 0) - fprintf(fp,"%s%04x:",pfx,i); - fprintf(fp," %02x",buf[i]); - if ((i+1) >= len || ((i+1) % 16) == 0) { - for (ii = i; ((ii+1) % 16) != 0; ii++) - fprintf(fp," "); - fprintf(fp," "); - for (; j <= i; j++) { - if (isprint(buf[j])) - fprintf(fp,"%c",buf[j]); - else - fprintf(fp,"."); - } - fprintf(fp,"\n"); - } - } - fflush(fp); -} - /* Take a 64 sized return buffer, and convert it to an ORD64, little endian */ static ORD64 buf2ord64(unsigned char *buf) { ORD64 val; @@ -1468,9 +1521,7 @@ static ccss *parse_EDR( char creatdate[30]; char dispdesc[256]; int ttmin, ttmax; /* Min & max technology strings (inclusive) */ - int *trefmodes; /* Corresponding refresh mode for tecnology */ - char **tsels; /* Corresponding UI selection chars */ - char **ttstrings; /* Corresponding technology strings */ + disptech *tdtypes; /* Corresponding ArgyllCMS display techology enumeration */ int ttype; /* Technology type idex */ int nsets, set; xspect *samples = NULL, sp; @@ -1479,55 +1530,46 @@ static ccss *parse_EDR( int nsamples; ccss *rv; - /* We hard code the Technology strings for now. In theory we could */ - /* read them from the TechnologyStrings.txt file that comes with the .edr's */ + /* We hard code the mapping between the .edr display technology and the */ + /* ArgyllCMS equivalent. We could in theory figure it out by reading the */ + /* TechnologyStrings.txt file that comes with the .edr's */ { ttmin = 0; ttmax = 22; - if ((ttstrings = (char **)malloc(sizeof(char *) * (ttmax - ttmin + 2))) == NULL) { - if (verb) printf("Malloc failed\n"); - return NULL; - } - if ((trefmodes = (int *)malloc(sizeof(int) * (ttmax - ttmin + 2))) == NULL) { - free(ttstrings); - if (verb) printf("Malloc failed\n"); - return NULL; - } - if ((tsels = (char **)malloc(sizeof(char *) * (ttmax - ttmin + 2))) == NULL) { - free(trefmodes); - free(ttstrings); + if ((tdtypes = (disptech *)malloc(sizeof(disptech) * (ttmax - ttmin + 2))) == NULL) { if (verb) printf("Malloc failed\n"); return NULL; } - trefmodes[0] = 0; tsels[0] = NULL; ttstrings[0] = "Color Matching Function"; - trefmodes[1] = 0; tsels[1] = NULL; ttstrings[1] = "Custom"; - trefmodes[2] = 1; tsels[2] = "c"; ttstrings[2] = "CRT"; - trefmodes[3] = 0; tsels[3] = "l"; ttstrings[3] = "LCD CCFL IPS"; - trefmodes[4] = 0; tsels[4] = NULL; ttstrings[4] = "LCD CCFL VPA"; - trefmodes[5] = 0; tsels[5] = NULL; ttstrings[5] = "LCD CCFL TFT"; - trefmodes[6] = 0; tsels[6] = "L"; ttstrings[6] = "LCD CCFL Wide Gamut IPS"; - trefmodes[7] = 0; tsels[7] = NULL; ttstrings[7] = "LCD CCFL Wide Gamut VPA"; - trefmodes[8] = 0; tsels[8] = NULL; ttstrings[8] = "LCD CCFL Wide Gamut TFT"; - trefmodes[9] = 0; tsels[9] = "e"; ttstrings[9] = "LCD White LED IPS"; - trefmodes[10] = 0; tsels[10] = NULL; ttstrings[10] = "LCD White LED VPA"; - trefmodes[11] = 0; tsels[11] = NULL; ttstrings[11] = "LCD White LED TFT"; - trefmodes[12] = 0; tsels[12] = "b"; ttstrings[12] = "LCD RGB LED IPS"; - trefmodes[13] = 0; tsels[13] = NULL; ttstrings[13] = "LCD RGB LED VPA"; - trefmodes[14] = 0; tsels[14] = NULL; ttstrings[14] = "LCD RGB LED TFT"; - trefmodes[15] = 0; tsels[15] = "o"; ttstrings[15] = "LED OLED"; - trefmodes[16] = 0; tsels[16] = "a"; ttstrings[16] = "LED AMOLED"; - trefmodes[17] = 1; tsels[17] = "m"; ttstrings[17] = "Plasma"; - trefmodes[18] = 0; tsels[18] = NULL; ttstrings[18] = "LCD RG Phosphor"; - trefmodes[19] = 1; tsels[19] = NULL; ttstrings[19] = "Projector RGB Filter Wheel"; - trefmodes[20] = 1; tsels[10] = NULL; ttstrings[20] = "Projector RGBW Filter Wheel"; - trefmodes[21] = 1; tsels[21] = NULL; ttstrings[21] = "Projector RGBCMY Filter Wheel"; - trefmodes[22] = 0; tsels[22] = "p"; ttstrings[22] = "Projector"; - trefmodes[23] = 0; tsels[23] = NULL; ttstrings[23] = "Unknown"; + tdtypes[0] = disptech_unknown; /* CMF */ + tdtypes[1] = disptech_unknown; /* Custom */ + tdtypes[2] = disptech_crt; + tdtypes[3] = disptech_lcd_ccfl_ips; + tdtypes[4] = disptech_lcd_ccfl_vpa; + tdtypes[5] = disptech_lcd_ccfl_tft; + tdtypes[6] = disptech_lcd_ccfl_wg_ips; + tdtypes[7] = disptech_lcd_ccfl_wg_vpa; + tdtypes[8] = disptech_lcd_ccfl_wg_tft; + tdtypes[9] = disptech_lcd_wled_ips; + tdtypes[10] = disptech_lcd_wled_vpa; + tdtypes[11] = disptech_lcd_wled_tft; + tdtypes[12] = disptech_lcd_rgbled_ips; + tdtypes[13] = disptech_lcd_rgbled_vpa; + tdtypes[14] = disptech_lcd_rgbled_tft; + tdtypes[15] = disptech_oled; + tdtypes[16] = disptech_amoled; + tdtypes[17] = disptech_plasma; + tdtypes[18] = disptech_lcd_rgledp; + tdtypes[19] = disptech_dlp_rgb; + tdtypes[10] = disptech_dlp_rgbw; + tdtypes[21] = disptech_dlp_rgbcmy; + tdtypes[22] = disptech_dlp; + tdtypes[23] = disptech_unknown; } if (len < 600) { if (verb) printf("Unable to read '%s' header\n",name); + if (tdtypes != NULL) free(tdtypes); return NULL; } nbuf = buf + 600; /* Next time we "read" the file */ @@ -1536,6 +1578,7 @@ static ccss *parse_EDR( /* See if it has the right file ID */ if (strncmp("EDR DATA1", (char *)buf, 16) != 0) { if (verb) printf("File '%s' isn't an EDR\n",name); + if (tdtypes != NULL) free(tdtypes); return NULL; } @@ -1556,6 +1599,7 @@ static ccss *parse_EDR( if (nsets < 3 || nsets > 100) { if (verb) printf("File '%s' number of data sets %d out of range\n",name,nsets); + if (tdtypes != NULL) free(tdtypes); return NULL; } @@ -1569,6 +1613,7 @@ static ccss *parse_EDR( hasspec = buf2short(buf + 0x022E); if (hasspec != 1) { if (verb) printf("Has Data flag != 1 in EDR file '%s'\n",name); + if (tdtypes != NULL) free(tdtypes); return NULL; } @@ -1590,6 +1635,7 @@ static ccss *parse_EDR( /* Allocate space for the sets */ if ((samples = (xspect *)malloc(sizeof(xspect) * nsets)) == NULL) { if (verb) printf("Malloc of spectral samples failed\n"); + if (tdtypes != NULL) free(tdtypes); return NULL; } } @@ -1603,6 +1649,7 @@ static ccss *parse_EDR( if (len < 128) { if (verb) printf("Unable to read file '%s' set %d data header\n",name,set); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } nbuf = buf + 128; /* Next time we "read" the file */ @@ -1612,6 +1659,7 @@ static ccss *parse_EDR( if (strncmp("DISPLAY DATA", (char *)buf, 16) != 0) { if (verb) printf("File '%s' set %d data header has unknown identifier\n",name,set); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } @@ -1626,6 +1674,7 @@ static ccss *parse_EDR( if (len < 28) { if (verb) printf("Unable to read file '%s' set %d spectral data header\n",name,set); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } nbuf = buf + 28; /* Next time we "read" the file */ @@ -1635,6 +1684,7 @@ static ccss *parse_EDR( if (strncmp("SPECTRAL DATA", (char *)buf, 16) != 0) { if (verb) printf("File '%s' set %d data header has unknown identifier\n",name,set); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } @@ -1644,6 +1694,7 @@ static ccss *parse_EDR( if (nsamples != sp.spec_n) { if (verb) printf("File '%s' set %d number of samles %d doesn't match wavelengths %d\n",name,set,nsamples,sp.spec_n); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } @@ -1653,6 +1704,7 @@ static ccss *parse_EDR( if (len < 8 * sp.spec_n) { if (verb) printf("Unable to read file '%s' set %d spectral data\n",name,set); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } nbuf = buf + 8 * sp.spec_n; /* Next time we "read" the file */ @@ -1700,6 +1752,7 @@ static ccss *parse_EDR( if (strncmp("CORRECTION DATA", (char *)buf, 16) != 0) { if (verb) printf("File '%s' correction data header has unknown identifier\n",name); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } @@ -1719,6 +1772,7 @@ static ccss *parse_EDR( } else { if (verb) printf("File '%s' correction data has unknown range %d\n\n",name,nsamples); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } @@ -1728,6 +1782,7 @@ static ccss *parse_EDR( if (len < 8 * sp.spec_n) { if (verb) printf("Unable to read file '%s' correction spectral data\n",name); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } nbuf = buf + 8 * sp.spec_n; /* Next time we "read" the file */ @@ -1763,6 +1818,7 @@ static ccss *parse_EDR( if ((rv = new_ccss()) == NULL) { if (verb) printf("Unable to read file '%s' correction spectral data\n",name); if (samples != NULL) free(samples); + if (tdtypes != NULL) free(tdtypes); return NULL; } @@ -1770,12 +1826,31 @@ static ccss *parse_EDR( ttype = ttmax + 1; /* Set to Unknown */ } - /* Set it's values */ - rv->set_ccss(rv, "X-Rite", creatdate, NULL, dispdesc, ttstrings[ttype], trefmodes[ttype], tsels[ttype], "CS1000", samples, nsets); + { + disptech_info *dinfo; + char *tsel = NULL; /* character selector string */ + disptech dtech; + int trefmode; + + dinfo = disptech_get_id(tdtypes[ttype]); + if (ttype == 0) { +// ttstring = "Color Matching Function"; + tsel = "C"; + } else if (ttype == 1) { +// ttstring = "Custom"; + tsel = "U"; + } else { + tsel = dinfo->sel; + } + dtech = dinfo->dtech; + trefmode = dinfo->refr; + + /* Set it's values */ + rv->set_ccss(rv, "X-Rite", creatdate, NULL, dispdesc, dtech, trefmode, tsel, + "CS1000", samples, nsets); + } - free(tsels); - free(trefmodes); - free(ttstrings); + free(tdtypes); free(samples); return rv; @@ -2105,7 +2180,7 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { if (verb > 1) printf("Decoded %ld bytes to created %ld bytes of Header output (ratio %.1f)\n",srclen,d1sz,(double)d1sz/srclen); // printf("d1buf, file names:\n"); -// dump_bytes(stdout, " ", d1buf, d1sz); +// adump_bytes(g_log, " ", d1buf, 0, d1sz); /* - - - - - - - - - - - - - - - - -*/ @@ -2163,7 +2238,7 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { if (verb > 1) printf("Decoded %ld bytes to created %ld bytes of File Location output (ratio %.1f)\n",srclen,d1sz,(double)d1sz/srclen); // printf("d2buf, file location data:\n"); -// dump_bytes(stdout, " ", d2buf, d2sz); +// adump_bytes(g_log, " ", d2buf, 0, d2sz); if (verb > 1) printf("Searching for file '%s' in Header\n",tfilename); if (unicode) { @@ -2476,7 +2551,7 @@ static xfile *msi_extract_cab(xfile **pxf, xfile *xi, char *tname, int verb) { xf->ftype = file_dllcab; xf->ttype = xi->ttype; - if (verb) printf("Extacted '%s' length %ld\n",xf->name,xf->len); + if (verb) printf("Extracted '%s' length %ld\n",xf->name,xf->len); return xf; } @@ -2545,7 +2620,7 @@ static xfile *ai_extract_cab(xfile **pxf, xfile *xi, char *tname, int verb) { xf->ftype = file_dllcab; xf->ttype = xi->ttype; - if (verb) printf("Extacted '%s' length %ld\n",xf->name,xf->len); + if (verb) printf("Extracted '%s' length %ld\n",xf->name,xf->len); save_xfile(xf, "temp.cab", NULL, verb); @@ -2769,7 +2844,7 @@ static xfile *aifile_extract(xfile **pxf, xfile *xi, char *tname, int verb) { xf->ftype = file_dllcab; xf->ttype = xi->ttype; - if (verb) printf("Extacted '%s' length %ld\n",xf->name,xf->len); + if (verb) printf("Extracted '%s' length %ld\n",xf->name,xf->len); return xf; #endif diff --git a/spectro/oemarch.h b/spectro/oemarch.h index 1b8c4a4..b0b1a65 100644 --- a/spectro/oemarch.h +++ b/spectro/oemarch.h @@ -36,10 +36,12 @@ typedef enum { /* Possible type of target */ typedef enum { targ_none = 0x0000, /* None */ - targ_spyd_pld = 0x0001, /* Spyder PLD pattern */ - targ_spyd_cal = 0x0002, /* Spyder spectral calibration */ - targ_i1d3_edr = 0x0004, /* i1d3 .edr or .ccss */ - targ_ccmx = 0x0008 /* .ccmx */ + targ_spyd1_pld = 0x0001, /* Spyder1 PLD pattern (???) */ + targ_spyd2_pld = 0x0002, /* Spyder2 PLD pattern */ + targ_spyd_cal = 0x0004, /* Spyder spectral calibration */ + targ_i1d3_edr = 0x0008, /* i1d3 .edr or .ccss */ + targ_ccmx = 0x0010, /* .ccmx */ + targ_unknown = 0x8000 /* Unrecognized file format */ } targ_type; /* An install path, volume name or archive name */ diff --git a/spectro/oeminst.c b/spectro/oeminst.c index 2aa96e5..bce7a67 100644 --- a/spectro/oeminst.c +++ b/spectro/oeminst.c @@ -30,8 +30,10 @@ #include "aglob.h" #include "oemarch.h" #include "xspect.h" +#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); @@ -127,7 +129,7 @@ main(int argc, char *argv[]) { for (; fa < argc; fa++) { xfile *xf; xf = new_add_xf(&files, argv[fa], NULL, 0, file_arch | file_dllcab | file_data, - targ_spyd_pld | targ_spyd_cal + targ_spyd1_pld | targ_spyd2_pld | targ_spyd_cal | targ_i1d3_edr | targ_ccmx); if (load_xfile(xf, verb)) error("Unable to load file '%s'",xf->name); @@ -201,7 +203,9 @@ main(int argc, char *argv[]) { if (cx->buf_read_ccmx(cx, xf->buf, xf->len)) { error("Reading '%s' failed with '%s'\n",xf->name,cx->err); } - if (cx->cbid <= 0) + if (cx->dtech == disptech_unknown) + warning("'%s' has an unknown display technology set",xf->name); + if (cx->cc_cbid <= 0) error("'%s' doesn't contain DISPLAY_TYPE_BASE_ID field :- it can't be installed without this!",xf->name); if (cx->refrmode < 0) warning("'%s' doesn't contain DISPLAY_TYPE_REFRESH field :- non-refresh will be assumed!",xf->name); diff --git a/spectro/spec2cie.c b/spectro/spec2cie.c index 3f2e9d6..2465842 100644 --- a/spectro/spec2cie.c +++ b/spectro/spec2cie.c @@ -37,7 +37,7 @@ * 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 - * XYZ_[XYZ] and LAB_[LAB] values are computed from the FWA corrected + * XYZ_[XYZ] and D50 LAB_[LAB] values are computed from the FWA corrected * reflectances as well. */ @@ -49,6 +49,11 @@ need to be normalised to Y=100 or marked as not normalised. 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. */ #define ALLOW_PLOT @@ -70,6 +75,7 @@ #ifdef ALLOW_PLOT #include "plot.h" #endif +#include "ui.h" void @@ -122,9 +128,9 @@ main(int argc, char *argv[]) icxIllumeType tillum = icxIT_none; /* Target/simulated instrument illuminant */ xspect cust_tillum, *tillump = NULL; /* Custom target/simulated illumination spectrum */ icxIllumeType illum = icxIT_D50; /* Spectral defaults */ - xspect cust_illum; /* Custom illumination spectrum */ + xspect cust_illum; /* Custom CIE illumination spectrum */ icxIllumeType inst_illum = icxIT_none; /* Spectral defaults */ - xspect inst_cust_illum; /* Custom illumination spectrum */ + xspect inst_cust_illum; /* Custom actual instrument illumination spectrum */ icxObserverType observ = icxOT_CIE_1931_2; int npat; /* Number of patches */ @@ -660,7 +666,7 @@ main(int argc, char *argv[]) xspect insp; /* Instrument illuminant */ if (inst_illum == icxIT_none) { - /* try to get from .ti3 file */ + /* try to get from .ti3 file */ if ((ti = icg->find_kword (icg, 0, "TARGET_INSTRUMENT")) < 0) error ("Can't find target instrument needed for FWA compensation"); @@ -672,11 +678,11 @@ main(int argc, char *argv[]) error ("Instrument doesn't have an FWA illuminent"); } else if (inst_illum == icxIT_custom) { - insp = inst_cust_illum; /* Structure copy */ + insp = inst_cust_illum; /* Structure copy */ } else { - if (standardIlluminant(&insp, inst_illum, 0) != 0) - error ("Failed to find standard illuminant"); + if (standardIlluminant(&insp, inst_illum, 0) != 0) + error ("Failed to find standard illuminant"); } /* If we are setting a specific simulated instrument illuminant */ @@ -704,7 +710,6 @@ main(int argc, char *argv[]) } for (i = 0; i < npat; i++) { - xspect corr_sp; /* copy all input colums to output (except spectral if nospec) */ @@ -735,7 +740,7 @@ main(int argc, char *argv[]) } if (fwacomp) { - corr_sp = sp; + corr_sp = sp; /* Copy spectrum */ /* Convert it to CIE space */ sp2cie->sconvert (sp2cie, &corr_sp, XYZ, &sp); @@ -802,6 +807,10 @@ main(int argc, char *argv[]) #endif } + /* Could use sp2cie->get_cie_il() to get CIE white point */ + /* if we wanted to return L*a*b* relative to that. */ + /* We would have to mark that in the .ti3 though. */ + /* This won't work for emmisive though, since get_cie_il() will return 'E' */ icmXYZ2Lab(&icmD50, Lab, XYZ); elems[Xi].d = XYZ[0] * 100.0; diff --git a/spectro/specbos.c b/spectro/specbos.c index f81f2d7..0d5bc5b 100644 --- a/spectro/specbos.c +++ b/spectro/specbos.c @@ -63,9 +63,6 @@ #include "icoms.h" #include "specbos.h" -/* Default flow control */ -#define DEFFC fc_none - static inst_code specbos_interp_code(inst *pp, int ec); #define MAX_MES_SIZE 500 /* Maximum normal message reply size */ @@ -96,6 +93,7 @@ int ctype, /* 0 = normal, 1 = *init, 2 = refr reading */ int nd /* nz to disable debug messages */ ) { int se; + int bread = 0; char *cp, *tc = "", *dp; if (ctype == 0) @@ -105,23 +103,34 @@ int nd /* nz to disable debug messages */ else if (ctype == 2) tc = "\r\025"; /* Return or Nak */ - if ((se = p->icom->write_read(p->icom, in, out, bsize, tc, ntc, to)) != 0) { + se = p->icom->write_read(p->icom, in, 0, out, bsize, &bread, tc, ntc, to); + + /* Because we are sometimes waiting for 3 x \r characters to terminate the read, */ + /* we will instead time out on getting a single NAK (\025), so convert timout */ + /* with bytes to non-timeout, so that we can process the error. */ + if (se == ICOM_TO && bread > 0) + se = ICOM_OK; + + if (se != 0) { if (!nd) a1logd(p->log, 1, "specbos_fcommand: serial i/o failure on write_read '%s' 0x%x\n",icoms_fix(in),se); return icoms2specbos_err(se); } /* See if there was an error, and remove any enquire codes */ - for (dp = cp = out; *cp != '\000' && (out - dp) < bsize; cp++) { - if (*cp == '\025') { /* Got a Nak */ + for (dp = cp = out; *cp != '\000' && (dp - out) < bsize; cp++) { + if (*cp == '\025') { /* Got a NAK */ char buf[100]; - if ((se = p->icom->write_read(p->icom, "*stat:err?\r", buf, 100, "\r", 1, 1.0)) != 0) { + if ((se = p->icom->write_read(p->icom, "*stat:err?\r", 0, buf, 100, NULL, "\r", 1, 1.0)) != 0) { if (!nd) a1logd(p->log, 1, "specbos_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in)); - if (sscanf(buf, "Error Code: %d ",&se) != 1) { - if (!nd) a1logd(p->log, 1, "specbos_fcommand: failed to parse error code '%s'\n",icoms_fix(buf)); - return icoms2specbos_err(se); - } + return icoms2specbos_err(se);; } + if (sscanf(buf, "Error Code: %d ",&se) != 1) { + if (!nd) a1logd(p->log, 1, "specbos_fcommand: failed to parse error code '%s'\n",icoms_fix(buf)); + return SPECBOS_DATA_PARSE_ERROR; + } + + if (!nd) a1logd(p->log, 1, "Got specbos error code %d\n",se); break; } if (*cp == '\005') /* Got an Enquire */ @@ -131,10 +140,9 @@ int nd /* nz to disable debug messages */ } out[bsize-1] = '\000'; - if (!nd) a1logd(p->log, 4, "specbos_fcommand: command '%s' returned '%s'\n", - icoms_fix(in), icoms_fix(out)); - - return SPECBOS_OK; + if (!nd) a1logd(p->log, 4, "specbos_fcommand: command '%s' returned '%s' bytes %d, err 0x%x\n", + icoms_fix(in), icoms_fix(out),strlen(out), se); + return se; } /* Do a normal command/response echange with the specbos. */ @@ -164,7 +172,7 @@ double to /* Timeout in seconds */ int rv, se; char *cp, *tc = "\r\006\025"; /* Return, Ack or Nak */ - if ((se = p->icom->read(p->icom, out, bsize, tc, 1, to)) != 0) { + if ((se = p->icom->read(p->icom, out, bsize, NULL, tc, 1, to)) != 0) { a1logd(p->log, 1, "specbos_readresp: serial i/o failure\n"); return icoms2specbos_err(se); } @@ -172,7 +180,6 @@ double to /* Timeout in seconds */ } /* Establish communications with a specbos */ -/* Ignore the serial parameters - leave serial in default state. */ /* Return SPECBOS_COMS_FAIL on failure to establish communications */ static inst_code specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { @@ -217,7 +224,7 @@ specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* Check instrument is responding */ - if (((ev = specbos_command(p, "*idn?\r", buf, MAX_MES_SIZE, 0.2)) & inst_mask) + if (((ev = specbos_command(p, "*idn?\r", buf, MAX_MES_SIZE, 0.5)) & inst_mask) != inst_coms_fail) { break; /* We've got coms or user abort */ } @@ -282,8 +289,6 @@ specbos_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { return inst_ok; } -static inst_code set_default_disp_type(specbos *p); - /* Notes on commands @@ -357,28 +362,33 @@ static inst_code set_default_disp_type(specbos *p); */ static inst_code specbos_get_diffpos(specbos *p, int *pos, int nd); +static inst_code specbos_get_target_laser(specbos *p, int *laser, int nd); -/* Diffuser position thread. */ +/* Diffuser position and laser state thread. */ /* Poll the instrument at 500msec intervals */ int specbos_diff_thread(void *pp) { int nfailed = 0; specbos *p = (specbos *)pp; - inst_code rv = inst_ok; + inst_code rv1 = inst_ok; + inst_code rv2 = inst_ok; a1logd(p->log,3,"Diffuser thread started\n"); - for (nfailed = 0; nfailed < 5;) { +// for (nfailed = 0; nfailed < 5;) + /* Try indefinitely, in case instrument is put to sleep */ + for (;;) { int pos; amutex_lock(p->lock); - rv = specbos_get_diffpos(p, &pos, 1); + rv1 = specbos_get_diffpos(p, &pos, 1); + rv2 = specbos_get_target_laser(p, &p->laser, 1); amutex_unlock(p->lock); if (p->th_term) { p->th_termed = 1; break; } - if (rv != inst_ok) { + if (rv1 != inst_ok || rv2 != inst_ok) { nfailed++; - a1logd(p->log,3,"Diffuser thread failed with 0x%x\n",rv); + a1logd(p->log,3,"Diffuser thread failed with 0x%x 0x%x\n",rv1,rv2); continue; } if (pos != p->dpos) { @@ -390,7 +400,7 @@ int specbos_diff_thread(void *pp) { msec_sleep(500); } a1logd(p->log,3,"Diffuser thread returning\n"); - return rv; + return rv1 != inst_ok ? rv1 : rv2; } /* Initialise the SPECBOS */ @@ -544,7 +554,7 @@ specbos_init_inst(inst *pp) { return inst_ok; } -static inst_code specbos_measure_set_refresh(specbos *p); +static inst_code specbos_imp_measure_set_refresh(specbos *p); static inst_code specbos_imp_set_refresh(specbos *p); /* Get the ambient diffuser position */ @@ -569,6 +579,29 @@ specbos_get_diffpos( return inst_ok; } +/* Get the target laser state */ +/* (This is not multithread safe) */ +static inst_code +specbos_get_target_laser( + specbos *p, /* Object */ + int *laser, /* 0 = off, 1 = on */ + int nd /* nz = no debug message */ +) { + char buf[MAX_RD_SIZE]; + int ec; + int lstate; + + if ((ec = specbos_fcommand(p, "*contr:laser?\r", buf, MAX_MES_SIZE, 1.0, 1, 0, nd)) != inst_ok) { + return specbos_interp_code((inst *)p, ec); + } + if (sscanf(buf, "laser: %d ",&lstate) != 1) { + a1loge(p->log, 2, "specbos_get_target_laser: failed to parse laser state\n"); + return inst_protocol_error; + } + *laser = lstate; + return inst_ok; +} + /* Read a single sample */ /* Return the dtp error code */ static inst_code @@ -649,50 +682,94 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ amutex_unlock(p->lock); return rv; } + p->laser = 0; /* Attempt a refresh display frame rate calibration if needed */ if (p->refrmode != 0 && p->rrset == 0) { - if ((rv = specbos_measure_set_refresh(p)) != inst_ok) { + a1logd(p->log, 1, "specbos: need refresh rate calibration before measure\n"); + if ((rv = specbos_imp_measure_set_refresh(p)) != inst_ok) { amutex_unlock(p->lock); return rv; } } /* Trigger a measurement */ - if ((ec = specbos_fcommand(p, "*init\r", buf, MAX_MES_SIZE, 5.0 * p->measto + 2.0 , 1, 1, 0)) != SPECBOS_OK) { + /* (Note that ESC will abort it) */ + if ((ec = specbos_fcommand(p, "*init\r", buf, MAX_MES_SIZE, 5.0 * p->measto + 10.0 , 1, 1, 0)) != SPECBOS_OK) { amutex_unlock(p->lock); return specbos_interp_code((inst *)p, ec); } - /* Read the XYZ */ - if ((ec = specbos_fcommand(p, "*fetch:XYZ\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0)) != SPECBOS_OK) { - amutex_unlock(p->lock); - return specbos_interp_code((inst *)p, ec); + + if (p->noXYZ) { /* Will fail, so assume it failed */ + ec = SPECBOS_COMMAND; + + } else { /* Read the XYZ */ + ec = specbos_fcommand(p, "*fetch:XYZ\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0); } - if (sscanf(buf, " X: %lf Y: %lf Z: %lf ", - &val->XYZ[0], &val->XYZ[1], &val->XYZ[2]) == 3) { - - /* This may not change anything since instrument may clamp */ - if (clamp) - icmClamp3(val->XYZ, val->XYZ); - val->loc[0] = '\000'; - if (p->mode & inst_mode_ambient) { - val->mtype = inst_mrt_ambient; - } else - val->mtype = inst_mrt_emission; - val->XYZ_v = 1; /* These are absolute XYZ readings */ - val->sp.spec_n = 0; - val->duration = 0.0; - rv = inst_ok; - } else { + + if (ec == SPECBOS_OK) { + + if (sscanf(buf, " X: %lf Y: %lf Z: %lf ", + &val->XYZ[0], &val->XYZ[1], &val->XYZ[2]) != 3) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); + return inst_protocol_error; + } + + /* Hmm. Some older firmware versions are reported to not support the */ + /* "fetch:XYZ" command. Use an alternative if it fails. */ + } else if (ec == SPECBOS_COMMAND) { + double Yxy[3]; + + p->noXYZ = 1; + + if ((ec = specbos_fcommand(p, "*fetch:PHOTOmetric\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0)) + != SPECBOS_OK) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, ec); + } + if (sscanf(buf, "Luminance[cd/m^2]: %lf ", &Yxy[0]) != 1) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); + return inst_protocol_error; + } + + if ((ec = specbos_fcommand(p, "*fetch:CHROMXY\r", buf, MAX_RD_SIZE, 0.5, 3, 0, 0)) + != SPECBOS_OK) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, ec); + } + if (sscanf(buf, "Chrom_x: %lf Chrom_y: %lf ", &Yxy[1], &Yxy[2]) != 2) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); + return inst_protocol_error; + } + icmYxy2XYZ(val->XYZ, Yxy); + + } else if (ec != SPECBOS_OK) { amutex_unlock(p->lock); - a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); - return inst_protocol_error; + return specbos_interp_code((inst *)p, ec); } + /* This may not change anything since instrument may clamp */ + if (clamp) + icmClamp3(val->XYZ, val->XYZ); + val->loc[0] = '\000'; + if (p->mode & inst_mode_ambient) { + val->mtype = inst_mrt_ambient; + } else + val->mtype = inst_mrt_emission; + val->XYZ_v = 1; /* These are absolute XYZ readings */ + val->sp.spec_n = 0; + val->duration = 0.0; + rv = inst_ok; + + /* spectrum data is returned only if requested */ if (p->mode & inst_mode_spectral) { + int tries, maxtries = 5; int i, xsize; char *cp, *ncp; @@ -700,32 +777,53 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ /* (Format 9 reportedly doesn't work on the 1201) */ /* The folling works on the 1211 and is reported to work on the 1201 */ - /* Fetch the spectral readings */ - if ((ec = specbos_fcommand(p, "*fetch:sprad\r", buf, MAX_RD_SIZE, 1.0, 2+p->nbands+1, 0, 0)) != SPECBOS_OK) { - return specbos_interp_code((inst *)p, ec); - } - - val->sp.spec_n = p->nbands; - val->sp.spec_wl_short = p->wl_short; - val->sp.spec_wl_long = p->wl_long; - - /* Spectral data is in W/nm/m^2 */ - val->sp.norm = 1.0; - cp = buf; - for (i = -2; i < val->sp.spec_n; i++) { - if ((ncp = strchr(cp, '\r')) == NULL) { - amutex_unlock(p->lock); - a1logd(p->log, 1, "specbos_read_sample: failed to parse spectral\n"); - return inst_protocol_error; + /* Because the specbos doesn't use flow control in its */ + /* internal serial communications, it may overrun */ + /* the FT232R buffer, so retry fetching the spectra if */ + /* we get a comm error or parsing error. */ + for (tries = 0;;) { + + /* Fetch the spectral readings */ + ec = specbos_fcommand(p, "*fetch:sprad\r", buf, MAX_RD_SIZE, 4.0, 2+p->nbands+1, 0, 0); + tries++; + if (ec != SPECBOS_OK) { + if (tries > maxtries) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_fcommand: failed with 0x%x\n",ec); + return specbos_interp_code((inst *)p, ec); + } + continue; /* Retry the fetch */ } - *ncp = '\000'; - if (i >= 0) { - val->sp.spec[i] = 1000.0 * atof(cp); /* Convert to mW/m^2/nm */ - if (p->mode & inst_mode_ambient) - val->mtype = inst_mrt_ambient; + + val->sp.spec_n = p->nbands; + val->sp.spec_wl_short = p->wl_short; + val->sp.spec_wl_long = p->wl_long; + + /* Spectral data is in W/nm/m^2 */ + val->sp.norm = 1.0; + cp = buf; + for (i = -2; i < val->sp.spec_n; i++) { + if ((ncp = strchr(cp, '\r')) == NULL) { + a1logd(p->log, 1, "specbos_read_sample: failed to parse spectra at %d/%d\n",i+1,val->sp.spec_n); + if (tries > maxtries) { + amutex_unlock(p->lock); + return inst_protocol_error; + } + continue; /* Retry the fetch and parse */ + } + *ncp = '\000'; + if (i >= 0) { + a1logd(p->log, 6, "sample %d/%d got %f from '%s'\n",i+1,val->sp.spec_n,atof(cp),cp); + val->sp.spec[i] = 1000.0 * atof(cp); /* Convert to mW/m^2/nm */ + if (p->mode & inst_mode_ambient) + val->mtype = inst_mrt_ambient; + } + cp = ncp+1; } - cp = ncp+1; + /* We've parsed correctly, so don't retry */ + break; } + a1logd(p->log, 1, "specbos_read_sample: got total %d samples/%d expected in %d tries\n",i,val->sp.spec_n, tries); } amutex_unlock(p->lock); @@ -778,6 +876,9 @@ double *ref_rate int ec; inst_code rv; + if (ref_rate != NULL) + *ref_rate = 0.0; + if (p->model == 1201) return inst_unsupported; @@ -822,6 +923,9 @@ double *ref_rate if (!p->inited) return inst_no_init; + if (ref_rate != NULL) + *ref_rate = 0.0; + amutex_lock(p->lock); if ((rv = specbos_imp_measure_refresh(p, &refrate)) != inst_ok) { amutex_unlock(p->lock); @@ -832,14 +936,16 @@ double *ref_rate if (refrate == 0.0) return inst_misread; - *ref_rate = refrate; + if (ref_rate != NULL) + *ref_rate = refrate; return inst_ok; } /* Measure and then set refperiod, refrate if possible */ +/* (Not thread safe) */ static inst_code -specbos_measure_set_refresh( +specbos_imp_measure_set_refresh( specbos *p /* Object */ ) { inst_code rv; @@ -847,9 +953,7 @@ specbos_measure_set_refresh( int mul; double pval; - amutex_lock(p->lock); if ((rv = specbos_imp_measure_refresh(p, &refrate)) != inst_ok) { - amutex_unlock(p->lock); return rv; } @@ -865,14 +969,25 @@ specbos_measure_set_refresh( p->rrset = 1; if ((rv = specbos_imp_set_refresh(p)) != inst_ok) { - amutex_unlock(p->lock); return rv; } - amutex_unlock(p->lock); return inst_ok; } +/* Measure and then set refperiod, refrate if possible */ +static inst_code +specbos_measure_set_refresh( + specbos *p /* Object */ +) { + int rv; + + amutex_lock(p->lock); + rv = specbos_imp_measure_set_refresh(p); + amutex_unlock(p->lock); + return rv; +} + /* Return needed and available inst_cal_type's */ static inst_code specbos_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_type *pa_cals) { specbos *p = (specbos *)pp; @@ -1291,7 +1406,7 @@ specbos_del(inst *pp) { } /* Return the instrument mode capabilities */ -void specbos_capabilities(inst *pp, +static void specbos_capabilities(inst *pp, inst_mode *pcap1, inst2_capability *pcap2, inst3_capability *pcap3) { @@ -1329,6 +1444,7 @@ inst3_capability *pcap3) { } /* Return current or given configuration available measurement modes. */ +/* NOTE that conf_ix values shoudn't be changed, as it is used as a persistent key */ static inst_code specbos_meas_config( inst *pp, inst_mode *mmodes, @@ -1383,7 +1499,7 @@ int *conf_ix } /* Check device measurement mode */ -inst_code specbos_check_mode(inst *pp, inst_mode m) { +static inst_code specbos_check_mode(inst *pp, inst_mode m) { inst_mode cap; if (!pp->gotcoms) @@ -1407,7 +1523,7 @@ inst_code specbos_check_mode(inst *pp, inst_mode m) { } /* Set device measurement mode */ -inst_code specbos_set_mode(inst *pp, inst_mode m) { +static inst_code specbos_set_mode(inst *pp, inst_mode m) { specbos *p = (specbos *)pp; int refrmode; inst_code ev; @@ -1438,13 +1554,14 @@ inst_code specbos_set_mode(inst *pp, inst_mode m) { return inst_ok; } -inst_disptypesel specbos_disptypesel[3] = { +static inst_disptypesel specbos_disptypesel[3] = { { inst_dtflags_default, 1, "nl", "Non-Refresh display", 0, + disptech_lcd, 0 }, { @@ -1453,6 +1570,7 @@ inst_disptypesel specbos_disptypesel[3] = { "rc", /* sel */ "Refresh display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 1 /* ix */ }, { @@ -1461,6 +1579,7 @@ inst_disptypesel specbos_disptypesel[3] = { "", "", 0, + disptech_none, 0 } }; @@ -1527,7 +1646,7 @@ static inst_code set_disp_type(specbos *p, inst_disptypesel *dentry) { return inst_ok; } -/* Set the display type */ +/* Set the display type - refresh or not */ static inst_code specbos_set_disptype(inst *pp, int ix) { specbos *p = (specbos *)pp; inst_code ev; @@ -1576,8 +1695,24 @@ specbos_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } + /* Get laser target state. */ + /* For speed we don't return the real time state, */ + /* but the state from the last poll */ + if (m == inst_opt_get_target_state) { + va_list args; + int *pstate; + + va_start(args, m); + pstate = va_arg(args, int *); + va_end(args); + + if (pstate != NULL) + *pstate = p->laser; + + return inst_ok; + /* Set laser target state */ - if (m == inst_opt_set_target_state) { + } else if (m == inst_opt_set_target_state) { va_list args; int state = 0; @@ -1586,41 +1721,31 @@ specbos_get_set_opt(inst *pp, inst_opt_type m, ...) va_end(args); amutex_lock(p->lock); - if (state == 2) { - int lstate = -1; - if ((ev = specbos_command(p, "*contr:laser?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - a1loge(p->log, 1, "specbos_get_set_opt: failed to send laser? command\n"); - return ev; - } - if (sscanf(buf, "laser: %d ",&lstate) != 1) { + if (state == 2) { /* Toggle */ + + /* Get the current state */ + if ((ev = specbos_get_target_laser(p, &p->laser, 0)) != inst_ok) { amutex_unlock(p->lock); - a1loge(p->log, 1, "specbos_get_set_opt: failed to parse laser state\n"); return ev; } - a1logd(p->log, 5, " Laser state = %d\n",lstate); - if (lstate == 0) - lstate = 1; - else if (lstate == 1) - lstate = 0; - if (lstate == 0 || lstate == 1) { - char mes[100]; - sprintf(mes,"*contr:laser %d\r",lstate); - if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { - amutex_unlock(p->lock); - return ev; - } - } - } else if (state == 1) { + a1logd(p->log, 5, " Laser state = %d\n",p->laser); + if (p->laser == 0) + state = 1; + else if (p->laser == 1) + state = 0; + } + if (state == 1) { if ((ev = specbos_command(p, "*contr:laser 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { amutex_unlock(p->lock); return ev; } + p->laser = 1; } else if (state == 0) { if ((ev = specbos_command(p, "*contr:laser 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { amutex_unlock(p->lock); return ev; } + p->laser = 0; } amutex_unlock(p->lock); return inst_ok; diff --git a/spectro/specbos.h b/spectro/specbos.h index 2bc601e..86e7405 100644 --- a/spectro/specbos.h +++ b/spectro/specbos.h @@ -128,6 +128,8 @@ struct _specbos { /* 1201 */ /* 1211 */ + int noXYZ; /* nz if firmware doesn't support fetch*XYZ */ + inst_mode mode; /* Currently instrument mode */ int refrmode; /* nz if in refresh display mode */ @@ -149,6 +151,7 @@ struct _specbos { volatile int th_term; /* nz to terminate thread */ volatile int th_termed; /* nz when thread terminated */ int dpos; /* Diffuser position, 0 = emissive, 1 = ambient */ + int laser; /* Target laser state, nz = on */ }; typedef struct _specbos specbos; diff --git a/spectro/spotread.c b/spectro/spotread.c index 4d65916..7689986 100644 --- a/spectro/spotread.c +++ b/spectro/spotread.c @@ -52,20 +52,20 @@ #include "xicc.h" #include "conv.h" #include "plot.h" +#include "ui.h" #else /* SALONEINSTLIB */ #include "sa_config.h" #include "numsup.h" #include "xspect.h" #include "conv.h" #endif /* SALONEINSTLIB */ -#include "ccss.h" -#include "ccmx.h" #include "inst.h" #include "icoms.h" +#include "ccss.h" +#include "ccmx.h" #include "instappsup.h" -#include "spyd2setup.h" -#ifdef EN_SPYD2 -#include "spyd2setup.h" +#ifdef ENABLE_USB +# include "spyd2.h" #endif #if defined (NT) @@ -238,12 +238,20 @@ static int gcc_bug_fix(int i) { } #endif /* APPLE */ +/* A user callback to trigger for -O option */ +static inst_code uicallback(void *cntx, inst_ui_purp purp) { + + if (purp == inst_armed) + return inst_user_trig; + return inst_ok; +} + /* Flags used: ABCDEFGHIJKLMNOPQRSTUVWXYZ - upper ... . . . . .. ... + upper ... . . .. . .. ... lower . .... .. . .. . .. */ @@ -255,8 +263,6 @@ usage(char *diag, ...) { inst2_capability cap2 = 0; fprintf(stderr,"Measure spot values, Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill, licensed under the GPL Version 2 or later\n"); - if (setup_spyd2() == 2) - fprintf(stderr,"WARNING: This file contains a proprietary firmware image, and may not be freely distributed !\n"); if (diag != NULL) { va_list args; fprintf(stderr,"Diagnostic: "); @@ -279,7 +285,8 @@ usage(char *diag, ...) { for (i = 0; ; i++) { if (paths[i] == NULL) break; - if (paths[i]->itype == instSpyder2 && setup_spyd2() == 0) + 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 fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name); @@ -301,7 +308,7 @@ usage(char *diag, ...) { fprintf(stderr," -I illum Set simulated instrument illumination using FWA (def -i illum):\n"); fprintf(stderr," M0, M1, M2, A, C, D50, D50M2, D65, F5, F8, F10 or file.sp]\n"); #endif - fprintf(stderr," -i illum Choose illuminant for computation of CIE XYZ from spectral data & FWA:\n"); + fprintf(stderr," -i illum Choose illuminant for computation of CIE XYZ from spectral reflectance & FWA:\n"); #ifndef SALONEINSTLIB fprintf(stderr," A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp\n"); #else @@ -330,6 +337,7 @@ usage(char *diag, ...) { #endif /* !SALONEINSTLIB */ // fprintf(stderr," -K type Run instrument calibration first\n"); fprintf(stderr," -N Disable auto calibration of instrument\n"); + fprintf(stderr," -O Do one cal. or measure and exit\n"); fprintf(stderr," -H Start in high resolution spectrum mode (if available)\n"); if (cap2 & inst2_ccmx) fprintf(stderr," -X file.ccmx Apply Colorimeter Correction Matrix\n"); @@ -356,6 +364,7 @@ int main(int argc, char *argv[]) { int debug = 0; int docalib = 0; /* Do a manual instrument calibration */ int nocal = 0; /* Disable auto calibration */ + int doone = 0; /* Do one calibration or measure and exit */ int pspec = 0; /* 1 = Print out the spectrum for each reading */ /* 2 = Plot out the spectrum for each reading */ int trans = 0; /* Use transmissioin mode */ @@ -393,8 +402,10 @@ int main(int argc, char *argv[]) { inst_code rv; int uswitch = 0; /* Instrument switch is enabled */ int spec = 0; /* Need spectral data for observer/illuminant flag */ + int tillum_set = 0; /* User asked for custom target illuminant spectrum */ icxIllumeType tillum = icxIT_none; /* Target/simulated instrument illuminant */ xspect cust_tillum, *tillump = NULL; /* Custom target/simulated illumination spectrum */ + int illum_set = 0; /* User asked for custom illuminant spectrum */ icxIllumeType illum = icxIT_D50; /* Spectral defaults */ xspect cust_illum; /* Custom illumination spectrum */ icxObserverType obType = icxOT_default; @@ -403,7 +414,8 @@ int main(int argc, char *argv[]) { xsp2cie *sp2cie = NULL; /* default conversion */ xsp2cie *sp2cief[26]; /* FWA corrected conversions */ double wXYZ[3] = { -10.0, 0, 0 };/* White XYZ for display white relative */ - double chmat[3][3]; /* Chromatic adapation matrix */ + double chmat[3][3]; /* Chromatic adapation matrix for white point relative */ + double XYZ[3] = { 0.0, 0.0, 0.0 }; /* Last XYZ scaled 0..100 or absolute */ double Lab[3] = { -10.0, 0, 0}; /* Last Lab */ double rXYZ[3] = { 0.0, -10.0, 0}; /* Reference XYZ */ double rLab[3] = { -10.0, 0, 0}; /* Reference Lab */ @@ -421,7 +433,6 @@ int main(int argc, char *argv[]) { set_exe_path(argv[0]); /* Set global exe_path and error_program */ check_if_not_interactive(); - setup_spyd2(); /* Load firware if available */ for (i = 0; i < 26; i++) sp2cief[i] = NULL; @@ -492,33 +503,33 @@ int main(int argc, char *argv[]) { if (na == NULL) usage("Paramater expected following -I"); if (strcmp(na, "A") == 0 || strcmp(na, "M0") == 0) { - spec = 1; + tillum_set = spec = 1; tillum = icxIT_A; } else if (strcmp(na, "C") == 0) { - spec = 1; + tillum_set = spec = 1; tillum = icxIT_C; } else if (strcmp(na, "D50") == 0 || strcmp(na, "M1") == 0) { - spec = 1; + tillum_set = spec = 1; tillum = icxIT_D50; } else if (strcmp(na, "D50M2") == 0 || strcmp(na, "M2") == 0) { - spec = 1; + tillum_set = spec = 1; tillum = icxIT_D50M2; } else if (strcmp(na, "D65") == 0) { - spec = 1; + tillum_set = spec = 1; tillum = icxIT_D65; } else if (strcmp(na, "F5") == 0) { - spec = 1; + tillum_set = spec = 1; tillum = icxIT_F5; } else if (strcmp(na, "F8") == 0) { - spec = 1; + tillum_set = spec = 1; tillum = icxIT_F8; } else if (strcmp(na, "F10") == 0) { - spec = 1; + tillum_set = spec = 1; tillum = icxIT_F10; } else { /* Assume it's a filename */ - spec = 1; + tillum_set = spec = 1; tillum = icxIT_custom; if (read_xspect(&cust_tillum, na) != 0) usage("Failed to read custom target illuminant spectrum in file '%s'",na); @@ -530,32 +541,32 @@ int main(int argc, char *argv[]) { fa = nfa; if (na == NULL) usage("Paramater expected following -i"); if (strcmp(na, "A") == 0) { - spec = 1; + illum_set = spec = 1; illum = icxIT_A; } else if (strcmp(na, "C") == 0) { - spec = 1; + illum_set = spec = 1; illum = icxIT_C; } else if (strcmp(na, "D50") == 0) { - spec = 1; + illum_set = spec = 1; illum = icxIT_D50; } else if (strcmp(na, "D50M2") == 0) { - spec = 1; + illum_set = spec = 1; illum = icxIT_D50M2; } else if (strcmp(na, "D65") == 0) { - spec = 1; + illum_set = spec = 1; illum = icxIT_D65; #ifndef SALONEINSTLIB } else if (strcmp(na, "F5") == 0) { - spec = 1; + illum_set = spec = 1; illum = icxIT_F5; } else if (strcmp(na, "F8") == 0) { - spec = 1; + illum_set = spec = 1; illum = icxIT_F8; } else if (strcmp(na, "F10") == 0) { - spec = 1; + illum_set = spec = 1; illum = icxIT_F10; } else { /* Assume it's a filename */ - spec = 1; + illum_set = spec = 1; illum = icxIT_custom; if (read_xspect(&cust_illum, na) != 0) usage("Unable to read custom illuminant file '%s'",na); @@ -622,9 +633,9 @@ int main(int argc, char *argv[]) { if (argv[fa][2] != '\000') { fa = nfa; if (argv[fa][2] == 'b' || argv[fa][2] == 'B') - emiss = 2; + emiss = 2; /* Display brightness relative */ else if (argv[fa][2] == 'w' || argv[fa][2] == 'W') - emiss = 3; + emiss = 3; /* Display white point relative */ else usage("-p modifier '%c' not recognised",argv[fa][2]); } @@ -698,6 +709,10 @@ int main(int argc, char *argv[]) { } else if (argv[fa][1] == 'N') { nocal = 1; + /* Do one cal. or measure and exit */ + } else if (argv[fa][1] == 'O') { + doone = 1; + /* High res mode */ } else if (argv[fa][1] == 'H') { highres = 1; @@ -774,6 +789,11 @@ int main(int argc, char *argv[]) { } } + /* Check for some user mistakes */ + + if ((tillum_set || illum_set) && emiss) + warning("-I or -i parameter makes no sense with emissive or ambient measurement!"); + /* - - - - - - - - - - - - - - - - - - - */ if ((icmps = new_icompaths(g_log)) == NULL) error("Finding instrument paths failed"); @@ -943,7 +963,7 @@ int main(int argc, char *argv[]) { return -1; } } else - printf("Display type ignored - instrument doesn't support display type\n"); + printf("Display type ignored - instrument doesn't support display type selection\n"); } } else { @@ -1083,7 +1103,7 @@ int main(int argc, char *argv[]) { it->del(it); return -1; } - if ((rv = it->col_cor_mat(it, cx->matrix)) != inst_ok) { + if ((rv = it->col_cor_mat(it, cx->dtech, cx->cc_cbid, cx->matrix)) != inst_ok) { printf("\nSetting Colorimeter Correction Matrix failed with error :'%s' (%s)\n", it->inst_interp_error(it, rv), it->interp_error(it, rv)); cx->del(cx); @@ -1122,7 +1142,7 @@ int main(int argc, char *argv[]) { it->del(it); return -1; } - if ((rv = it->col_cal_spec_set(it, cs->samples, cs->no_samp)) != inst_ok) { + if ((rv = it->col_cal_spec_set(it, cs->dtech, cs->samples, cs->no_samp)) != inst_ok) { printf("\nSetting Colorimeter Calibration Spectral Samples failed with error :'%s' (%s)\n", it->inst_interp_error(it, rv), it->interp_error(it, rv)); cs->del(cs); @@ -1229,6 +1249,10 @@ int main(int argc, char *argv[]) { if (verb) printf("Init instrument success !\n"); + if (doone) { /* Set to trigger immediately */ + it->set_uicallback(it, uicallback, NULL); + } + if (spec) { /* Any non-illuminated mode has no illuminant */ if (emiss || tele || ambient) @@ -1267,7 +1291,6 @@ int main(int argc, char *argv[]) { /* Read spots until the user quits */ for (ix = 1;; ix++) { ipatch val; - double XYZ[3] = { 0.0, 0.0, 0.0 }; /* XYZ scaled 0..100 or absolute */ double tXYZ[3]; #ifndef SALONEINSTLIB double cct, vct, vdt; @@ -1425,7 +1448,7 @@ int main(int argc, char *argv[]) { } } - ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL); + ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, doone); if (ev != inst_ok) { /* Abort or fatal error */ printf("\nSpot read got abort or error from calibration\n"); break; @@ -1445,6 +1468,9 @@ int main(int argc, char *argv[]) { break; /* Abort */ } } + + if (doone) + break; } if (ambient == 2) { /* Flash ambient */ @@ -1609,7 +1635,7 @@ int main(int argc, char *argv[]) { } else if ((rv & inst_mask) == inst_needs_cal) { inst_code ev; printf("\n\nSpot read failed because instruments needs calibration.\n"); - ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL); + ev = inst_handle_calibrate(it, inst_calt_needed, inst_calc_none, NULL, NULL, doone); if (ev != inst_ok) { /* Abort or fatal error */ printf("\nSpot read got abort or error from calibrate\n"); break; @@ -1790,7 +1816,7 @@ int main(int argc, char *argv[]) { } } - ev = inst_handle_calibrate(it, inst_calt_available, inst_calc_none, NULL, NULL); + ev = inst_handle_calibrate(it, inst_calt_available, inst_calc_none, NULL, NULL, doone); if (ev != inst_ok) { /* Abort or fatal error */ printf("\nSpot read got abort or error from calibrate\n"); break; @@ -1874,8 +1900,9 @@ int main(int argc, char *argv[]) { printf("\nRefresh rate hasn't been calibrated\n"); - if ((ev = it->get_set_opt(it, inst_opt_get_dtinfo, &refrmode, NULL)) != inst_ok) { - printf("Can't get curretn refresh mode from instrument\n"); + /* (refrmode may be the default disptype_unknown refrmode) */ + if ((ev = it->get_disptechi(it, NULL, &refrmode, NULL)) != inst_ok) { + printf("Can't get current refresh mode from instrument\n"); --ix; continue; } @@ -1885,7 +1912,7 @@ int main(int argc, char *argv[]) { continue; } - ev = inst_handle_calibrate(it, inst_calt_ref_freq, inst_calc_none, NULL, NULL); + ev = inst_handle_calibrate(it, inst_calt_ref_freq, inst_calc_none, NULL, NULL, doone); if (ev != inst_ok) { /* Abort or fatal error */ printf("\nSpot read got abort or error from calibrate\n"); @@ -2169,9 +2196,6 @@ int main(int argc, char *argv[]) { else { /* emiss == 3, white point relative */ /* Normalize to white and scale to 0..100 */ -// XYZ[0] = XYZ[0] * icmD50_100.X / wXYZ[0]; -// XYZ[1] = XYZ[1] * icmD50_100.Y / wXYZ[1]; -// XYZ[2] = XYZ[2] * icmD50_100.Z / wXYZ[2]; icmMulBy3x3(XYZ, chmat, XYZ); icmScale3(XYZ, XYZ, 100.0); } @@ -2326,6 +2350,9 @@ int main(int argc, char *argv[]) { fprintf(fp,"\n"); } } + if (doone) + break; + } /* Next reading */ /* Release paper */ diff --git a/spectro/spyd2.c b/spectro/spyd2.c index dc09cea..72c0b21 100644 --- a/spectro/spyd2.c +++ b/spectro/spyd2.c @@ -29,15 +29,15 @@ enable the Argyll driver for this instrument by using the oeminst utility to create a spyd2PLD.bin file. - [ The Spyder 3 & 4 don't need a PLD firmware file. ] + [ The Spyder 3, 4 & 5 don't need a PLD firmware file. ] - The Spyder 4 instrument will not have the full range of manufacturer + The Spyder 4 & 5 instrument will not have the full range of manufacturer calibration settings available without the vendor calibration data. This calibration day is not provided with Argyll, since it is not available under a compatible license. - The purchaser of a Spyder 4 instrument should have received a copy + The purchaser of a Spyder 4 or 5 instrument should have received a copy of this calibration data along with their instrument, and should therefore be able to enable the use of the full range of calibration settings by using the spyd4en utility to create a spyd4cal.bin file. @@ -52,7 +52,7 @@ (Perhaps it has only 4 sensors ?) The frequency measurement is not very accurate, particularly for - the Spyder 3 & 4, being too low by about 3.5%. + the Spyder 3, 4 & 5, being too low by about 3.5%. */ @@ -103,9 +103,9 @@ #undef PLOT_SPECTRA /* Plot the sensor senitivity spectra */ #undef PLOT_SPECTRA_EXTRA /* Plot the sensor senitivity spectra extra values */ -#undef SAVE_SPECTRA /* Save the sensor senitivity spectra to "sensors.sp" */ -#undef SAVE_XYZSPECTRA /* Save the XYZ senitivity spectra to "sensorsxyz.sp" (scale 1.4) */ -#undef SAVE_STDXYZ /* save 1931 2 degree to stdobsxyz.sp */ +#undef SAVE_SPECTRA /* Save the sensor senitivity spectra to "sensors.cmf" */ +#undef SAVE_XYZSPECTRA /* Save the XYZ senitivity spectra to "sensorsxyz.cmf" (scale 1.4) */ +#undef SAVE_STDXYZ /* save 1931 2 degree to stdobsxyz.cmf */ #define DO_RESETEP /* Do the miscelanous resetep()'s */ @@ -171,6 +171,14 @@ static int buf2ushort(unsigned char *buf) { return val; } +/* Take a unsigned short sized buffer, and convert it to an int little endian */ +static int buf2uleshort(unsigned char *buf) { + int val; + val = (0xff & buf[1]); + val = ((val << 8) + (0xff & buf[0])); + return val; +} + /* Take a word sized buffer, and convert it to an int big endian. */ static int buf2int(unsigned char *buf) { int val; @@ -267,6 +275,7 @@ spyd2_reset( if (retr >= RETRIES ) { a1logd(p->log, 1, "spyd2_reset: failed with ICOM err 0x%x\n",se); return spyd2_interp_code((inst *)p, icoms2spyd2_err(se)); + break; } msec_sleep(500); a1logd(p->log, 1, "spyd2_reset: reset retry with ICOM err 0x%x\n",se); @@ -333,7 +342,7 @@ spyd2_readEEProm_imp( if (addr < 0 || (p->hwver < 7 && (addr + size) > 512) - || (p->hwver == 7 && (addr + size) > 1024)) + || (p->hwver >= 7 && (addr + size) > 1024)) return spyd2_interp_code((inst *)p, SPYD2_BAD_EE_ADDRESS); if (size >= 256) @@ -371,7 +380,7 @@ spyd2_readEEProm( if (addr < 0 || (p->hwver < 7 && (addr + size) > 512) - || (p->hwver == 7 && (addr + size) > 1024)) + || (p->hwver >= 7 && (addr + size) > 1024)) return spyd2_interp_code((inst *)p, SPYD2_BAD_EE_ADDRESS); while (size > 255) { /* Single read is too big */ @@ -898,7 +907,7 @@ spyd2_GetReading_ll( #endif } - /* Spyder 3/4 decoding */ + /* Spyder 3/4/5 decoding */ } else { /* Convert the raw buffer readings into 3 groups of 8 integers. */ /* At the start of each reading, the HW starts counting master */ @@ -915,7 +924,17 @@ spyd2_GetReading_ll( /* The light level is directly proportional to the frequency, */ /* hence the transitions-1 counted. */ - int map[8] = { 0,0,1,2,5,6,7,4 }; /* Map sensors into Spyder 2 order */ + int *map; + int nat[8] = { 0,1,2,3,4,5,6,7 }; /* Natural order */ + int map3[8] = { 0,0,1,2,5,6,7,4 }; /* Map Sp3 sensors into Spyder 2 order */ + int map4[8] = { 0,0,1,2,5,6,7,4 }; /* Map Sp4 sensors into Spyder 2 order */ + int map5[8] = { 1,1,0,5,2,7,6,4 }; /* Map Sp5 sensors into Spyder 2 order */ + + map = map3; + if (p->hwver == 7) + map = map4; + else if (p->hwver == 10) + map = map5; for (j = 0; j < 3; j++) { for (k = 0; k < 8; k++) { @@ -1058,7 +1077,7 @@ spyd2_SetAmbReg( return rv; } -/* Spyder3/4: Read ambient light timing */ +/* Spyder3/4/5: Read ambient light timing */ /* The byte value seems to be composed of: bits 0,1 bits 4 @@ -1101,7 +1120,7 @@ spyd2_ReadAmbTiming( } -/* Spyder3/4: Read ambient light channel 0 or 1 */ +/* Spyder3/4/5: Read ambient light channel 0 or 1 */ static inst_code spyd2_ReadAmbChan( spyd2 *p, @@ -1144,7 +1163,7 @@ spyd2_ReadAmbChan( /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* Spyder3/4: Read temperature config */ +/* Spyder3/4/5: Read temperature config */ static inst_code spyd2_ReadTempConfig( spyd2 *p, @@ -1182,7 +1201,7 @@ spyd2_ReadTempConfig( return rv; } -/* Spyder 3/4: Write Register */ +/* Spyder 3/4/5: Write Register */ static inst_code spyd2_WriteReg( spyd2 *p, @@ -1227,7 +1246,7 @@ spyd2_WriteReg( } -/* Spyder3/4: Read Register */ +/* Spyder3/4/5: Read Register */ static inst_code spyd2_ReadRegister( spyd2 *p, @@ -1501,7 +1520,34 @@ spyd2_rdreg_7x41xshort( return inst_ok; } -/* Get refresh rate command. Set it to DEFRRATE if not detectable */ +/* Special purpose LE short read, */ +/* Read 7 x 41 vectors of Little Endian ints from the EEprom */ +static inst_code +spyd2_rdreg_7x41xleshort( + spyd2 *p, /* Object */ + double sens[7][41], /* Destination */ + int addr /* Register Address, 0 - 1023 */ +) { + inst_code ev; + unsigned char buf[7 * 41 * 2], *bp; + int i, j; + + if ((ev = spyd2_readEEProm(p, buf, addr, 7 * 41 * 2)) != inst_ok) + return ev; + + bp = buf; + for (i = 0; i < 7; i++) { + for (j = 0; j < 41; j++, bp += 2) { + int val; + val = buf2uleshort(bp); + sens[i][j] = val / 100.0; + } + } + + return inst_ok; +} + +/* Get refresh rate command. Set it to 0.0 if not detectable */ /* if no refresh rate can be established */ /* (This isn't used by the manufacturers Spyder3/4 driver, */ /* but the instrument seems to impliment it.) */ @@ -1517,6 +1563,9 @@ spyd2_read_refrate( a1logd(p->log, 3, "spyd2_read_refrate: called\n"); + if (ref_rate != NULL) + *ref_rate = 0.0; + /* Establish the frame rate detect threshold level */ clocks = (10 * CLKRATE)/DEFRRATE; @@ -1527,7 +1576,7 @@ spyd2_read_refrate( a1logd(p->log, 3, "spyd2_read_refrate: no refresh rate detectable\n"); if (ref_rate != NULL) *ref_rate = 0.0; - return inst_ok; + return inst_misread | SPYD2_NO_REFRESH_DET; } else { int frclocks; /* notional clocks per frame */ int nframes; /* Number of frames to count */ @@ -1568,6 +1617,8 @@ spyd2_GetRefRate( a1logd(p->log, 3, "Frequency calibration called\n"); if ((ev = spyd2_read_refrate((inst *)p, &p->refrate)) != inst_ok) { + p->refrate = DEFRRATE; + p->refrvalid = 0; return ev; } if (p->refrate != 0.0) { @@ -1611,7 +1662,7 @@ spyd2_GetReading( a1logd(p->log, 3, "spyd2_GetReading: called\n"); - if (p->refrmode) + if (p->refrmode != 0 && p->rrset != 0) inttime = RINTTIME; /* ie. 1 second */ else inttime = NINTTIME; /* ie. 1 second */ @@ -1650,7 +1701,7 @@ spyd2_GetReading( } a1logd(p->log, 3, "spyd2_GetReading: Using cal table %d\n",(p->icx & 1)); - if (p->hwver) + if (p->hwver >= 7) a1logd(p->log, 3, "spyd2_GetReading: using spectral cal table %d\n",p->icx >> 1); for (k = 0; k < 8; k++) /* Zero weighted average */ @@ -1737,8 +1788,9 @@ spyd2_GetReading( /* Convert sensor readings to XYZ value */ for (j = 0; j < 3; j++) { XYZ[j] = p->cal_A[p->icx & 1][j][0]; /* First entry is a constant */ - for (k = 1; k < 8; k++) + for (k = 1; k < 8; k++) { XYZ[j] += a_sensv[k] * p->cal_A[p->icx & 1][j][k+1]; + } } } @@ -1778,7 +1830,7 @@ spyd2_GetReading( return ev; } -/* Spyder3/4: Do an ambient reading */ +/* Spyder3/4/5: Do an ambient reading */ /* NOTE :- the ambient sensor is something like a TAOS TLS 2562CS. */ /* It has two sensors, one wide band and the other infra-red, */ @@ -1866,12 +1918,11 @@ spyd2_GetAmbientReading( /* Compute ambient in Lux */ amb = s0[i] * amb0 - s1[i] * amb1; -// a1logd(p->log, 4, "spyd2_GetAmbientReading: combined ambient = %f Lux\n",amb); - /* Compute the Y value */ +// a1logd(p->log, 4, "spyd2_GetAmbientReading: combined ambient = %f cd/^m\n",amb); -// XYZ[1] = amb; /* cd/m^2 ??? - not very accurate, due to */ - XYZ[1] = 3.141592654 * amb; /* Lux ??? - not very accurate, due to */ - /* spectral response and/or integration angle. */ + /* Compute the Y value */ + XYZ[1] = amb; /* cd/m^2 ??? - not very accurate, due to */ + /* spectral response and/or integration angle ? */ XYZ[0] = icmD50.X * XYZ[1]; /* Convert to D50 neutral */ XYZ[2] = icmD50.Z * XYZ[1]; @@ -1881,8 +1932,10 @@ spyd2_GetAmbientReading( } /* ------------------------------------------------------------ */ -/* Spyder 4 manufacturer calibration data */ +/* Spyder 4/5 manufacturer calibration data */ int spyd4_nocals = 0; /* Number of calibrations */ + /* 6 for Spyder 4 */ + /* 7 for Spyder 5 */ xspect *spyd4_cals = NULL; /* [nocals] Device spectrum */ /* ------------------------------------------------------------ */ @@ -1989,7 +2042,7 @@ spyd4_set_cal_ix( } #endif /* PLOT_SPECTRA */ -#ifdef SAVE_XYZSPECTRA /* Save the default XYZ senitivity spectra to "sensorsxyz.sp" */ +#ifdef SAVE_XYZSPECTRA /* Save the default XYZ senitivity spectra to "sensorsxyz.cmf" */ { int i, j, k; xspect xyz[3]; @@ -2008,14 +2061,14 @@ spyd4_set_cal_ix( xyz[j].spec[i] *= 1.4; /* Align with std XYZ */ } } - write_nxspect("sensorsxyz.sp", xyz, 3, 0); + write_nxspect("sensorsxyz.cmf", xyz, 3, 0); } #endif #ifdef SAVE_STDXYZ { xspect xyz[3]; standardObserver(&xyz[0], &xyz[1], &xyz[2],icxOT_CIE_1931_2); - write_nxspect("stdobsxyz.sp", xyz, 3, 0); + write_nxspect("stdobsxyz.cmf", xyz, 3, 0); } #endif /* SAVE_STDXYZ */ @@ -2275,15 +2328,16 @@ spyd2_set_speccal( /* Preset the calibration to a matrix. The spectral type is set to none */ static inst_code spyd2_set_matcal(spyd2 *p, double mtx[3][3]) { - if (mtx == NULL) { + + if (p->samples != NULL) + free(p->samples); + p->samples = NULL; + p->nsamp = 0; + + if (mtx == NULL) icmSetUnity3x3(p->ccmat); - } else { - if (p->cbid == 0) { - a1loge(p->log, 1, "spyd2: can't set col_cor_mat over non-base display type\n"); - return inst_wrong_setup; - } + else icmCpy3x3(p->ccmat, mtx); - } return inst_ok; } @@ -2328,12 +2382,14 @@ spyd2_set_cal(spyd2 *p) { p->cal_A[1][0][2+i], p->cal_A[1][1][2+i], p->cal_A[1][2][2+i]); } } + a1logd(p->log,4,"\n"); a1logd(p->log,4,"ccmat = %f %f %f\n", p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); a1logd(p->log,4," %f %f %f\n", p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); a1logd(p->log,4," %f %f %f\n\n", p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); a1logd(p->log,4,"\n"); } @@ -2351,6 +2407,20 @@ spyd2_read_all_regs( a1logd(p->log, 3, "spyd2_read_all_regs: about to read all the EEProm values\n"); + if (p->log->debug >= 8) { + unsigned char buf[1024]; + int len = 512; + + if (p->hwver == 7 + || p->hwver == 10) + len = 1024; + + if ((ev = spyd2_readEEProm(p, buf, 0, len)) != inst_ok) + return ev; + a1logd(p->log, 8, "EEPROM:\n"); + adump_bytes(p->log, " ", buf, 0, len); + } + /* HW version */ if ((ev = spyd2_rd_ee_uchar(p, &p->hwver, 5)) != inst_ok) return ev; @@ -2359,12 +2429,16 @@ spyd2_read_all_regs( if ((ev = spyd2_rd_ee_uchar(p, &p->fbits, 6)) != inst_ok) return ev; - a1logd(p->log, 3, "spyd2_read_all_regs: hwver = 0x%02x%02x\n",p->hwver,p->fbits); + a1logd(p->log, 3, "spyd2_read_all_regs: hwver+fbits = 0x%02x%02x\n",p->hwver,p->fbits); /* Check the EEProm checksum */ - if (p->hwver == 7) { - if ((ev = spyd2_checkEECRC(p)) != inst_ok) - return ev; + if (p->hwver == 7 + || p->hwver == 10) { + if ((ev = spyd2_checkEECRC(p)) != inst_ok) { + a1logd(p->log, 3, "spyd2_read_all_regs: checksum failed\n"); + return ev; + } + a1logd(p->log, 6, "spyd2_read_all_regs: checksum OK\n"); } /* Serial number */ @@ -2470,14 +2544,20 @@ spyd2_read_all_regs( a1logd(p->log, 4, "\n"); } - } else if (p->hwver == 7) { + } else if (p->hwver == 7 + || p->hwver == 10) { int i, j; unsigned int sscal; double tsens[7][41]; /* Read sensor sensitivity spectral data */ - if ((ev = spyd2_rdreg_7x41xshort(p, tsens, 170)) != inst_ok) - return ev; + if (p->hwver == 7) { /* Spyder 4 */ + if ((ev = spyd2_rdreg_7x41xshort(p, tsens, 0xAA)) != inst_ok) + return ev; + } else { /* Spyder 5 */ + if ((ev = spyd2_rdreg_7x41xleshort(p, tsens, 0x12C)) != inst_ok) + return ev; + } /* Sensor scale factor */ if ((ev = spyd2_rd_ee_ushort(p, &sscal, 21)) != inst_ok) @@ -2502,7 +2582,7 @@ spyd2_read_all_regs( } } #ifdef SAVE_SPECTRA - write_nxspect("sensors.sp", p->sens, 7, 0); + write_nxspect("sensors.cmf", p->sens, 7, 0); #endif /* Linearization */ @@ -2531,7 +2611,6 @@ spyd2_read_all_regs( printf("The sensor and ambient sensor sensitivy curves\n"); do_plot10(xx, yp[0], yp[1], yp[2], yp[3], yp[4], yp[5], yp[6], yp[7], yp[8], yp[9], 81, 0); - for (j = 0; j < spyd4_nocals; j++) { double max = 0; for (i = 0; i < 81; i++) { @@ -2545,7 +2624,7 @@ spyd2_read_all_regs( for (; j < 10; j++) yp[j] = NULL; - printf("The display spectra\n"); + printf("The %d display spectra\n",spyd4_nocals); do_plot10(xx, yp[0], yp[1], yp[2], yp[3], yp[4], yp[5], yp[6], yp[7], yp[8], yp[9], 81, 0); } #endif /* PLOT_SPECTRA */ @@ -2559,10 +2638,10 @@ spyd2_read_all_regs( /* ------------------------------------------------------------ */ -/* Table to hold Spyder 2 Firmware, if it's installed */ -unsigned int _spyder2_pld_size = 0; /* Number of bytes to download */ -unsigned int *spyder2_pld_size = &_spyder2_pld_size; -unsigned char *spyder2_pld_bytes = NULL; +/* Spyder 1/2 Colorimeter Xilinx XCS05XL firmware pattern */ +unsigned int spyder_pld_xsize[2] = { 6817, 6817 }; /* Expected size */ +unsigned int spyder_pld_size[2] = { 0, 0 }; /* Number of bytes to download */ +unsigned char *spyder_pld_bytes[2] = { NULL, NULL }; /* Bytes to download */ /* Spyder 2: Download the PLD if it is available, and check status */ static inst_code @@ -2572,16 +2651,25 @@ spyd2_download_pld( inst_code ev; int stat; int i; + int id; + + if (p->itype == instSpyder1) + id = 0; + else + id = 1; a1logd(p->log, 2, "spyd2_download_pld: called\n"); - if (*spyder2_pld_size == 0 || *spyder2_pld_size == 0x11223344) { + if (spyder_pld_size[id] == 0) /* Try and read PLD pattern */ + setup_spyd2(id); + + if (spyder_pld_size[id] == 0) { a1logd(p->log, 1, "spyd2_download_pld: No PLD pattern available! (have you run oeminst ?)\n"); return spyd2_interp_code((inst *)p, SPYD2_NO_PLD_PATTERN) ; } - for (i = 0; i < *spyder2_pld_size; i += 8) { - if ((ev = spyd2_loadPLD(p, spyder2_pld_bytes + i, 8)) != inst_ok) + for (i = 0; i < spyder_pld_size[id]; i += 8) { + if ((ev = spyd2_loadPLD(p, spyder_pld_bytes[id] + i, 8)) != inst_ok) return ev; } @@ -2667,7 +2755,8 @@ spyd4_load_cal(spyd2 *p) { } nocals = size/(41 * 8); - if (nocals != 6) { + if (nocals != 6 + && nocals != 7) { fclose(fp); a1logd(p->log, 1, "spyd4_load_cal: calibration file '%s' is unexpected number of calibrations (%d)\n",bin_paths[0],nocals); break; @@ -2778,7 +2867,8 @@ spyd2_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { /* On OS X the Spyder 2 can't close properly */ #if defined(__APPLE__) /* OS X*/ - if (p->itype == instSpyder2) { + if (p->itype == instSpyder1 + || p->itype == instSpyder2) { usbflags |= icomuf_reset_before_close; /* The spyder 2 USB is buggy ? */ } #endif @@ -2787,7 +2877,8 @@ spyd2_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { #if defined(UNIX_X11) /* Linux*/ /* On Linux the Spyder 2 doesn't work reliably unless each */ /* read is preceeded by a reset endpoint. */ - if (p->itype == instSpyder2) { + if (p->itype == instSpyder1 + || p->itype == instSpyder2) { usbflags |= icomuf_resetep_before_read; /* The spyder USB is buggy ? */ } #endif @@ -2822,9 +2913,11 @@ spyd2_init_inst(inst *pp) { if (p->gotcoms == 0) /* Must establish coms before calling init */ return spyd2_interp_code((inst *)p, SPYD2_NO_COMS); - if (p->itype != instSpyder2 + if (p->itype != instSpyder1 + && p->itype != instSpyder2 && p->itype != instSpyder3 - && p->itype != instSpyder4) + && p->itype != instSpyder4 + && p->itype != instSpyder5) return spyd2_interp_code((inst *)p, SPYD2_UNKNOWN_MODEL); p->refrate = DEFRRATE; @@ -2834,7 +2927,8 @@ spyd2_init_inst(inst *pp) { /* For Spyder 1 & 2, reset the hardware and wait for it to become ready. */ if (p->itype != instSpyder3 - && p->itype != instSpyder4) { + && p->itype != instSpyder4 + && p->itype != instSpyder5) { /* Reset the instrument */ if ((ev = spyd2_reset(p)) != inst_ok) @@ -2852,7 +2946,7 @@ spyd2_init_inst(inst *pp) { return spyd2_interp_code((inst *)p, SPYD2_BADSTATUS); } else { - /* Because the Spyder 3/4 doesn't have a reset command, */ + /* Because the Spyder 3/4/5 doesn't have a reset command, */ /* it may be left in a borked state if the driver is aborted. */ /* Make sure there's no old read data hanging around. */ /* Sometimes it takes a little while for the old data to */ @@ -2974,24 +3068,34 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } if (IMODETST(p->mode, inst_mode_emis_ambient)) { - if ((ev = spyd2_GetAmbientReading(p, val->XYZ)) != inst_ok) - return ev; + ev = spyd2_GetAmbientReading(p, val->XYZ); } else { + ev = inst_ok; /* Attempt a CRT frame rate calibration if needed */ - if (p->refrmode != 0 && p->rrset == 0) { - if ((ev = spyd2_GetRefRate(p)) != inst_ok) - return ev; + if (p->refrmode != 0 && p->rrset == 0) + ev = spyd2_GetRefRate(p); + + if (ev != inst_ok) { + warning("Spyder: measuring refresh rate failed"); + ev = inst_ok; } - /* Read the XYZ value */ - if ((ev = spyd2_GetReading(p, val->XYZ)) != inst_ok) - return ev; + if (ev == inst_ok) { + /* Read the XYZ value */ + if ((ev = spyd2_GetReading(p, val->XYZ)) == inst_ok) { - /* Apply the colorimeter correction matrix */ - icmMulBy3x3(val->XYZ, p->ccmat, val->XYZ); + /* Apply the colorimeter correction matrix */ + icmMulBy3x3(val->XYZ, p->ccmat, val->XYZ); + } + } } + + if (ev != inst_ok) + return ev; + + /* This may not change anything since instrument may clamp */ if (clamp) icmClamp3(val->XYZ, val->XYZ); @@ -3011,11 +3115,31 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ return ev; } +/* Make a possible change of the refresh mode */ +static void update_refmode(spyd2 *p, int refrmode) { + + if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */ + refrmode = 0; + } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) { + refrmode = 1; + } + + if (p->refrmode != refrmode) { + p->rrset = 0; /* This is a hint we may have swapped displays */ + p->refrvalid = 0; + } + p->refrmode = refrmode; +} + +static inst_code set_base_disp_type(spyd2 *p, int cbid); + /* Insert a colorimetric correction matrix in the instrument XYZ readings */ /* This is only valid for colorimetric instruments. */ /* To remove the matrix, pass NULL for the filter filename */ inst_code spyd2_col_cor_mat( inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ \ +int cbid, /* Calibration display type base ID, 1 if unknown */\ double mtx[3][3] ) { spyd2 *p = (spyd2 *)pp; @@ -3026,6 +3150,13 @@ double mtx[3][3] if (!p->inited) return inst_no_init; + if ((ev = set_base_disp_type(p, cbid)) != inst_ok) + return ev; + + p->dtech = dtech; + update_refmode(p, disptech_get_id(dtech)->refr); + p->cbid = 0; + if ((ev = spyd2_set_matcal(p, mtx)) != inst_ok) return ev; @@ -3038,6 +3169,7 @@ double mtx[3][3] /* To set calibration back to default, pass NULL for sets. */ inst_code spyd2_col_cal_spec_set( inst *pp, +disptech dtech, xspect *sets, int no_sets ) { @@ -3051,6 +3183,8 @@ int no_sets if (p->hwver < 7) return inst_unsupported; + p->dtech = dtech; + if (sets == NULL || no_sets <= 0) { if ((ev = set_default_disp_type(p)) != inst_ok) return ev; @@ -3058,8 +3192,10 @@ int no_sets if ((ev = spyd2_set_speccal(p, sets, no_sets)) != inst_ok) return ev; + p->ucbid = 0; /* We're using external samples */ ev = spyd2_set_cal(p); } + update_refmode(p, disptech_get_id(dtech)->refr); return ev; } @@ -3093,7 +3229,7 @@ static inst_code spyd2_get_n_a_cals(inst *pp, inst_cal_type *pn_cals, inst_cal_t /* returning inst_needs_cal. Initially us an inst_cal_cond of inst_calc_none, */ /* and then be prepared to setup the right conditions, or ask the */ /* user to do so, each time the error inst_cal_setup is returned. */ -inst_code spyd2_calibrate( +static inst_code spyd2_calibrate( inst *pp, inst_cal_type *calt, /* Calibration type to do/remaining */ inst_cal_cond *calc, /* Current condition/desired condition */ @@ -3153,11 +3289,12 @@ double *ref_rate ) { spyd2 *p = (spyd2 *)pp; if (p->refrvalid) { + *ref_rate = p->refrate; return inst_ok; } else if (p->rrset) { *ref_rate = 0.0; - return inst_misread; + return inst_misread | SPYD2_NO_REFRESH_DET; } return inst_needs_cal; } @@ -3194,7 +3331,7 @@ spyd2_interp_error(inst *pp, int ec) { case SPYD2_COMS_FAIL: return "Communications failure"; case SPYD2_UNKNOWN_MODEL: - return "Not a Spyder 2 or 3"; + return "Not a Spyder 2, 3, 4 or 5"; case SPYD2_DATA_PARSE_ERROR: return "Data from i1 Display didn't parse as expected"; @@ -3245,6 +3382,10 @@ spyd2_interp_error(inst *pp, int ec) { case SPYD2_DISP_SEL_RANGE: return "Display device selection out of range"; + /* User error */ + case SPYD2_NO_REFRESH_DET: + return "Unable to detect & measure refresh rate"; + default: return "Unknown error code"; } @@ -3296,6 +3437,9 @@ spyd2_interp_code(inst *pp, int ec) { case SPYD2_DISP_SEL_RANGE: return inst_wrong_setup | ec; + case SPYD2_NO_REFRESH_DET: + return inst_misread | ec; + } return inst_other_error | ec; } @@ -3313,7 +3457,7 @@ spyd2_del(inst *pp) { } /* Return the instrument mode capabilities */ -void spyd2_capabilities(inst *pp, +static void spyd2_capabilities(inst *pp, inst_mode *pcap1, inst2_capability *pcap2, inst3_capability *pcap3) { @@ -3331,7 +3475,8 @@ inst3_capability *pcap3) { /* of ambinent capability, short of doing a read */ /* and noticing the result is zero. */ if (p->itype == instSpyder3 - || p->itype == instSpyder4) { + || p->itype == instSpyder4 + || p->itype == instSpyder5) { cap1 |= inst_mode_emis_ambient; } @@ -3344,7 +3489,8 @@ inst3_capability *pcap3) { ; if (p->itype == instSpyder3 - || p->itype == instSpyder4) { + || p->itype == instSpyder4 + || p->itype == instSpyder5) { cap2 |= inst2_disptype; cap2 |= inst2_has_leds; cap2 |= inst2_ambient_mono; @@ -3352,8 +3498,9 @@ inst3_capability *pcap3) { cap2 |= inst2_disptype; } - if (p->itype == instSpyder4) - cap2 |= inst2_ccss; /* Spyder4 has spectral sensiivities */ + if (p->itype == instSpyder4 + || p->itype == instSpyder5) + cap2 |= inst2_ccss; /* Spyder4 & 5 has spectral sensivities */ if (pcap1 != NULL) *pcap1 = cap1; @@ -3364,7 +3511,7 @@ inst3_capability *pcap3) { } /* Check device measurement mode */ -inst_code spyd2_check_mode(inst *pp, inst_mode m) { +static inst_code spyd2_check_mode(inst *pp, inst_mode m) { spyd2 *p = (spyd2 *)pp; inst_mode cap; @@ -3388,7 +3535,7 @@ inst_code spyd2_check_mode(inst *pp, inst_mode m) { } /* Set device measurement mode */ -inst_code spyd2_set_mode(inst *pp, inst_mode m) { +static inst_code spyd2_set_mode(inst *pp, inst_mode m) { spyd2 *p = (spyd2 *)pp; inst_code ev; @@ -3397,21 +3544,20 @@ inst_code spyd2_set_mode(inst *pp, inst_mode m) { p->mode = m; - if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) /* Must test this first! */ - p->refrmode = 0; - else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) - p->refrmode = 1; + /* Effective refresh mode may change */ + update_refmode(p, p->refrmode); return inst_ok; } -inst_disptypesel spyd2_disptypesel[3] = { +static inst_disptypesel spyd2_disptypesel[3] = { { inst_dtflags_default, 1, "l", "LCD display", 0, + disptech_lcd, 1 }, { @@ -3420,6 +3566,7 @@ inst_disptypesel spyd2_disptypesel[3] = { "c", /* sel */ "CRT display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 0 /* ix */ }, { @@ -3428,17 +3575,19 @@ inst_disptypesel spyd2_disptypesel[3] = { "", "", 0, + disptech_none, 0 } }; -inst_disptypesel spyd3_disptypesel[3] = { +static inst_disptypesel spyd3_disptypesel[3] = { { inst_dtflags_default, 1, "nl", "Non-Refresh display", 0, + disptech_lcd, 1 }, { @@ -3447,6 +3596,7 @@ inst_disptypesel spyd3_disptypesel[3] = { "rc", /* sel */ "Refresh display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 1 /* ix */ }, { @@ -3455,17 +3605,19 @@ inst_disptypesel spyd3_disptypesel[3] = { "", "", 0, + disptech_unknown, 0 } }; -inst_disptypesel spyd4_disptypesel_1[8] = { +static inst_disptypesel spyd4_disptypesel_1[8] = { { inst_dtflags_default, 1, "nl", "Generic Non-Refresh Display", 0, + disptech_lcd, 1 }, { @@ -3474,6 +3626,7 @@ inst_disptypesel spyd4_disptypesel_1[8] = { "rc", /* sel */ "Generic Refresh Display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 1 /* ix */ }, { @@ -3482,17 +3635,19 @@ inst_disptypesel spyd4_disptypesel_1[8] = { "", "", 0, + disptech_none, 0 } }; -inst_disptypesel spyd4_disptypesel[8] = { +static inst_disptypesel spyd4_disptypesel[8] = { { inst_dtflags_default, 1, "nl", "Generic Non-Refresh Display", 0, + disptech_lcd, 1 }, { @@ -3501,6 +3656,7 @@ inst_disptypesel spyd4_disptypesel[8] = { "rc", /* sel */ "Generic Refresh Display", /* desc */ 1, /* refr */ + disptech_crt, /* disptype */ 1 /* ix = hw bit + spec table << 1 */ }, { @@ -3508,6 +3664,7 @@ inst_disptypesel spyd4_disptypesel[8] = { 0, "f", "LCD, CCFL Backlight", + disptech_lcd_ccfl, 0, (1 << 1) | 1 }, @@ -3517,6 +3674,7 @@ inst_disptypesel spyd4_disptypesel[8] = { "L", "Wide Gamut LCD, CCFL Backlight", 0, + disptech_lcd_ccfl_wg, (2 << 1) | 1 }, { @@ -3524,6 +3682,7 @@ inst_disptypesel spyd4_disptypesel[8] = { 0, "e", "LCD, White LED Backlight", + disptech_lcd_wled, 0, (3 << 1) | 1 }, @@ -3533,6 +3692,7 @@ inst_disptypesel spyd4_disptypesel[8] = { "B", "Wide Gamut LCD, RGB LED Backlight", 0, + disptech_lcd_rgbled, (4 << 1) | 1 }, { @@ -3541,6 +3701,7 @@ inst_disptypesel spyd4_disptypesel[8] = { "x", "LCD, CCFL Backlight (Laptop ?)", 0, + disptech_lcd_ccfl, (5 << 1) | 1 }, { @@ -3549,16 +3710,95 @@ inst_disptypesel spyd4_disptypesel[8] = { "", "", 0, + disptech_none, + 0 + } +}; + +static inst_disptypesel spyd5_disptypesel[8] = { + { + inst_dtflags_default, + 1, + "nl", + "Generic Non-Refresh Display", + 0, + disptech_lcd, + 1 + }, + { + inst_dtflags_none, /* flags */ + 2, /* cbid */ + "rc", /* sel */ + "Generic Refresh Display", /* desc */ + 1, /* refr */ + disptech_crt, /* disptype */ + 1 /* ix = hw bit + spec table << 1 */ + }, + { + inst_dtflags_none, /* flags */ + 0, + "f", + "LCD, CCFL Backlight", + disptech_lcd_ccfl, + 0, + (1 << 1) | 1 + }, + { + inst_dtflags_none, /* flags */ + 0, + "L", + "Wide Gamut LCD, CCFL Backlight", + 0, + disptech_lcd_ccfl_wg, + (2 << 1) | 1 + }, + { + inst_dtflags_none, /* flags */ + 0, + "e", + "LCD, White LED Backlight", + disptech_lcd_wled, + 0, + (3 << 1) | 1 + }, + { + inst_dtflags_none, /* flags */ + 0, + "B", + "Wide Gamut LCD, RGB LED Backlight", + 0, + disptech_lcd_rgbled, + (4 << 1) | 1 + }, + { + inst_dtflags_none, /* flags */ + 0, + "x", + "LCD, CCFL Backlight (Laptop ?)", + 0, + disptech_lcd_ccfl, + (5 << 1) | 1 + }, + { + inst_dtflags_end, + 0, + "", + "", + 0, + disptech_none, 0 } }; static void set_base_disptype_list(spyd2 *p) { /* set the base display type list */ - if (p->itype == instSpyder4) { + if (p->itype == instSpyder4 + || p->itype == instSpyder5) { if (spyd4_nocals <= 1) { p->_dtlist = spyd4_disptypesel_1; - } else { + } else { /* spyd4_nocals == 6 or 7, Spyder 4 or 5. */ + /* Spyder 5 has exactly the same list as the Spyder 4, with an extra */ + /* entry at the end that is the same as the first (flat spectrum). */ p->_dtlist = spyd4_disptypesel; } } else if (p->itype == instSpyder3) { @@ -3601,40 +3841,65 @@ static inst_code set_disp_type(spyd2 *p, inst_disptypesel *dentry) { int refrmode; p->icx = dentry->ix; + p->dtech = dentry->dtech; p->cbid = dentry->cbid; - refrmode = dentry->refr; - if ( IMODETST(p->mode, inst_mode_emis_norefresh_ovd)) { /* Must test this first! */ - refrmode = 0; - } else if (IMODETST(p->mode, inst_mode_emis_refresh_ovd)) { - refrmode = 1; - } - - if (p->refrmode != refrmode) { - p->rrset = 0; /* This is a hint we may have swapped displays */ - p->refrvalid = 0; - } - p->refrmode = refrmode; + update_refmode(p, dentry->refr); if (dentry->flags & inst_dtflags_ccss) { /* Spectral sample */ if ((ev = spyd2_set_speccal(p, dentry->sets, dentry->no_sets)) != inst_ok) return ev; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ } else { /* Matrix */ if (dentry->flags & inst_dtflags_ccmx) { + if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok) + return ev; if ((ev = spyd2_set_matcal(p, dentry->mat)) != inst_ok) return ev; + p->cbid = 0; /* Matrix is an override of cbid */ + } else { if ((ev = spyd2_set_matcal(p, NULL)) != inst_ok) /* Noop */ return ev; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ } } return spyd2_set_cal(p); } +/* Set the display type */ +static inst_code spyd2_set_disptype(inst *pp, int ix) { + spyd2 *p = (spyd2 *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + p->_dtlist, p->hwver >= 7 ? 1 : 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + if (ix < 0 || ix >= p->ndtlist) + return inst_unsupported; + + dentry = &p->dtlist[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; + } + + return inst_ok; +} + /* Setup the default display type */ static inst_code set_default_disp_type(spyd2 *p) { inst_code ev; @@ -3661,35 +3926,56 @@ static inst_code set_default_disp_type(spyd2 *p) { return inst_ok; } -/* Set the display type */ -static inst_code spyd2_set_disptype(inst *pp, int ix) { - spyd2 *p = (spyd2 *)pp; +/* Setup the display type to the given base type */ +static inst_code set_base_disp_type(spyd2 *p, int cbid) { inst_code ev; - inst_disptypesel *dentry; - - if (!p->gotcoms) - return inst_no_coms; - if (!p->inited) - return inst_no_init; + int i; + if (cbid == 0) { + a1loge(p->log, 1, "spyd2 set_base_disp_type: can't set base display type of 0\n"); + return inst_wrong_setup; + } if (p->dtlist == NULL) { - if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, p->_dtlist, p->hwver >= 7 ? 1 : 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) return ev; } - if (ix < 0 || ix >= p->ndtlist) - return inst_unsupported; - - dentry = &p->dtlist[ix]; - - if ((ev = set_disp_type(p, dentry)) != inst_ok) { + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (!(p->dtlist[i].flags & inst_dtflags_ccmx) /* Prevent infinite recursion */ + && p->dtlist[i].cbid == cbid) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid); + return inst_wrong_setup; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { return ev; } return inst_ok; } +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code spyd2_get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + spyd2 *p = (spyd2 *)pp; + if (dtech != NULL) + *dtech = p->dtech; + if (refrmode != NULL) + *refrmode = p->refrmode; + if (cbid != NULL) + *cbid = p->cbid; + return inst_ok; +} + /* * set or reset an optional mode * @@ -3715,24 +4001,6 @@ spyd2_get_set_opt(inst *pp, inst_opt_type m, ...) { if (!p->inited) return inst_no_init; - /* Get the display type information */ - if (m == inst_opt_get_dtinfo) { - va_list args; - int *refrmode, *cbid; - - va_start(args, m); - refrmode = va_arg(args, int *); - cbid = va_arg(args, int *); - va_end(args); - - if (refrmode != NULL) - *refrmode = p->refrmode; - if (cbid != NULL) - *cbid = p->cbid; - - return inst_ok; - } - /* Set the ccss observer type */ if (m == inst_opt_set_ccss_obs) { va_list args; @@ -3860,6 +4128,7 @@ extern spyd2 *new_spyd2(icoms *icom, instType itype) { p->set_mode = spyd2_set_mode; p->get_disptypesel = spyd2_get_disptypesel; p->set_disptype = spyd2_set_disptype; + p->get_disptechi = spyd2_get_disptechi; p->get_set_opt = spyd2_get_set_opt; p->read_sample = spyd2_read_sample; p->read_refrate = spyd2_read_refrate; @@ -3876,7 +4145,8 @@ extern spyd2 *new_spyd2(icoms *icom, instType itype) { p->itype = icom->itype; /* Load manufacturers Spyder4 calibrations */ - if (itype == instSpyder4) { + if (itype == instSpyder4 + || itype == instSpyder5) { int rv; p->hwver = 7; /* Set preliminary version */ if ((rv = spyd4_load_cal(p)) != SPYD2_OK) @@ -3887,13 +4157,105 @@ extern spyd2 *new_spyd2(icoms *icom, instType itype) { if (itype == instSpyder3) { p->hwver = 4; /* Set preliminary version */ } - if (itype == instSpyder2) { + if (itype == instSpyder1 // ???? + || itype == instSpyder2) { p->hwver = 3; /* Set preliminary version */ } icmSetUnity3x3(p->ccmat); /* Set the colorimeter correction matrix to do nothing */ set_base_disptype_list(p); + p->dtech = disptech_unknown; return p; } + +/* This is called by utilities that need to be able to access the Spyder 2 colorimeter. */ +/* and be able to check if the firmware is available. */ + +/* id = 0 for Spyder 1, 1 for Spyder 2 */ +/* Return 0 if Spyder firmware is not available */ +/* Return 1 if Spyder firmware is available */ +int setup_spyd2(int id) { +#ifdef ENABLE_USB + char **bin_paths = NULL; + int no_paths = 0; + unsigned int size, rsize; + char *p1; + FILE *fp; + int i; + + id &= 1; + + /* If not loaded, try and load it */ + if (spyder_pld_size[id] == 0) { + + + for (;;) { /* So we can break out */ + if (id == 0) + p1 = "ArgyllCMS/spyd1PLD.bin" XDG_FUDGE "color/spyd1PLD.bin"; + else + p1 = "ArgyllCMS/spyd2PLD.bin" XDG_FUDGE "color/spyd2PLD.bin"; + + if ((no_paths = xdg_bds(NULL, &bin_paths, xdg_data, xdg_read, xdg_user, p1)) < 1) { + a1logd(g_log, 1, "setup_spyd2: failed to find PLD file on path '%s'\n",p1); + break; + } + + /* open binary file */ +#if !defined(O_CREAT) && !defined(_O_CREAT) +# error "Need to #include fcntl.h!" +#endif +#if defined(O_BINARY) || defined(_O_BINARY) + if ((fp = fopen(bin_paths[0],"rb")) == NULL) +#else + if ((fp = fopen(bin_paths[0],"r")) == NULL) +#endif + { + a1logd(g_log, 1, "setup_spyd2: couldn't find '%s'\n",bin_paths[0]); + break; + } + + /* Figure out how file it is */ + if (fseek(fp, 0, SEEK_END)) { + fclose(fp); + break; + } + size = (unsigned long)ftell(fp); + rsize = (size + 7) & ~7; /* Rounded up size */ + + if ((spyder_pld_bytes[id] = malloc(rsize)) == NULL) { + a1logd(g_log,1,"Spyder pld load malloc failed\n"); + fclose(fp); + break; + } + + if (fseek(fp, 0, SEEK_SET)) { + fclose(fp); + break; + } + + if (fread(spyder_pld_bytes[id], 1, size, fp) != size) { + fclose(fp); + break; + } + + /* Pad out to even 8 bytes */ + for (i = size; i < rsize; i++) + spyder_pld_bytes[id][i] = 0xff; + + spyder_pld_size[id] = rsize; + + a1logd(g_log, 1, "setup_spyd2: loaded '%s' OK\n",bin_paths[0]); + + fclose(fp); + break; + } + xdg_free(bin_paths, no_paths); + } + + if (spyder_pld_size[id] != 0) + return 1; /* Available */ +#endif /* ENABLE_USB */ + return 0; /* Not available */ +} diff --git a/spectro/spyd2.h b/spectro/spyd2.h index 2c34d88..923234f 100644 --- a/spectro/spyd2.h +++ b/spectro/spyd2.h @@ -74,6 +74,9 @@ /* Configuration */ #define SPYD2_DISP_SEL_RANGE 0x40 /* Calibration selection is out of range */ +/* User errors */ +#define SPYD2_NO_REFRESH_DET 0x50 /* Calibration selection is out of range */ + /* SPYD2/3 communication object */ struct _spyd2 { INST_OBJ_BASE @@ -94,6 +97,7 @@ struct _spyd2 { /* Spyder2 = 3 */ /* Spyder3 = 4 */ /* Spyder4 = 7 */ + /* Spyder5 = 10 */ unsigned int fbits; /* 6:B Feature bits 0,1,2,3 correspond to calibration types */ /* CRT/UNK, LCD/NORM, TOK, CRT/UNK */ @@ -131,7 +135,7 @@ struct _spyd2 { /* This might be Y only weightings for the 7 sensor values, */ /* with no offset value (TOK type ?). */ - /* hwver 7 (Spyder 4) uses computed calibrations */ + /* hwver 7 & 10 (Spyder 4/5) uses computed calibrations */ xspect sens[7]; /* Sensor sensitivity curves in Hz per mW/nm/m^2 */ /* Computed factors and state */ @@ -140,9 +144,11 @@ struct _spyd2 { int ndtlist; /* Number of valid dtlist entries */ int refrmode; /* 0 for constant, 1 for refresh display */ - int cbid; /* calibration base ID, 0 if not a base */ + int cbid; /* current calibration base ID, 0 if not a base */ + int ucbid; /* Underlying base ID if being used for matrix, 0 othewise */ int icx; /* Bit 0: Cal table index, 0 = CRT, 1 = LCD/normal */ /* Bits 31-1: Spyder 4 spectral cal index, 0..spyd4_nocals-1 */ + disptech dtech; /* Display technology enum */ int rrset; /* Flag, nz if the refresh rate has been determined */ double refrate; /* Current refresh rate. Set to DEFREFR if not measurable */ int refrvalid; /* nz if refrate was measured */ @@ -165,6 +171,11 @@ struct _spyd2 { /* Constructor */ extern spyd2 *new_spyd2(icoms *icom, instType itype); +/* PLD pattern loader */ +/* id = 0 for Spyder 1, 1 for Spyder 2 */ +/* Return 0 if Spyder firmware is not available */ +/* Return 1 if Spyder firmware is available */ +extern int setup_spyd2(int id); #define SPYD2_H #endif /* SPYD2_H */ diff --git a/spectro/spyd2PLD.h b/spectro/spyd2PLD.h deleted file mode 100644 index 19d2a48..0000000 --- a/spectro/spyd2PLD.h +++ /dev/null @@ -1,10 +0,0 @@ - -/* Spyder 2 Colorimeter Xilinx XCS05XL firmware pattern */ -/* needs to be transfered here for the instrument to work. */ - -/* The end user should see the oeminst utility.*/ - -static unsigned int pld_size = 0x11223344; /* Endian indicator */ -static unsigned int pld_space = 6824; -static unsigned char pld_bytes[6824] = "XCS05XL firmware pattern"; /* Magic number */ - diff --git a/spectro/spyd2setup.h b/spectro/spyd2setup.h deleted file mode 100644 index 60375dd..0000000 --- a/spectro/spyd2setup.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef SPYD2SETUP_H - -/* - * Argyll Color Correction System - * - * ColorVision Spyder 2 related software. - * - * Author: Graeme W. Gill - * Date: 19/10/2006 - * - * Copyright 2006 - 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. - */ - -/* This file is only included in top utilities that need to */ -/* be able to access the Spyder 2 colorimeter. This provides */ -/* a mechanism for ensuring that only such utilities load the */ -/* proprietary Spyder firmware, as well as providing a means to */ -/* detect if the spyder driver is going to be funcional. */ - -#ifdef __cplusplus -extern "C" { -#endif - -extern unsigned int *spyder2_pld_size; /* in spyd2.c */ -extern unsigned char *spyder2_pld_bytes; - -#ifdef __cplusplus -} -#endif - -/* Return 0 if Spyder 2 firmware is not available */ -/* Return 1 if Spyder 2 firmware is available from an external file */ -/* Return 2 if Spyder 2 firmware is part of this executable */ -int setup_spyd2() { -#ifdef ENABLE_USB - static int loaded = 0; /* Was loaded from a file */ - char **bin_paths = NULL; - int no_paths = 0; - unsigned int size, rsize; - FILE *fp; - int i; - - /* Spyder 2 Colorimeter Xilinx XCS05XL firmware pattern. */ - /* This is a placeholder in the distributed files. */ - /* It could be replaced with the actual end users firmware */ - /* by using the spyd2trans utility, but normally the spyd2PLD.bin */ - /* file is loaded instead. */ - -#include "spyd2PLD.h" - - spyder2_pld_size = &pld_size; - spyder2_pld_bytes = pld_bytes; - - /* If no firmware compiled in, see if there is a file to load from. */ - if ((pld_size == 0 || pld_size == 0x11223344) && loaded == 0) { - - - for (;;) { /* So we can break out */ - if ((no_paths = xdg_bds(NULL, &bin_paths, xdg_data, xdg_read, xdg_user, - "ArgyllCMS/spyd2PLD.bin" XDG_FUDGE "color/spyd2PLD.bin" -)) < 1) { - a1logd(g_log, 1, "setup_spyd2: failed to find PLD file\n"); - break; - } - - /* open binary file */ -#if !defined(O_CREAT) && !defined(_O_CREAT) -# error "Need to #include fcntl.h!" -#endif -#if defined(O_BINARY) || defined(_O_BINARY) - if ((fp = fopen(bin_paths[0],"rb")) == NULL) -#else - if ((fp = fopen(bin_paths[0],"r")) == NULL) -#endif - break; - xdg_free(bin_paths, no_paths); - - /* Figure out how file it is */ - if (fseek(fp, 0, SEEK_END)) { - fclose(fp); - break; - } - size = (unsigned long)ftell(fp); - - if (size > pld_space) - size = pld_space; - - if (fseek(fp, 0, SEEK_SET)) { - fclose(fp); - break; - } - - if (fread(pld_bytes, 1, size, fp) != size) { - fclose(fp); - break; - } - pld_size = size; - loaded = 1; /* We've loaded it from a file */ -// a1logd(g_log,0,"Spyder2 pld bytes = 0x%x 0x%x 0x%x 0x%x\n",pld_bytes[0], pld_bytes[1], pld_bytes[2], pld_bytes[3]); - fclose(fp); - break; - } - } - - if (pld_size != 0 && pld_size != 0x11223344) { - if (loaded) - return 1; /* Was loaded from a file */ - return 2; /* Was compiled in */ - } -#endif /* ENABLE_USB */ - return 0; /* Not available */ -} - -#define SPYD2SETUP_H -#endif /* SPYD2SETUP_H */ diff --git a/spectro/ss.c b/spectro/ss.c index c72e189..e01dd7a 100644 --- a/spectro/ss.c +++ b/spectro/ss.c @@ -1777,7 +1777,6 @@ ss_interp_error(inst *pp, int ec) { /* Our own communication errors here too. */ case ss_et_SerialFail: return "Serial communications failure"; - case ss_et_SendBufferFull: return "Message send buffer is full"; case ss_et_RecBufferEmpty: diff --git a/spectro/ss_imp.c b/spectro/ss_imp.c index 359d4c4..6f184b3 100644 --- a/spectro/ss_imp.c +++ b/spectro/ss_imp.c @@ -438,7 +438,6 @@ inst_code ss_inst_err(ss *p) { case ss_et_FilterOutOfPos: case ss_et_ProgrammingError: return inst_hardware_fail | ec; - } return inst_other_error | ec; } @@ -512,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, p->_rbuf, SS_MAX_RD_SIZE, "\n", 1, tmo)) != 0) { + if ((se = p->icom->write_read(p->icom, p->_sbuf, 0, p->_rbuf, SS_MAX_RD_SIZE, NULL, "\n", 1, tmo)) != 0) { p->snerr = icoms2ss_err(se); return; } diff --git a/spectro/strange.cal b/spectro/strange.cal new file mode 100644 index 0000000..ee5bf74 --- /dev/null +++ b/spectro/strange.cal @@ -0,0 +1,275 @@ +CAL + +DESCRIPTOR "Argyll Device Calibration Curves" +ORIGINATOR "Argyll synthcal" +CREATED "Tue Apr 28 15:22:30 2015" +KEYWORD "DEVICE_CLASS" +DEVICE_CLASS "DISPLAY" +KEYWORD "COLOR_REP" +COLOR_REP "RGB" + +KEYWORD "RGB_I" +NUMBER_OF_FIELDS 4 +BEGIN_DATA_FORMAT +RGB_I RGB_R RGB_G RGB_B +END_DATA_FORMAT + +NUMBER_OF_SETS 256 +BEGIN_DATA +0.00000 0.00000 0.00000 0.00000 +3.92157e-003 5.67518e-005 0.0118787 0.0186065 +7.84314e-003 1.84387e-004 0.0206820 0.0302263 +0.0117647 3.67355e-004 0.0286065 0.0401466 +0.0156863 5.99076e-004 0.0360094 0.0491028 +0.0196078 8.75445e-004 0.0430471 0.0574042 +0.0235294 1.19354e-003 0.0498068 0.0652184 +0.0274510 1.55112e-003 0.0563438 0.0726496 +0.0313725 1.94640e-003 0.0626960 0.0797678 +0.0352941 2.37789e-003 0.0688909 0.0866232 +0.0392157 2.84433e-003 0.0749493 0.0932533 +0.0431373 3.34462e-003 0.0808876 0.0996872 +0.0470588 3.87782e-003 0.0867187 0.105948 +0.0509804 4.44307e-003 0.0924533 0.112053 +0.0549020 5.03962e-003 0.0981003 0.118020 +0.0588235 5.66676e-003 0.103667 0.123859 +0.0627451 6.32388e-003 0.109160 0.129583 +0.0666667 7.01040e-003 0.114585 0.135201 +0.0705882 7.72579e-003 0.119946 0.140720 +0.0745098 8.46956e-003 0.125248 0.146148 +0.0784314 9.24125e-003 0.130494 0.151490 +0.0823529 0.0100404 0.135689 0.156754 +0.0862745 0.0108667 0.140834 0.161942 +0.0901961 0.0117197 0.145932 0.167061 +0.0941176 0.0125991 0.150986 0.172112 +0.0980392 0.0135045 0.155998 0.177102 +0.101961 0.0144356 0.160971 0.182031 +0.105882 0.0153921 0.165905 0.186904 +0.109804 0.0163738 0.170803 0.191723 +0.113725 0.0173803 0.175665 0.196491 +0.117647 0.0184114 0.180495 0.201210 +0.121569 0.0194668 0.185292 0.205882 +0.125490 0.0205464 0.190059 0.210508 +0.129412 0.0216498 0.194796 0.215092 +0.133333 0.0227769 0.199504 0.219634 +0.137255 0.0239274 0.204184 0.224136 +0.141176 0.0251012 0.208838 0.228600 +0.145098 0.0262980 0.213466 0.233027 +0.149020 0.0275177 0.218069 0.237418 +0.152941 0.0287600 0.222648 0.241774 +0.156863 0.0300249 0.227204 0.246097 +0.160784 0.0313121 0.231737 0.250388 +0.164706 0.0326215 0.236248 0.254647 +0.168627 0.0339528 0.240737 0.258876 +0.172549 0.0353061 0.245205 0.263076 +0.176471 0.0366810 0.249654 0.267247 +0.180392 0.0380775 0.254082 0.271391 +0.184314 0.0394954 0.258491 0.275507 +0.188235 0.0409345 0.262882 0.279597 +0.192157 0.0423948 0.267254 0.283662 +0.196078 0.0438762 0.271609 0.287702 +0.200000 0.0453784 0.275946 0.291718 +0.203922 0.0469014 0.280266 0.295710 +0.207843 0.0484450 0.284570 0.299680 +0.211765 0.0500091 0.288857 0.303627 +0.215686 0.0515937 0.293128 0.307552 +0.219608 0.0531985 0.297384 0.311455 +0.223529 0.0548235 0.301625 0.315338 +0.227451 0.0564686 0.305851 0.319201 +0.231373 0.0581337 0.310063 0.323043 +0.235294 0.0598187 0.314260 0.326866 +0.239216 0.0615234 0.318443 0.330670 +0.243137 0.0632478 0.322613 0.334456 +0.247059 0.0649918 0.326769 0.338223 +0.250980 0.0667553 0.330911 0.341972 +0.254902 0.0685382 0.335041 0.345703 +0.258824 0.0703403 0.339159 0.349418 +0.262745 0.0721617 0.343264 0.353115 +0.266667 0.0740022 0.347356 0.356797 +0.270588 0.0758618 0.351437 0.360461 +0.274510 0.0777403 0.355505 0.364110 +0.278431 0.0796377 0.359563 0.367744 +0.282353 0.0815539 0.363608 0.371362 +0.286275 0.0834889 0.367643 0.374965 +0.290196 0.0854424 0.371666 0.378553 +0.294118 0.0874146 0.375679 0.382127 +0.298039 0.0894052 0.379681 0.385686 +0.301961 0.0914143 0.383672 0.389231 +0.305882 0.0934417 0.387653 0.392763 +0.309804 0.0954873 0.391624 0.396281 +0.313725 0.0975512 0.395585 0.399786 +0.317647 0.0996332 0.399536 0.403277 +0.321569 0.101733 0.403477 0.406756 +0.325490 0.103851 0.407409 0.410222 +0.329412 0.105987 0.411331 0.413676 +0.333333 0.108141 0.415244 0.417117 +0.337255 0.110313 0.419147 0.420546 +0.341176 0.112503 0.423042 0.423963 +0.345098 0.114710 0.426927 0.427368 +0.349020 0.116935 0.430804 0.430762 +0.352941 0.119177 0.434672 0.434144 +0.356863 0.121437 0.438532 0.437515 +0.360784 0.123714 0.442383 0.440875 +0.364706 0.126009 0.446225 0.444224 +0.368627 0.128321 0.450060 0.447563 +0.372549 0.130650 0.453886 0.450890 +0.376471 0.132997 0.457704 0.454207 +0.380392 0.135360 0.461514 0.457514 +0.384314 0.137741 0.465317 0.460811 +0.388235 0.140139 0.469111 0.464097 +0.392157 0.142554 0.472898 0.467374 +0.396078 0.144986 0.476678 0.470641 +0.400000 0.147435 0.480450 0.473898 +0.403922 0.149900 0.484214 0.477145 +0.407843 0.152383 0.487972 0.480383 +0.411765 0.154882 0.491722 0.483612 +0.415686 0.157398 0.495465 0.486831 +0.419608 0.159931 0.499200 0.490042 +0.423529 0.162480 0.502929 0.493243 +0.427451 0.165046 0.506651 0.496436 +0.431373 0.167628 0.510366 0.499619 +0.435294 0.170227 0.514075 0.502794 +0.439216 0.172842 0.517776 0.505961 +0.443137 0.175474 0.521472 0.509119 +0.447059 0.178122 0.525160 0.512269 +0.450980 0.180787 0.528842 0.515410 +0.454902 0.183467 0.532518 0.518543 +0.458824 0.186164 0.536187 0.521668 +0.462745 0.188877 0.539850 0.524785 +0.466667 0.191606 0.543507 0.527895 +0.470588 0.194351 0.547158 0.530996 +0.474510 0.197113 0.550803 0.534090 +0.478431 0.199890 0.554441 0.537176 +0.482353 0.202684 0.558074 0.540254 +0.486275 0.205493 0.561701 0.543325 +0.490196 0.208318 0.565322 0.546388 +0.494118 0.211159 0.568937 0.549444 +0.498039 0.214016 0.572547 0.552493 +0.501961 0.216889 0.576150 0.555535 +0.505882 0.219777 0.579748 0.558569 +0.509804 0.222681 0.583341 0.561597 +0.513725 0.225601 0.586928 0.564617 +0.517647 0.228536 0.590510 0.567631 +0.521569 0.231487 0.594086 0.570638 +0.525490 0.234454 0.597657 0.573638 +0.529412 0.237436 0.601222 0.576631 +0.533333 0.240434 0.604782 0.579618 +0.537255 0.243447 0.608337 0.582598 +0.541176 0.246476 0.611887 0.585571 +0.545098 0.249520 0.615431 0.588538 +0.549020 0.252579 0.618971 0.591499 +0.552941 0.255654 0.622505 0.594453 +0.556863 0.258744 0.626035 0.597401 +0.560784 0.261849 0.629559 0.600343 +0.564706 0.264970 0.633079 0.603279 +0.568627 0.268105 0.636594 0.606208 +0.572549 0.271256 0.640103 0.609132 +0.576471 0.274422 0.643608 0.612049 +0.580392 0.277603 0.647109 0.614961 +0.584314 0.280800 0.650604 0.617867 +0.588235 0.284011 0.654095 0.620766 +0.592157 0.287237 0.657581 0.623661 +0.596078 0.290478 0.661063 0.626549 +0.600000 0.293735 0.664540 0.629431 +0.603922 0.297006 0.668012 0.632308 +0.607843 0.300292 0.671480 0.635180 +0.611765 0.303593 0.674944 0.638045 +0.615686 0.306909 0.678403 0.640906 +0.619608 0.310239 0.681857 0.643761 +0.623529 0.313585 0.685308 0.646610 +0.627451 0.316945 0.688754 0.649454 +0.631373 0.320320 0.692195 0.652293 +0.635294 0.323709 0.695633 0.655126 +0.639216 0.327114 0.699066 0.657954 +0.643137 0.330533 0.702495 0.660777 +0.647059 0.333966 0.705919 0.663595 +0.650980 0.337414 0.709340 0.666408 +0.654902 0.340877 0.712756 0.669215 +0.658824 0.344354 0.716169 0.672018 +0.662745 0.347846 0.719577 0.674816 +0.666667 0.351352 0.722981 0.677608 +0.670588 0.354873 0.726381 0.680396 +0.674510 0.358408 0.729778 0.683179 +0.678431 0.361958 0.733170 0.685957 +0.682353 0.365522 0.736559 0.688730 +0.686275 0.369100 0.739943 0.691498 +0.690196 0.372693 0.743324 0.694262 +0.694118 0.376300 0.746701 0.697021 +0.698039 0.379921 0.750074 0.699775 +0.701961 0.383557 0.753443 0.702525 +0.705882 0.387207 0.756808 0.705270 +0.709804 0.390871 0.760170 0.708010 +0.713725 0.394549 0.763528 0.710746 +0.717647 0.398242 0.766882 0.713477 +0.721569 0.401948 0.770233 0.716204 +0.725490 0.405669 0.773580 0.718927 +0.729412 0.409404 0.776923 0.721645 +0.733333 0.413153 0.780263 0.724358 +0.737255 0.416916 0.783599 0.727068 +0.741176 0.420693 0.786932 0.729773 +0.745098 0.424484 0.790261 0.732473 +0.749020 0.428289 0.793587 0.735170 +0.752941 0.432108 0.796909 0.737862 +0.756863 0.435940 0.800228 0.740550 +0.760784 0.439787 0.803543 0.743234 +0.764706 0.443648 0.806855 0.745914 +0.768627 0.447523 0.810164 0.748589 +0.772549 0.451411 0.813469 0.751261 +0.776471 0.455314 0.816770 0.753928 +0.780392 0.459230 0.820069 0.756592 +0.784314 0.463160 0.823364 0.759251 +0.788235 0.467103 0.826656 0.761906 +0.792157 0.471061 0.829944 0.764558 +0.796078 0.475032 0.833230 0.767205 +0.800000 0.479017 0.836512 0.769849 +0.803922 0.483016 0.839790 0.772489 +0.807843 0.487028 0.843066 0.775124 +0.811765 0.491054 0.846339 0.777756 +0.815686 0.495094 0.849608 0.780385 +0.819608 0.499147 0.852874 0.783009 +0.823529 0.503214 0.856137 0.785630 +0.827451 0.507294 0.859397 0.788247 +0.831373 0.511388 0.862654 0.790860 +0.835294 0.515496 0.865908 0.793469 +0.839216 0.519617 0.869158 0.796075 +0.843137 0.523751 0.872406 0.798677 +0.847059 0.527899 0.875651 0.801276 +0.850980 0.532061 0.878892 0.803871 +0.854902 0.536236 0.882131 0.806462 +0.858824 0.540424 0.885367 0.809050 +0.862745 0.544626 0.888599 0.811634 +0.866667 0.548841 0.891829 0.814215 +0.870588 0.553070 0.895056 0.816792 +0.874510 0.557311 0.898280 0.819366 +0.878431 0.561567 0.901501 0.821936 +0.882353 0.565835 0.904719 0.824503 +0.886275 0.570117 0.907935 0.827066 +0.890196 0.574412 0.911147 0.829626 +0.894118 0.578721 0.914357 0.832183 +0.898039 0.583042 0.917564 0.834736 +0.901961 0.587377 0.920768 0.837286 +0.905882 0.591725 0.923969 0.839833 +0.909804 0.596087 0.927168 0.842376 +0.913725 0.600461 0.930363 0.844916 +0.917647 0.604849 0.933556 0.847453 +0.921569 0.609249 0.936747 0.849986 +0.925490 0.613663 0.939934 0.852516 +0.929412 0.618090 0.943119 0.855044 +0.933333 0.622530 0.946301 0.857567 +0.937255 0.626984 0.949481 0.860088 +0.941176 0.631450 0.952658 0.862606 +0.945098 0.635929 0.955832 0.865120 +0.949020 0.640421 0.959003 0.867631 +0.952941 0.644927 0.962172 0.870139 +0.956863 0.649445 0.965339 0.872644 +0.960784 0.653976 0.968502 0.875146 +0.964706 0.658521 0.971664 0.877645 +0.968627 0.663078 0.974822 0.880141 +0.972549 0.667648 0.977978 0.882634 +0.976471 0.672231 0.981132 0.885124 +0.980392 0.676827 0.984283 0.887610 +0.984314 0.681436 0.987431 0.890094 +0.988235 0.686058 0.990577 0.892575 +0.992157 0.690692 0.993721 0.895053 +0.996078 0.695340 0.996862 0.897528 +1.00000 0.700000 1.00000 0.900000 +END_DATA diff --git a/spectro/usbio.c b/spectro/usbio.c index f8a0529..508b357 100644 --- a/spectro/usbio.c +++ b/spectro/usbio.c @@ -51,14 +51,14 @@ int in_usb_rw = 0; void usb_init_cancel(usb_cancelt *p) { amutex_init(p->cmtx); - amutex_init(p->cond); + amutex_init(p->condx); p->hcancel = NULL; } void usb_uninit_cancel(usb_cancelt *p) { amutex_del(p->cmtx); - amutex_del(p->cond); + amutex_del(p->condx); } /* Used by caller of icoms to re-init for wait_io */ @@ -69,7 +69,7 @@ void usb_reinit_cancel(usb_cancelt *p) { p->hcancel = NULL; p->state = 0; - amutex_lock(p->cond); /* Block until IO is started */ + amutex_lock(p->condx); /* Block until IO is started */ amutex_unlock(p->cmtx); } @@ -79,8 +79,8 @@ static int icoms_usb_wait_io( icoms *p, usb_cancelt *cancelt ) { - amutex_lock(cancelt->cond); /* Wait for unlock */ - amutex_unlock(cancelt->cond); /* Free it up for next time */ + amutex_lock(cancelt->condx); /* Wait for unlock */ + amutex_unlock(cancelt->condx); /* Free it up for next time */ return ICOM_OK; } @@ -94,7 +94,7 @@ static int icoms_usb_wait_io( # include "usbio_ox.c" # endif # if defined(UNIX_X11) -# if defined(__FreeBSD__) +# if defined(__FreeBSD__) || defined(__OpenBSD__) # include "usbio_bsd.c" # else # include "usbio_lx.c" @@ -281,10 +281,19 @@ void (*usbio_term)(int sig) = SIG_DFL; /* On something killing our process, deal with USB cleanup */ static void icoms_sighandler(int arg) { + static amutex_static(lock); + a1logd(g_log, 6, "icoms_sighandler: invoked with arg = %d\n",arg); + + /* Make sure we don't re-enter */ + if (amutex_trylock(lock)) { + return; + } + if (in_usb_rw != 0) in_usb_rw = -1; icoms_cleanup(); + /* Call the existing handlers */ #ifdef UNIX if (arg == SIGHUP && usbio_hup != SIG_DFL && usbio_hup != SIG_IGN) @@ -296,6 +305,8 @@ static void icoms_sighandler(int arg) { usbio_term(arg); a1logd(g_log, 6, "icoms_sighandler: calling exit()\n"); + + amutex_unlock(lock); exit(0); } @@ -353,7 +364,8 @@ void usb_delete_from_cleanup_list(icoms *p) { static int icoms_usb_ser_write( icoms *p, -char *wbuf, +char *wbuf, /* null terminated unless nch > 0 */ +int nwch, /* if > 0, number of characters to write */ double tout) { int len, wbytes; @@ -385,7 +397,10 @@ double tout) else type = icom_usb_trantype_interrutpt; - len = strlen(wbuf); + if (nwch != 0) + len = nwch; + else + len = strlen(wbuf); tout *= 1000.0; /* Timout in msec */ top = (int)(tout + 0.5); /* Timeout period in msecs */ @@ -424,14 +439,15 @@ double tout) /* Read characters into the buffer */ /* Return string will be terminated with a nul */ /* Read only in packet sized chunks, and retry if */ -/* the bytes requested aren'r read, untill we get a */ +/* the bytes requested aren't read, untill we get a */ /* timeout or a terminating char is read */ static int icoms_usb_ser_read(icoms *p, char *rbuf, /* Buffer to store characters read */ int bsize, /* Buffer size */ -char *tc, /* Terminating characers, NULL if none */ -int ntc, /* Number of terminating characters needed to terminate */ +int *pbread, /* Bytes read (not including forced '\000') */ +char *tc, /* Terminating characers, NULL for none or char count mode */ +int ntc, /* Number of terminating characters or char count needed, if 0 use bsize */ double tout) /* Time out in seconds */ { int j, rbytes; @@ -441,10 +457,8 @@ double tout) /* Time out in seconds */ int ep = p->rd_ep; /* End point */ icom_usb_trantype type; /* bulk or interrupt */ int retrv = ICOM_OK; - -#ifdef QUIET_MEMCHECKERS - memset(rbuf, 0, bsize); -#endif + int nreads; /* Number of reads performed */ + int fastserial = 0; /* If fast serial type */ if (!p->is_open) { a1loge(p->log, ICOM_SYS, "icoms_usb_ser_read: device is not open\n"); @@ -472,40 +486,62 @@ double tout) /* Time out in seconds */ return ICOM_SYS; } - for (j = 0; j < bsize; j++) rbuf[j] = 0; - - bsize -= 1; /* Allow space for null */ - bsize -= p->ms_bytes; /* Allow space for modem status bytes */ + if (p->port_type(p) == icomt_usbserial) + fastserial = 1; + for (j = 0; j < bsize; j++) + rbuf[j] = 0; + /* The DTP94 doesn't cope with a timeout on OS X, so we need to avoid */ /* them by giving each read the largest timeout period possible. */ /* This also reduces the problem of libusb 0.1 not returning the */ - /* number of characters read on a timeou. */ + /* number of characters read on a timeout. */ ttop = (int)(tout * 1000.0 + 0.5); /* Total timeout period in msecs */ - a1logd(p->log, 8, "\nicoms_usb_ser_read: ep 0x%x, ttop %d, quant %d\n", p->rd_ep, ttop, p->rd_qa); + a1logd(p->log, 8, "\nicoms_usb_ser_read: ep 0x%x, bytes %d, ttop %d, ntc %d, quant %d\n", p->rd_ep, bsize, ttop, ntc, p->rd_qa); + + bsize -= 1; /* Allow space for null */ + bsize -= p->ms_bytes; /* Allow space for modem status bytes */ /* Until data is all read, we time out, or the user aborts */ - stime = msec_time(); + etime = stime = msec_time(); top = ttop; - for (j = 0; top > 0 && bsize > 1 && j < ntc ;) { + j = (tc == NULL && ntc <= 0) ? -1 : 0; + + for (nreads = 0; top > 0 && bsize > 0 && j < ntc ;) { int c, rv; - int rsize = p->rd_qa < bsize ? p->rd_qa : bsize; + int rsize = bsize; - a1logd(p->log, 8, "icoms_usb_ser_read: attempting to read %d bytes from usb, top = %d, j = %d\n",bsize > p->rd_qa ? p->rd_qa : bsize,top,j); + /* If not a fast USB serial port, read in quanta size chunks */ + if (!fastserial && rsize > p->rd_qa) + rsize = p->rd_qa; + + a1logd(p->log, 8, "icoms_usb_ser_read: attempting to read %d bytes from usb, top = %d, j = %d\n",rsize,top,j); rv = icoms_usb_transaction(p, NULL, &rbytes, type, (unsigned char)ep, (unsigned char *)rbuf, rsize, top); etime = msec_time(); + nreads++; if (rbytes > 0) { /* Account for bytes read */ - /* Account for modem status bytes. Modem bytes are per usb read. */ + /* Account for modem status bytes. Modem bytes are per usb read, */ + /* or every p->rd_qa bytes. */ if (p->ms_bytes) { /* Throw away modem bytes */ - int nb = rbytes < p->ms_bytes ? rbytes : p->ms_bytes; - rbytes -= nb; - memmove(rbuf, rbuf+nb, rbytes); - a1logd(p->log, 8, "icoms_usb_ser_read: discarded %d modem bytes\n",nb); + char *bp = rbuf; + int rb = rbytes; + for (; rb > 0; ) { + int nb = rb < p->ms_bytes ? rb : p->ms_bytes; /* Bytes to shift */ + if (p->interp_ms != NULL && nb >= p->ms_bytes) + retrv |= p->interp_ms(p, (unsigned char *)bp); /* Deal with error flags in ms bytes */ + a1logd(p->log, 8, "icoms_usb_ser_read: discarded %d modem bytes 0x%02x 0x%02x\n",nb,nb >= 1 ? (bp[0] & 0xff) : 0, nb >= 2 ? (bp[1] & 0xff) : 0); + rb -= nb; + rbytes -= nb; + memmove(bp, bp+nb, rb); + bp += p->rd_qa - p->ms_bytes; + rb -= p->rd_qa - p->ms_bytes; + } + rbuf[rbytes] = 0; } a1logd(p->log, 8, "icoms_usb_ser_read: read %d bytes, rbuf = '%s'\n",rbytes,icoms_fix(rrbuf)); @@ -521,7 +557,10 @@ double tout) /* Time out in seconds */ tcp++; } } + a1logd(p->log, 8, "icoms_usb_ser_read: tc count %d\n",j); } else { + if (ntc > 0) + j += rbytes; rbuf += rbytes; } } @@ -534,14 +573,20 @@ double tout) /* Time out in seconds */ } top = ttop - (etime - stime); /* Remaining time */ - if (top <= 0) { /* Run out of time */ - a1logd(p->log, 8, "icoms_usb_ser_read: read ran out of time\n"); - retrv |= ICOM_TO; - break; - } } *rbuf = '\000'; + a1logd(p->log, 8, "icoms_usb_ser_read: read %d total bytes with %d reads\n",rbuf - rrbuf, nreads); + if (pbread != NULL) + *pbread = (rbuf - rrbuf); + + /* If ran out of time and not completed */ + a1logd(p->log, 8, "icoms_usb_ser_read: took %d msec\n",etime - stime); + if (top <= 0 && bsize > 0 && j < ntc) { + a1logd(p->log, 8, "icoms_usb_ser_read: read ran out of time\n"); + a1logd(p->log, 8, "ttop %d, etime - stime %d\n",ttop,etime - stime); + retrv |= ICOM_TO; + } a1logd(p->log, 8, "icoms_usb_ser_read: returning '%s' ICOM err 0x%x\n",icoms_fix(rrbuf),retrv); diff --git a/spectro/usbio.h b/spectro/usbio.h index 1181801..578bd83 100644 --- a/spectro/usbio.h +++ b/spectro/usbio.h @@ -185,7 +185,7 @@ typedef enum { struct _usb_cancelt { amutex cmtx; int state; /* 0 = init, 1 = pending, 2 = complete */ - amutex cond; /* Wait for state 0->1 sync. mutex */ + amutex condx; /* Wait for state 0->1 sync. mutex */ void *hcancel; /* Pointer to implementation cancel handle */ }; diff --git a/spectro/usbio_bsd.c b/spectro/usbio_bsd.c index 7649caf..0af13a1 100644 --- a/spectro/usbio_bsd.c +++ b/spectro/usbio_bsd.c @@ -15,9 +15,14 @@ * see the License2.txt file for licencing details. */ +#pragma message("!!!!!! usbio_bsd.c is INCOMPLETE and USB instruments will NOT WORK !!!!!!") + /* !!!! This driver is incomplete and non-functional !!!! + ( Most of the below code is stubbed out, with the Linux + code as a placeholder. ) + BSD uses fd per end point, so simplifies things. No clear ep or abort i/o though, so we could try clear halt, @@ -504,7 +509,7 @@ static int icoms_usb_transaction( bp += req.urbs[i].urb.buffer_length; req.urbs[i].urb.status = -EINPROGRESS; } -a1logd(p->log, 8, "icoms_usb_transaction: reset req 0x%p nourbs to %d\n",&req,req.nourbs); +a1logd(p->log, 8, "icoms_usb_transaction: reset req %p nourbs to %d\n",&req,req.nourbs); /* Add our request to the req list so that it can be cancelled on reap failure */ pthread_mutex_lock(&p->usbd->lock); diff --git a/spectro/usbio_lx.c b/spectro/usbio_lx.c index 44307fd..7cc37e6 100644 --- a/spectro/usbio_lx.c +++ b/spectro/usbio_lx.c @@ -460,8 +460,11 @@ char **pnames /* List of process names to try and kill before opening */ if ((rv = p->usbd->fd = open(p->usbd->dpath, O_RDWR)) < 0) { a1logd(p->log, 8, "usb_open_port: open '%s' config %d failed (%d) (Permissions ?)\n",p->usbd->dpath,config,rv); if (retries <= 0) { - if (kpc != NULL) + if (kpc != NULL) { + if (kpc->th->result < 0) + a1logw(p->log, "usb_open_port: killing competing processes failed\n"); kpc->del(kpc); + } a1loge(p->log, ICOM_SYS, "usb_open_port: open '%s' config %d failed (%d) (Permissions ?)\n",p->usbd->dpath,config,rv); return ICOM_SYS; } @@ -636,25 +639,53 @@ static void *urb_reaper(void *context) { pa[0].events = POLLIN | POLLOUT; pa[0].revents = 0; + /* Setup to wait for a shutdown signal via the sd_pipe */ pa[1].fd = p->usbd->sd_pipe[0]; pa[1].events = POLLIN; pa[1].revents = 0; - /* Wait for fd to become ready or shutdown */ - if ((rv = poll_x(pa, 2, -1)) < 0 || pa[1].revents || pa[0].revents == 0) { - a1logd(p->log, 6, "urb_reaper: poll returned %d and events %d %d\n",rv,pa[0].revents,pa[1].revents); + /* Wait for fd to become ready or fail */ + rv = poll_x(pa, 2, -1); + + /* Failed */ + if (rv < 0) { + a1logd(p->log, 2, "urb_reaper: poll failed with %d\n",rv); + if (errc++ < 5) { + continue; + } + a1logd(p->log, 2, "urb_reaper: poll failed too many times - shutting down\n"); p->usbd->shutdown = 1; break; } + /* Shutdown event */ + if (pa[1].revents != 0) { + a1logd(p->log, 6, "urb_reaper: poll returned events %d %d - shutting down\n", + pa[0].revents,pa[1].revents); + p->usbd->shutdown = 1; + break; + } + + /* Hmm. poll returned without event from fd. */ + if (pa[0].revents == 0) { + a1logd(p->log, 6, "urb_reaper: poll returned events %d %d - ignoring\n", + pa[0].revents,pa[1].revents); + continue; + } + /* Not sure what this returns if there is nothing there */ rv = ioctl(p->usbd->fd, USBDEVFS_REAPURBNDELAY, &out); + if (rv == EAGAIN) { + a1logd(p->log, 2, "urb_reaper: reap returned EAGAIN - ignored\n"); + continue; + } if (rv < 0) { a1logd(p->log, 2, "urb_reaper: reap failed with %d\n",rv); if (errc++ < 5) { continue; } + a1logd(p->log, 2, "urb_reaper: reap failed too many times - shutting down\n"); p->usbd->shutdown = 1; break; } @@ -662,7 +693,7 @@ static void *urb_reaper(void *context) { errc = 0; if (out == NULL) { - a1logd(p->log, 2, "urb_reaper: reap returned NULL URB\n"); + a1logd(p->log, 2, "urb_reaper: reap returned NULL URB - ignored\n"); continue; } @@ -710,7 +741,7 @@ static void *urb_reaper(void *context) { pthread_mutex_lock(&req->lock); for (i = req->nourbs-1; i >= 0; i--) { - req->urbs[i].urb.status = ICOM_SYS; + req->urbs[i].urb.status = -ENOMSG; /* Use ENOMSG as error marker */ } req->nourbs = 0; pthread_cond_signal(&req->cond); @@ -803,7 +834,7 @@ static int icoms_usb_transaction( bp += req.urbs[i].urb.buffer_length; req.urbs[i].urb.status = -EINPROGRESS; } -a1logd(p->log, 8, "icoms_usb_transaction: reset req 0x%p nourbs to %d\n",&req,req.nourbs); +a1logd(p->log, 8, "icoms_usb_transaction: reset req %p nourbs to %d\n",&req,req.nourbs); /* Add our request to the req list so that it can be cancelled on reap failure */ pthread_mutex_lock(&p->usbd->lock); @@ -815,7 +846,7 @@ a1logd(p->log, 8, "icoms_usb_transaction: reset req 0x%p nourbs to %d\n",&req,re for (i = 0; i < req.nurbs; i++) { if ((rv = ioctl(p->usbd->fd, USBDEVFS_SUBMITURB, &req.urbs[i].urb)) < 0) { a1logd(p->log, 1, "coms_usb_transaction: Submitting urb to fd %d failed with %d\n",p->usbd->fd, rv); - req.urbs[i].urb.status = ICOM_SYS; /* Mark it as failed to submit */ + req.urbs[i].urb.status = -ENOMSG; /* Mark it as failed to submit */ req.nourbs--; } } @@ -824,7 +855,7 @@ a1logd(p->log, 8, "icoms_usb_transaction: reset req 0x%p nourbs to %d\n",&req,re amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)&req; cancelt->state = 1; - amutex_unlock(cancelt->cond); /* Signal any thread waiting for IO start */ + amutex_unlock(cancelt->condx); /* Signal any thread waiting for IO start */ amutex_unlock(cancelt->cmtx); } @@ -887,7 +918,7 @@ a1logd(p->log, 8, "icoms_usb_transaction: reset req 0x%p nourbs to %d\n",&req,re int stat = req.urbs[i].urb.status; xlength += req.urbs[i].urb.actual_length; - if (stat == ICOM_SYS) { /* Submit or cancel failed */ + if (stat == -ENOMSG) { /* Submit or cancel failed */ reqrv = ICOM_SYS; } else if (reqrv == ICOM_OK && stat < 0 && stat != -ECONNRESET) { /* Error result */ if ((endpoint & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT) @@ -919,7 +950,7 @@ done:; amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)NULL; if (cancelt->state == 0) - amutex_unlock(cancelt->cond); + amutex_unlock(cancelt->condx); cancelt->state = 2; amutex_unlock(cancelt->cmtx); } diff --git a/spectro/usbio_nt.c b/spectro/usbio_nt.c index de9b265..8eb791f 100644 --- a/spectro/usbio_nt.c +++ b/spectro/usbio_nt.c @@ -409,6 +409,7 @@ void usb_close_port(icoms *p) { &req, sizeof(libusb_request), NULL, 0, NULL)) != ICOM_OK) { a1logd(p->log, 1, "usb_close_port: reset returned %d\n",rv); } + msec_sleep(500); /* Things foul up unless we wait for the reset... */ } CloseHandle(p->usbd->handle); @@ -466,9 +467,12 @@ char **pnames /* List of process names to try and kill before opening */ FILE_FLAG_OVERLAPPED, NULL)) == INVALID_HANDLE_VALUE) { a1logd(p->log, 8, "usb_open_port: open '%s' config %d failed (%d) (Device being used ?)\n",p->usbd->dpath,config,GetLastError()); if (retries <= 0) { - if (kpc != NULL) + if (kpc != NULL) { + if (kpc->th->result < 0) + a1logw(p->log, "usb_open_port: killing competing processes failed\n"); kpc->del(kpc); - a1loge(p->log, ICOM_SYS, "usb_open_port: open '%s' config %d failed (%d) (Device being used ?)\n",p->usbd->dpath,config,GetLastError()); + } + a1logw(p->log, "usb_open_port: open '%s' config %d failed (%d) (Device being used ?)\n",p->usbd->dpath,config,GetLastError()); return ICOM_SYS; } continue; @@ -614,7 +618,7 @@ static int icoms_usb_transaction( amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)&endpoint; cancelt->state = 1; - amutex_unlock(cancelt->cond); /* Signal any thread waiting for IO start */ + amutex_unlock(cancelt->condx); /* Signal any thread waiting for IO start */ amutex_unlock(cancelt->cmtx); } @@ -643,7 +647,7 @@ done:; amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)NULL; if (cancelt->state == 0) - amutex_unlock(cancelt->cond); /* Make sure this gets unlocked */ + amutex_unlock(cancelt->condx); /* Make sure this gets unlocked */ cancelt->state = 2; amutex_unlock(cancelt->cmtx); } diff --git a/spectro/usbio_ox.c b/spectro/usbio_ox.c index c92aed5..312c355 100644 --- a/spectro/usbio_ox.c +++ b/spectro/usbio_ox.c @@ -244,16 +244,6 @@ static void cleanup_device(icoms *p) { if (p->usbd->device != NULL) { /* device is open */ int i; - /* Stop the RunLoop and wait for it */ - if (p->usbd->cfrunloop != NULL) { - - a1logd(p->log, 6, "usb_close_port: waiting for RunLoop thread to exit\n"); - CFRunLoopStop(p->usbd->cfrunloop); - CFRelease(p->usbd->cfrunloop); - if (p->usbd->thread != NULL) - pthread_join(p->usbd->thread, NULL); - } - /* Release all the interfaces */ for (i = 0; i < p->usbd->nifce; i++) { if (p->usbd->interfaces[i] != NULL) { @@ -266,9 +256,6 @@ static void cleanup_device(icoms *p) { (*(p->usbd->device))->USBDeviceClose(p->usbd->device); (*(p->usbd->device))->Release(p->usbd->device); } - pthread_cond_destroy(&p->usbd->cond); - pthread_mutex_destroy(&p->usbd->lock); - memset(p->usbd, 0, sizeof(struct usb_idevice)); } } @@ -336,9 +323,6 @@ char **pnames /* List of process names to try and kill before opening */ return ICOM_NOTS; } - pthread_mutex_init(&p->usbd->lock, NULL); - pthread_cond_init(&p->usbd->cond, NULL); - /* Do open retries */ for (tries = 0; retries >= 0; retries--, tries++) { IOCFPlugInInterface **piif = NULL; @@ -353,6 +337,7 @@ char **pnames /* List of process names to try and kill before opening */ if (tries > 0 && pnames != NULL && kpc == NULL) { if ((kpc = kkill_nprocess(pnames, p->log)) == NULL) { a1logd(p->log, 1, "kkill_nprocess returned error!\n"); + return ICOM_SYS; } } @@ -377,8 +362,11 @@ char **pnames /* List of process names to try and kill before opening */ if ((rv = (*p->usbd->device)->USBDeviceOpenSeize(p->usbd->device)) != kIOReturnSuccess) { a1logd(p->log, 8, "usb_open_port: open '%s' config %d failed (0x%x) (Device being used ?)\n",p->name,config,rv); if (retries <= 0) { - if (kpc != NULL) + if (kpc != NULL) { + if (kpc->th->result < 0) + a1logw(p->log, "usb_open_port: killing competing processes failed\n"); kpc->del(kpc); + } a1loge(p->log, rv, "usb_open_port: open '%s' config %d failed (0x%x) (Device being used ?)\n",p->name, config, rv); (*p->usbd->device)->Release(p->usbd->device); return ICOM_SYS; @@ -508,28 +496,6 @@ char **pnames /* List of process names to try and kill before opening */ IOObjectRelease(ioit); } - { - /* Setup the RunLoop thread */ - a1logd(p->log, 6, "usb_open_port: Starting RunLoop thread\n"); - if ((rv = pthread_create(&p->usbd->thread, NULL, io_runloop, (void*)p)) < 0) { - a1loge(p->log, ICOM_SYS, "usb_open_port: creating RunLoop thread failed with %s\n",rv); - cleanup_device(p); - return ICOM_SYS; - } - - /* Wait for the runloop thread to start and create a cfrunloop */ - pthread_mutex_lock(&p->usbd->lock); - while (p->usbd->cfrunloop == NULL) - pthread_cond_wait(&p->usbd->cond, &p->usbd->lock); - pthread_mutex_unlock(&p->usbd->lock); - if (p->usbd->thrv != kIOReturnSuccess) { /* Thread failed */ - pthread_join(p->usbd->thread, NULL); - cleanup_device(p); - return ICOM_SYS; - } - CFRetain(p->usbd->cfrunloop); - a1logd(p->log, 6, "usb_open_port: RunLoop thread started\n"); - } /* Clear any errors */ /* (Some I/F seem to hang if we do this, some seem to hang if we don't !) */ @@ -562,70 +528,6 @@ char **pnames /* List of process names to try and kill before opening */ /* -------------------------------------------------------------- */ -/* The run loop thread */ -static void *io_runloop(void *context) { - icoms *p = (icoms *)context; - int i; - - /* Register this thread with the Objective-C garbage collector */ -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 - objc_registerThreadWithCollector(); -#endif - - a1logd(p->log, 6, "io_runloop: thread started\n"); - - p->usbd->cfrunloop = CFRunLoopGetCurrent(); /* Get this threads RunLoop */ - CFRetain(p->usbd->cfrunloop); - - /* Add a device event source */ - if ((p->usbd->thrv = (*(p->usbd->device))->CreateDeviceAsyncEventSource( - p->usbd->device, &p->usbd->cfsource)) != kIOReturnSuccess) { - a1loge(p->log, p->usbd->thrv, "io_runloop: CreateDeviceAsyncEventSource failed with 0x%x\n",p->usbd->thrv); - } else { - CFRunLoopAddSource(p->usbd->cfrunloop, p->usbd->cfsource, kCFRunLoopDefaultMode); - } - - /* Create an async event source for the interfaces */ - for (i = 0; p->usbd->thrv == kIOReturnSuccess && i < p->usbd->nifce; i++) { - if ((p->usbd->thrv = (*(p->usbd->interfaces[i]))->CreateInterfaceAsyncEventSource( - p->usbd->interfaces[i], &p->usbd->cfsources[i])) != kIOReturnSuccess) { - a1loge(p->log, p->usbd->thrv, "io_runloop: CreateInterfaceAsyncEventSource failed with 0x%x\n",p->usbd->thrv); - } else { - /* Add it to the RunLoop */ - CFRunLoopAddSource(p->usbd->cfrunloop, p->usbd->cfsources[i], kCFRunLoopDefaultMode); - } - } - - /* Signal main thread that we've started */ - pthread_mutex_lock(&p->usbd->lock); - pthread_cond_signal(&p->usbd->cond); - pthread_mutex_unlock(&p->usbd->lock); - - /* Run the loop, or exit on error */ - if (p->usbd->thrv == kIOReturnSuccess) { - CFRunLoopRun(); /* Run the loop and deliver events */ - } - - /* Delete the interfaces async event sources */ - for (i = 0; i < p->usbd->nifce; i++) { - if (p->usbd->cfsources[i] != NULL) { - CFRunLoopRemoveSource(p->usbd->cfrunloop, p->usbd->cfsources[i], kCFRunLoopDefaultMode); - CFRelease(p->usbd->cfsources[i]); - } - } - - /* Delete the devices event sources */ - if (p->usbd->cfsource != NULL) { - CFRunLoopRemoveSource(p->usbd->cfrunloop, p->usbd->cfsource, kCFRunLoopDefaultMode); - CFRelease(p->usbd->cfsource); - } - - CFRelease(p->usbd->cfrunloop); - - a1logd(p->log, 6, "io_runloop: thread done\n"); - return NULL; -} - /* I/O structures */ typedef struct _usbio_req { @@ -633,14 +535,12 @@ typedef struct _usbio_req { int iix; /* Interface index */ UInt8 pno; /* pipe index */ volatile int done; /* Done flag */ - pthread_mutex_t lock; /* Protect req & callback access */ - pthread_cond_t cond; /* Signal to thread waiting on req */ int xlength; /* Bytes transferred */ IOReturn result; /* Result of transaction */ + CFRunLoopRef rlr; /* RunLoop of calling thread */ } usbio_req; - -/* Async completion callback - called by RunLoop thread */ +/* Async completion callback - called by RunLoop */ static void io_callback(void *refcon, IOReturn result, void *arg0) { usbio_req *req = (usbio_req *)refcon; @@ -649,9 +549,8 @@ static void io_callback(void *refcon, IOReturn result, void *arg0) { req->xlength = (int)(long)arg0; req->result = result; req->done = 1; - pthread_mutex_lock(&req->lock); - pthread_cond_signal(&req->cond); - pthread_mutex_unlock(&req->lock); + + CFRunLoopStop(req->rlr); /* We're done */ } /* Our universal USB transfer function */ @@ -669,6 +568,7 @@ static int icoms_usb_transaction( int dirw = (endpoint & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT ? 1 : 0; usbio_req req; IOReturn result; + CFRunLoopSourceRef cfsource; /* Device event sources */ int iix = p->EPINFO(endpoint).interface; UInt8 pno = (UInt8)p->EPINFO(endpoint).pipe; @@ -687,16 +587,30 @@ static int icoms_usb_transaction( req.pno = pno; req.xlength = 0; req.done = 0; - pthread_mutex_init(&req.lock, NULL); - pthread_cond_init(&req.cond, NULL); + + req.rlr = CFRunLoopGetCurrent(); /* Get this threads RunLoop */ + CFRetain(req.rlr); + + /* Create an async event source for the interface */ + if ((result = (*(p->usbd->interfaces[iix]))->CreateInterfaceAsyncEventSource( + p->usbd->interfaces[iix], &cfsource)) != kIOReturnSuccess) { + a1logw(p->log, "icoms_usb_transaction: CreateInterfaceAsyncEventSource failed with 0x%x\n",result); + CFRelease(req.rlr); + return ICOM_SYS; + } + CFRetain(cfsource); + + /* Add it to the RunLoop */ + CFRunLoopAddSource(req.rlr, cfsource, kCFRunLoopDefaultMode); if (dirw) result = (*p->usbd->interfaces[iix])->WritePipeAsync(p->usbd->interfaces[iix], pno, buffer, length, io_callback, &req); - else + else result = (*p->usbd->interfaces[iix])->ReadPipeAsync(p->usbd->interfaces[iix], pno, buffer, length, io_callback, &req); + if (result != kIOReturnSuccess) { a1loge(p->log, ICOM_SYS, "icoms_usb_transaction: %sPipeAsync failed with 0x%x\n",dirw ? "Write" : "Read", result); reqrv = ICOM_SYS; @@ -707,66 +621,46 @@ static int icoms_usb_transaction( amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)&req; cancelt->state = 1; - amutex_unlock(cancelt->cond); /* Signal any thread waiting for IO start */ + amutex_unlock(cancelt->condx); /* Signal any thread waiting for IO start */ amutex_unlock(cancelt->cmtx); } - /* Wait for the callback to complete */ - pthread_mutex_lock(&req.lock); - if (!req.done) { - struct timeval tv; - struct timespec ts; - - // this is unduly complicated... - gettimeofday(&tv, NULL); - ts.tv_sec = tv.tv_sec + timeout/1000; - ts.tv_nsec = (tv.tv_usec + (timeout % 1000) * 1000) * 1000L; - if (ts.tv_nsec > 1000000000L) { - ts.tv_nsec -= 1000000000L; - ts.tv_sec++; - } - - for(;;) { /* Ignore spurious wakeups */ - if ((rv = pthread_cond_timedwait(&req.cond, &req.lock, &ts)) != 0) { - if (rv != ETIMEDOUT) { - a1logd(p->log, 1, "coms_usb_transaction: pthread_cond_timedwait failed with %d\n",rv); - (*p->usbd->interfaces[iix])->AbortPipe(p->usbd->interfaces[iix], pno); - req.result = kIOReturnAborted; - reqrv = ICOM_SYS; - break; - } - - /* Timed out */ - a1logd(p->log, 8, "coms_usb_transaction: time out - aborting io\n"); - (*p->usbd->interfaces[iix])->AbortPipe(p->usbd->interfaces[iix], pno); - reqrv = ICOM_TO; - /* Wait for the cancelled io to be signalled */ - if ((rv = pthread_cond_wait(&req.cond, &req.lock)) != 0) { - pthread_mutex_unlock(&req.lock); - a1logd(p->log, 1, "coms_usb_transaction: pthread_cond_wait failed with %d\n",rv); - req.result = kIOReturnAborted; - reqrv = ICOM_SYS; - break; - } - break; - } - if (req.done) /* Ignore spurious wakeups */ - break; - } + /* Wait for the callback to complete or for a timeout */ + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout / 1000.0, false); + + // kCFRunLoopRunFinished = 1 + // kCFRunLoopRunStopped = 2 + // kCFRunLoopRunTimedOut = 3 + // kCFRunLoopRunHandledSource = 4 + + if (result == kCFRunLoopRunTimedOut) { + /* Timed out - so abort i/o */ + a1logd(p->log, 8, "coms_usb_transaction: time out - aborting io\n"); + (*p->usbd->interfaces[iix])->AbortPipe(p->usbd->interfaces[iix], pno); + CFRunLoopRun(); /* Wait for abort */ + reqrv = ICOM_TO; + } else if (result != kCFRunLoopRunStopped) { + a1logd(p->log, 8, "coms_usb_transaction: unexpected result 0x%x\n",result); + reqrv = ICOM_USBR; } - pthread_mutex_unlock(&req.lock); + + done:; + CFRunLoopRemoveSource(req.rlr, cfsource, kCFRunLoopDefaultMode); + CFRelease(cfsource); + CFRelease(req.rlr); a1logd(p->log, 8, "coms_usb_transaction: completed with reqrv 0x%x and xlength %d\n",req.result,req.xlength); /* If io was aborted, ClearPipeStall */ if (req.result == kIOReturnAborted) { + a1logd(p->log, 8, "coms_usb_transaction: io was aborted\n"); #if (InterfaceVersion > 182) (*p->usbd->interfaces[iix])->ClearPipeStallBothEnds(p->usbd->interfaces[iix], pno); #else (*p->usbd->interfaces[iix])->ClearPipeStall(p->usbd->interfaces[iix], pno); - icoms_usb_control_msg(p, NULL, IUSB_REQ_HOST_TO_DEV | IUSB_REQ_TYPE_STANDARD - | IUSB_REQ_RECIP_ENDPOINT, IUSB_REQ_CLEAR_FEATURE, - IUSB_FEATURE_EP_HALT, endpoint, NULL, 0, 200); +// icoms_usb_control_msg(p, NULL, IUSB_REQ_HOST_TO_DEV | IUSB_REQ_TYPE_STANDARD +// | IUSB_REQ_RECIP_ENDPOINT, IUSB_REQ_CLEAR_FEATURE, +// IUSB_FEATURE_EP_HALT, endpoint, NULL, 0, 200); #endif if (reqrv == ICOM_OK) /* If not aborted for a known reason, must be cancelled */ reqrv = ICOM_CANC; @@ -788,19 +682,15 @@ static int icoms_usb_transaction( if (transferred != NULL) *transferred = req.xlength; -done:; if (cancelt != NULL) { amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)NULL; if (cancelt->state == 0) - amutex_unlock(cancelt->cond); + amutex_unlock(cancelt->condx); cancelt->state = 2; amutex_unlock(cancelt->cmtx); } - pthread_cond_destroy(&req.cond); - pthread_mutex_destroy(&req.lock); - if (in_usb_rw < 0) exit(0); diff --git a/spectro/webwin.c b/spectro/webwin.c index 1de010a..e00fd9b 100644 --- a/spectro/webwin.c +++ b/spectro/webwin.c @@ -24,7 +24,7 @@ # include <ifaddrs.h> # include <netinet/in.h> # include <arpa/inet.h> -# ifdef __FreeBSD__ +# if defined(__FreeBSD__) || defined(__OpenBSD__) # include <sys/socket.h> # endif /* __FreeBSD__ */ #endif @@ -220,8 +220,7 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ int j; double orgb[3]; /* Previous RGB value */ double kr, kf; - int update_delay = p->update_delay; - double xdelay = 0.0; /* Extra delay for response time */ + int update_delay = 0; debugr("webwin_set_color called\n"); @@ -256,60 +255,18 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ msec_sleep(50); } - /* Don't want extra delay if we're measuring update delay */ - if (update_delay != 0 && p->do_resp_time_del) { - /* Compute am expected response time for the change in level */ - kr = DISPLAY_RISE_TIME/log(1 - 0.9); /* Exponent constant */ - kf = DISPLAY_FALL_TIME/log(1 - 0.9); /* Exponent constant */ -//printf("~1 k2 = %f\n",k2); - for (j = 0; j < 3; j++) { - double el, dl, n, t; - - el = pow(p->rgb[j], 2.2); - dl = el - pow(orgb[j], 2.2); /* Change in level */ - if (fabs(dl) > 0.01) { /* More than 1% change in level */ - n = DISPLAY_SETTLE_AIM * el; - if (n < DISPLAY_ABS_AIM) - n = DISPLAY_ABS_AIM; -//printf("~1 sl %f, el %f, log (%f / %f)\n",sl,el,n,fabs(sl - el)); - if (dl > 0.0) - t = kr * log(n/dl); - else - t = kf * log(n/-dl); - - if (t > xdelay) - xdelay = t; - } - } -//printf("~1 xdelay = %f secs\n",xdelay); - xdelay *= 1000.0; /* To msec */ - /* This is kind of a fudge since update delay is after latency, */ - /* but displays with long delay (ie. CRT) have short latency, and visa versa */ - if ((int)xdelay > update_delay) - update_delay = (int)xdelay; - } - - /* Allow some time for the display to update before */ - /* a measurement can take place. This allows for CRT */ - /* refresh, or LCD processing/update time, + */ - /* display settling time (quite long for smaller LCD changes). */ + /* Allow for display update & instrument delays */ + update_delay = dispwin_compute_delay(p, orgb); + debugr2((errout, "webwin_set_color delaying %d msec\n",update_delay)); msec_sleep(update_delay); return 0; } -/* ----------------------------------------------- */ -/* Set an update delay, and return the previous value */ -/* Value can be set to zero, but othewise will be forced */ -/* to be >= min_update_delay */ -static int webwin_set_update_delay( -dispwin *p, -int update_delay) { - int cval = p->update_delay; - p->update_delay = update_delay; - if (update_delay != 0 && p->update_delay < p->min_update_delay) - p->update_delay = p->min_update_delay; - return cval; +/* Set/unset the blackground color flag */ +/* Return nz on error */ +static int webwin_set_bg(dispwin *p, int blackbg) { + return 1; /* Setting black BG not supported */ } /* ----------------------------------------------- */ @@ -371,6 +328,7 @@ int ddebug /* >0 to print debug statements to stderr */ struct mg_context *mg; const char *options[3]; char port[50]; + char *url; debug("new_webwin called\n"); @@ -381,20 +339,25 @@ int ddebug /* >0 to print debug statements to stderr */ /* !!!! Make changes in dispwin.c & madvrwin.c as well !!!! */ p->name = strdup("Web Window"); + p->width = width; + p->height = height; p->nowin = nowin; p->native = native; p->out_tvenc = out_tvenc; p->blackbg = blackbg; p->ddebug = ddebug; - p->get_ramdac = webwin_get_ramdac; - p->set_ramdac = webwin_set_ramdac; - p->install_profile = webwin_install_profile; - p->uninstall_profile = webwin_uninstall_profile; - p->get_profile = webwin_get_profile; - p->set_color = webwin_set_color; - p->set_update_delay = webwin_set_update_delay; - p->set_callout = webwin_set_callout; - p->del = webwin_del; + p->get_ramdac = webwin_get_ramdac; + p->set_ramdac = webwin_set_ramdac; + p->install_profile = webwin_install_profile; + p->uninstall_profile = webwin_uninstall_profile; + p->get_profile = webwin_get_profile; + p->set_color = webwin_set_color; + p->set_bg = webwin_set_bg; + p->set_update_delay = dispwin_set_update_delay; + p->set_settling_delay = dispwin_set_settling_delay; + p->enable_update_delay = dispwin_enable_update_delay; + p->set_callout = webwin_set_callout; + p->del = webwin_del; if (noramdac != NULL) *noramdac = 1; @@ -405,21 +368,8 @@ int ddebug /* >0 to print debug statements to stderr */ p->native &= ~2; p->rgb[0] = p->rgb[1] = p->rgb[2] = 0.5; /* Set Grey as the initial test color */ - - p->min_update_delay = 20; - - if ((cp = getenv("ARGYLL_MIN_DISPLAY_UPDATE_DELAY_MS")) != NULL) { - p->min_update_delay = atoi(cp); - if (p->min_update_delay < 20) - p->min_update_delay = 20; - if (p->min_update_delay > 60000) - p->min_update_delay = 60000; - debugr2((errout, "new_webwin: Minimum display update delay set to %d msec\n",p->min_update_delay)); - } - - p->update_delay = DISPLAY_UPDATE_DELAY; /* Default update delay */ - if (p->update_delay < p->min_update_delay) - p->update_delay = p->min_update_delay; + + dispwin_set_default_delays(p); p->ncix = 1; @@ -438,74 +388,19 @@ int ddebug /* >0 to print debug statements to stderr */ //printf("Domain = %s'\n",mg_get_option(mg, "authentication_domain")); - /* Create a suitable description */ -#if NT + /* Create a suitable description/url */ { - char szHostName[255]; - struct hostent *host_entry; - char *localIP; - char buf[1000]; + char buf[100], *url; - /* We assume WinSock has been started by mongoose */ - - // Get the local hostname - gethostname(szHostName, 255); - host_entry=gethostbyname(szHostName); - /* Get first entry */ - localIP = inet_ntoa(*(struct in_addr *)*host_entry->h_addr_list); - - sprintf(buf,"Web Window at http://%s:%d",localIP,webdisp); + if ((url = mg_get_url(mg)) == NULL) + error("Failed to get Web server URL"); + sprintf(buf,"Web Window at '%s'",url); p->description = strdup(buf); - if (verb) - printf("Created web server at 'http://%s:%d', now waiting for browser to connect\n",localIP,webdisp); - } -#else - { - struct ifaddrs * ifAddrStruct=NULL; - struct ifaddrs * ifa=NULL; - void *tmpAddrPtr=NULL; - char abuf[INET_ADDRSTRLEN] = ""; - char abuf6[INET6_ADDRSTRLEN] = ""; - char *addr = abuf; - char buf[1000]; - - getifaddrs(&ifAddrStruct); - - /* Stop at the first non local adderss */ - for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { -#ifdef AF_INET6 - if (ifa->ifa_addr->sa_family==AF_INET) { /* IP4 ? */ -#endif - if (strncmp(ifa->ifa_name, "lo",2) == 0 || abuf[0] != '\000') - continue; - tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - inet_ntop(AF_INET, tmpAddrPtr, abuf, INET_ADDRSTRLEN); -// printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); -#ifdef AF_INET6 - } else if (ifa->ifa_addr->sa_family==AF_INET6) { /* IP6 ? */ - if (strncmp(ifa->ifa_name, "lo",2) == 0 || abuf6[0] != '\000') - continue; - tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; - inet_ntop(AF_INET6, tmpAddrPtr, abuf6, INET6_ADDRSTRLEN); -// printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); - } -#endif - } - if (ifAddrStruct!=NULL) - freeifaddrs(ifAddrStruct); - if (addr[0] == '\000') - addr = abuf6; - if (addr[0] == '\000') - addr = "Unknown"; - - sprintf(buf,"Web Window at http://%s:%d",addr,webdisp); - p->description = strdup(buf); + printf("Created web server at '%s', now waiting for browser to connect\n",url); - if (verb) - printf("Created web server at 'http://%s:%d', now waiting for browser to connect\n",addr,webdisp); + free(url); } -#endif /* Wait for the web server to connect */ debugr("new_webwin: waiting for web browser to connect\n"); diff --git a/spectro/xdg_bds.c b/spectro/xdg_bds.c index 9d81291..c1805ed 100644 --- a/spectro/xdg_bds.c +++ b/spectro/xdg_bds.c @@ -49,7 +49,7 @@ $XDG_DATA_HOME $HOME/.local/share - $XDG_CONF_HOME + $XDG_CONFIG_HOME $HOME/.config $XDG_CACHE_HOME @@ -58,14 +58,14 @@ $XDG_DATA_DIRS /usr/local/share:/usr/share - $XDG_CONF_DIRS + $XDG_CONFIG_DIRS /etc/xdg OS X: $XDG_DATA_HOME $HOME/Library/Application Support - $XDG_CONF_HOME + $XDG_CONFIG_HOME $HOME/Library/Preferences $XDG_CACHE_HOME @@ -74,7 +74,7 @@ $XDG_DATA_DIRS /Library/Application Support - $XDG_CONF_DIRS + $XDG_CONFIG_DIRS /Library/Preferences MSWin: @@ -82,7 +82,7 @@ $APPDATA $HOME/.local/share - $XDG_CONF_HOME + $XDG_CONFIG_HOME $APPDATA $HOME/.config @@ -93,7 +93,7 @@ $XDG_DATA_DIRS $ALLUSERSPROFILE - $XDG_CONF_DIRS + $XDG_CONFIG_DIRS $ALLUSERSPROFILE */ @@ -119,6 +119,7 @@ #include "aglob.h" #include "xdg_bds.h" + #undef DEBUG #ifdef DEBUG @@ -345,7 +346,7 @@ int xdg_bds( } } else if (st == xdg_conf) { char *xdg, *home; - if ((xdg = getenv("XDG_CONF_HOME")) != NULL) { + if ((xdg = getenv("XDG_CONFIG_HOME")) != NULL) { if ((path = cappend(path, xdg)) == NULL) { if (er != NULL) *er = xdg_alloc; a1loge(g_log, 1, "xdg_bds: malloc failed\n"); @@ -489,7 +490,7 @@ int xdg_bds( } } } else if (st == xdg_conf) { - if ((xdg = getenv("XDG_CONF_DIRS")) != NULL) { + if ((xdg = getenv("XDG_CONFIG_DIRS")) != NULL) { if ((path = cappend(path, xdg)) == NULL) { if (er != NULL) *er = xdg_alloc; a1loge(g_log, 1, "xdg_bds: malloc failed\n"); @@ -744,9 +745,11 @@ int xdg_bds( if (getenv("SUDO_UID") != NULL && getenv("SUDO_GID") != NULL) { DBG((DBGA,"We're setting a local system dir/file with uid = 0 && euid != 0\n")) - setegid(getgid()); - seteuid(getuid()); - DBG((DBGA,"Set euid %d, egid %d\n",geteuid(),getegid())) + if (setegid(getgid()) || seteuid(getuid())) { + DBG((DBGA,"seteuid or setegid failed\n")) + } else { + DBG((DBGA,"Set euid %d, egid %d\n",geteuid(),getegid())) + } } } #endif /* !NT */ @@ -864,7 +867,7 @@ char *xdg_errstr(xdg_error er) { case xdg_nopath: return "There is no resulting path"; case xdg_mallformed: - return "Malfomed path fount"; + return "Malformed path fount"; default: return "unknown"; } @@ -1031,26 +1034,26 @@ main() { { xdg_data, xdg_user, {"ALLUSERSPROFILE", NULL}, { "XDG_DATA_HOME", "APPDATA", "HOME", "APPDATA", NULL } }, { xdg_conf, xdg_user, {"ALLUSERSPROFILE", NULL}, - { "XDG_CONF_HOME", "APPDATA", "HOME", "APPDATA", NULL } }, + { "XDG_CONFIG_HOME", "APPDATA", "HOME", "APPDATA", NULL } }, { xdg_cache, xdg_user, {NULL, NULL}, { "XDG_CACHE_HOME", "APPDATA", "HOME", "APPDATA", NULL } }, { xdg_data, xdg_local, {NULL, "HOME"}, { "XDG_DATA_DIRS", "ALLUSERSPROFILE", NULL } }, { xdg_conf, xdg_local, {NULL, "HOME"}, - { "XDG_CONF_DIRS", "ALLUSERSPROFILE", NULL } } + { "XDG_CONFIG_DIRS", "ALLUSERSPROFILE", NULL } } }; #else /* Apple, Unix, Default */ testcase cases[5] = { { xdg_data, xdg_user, {NULL, NULL}, { "XDG_DATA_HOME", "HOME", NULL } }, { xdg_conf, xdg_user, {NULL, NULL}, - { "XDG_CONF_HOME", "HOME", NULL } }, + { "XDG_CONFIG_HOME", "HOME", NULL } }, { xdg_cache, xdg_user, {NULL, NULL}, { "XDG_CACHE_HOME", "HOME", NULL } }, { xdg_data, xdg_local, {NULL, "HOME"}, { "XDG_DATA_DIRS", "", NULL } }, { xdg_conf, xdg_local, {NULL, "HOME"}, - { "XDG_CONF_DIRS", "", NULL } } + { "XDG_CONFIG_DIRS", "", NULL } } }; #endif |