diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-09-07 13:29:54 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-09-07 13:29:54 +0200 |
commit | a879b4e708b3e46c9697ba6581687eeb5b02a320 (patch) | |
tree | f85acc5cb1a7c0b03a050c879c20cab3deb9f40e /spectro | |
parent | 556dffcdad42b938bc196819aa463247de709765 (diff) | |
parent | c07d0c2d2f6f7b0eb6e92cc6204bf05037957e82 (diff) |
state from 2014-09-07 13:30 MESZ
Diffstat (limited to 'spectro')
76 files changed, 11217 insertions, 4205 deletions
diff --git a/spectro/Jamfile b/spectro/Jamfile index 87dee2f..b5ad548 100644 --- a/spectro/Jamfile +++ b/spectro/Jamfile @@ -10,13 +10,16 @@ if $(OS) = MACOSX { ObjectCcFlags dispwin_dispwin : -ObjC ; } +MADVRSOURCE = ; + # Setup the right hardware access libraries if $(NT) { - if $(USE_NATIVE_USB) = true { - DEFINES += NATIVE_USB ; - LIBUSBHDRS = ../usb/driver ; # libusb-win32 kernel driver info - } else { + MADVRSOURCE = madvrwin.c ; + + if $(USE_LIBUSB) = true { + DEFINES += USE_LIBUSB ; if $(USE_LIBUSB1) = true { + DEFINES += USE_LIBUSB1 ; LIBUSBDIR = ../libusb1 ; LIBUSBHDRS = ../libusb1 ; if $(MSVCNT) { @@ -28,19 +31,21 @@ if $(NT) { } else { LIBUSB = $(LIBUSB1NAME)$(SUFLIB) ; } - DEFINES += USE_LIBUSB1 ; } else { LIBUSBDIR = ../libusbw ; LIBUSBHDRS = ../libusbw ; LIBUSB = libusb ; } + } else { + LIBUSBHDRS = ../usb/driver ; # libusb-win32 kernel driver info } } + if $(UNIX) { - if $(USE_NATIVE_USB) = true { - DEFINES += NATIVE_USB ; - } else { + if $(USE_LIBUSB) = true { + DEFINES += USE_LIBUSB ; if $(USE_LIBUSB1) = true { + DEFINES += USE_LIBUSB1 ; LIBUSBDIR = ../libusb1 ; LIBUSBHDRS = ../libusb1 ; if $(LIBUSB_IS_DLL) = true { @@ -49,7 +54,6 @@ if $(UNIX) { } else { LIBUSB = $(LIBUSB1NAME)$(SUFLIB) ; } - DEFINES += USE_LIBUSB1 ; } else { LIBUSBDIR = ../libusb ; LIBUSBHDRS = ../libusb ; @@ -84,12 +88,14 @@ HDRS = ../h ../numlib ../icc ../cgats ../rspl ../xicc ../gamut ../spectro # Instrument access library library SER_INSTS = dtp22.c dtp41.c dtp51.c ss.c ss_imp.c ; -SER_USB_INSTS = dtp92.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 ; +FAST_SER_INSTS = specbos.c ; + +SER_USB_INSTS = dtp92.c ; + if $(USE_SERIAL) = true { DEFINES += ENABLE_SERIAL ; INST_SRCS += $(SER_INSTS) ; @@ -100,15 +106,26 @@ if $(USE_USB) = true { INST_SRCS += $(USB_INSTS) ; } +if $(USE_FAST_SERIAL) = true || $(USE_SERIAL) = true { + DEFINES += ENABLE_FAST_SERIAL ; + INST_SRCS += $(FAST_SER_INSTS) ; +} + if $(USE_SERIAL) = true || $(USE_USB) = true { INST_SRCS += $(SER_USB_INSTS) ; } +if $(USE_DEMOINST) = true && [ GLOB . : demoinst.c ] { + echo "Compiling demo instrument support" ; + DEFINES += ENABLE_DEMOINST ; + INST_SRCS += demoinst.c ; +} + Library libinst : inst.c insttypes.c icoms.c $(INST_SRCS) ; # Display access library ObjectKeep mongoose.c ; -Library libdisp : dispsup.c dispwin.c webwin.c : : : $(LibWinH) : mongoose ; +Library libdisp : dispsup.c dispwin.c webwin.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. */ @@ -173,7 +190,7 @@ Main dispread : dispread.c : : : : : libdisp ; #display test window test/Lut loader utility # [ Could avoid need for libisnt libusb etc. # by separating system dependent utils to a separate library .] -MainVariant dispwin : dispwin.c webwin.c : : STANDALONE_TEST : : mongoose : $(LibWin) ; +MainVariant dispwin : dispwin.c webwin.c $(MADVRSOURCE) : : STANDALONE_TEST : : mongoose : $(LibWin) ; LINKLIBS = libinsttypes ../xicc/libxicc ../gamut/libgamut ../rspl/librspl ../cgats/libcgats ../icc/libicc ../numlib/libnum ../plot/libplot diff --git a/spectro/Makefile.OSX b/spectro/Makefile.OSX index f98d078..485cbf4 100644 --- a/spectro/Makefile.OSX +++ b/spectro/Makefile.OSX @@ -26,10 +26,10 @@ LIBU = ar -r LIBOF = RANLIB = ranlib AS = as -CCFLAGSDEF = -DUNIX -c +CCFLAGSDEF = -DUNIX -Wno-sign-compare -fpascal-strings -c CC = cc $(CCFLAGS) $(STDHDRS) CCOF = -o -LINKFLAGSDEF = -lm -framework Carbon -framework IOKit -framework CoreFoundation -framework AudioToolbox +LINKFLAGSDEF = -lm -framework Carbon -framework Cocoa -framework IOKit -framework CoreFoundation -framework AudioToolbox -framework AppKit LINKLIBS = LINK = cc $(LINKFLAGS) $(LINKLIBS) LINKOF = -o diff --git a/spectro/Makefile.SA b/spectro/Makefile.SA index 4d253ec..b1fe55f 100644 --- a/spectro/Makefile.SA +++ b/spectro/Makefile.SA @@ -20,7 +20,7 @@ include Makefile.WNT ############################### -CCDEFINES = $(DEFFLAG)SALONEINSTLIB $(DEFFLAG)ENABLE_SERIAL $(DEFFLAG)ENABLE_USB $(DEFFLAG)NATIVE_USB +CCDEFINES = $(DEFFLAG)SALONEINSTLIB $(DEFFLAG)ENABLE_SERIAL $(DEFFLAG)ENABLE_FAST_SERIAL $(DEFFLAG)ENABLE_USB #Set optimisation on CCFLAGS = $(CCFLAGSDEF) $(CCOPTFLAG) $(CCDEFINES) $(BCONFIG) @@ -38,8 +38,8 @@ 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 -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) +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) 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 @@ -139,6 +139,9 @@ colorhug$(SUFOBJ): colorhug.c $(HEADERS) spyd2$(SUFOBJ): spyd2.c $(HEADERS) $(CC) spyd2.c +specbos$(SUFOBJ): specbos.c $(HEADERS) + $(CC) specbos.c + oemarch$(SUFOBJ): oemarch.c $(HEADERS) $(CC) oemarch.c @@ -168,7 +171,7 @@ instappsup$(SUFOBJ): instappsup.c $(HEADERS) libinstappsup$(SUFLIB): $(SUPOBJS) $(LIBU) $(LIBOF)$@ $(SUPOBJS) - $(RANLIB) instappsup$(SUFLIB) + $(RANLIB) libinstappsup$(SUFLIB) # test/example code diff --git a/spectro/Makefile.UNIX b/spectro/Makefile.UNIX index 5eaf6b5..adafa67 100644 --- a/spectro/Makefile.UNIX +++ b/spectro/Makefile.UNIX @@ -26,7 +26,7 @@ LIBU = ar -r LIBOF = RANLIB = echo AS = as -CCFLAGSDEF = -DUNIX -DNATIVE_USB -c +CCFLAGSDEF = -DUNIX -c CC = cc $(CCFLAGS) $(STDHDRS) CCOF = -o LINKFLAGSDEF = -lm -lpthread -lrt diff --git a/spectro/Makefile.am b/spectro/Makefile.am deleted file mode 100644 index 835336b..0000000 --- a/spectro/Makefile.am +++ /dev/null @@ -1,53 +0,0 @@ -include $(top_srcdir)/Makefile.shared - -privatelib_LTLIBRARIES = libinsttypes.la libconv.la libinst.la libinstapp.la libdisp.la -privatelibdir = $(pkglibdir) - -libinsttypes_la_SOURCES = insttypes.h insttypes.c insttypeinst.h -libinsttypes_la_LIBADD = ../libargyll.la - -libinst_la_SOURCES = inst.h inst.c insttypes.c dtp20.c dtp20.h dtp22.c \ - dtp22.h dtp41.c dtp41.h dtp51.c dtp51.h dtp92.c dtp92.h \ - i1disp.c i1disp.h i1pro.c i1pro.h i1pro_imp.c i1pro_imp.h \ - munki.c munki_imp.c ss.c ss.h ss_imp.c ss_imp.h hcfr.c hcfr.h \ - spyd2.c spyd2.h spyd2setup.h spyd2PLD.h huey.c huey.h \ - usbio.c hidio.c pollem.c pollem.h icoms.h conv.h usbio.h \ - hidio.h i1d3.h i1d3.c colorhug.c colorhug.h icoms.c \ - oemarch.h oemarch.c iusb.h vinflate.c inflate.c -libinst_la_LIBADD = $(ICC_LIBS) ../numlib/libargyllnum.la \ - ../libargyll.la ../rspl/librspl.la libconv.la - -libinst_la_LDFLAGS = $(shell libusb-config --libs) - -libdisp_la_SOURCES = dispsup.c dispwin.c dispwin.h dispsup.h webwin.c webwin.h mongoose.c mongoose.h -libdisp_la_LIBADD = $(X_LIBS) ../ucmm/libucmm.la $(ICC_LIBS) -ldl \ - ../numlib/libargyllnum.la libconv.la libinst.la libinstapp.la \ - ../libargyll.la - -libconv_la_SOURCES = conv.c pollem.c xdg_bds.h xdg_bds.c aglob.c ../xicc/ccss.c -libconv_la_LIBADD = ../libargyll.la ../numlib/libargyllnum.la \ - ../cgats/libcgats.la - -libinstapp_la_SOURCES = instappsup.c instappsup.h -libinstapp_la_LIBADD = libinst.la ../libargyll.la ../numlib/libargyllnum.la \ - libconv.la - -LDADD = ./libinsttypes.la ./libinstapp.la ./libdisp.la ./libinst.la \ - ./libconv.la ../ucmm/libucmm.la ../jcnf/libjcnf.la $(YAJL_LIBS) \ - ../xicc/libxicc.la $(ICC_LIBS) ../cgats/libcgats.la \ - ../rspl/librspl.la ../gamut/libgamut.la ../target/libtarget.la \ - ../plot/libplot.la ../numlib/libargyllnum.la $(X_LIBS) \ - ../libargyll.la - -bin_PROGRAMS = dispwin synthcal dispread dispcal fakeread synthread \ - chartread spotread illumread ccxxmake spec2cie average oeminst - -dispwin_CFLAGS = $(AM_CFLAGS) -DSTANDALONE_TEST - -synthcal_DEPENDENCIES = ../gamut/libgammap.la ../target/libtarget.la - -refdir = $(datadir)/color/argyll/ref - -ref_DATA = ccxx.ti1 SOtele.sp $(wildcard *.cal) - -EXTRA_DIST = Readme.txt diff --git a/spectro/afiles b/spectro/afiles index 045614c..e3fee3e 100644 --- a/spectro/afiles +++ b/spectro/afiles @@ -17,6 +17,8 @@ dispsup.h dispsup.c webwin.h webwin.c +madvrwin.h +madvrwin.c mongoose.h mongoose.c insttypes.h @@ -58,6 +60,8 @@ spyd2.c spyd2.h spyd2setup.h spyd2PLD.h +specbos.c +specbos.h oemarch.h oemarch.c oeminst.c @@ -85,10 +89,10 @@ icoms_ux.c iusb.h usbio.h usbio.c -usbio_lusb.c usbio_nt.c usbio_ox.c usbio_lx.c +usbio_bsd.c hidio.h hidio.c pollem.h diff --git a/spectro/average.c b/spectro/average.c index d031154..5e659b8 100644 --- a/spectro/average.c +++ b/spectro/average.c @@ -311,7 +311,7 @@ int main(int argc, char *argv[]) { /* 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[0].c, 0, i, setel); + inps[n].c->get_setarr(inps[n].c, 0, i, setel); ocg->add_setarr(ocg, 0, setel); } diff --git a/spectro/ccxxmake.c b/spectro/ccxxmake.c index 737600c..eff3afa 100644 --- a/spectro/ccxxmake.c +++ b/spectro/ccxxmake.c @@ -72,6 +72,9 @@ #include "inst.h" #include "dispwin.h" #include "webwin.h" +#ifdef NT +# include "madvrwin.h" +#endif #include "dispsup.h" #include "ccss.h" #include "ccmx.h" @@ -144,6 +147,9 @@ usage(char *diag, ...) { } free_disppaths(dp); fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); +#ifdef NT + 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"); cap = inst_show_disptype_options(stderr, " -y c|l ", icmps, 1); @@ -168,6 +174,7 @@ usage(char *diag, ...) { 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," correction.ccmx | calibration.ccss\n"); fprintf(stderr," File to save result to\n"); @@ -201,11 +208,15 @@ int main(int argc, char *argv[]) int highres = 0; /* High res mode if available */ int dtype = 0; /* Display kind, 0 = default, 1 = CRT, 2 = LCD */ 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 */ int nadaptive = 0; /* Use non-adaptive mode if available */ int tele = 0; /* NZ if telephoto mode */ int noinitcal = 0; /* Disable initial calibration */ int webdisp = 0; /* NZ for web display, == port number */ +#ifdef NT + int madvrdisp = 0; /* NZ for MadVR display */ +#endif char *ccallout = NULL; /* Change color Shell callout */ int msteps = DEFAULT_MSTEPS; /* Patch surface size */ int npat = 0; /* Number of patches/colors */ @@ -289,6 +300,12 @@ int main(int argc, char *argv[]) usage("Web port number must be in range 1..65535"); } fa = nfa; +#ifdef NT + } else if (strncmp(na,"madvr",5) == 0 + || strncmp(na,"MADVR",5) == 0) { + madvrdisp = 1; + fa = nfa; +#endif } else { #if defined(UNIX_X11) int ix, iv; @@ -474,15 +491,22 @@ int main(int argc, char *argv[]) if (na == NULL) usage("Flag '-Y' expects extra flag"); - if (na[0] == 'A') { - nadaptive = 1; - } else if (na[0] == 'r') { + if (na[0] == 'r') { refrmode = 1; } else if (na[0] == 'n') { refrmode = 0; + } else if (na[0] == 'R') { + if (na[1] != ':') + usage("-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); + } else if (na[0] == 'A') { + nadaptive = 1; } else { usage("Flag '-Z %c' not recognised",na[0]); } + fa = nfa; /* UI selection character */ } else if (argv[fa][1] == 'U') { @@ -911,9 +935,13 @@ int main(int argc, char *argv[]) /* No explicit display has been set */ if ( #ifndef SHOW_WINDOW_ONFAKE - !fake && + !fake && #endif - webdisp == 0 && disp == NULL) { + webdisp == 0 +#ifdef NT + && madvrdisp == 0 +#endif + && disp == NULL) { int ix = 0; #if defined(UNIX_X11) char *dn, *pp; @@ -1143,15 +1171,20 @@ int main(int argc, char *argv[]) inst3_capability cap3 = inst3_none; /* Instrument capabilities 3 */ if (fake) - comno = -99; + comno = FAKE_DEVICE_PORT; if (icmps == NULL) icmps = new_icompaths(g_log); /* Should we use current cal rather than native ??? */ if ((dr = new_disprd(&errc, icmps->get_path(icmps, comno), fc, dtype, 1, tele, nadaptive, - noinitcal, highres, 2, NULL, NULL, 0, 0, disp, blackbg, - override, webdisp, ccallout, NULL, + noinitcal, 0, highres, refrate, 3, NULL, NULL, + NULL, 0, disp, 0, blackbg, + override, webdisp, +#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) diff --git a/spectro/chartread.c b/spectro/chartread.c index 1c2117a..376fc1c 100644 --- a/spectro/chartread.c +++ b/spectro/chartread.c @@ -438,22 +438,6 @@ a1log *log /* verb, debug & error log */ printf("The battery charged level is %.0f%%\n",batstat * 100.0); } - /* Set it to the appropriate mode */ - if (highres) { - if (IMODETST(cap, inst_mode_highres)) { - inst_code ev; - if ((ev = it->get_set_opt(it, inst_opt_highres)) != inst_ok) { - printf("\nSetting high res mode failed with error :'%s' (%s)\n", - it->inst_interp_error(it, ev), it->interp_error(it, ev)); - it->del(it); - return -1; - } - highres = 1; - } else { - a1logv(log, 1, "high resolution ignored - instrument doesn't support high res. mode\n"); - } - } - if (scan_tol != 1.0) { if (cap2 & inst2_has_scan_toll) { inst_code ev; @@ -675,6 +659,14 @@ a1log *log /* verb, debug & error log */ if (spectral) mode |= inst_mode_spectral; + if (highres) { + if (IMODETST(cap, inst_mode_highres)) { + mode |= inst_mode_highres; + } else { + a1logv(log, 1, "high resolution ignored - instrument doesn't support high res. mode\n"); + } + } + // ~~~ i1pro2 test code ~~~ */ if (uvmode) { if (!IMODETST(cap, inst_mode_ref_uv)) { @@ -2068,7 +2060,7 @@ usage() { if (icmps != NULL) icmps->del(icmps); exit(1); - } +} int main(int argc, char *argv[]) { int i, j; @@ -2206,6 +2198,7 @@ int main(int argc, char *argv[]) { /* Scan tolerance ratio */ else if (argv[fa][1] == 'T') { + fa = nfa; if (na == NULL) usage(); scan_tol = atof(na); @@ -2333,6 +2326,7 @@ int main(int argc, char *argv[]) { } else { usage(); } + fa = nfa; } else usage(); diff --git a/spectro/colorhug.c b/spectro/colorhug.c index dad7012..0152000 100644 --- a/spectro/colorhug.c +++ b/spectro/colorhug.c @@ -8,7 +8,7 @@ * Author: Richard Hughes * Date: 30/11/2011 * - * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2006 - 2014, Graeme W. Gill * Copyright 2011, Richard Hughes * All rights reserved. * @@ -157,7 +157,7 @@ colorhug_command(colorhug *p, int i; unsigned char buf[64]; int xwbytes, wbytes; - int xrbytes, rbytes; + int xrbytes, xrbytes2, rbytes; int se, ua = 0, rv = inst_ok; int ishid = p->icom->port_type(p->icom) == icomt_hid; @@ -177,6 +177,7 @@ colorhug_command(colorhug *p, xwbytes = 64; se = p->icom->usb_write(p->icom, NULL, 0x01, buf, xwbytes, &wbytes, timeout); } + a1logd(p->log,8,"colorhug_command: Send %d bytes and %d sent\n",xwbytes,wbytes); if (se != 0) { a1logd(p->log,1,"colorhug_command: command send failed with ICOM err 0x%x\n",se); return colorhug_interp_code((inst *)p, COLORHUG_COMS_FAIL); @@ -199,14 +200,15 @@ colorhug_command(colorhug *p, a1logd(p->log,6,"colorhug_command: Reading response\n"); if (ishid) { - xrbytes = 64; + xrbytes = xrbytes2 = 64; se = p->icom->hid_read(p->icom, buf, xrbytes, &rbytes, timeout); } else { -// xrbytes = out_size + 2; xrbytes = 64; + xrbytes2 = out_size + 2; /* For backwards compatibility with fw <= 1.1.8 */ se = p->icom->usb_read(p->icom, NULL, 0x81, buf, xrbytes, &rbytes, timeout); } + a1logd(p->log,8,"colorhug_command: Read %d bytes and %d read\n",xrbytes,rbytes); if (rbytes >= 2) { a1logd(p->log,6,"colorhug_command: recieved cmd '%s' error '%s' args '%s'\n", inst_desc(buf[1]), @@ -217,7 +219,6 @@ colorhug_command(colorhug *p, if (se != 0) { /* deal with command error */ -// if (rbytes == 2 && buf[0] != COLORHUG_OK) { if (buf[0] != COLORHUG_OK) { a1logd(p->log,1,"colorhug_command: Got Colorhug !OK\n"); rv = colorhug_interp_code((inst *)p, buf[0]); @@ -225,15 +226,18 @@ colorhug_command(colorhug *p, } /* deal with underrun or overrun */ - if (rbytes != xrbytes) { + if (rbytes != xrbytes + && rbytes != xrbytes2) { a1logd(p->log,1,"colorhug_command: got underrun or overrun\n"); rv = colorhug_interp_code((inst *)p, COLORHUG_BAD_RD_LENGTH); return rv; } - /* there's another reason it failed */ - a1logd(p->log,1,"colorhug_command: read failed with ICOM err 0x%x\n",se); - return colorhug_interp_code((inst *)p, COLORHUG_COMS_FAIL); + if (se != ICOM_SHORT) { /* Allow short read for firware compatibility */ + /* there's another reason it failed */ + a1logd(p->log,1,"colorhug_command: read failed with ICOM err 0x%x\n",se); + return colorhug_interp_code((inst *)p, COLORHUG_COMS_FAIL); + } } rv = colorhug_interp_code((inst *)p, icoms2colorhug_err(ua)); @@ -451,7 +455,7 @@ colorhug_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } else { a1logd(p->log, 1, "colorhug_init_coms: wrong communications type for device!\n"); - return colorhug_interp_code((inst *)p, COLORHUG_UNKNOWN_MODEL); + return inst_internal_error; } a1logd(p->log, 2, "colorhug_init_coms: inited coms OK\n"); @@ -467,7 +471,6 @@ colorhug_get_firmwareversion (colorhug *p) inst_code ev; unsigned char obuf[6]; - /* Hmm. The post scale is in the 2nd short returned */ ev = colorhug_command(p, ch_get_firmware_version, NULL, 0, obuf, 6, @@ -484,6 +487,27 @@ colorhug_get_firmwareversion (colorhug *p) return ev; } +/* Get the serial number */ +static inst_code +colorhug_get_serialnumber (colorhug *p) +{ + inst_code ev; + unsigned char obuf[6]; + + ev = colorhug_command(p, ch_get_serial, + NULL, 0, + obuf, 4, + 2.0); + if (ev != inst_ok) + return ev; + + p->ser_no = buf2uint_le(obuf + 0); + + a1logd(p->log,2,"colorhug: Serial number = %d\n",p->ser_no); + + return ev; +} + /* Set the device multiplier */ static inst_code colorhug_set_multiplier (colorhug *p, int multiplier) @@ -553,6 +577,11 @@ colorhug_init_inst(inst *pp) if (ev != inst_ok) return ev; + /* Get the serial number */ + ev = colorhug_get_serialnumber(p); + if (ev != inst_ok) + return ev; + /* Turn the LEDs off */ ev = colorhug_set_LEDs(p, 0x0); if (ev != inst_ok) @@ -591,6 +620,10 @@ colorhug_init_inst(inst *pp) p->inited = 1; a1logd(p->log, 2, "colorhug_init: inited coms OK\n"); + a1logv(p->log,1,"Serial Number: %06u\n" + "Firmware Version: %d.%d.%d\n" + ,p->ser_no,p->maj,p->min,p->uro); + /* Flash the LEDs */ ev = colorhug_set_LEDs(p, 0x1); if (ev != inst_ok) @@ -670,6 +703,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ val->sp.spec_n = 0; val->duration = 0.0; + if (user_trig) return inst_user_trig; return rv; @@ -692,8 +726,8 @@ double mtx[3][3] 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"); - inst_wrong_setup; + a1loge(p->log, 1, "colorhug: can't set col_cor_mat over non-base display type\n"); + return inst_wrong_setup; } icmCpy3x3(p->ccmat, mtx); } diff --git a/spectro/conv.c b/spectro/conv.c index 0f56ff9..752d483 100644 --- a/spectro/conv.c +++ b/spectro/conv.c @@ -544,33 +544,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){ + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + uint64_t time; + 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; +} +#endif + /* Return the current time in msec */ /* since the first invokation of msec_time() */ unsigned int msec_time() { unsigned int rv; - static struct timeval startup = { 0, 0 }; - struct timeval cv; + static struct timespec startup = { 0, 0 }; + struct timespec cv; - /* Is this monotonic ? */ - /* On Linux, should clock_gettime with CLOCK_MONOTONIC be used instead ? */ - /* On OS X, should mach_absolute_time() be used ? */ - /* or host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clk) */ - gettimeofday(&cv, NULL); + clock_gettime(CLOCK_MONOTONIC, &cv); /* Set time to 0 on first invocation */ - if (startup.tv_sec == 0 && startup.tv_usec == 0) + if (startup.tv_sec == 0 && startup.tv_nsec == 0) startup = cv; /* Subtract, taking care of carry */ cv.tv_sec -= startup.tv_sec; - if (startup.tv_usec > cv.tv_usec) { + if (startup.tv_nsec > cv.tv_nsec) { cv.tv_sec--; - cv.tv_usec += 1000000; + cv.tv_nsec += 100000000; } - cv.tv_usec -= startup.tv_usec; + cv.tv_nsec -= startup.tv_nsec; - /* Convert usec to msec */ - rv = cv.tv_sec * 1000 + cv.tv_usec / 1000; + /* Convert nsec to msec */ + rv = cv.tv_sec * 1000 + cv.tv_nsec / 1000000; return rv; } @@ -579,29 +594,25 @@ unsigned int msec_time() { /* since the first invokation of usec_time() */ double usec_time() { double rv; - static struct timeval startup = { 0, 0 }; - struct timeval cv; + static struct timespec startup = { 0, 0 }; + struct timespec cv; - /* Is this monotonic ? */ - /* On Linux, should clock_gettime with CLOCK_MONOTONIC/CLOCK_REALTIME/CLOCK_REALTIME_HR ? */ - /* On OS X, should mach_absolute_time() be used ? */ - /* or host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clk) */ - gettimeofday(&cv, NULL); + clock_gettime(CLOCK_MONOTONIC, &cv); /* Set time to 0 on first invocation */ - if (startup.tv_sec == 0 && startup.tv_usec == 0) + if (startup.tv_sec == 0 && startup.tv_nsec == 0) startup = cv; /* Subtract, taking care of carry */ cv.tv_sec -= startup.tv_sec; - if (startup.tv_usec > cv.tv_usec) { + if (startup.tv_nsec > cv.tv_nsec) { cv.tv_sec--; - cv.tv_usec += 1000000; + cv.tv_nsec += 1000000000; } - cv.tv_usec -= startup.tv_usec; + cv.tv_nsec -= startup.tv_nsec; /* Convert to usec */ - rv = cv.tv_sec * 1000000.0 + cv.tv_usec; + rv = cv.tv_sec * 1000000.0 + cv.tv_nsec/1000; return rv; } diff --git a/spectro/conv.h b/spectro/conv.h index ab5e850..5d63efe 100644 --- a/spectro/conv.h +++ b/spectro/conv.h @@ -151,6 +151,7 @@ int set_normal_priority(); #ifdef NT # define amutex CRITICAL_SECTION +# define amutex_static(lock) CRITICAL_SECTION lock = { NULL, -1 } # define amutex_init(lock) InitializeCriticalSection(&(lock)) # define amutex_del(lock) DeleteCriticalSection(&(lock)) # define amutex_lock(lock) EnterCriticalSection(&(lock)) @@ -160,6 +161,7 @@ int set_normal_priority(); #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) # define amutex_del(lock) pthread_mutex_destroy(&(lock)) # define amutex_lock(lock) pthread_mutex_lock(&(lock)) @@ -286,6 +288,10 @@ int sa_lu_psinvert(double **out, double **in, int m, int n); #define lu_psinvert sa_lu_psinvert #endif /* SALONEINSTLIB */ + +/* - - - - - - - - - - - - - - - - - - -- */ + + /* - - - - - - - - - - - - - - - - - - -- */ diff --git a/spectro/dispcal.c b/spectro/dispcal.c index accb79a..b8f3ebe 100644 --- a/spectro/dispcal.c +++ b/spectro/dispcal.c @@ -20,6 +20,21 @@ /* TTBD + 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 + slope is getting shallower and shallower. Need to + be able to figure when to increase rgain in those circumstances, + rather than reducing it ? + + Dealing with noisy/inconsistent readings could probably + be improved - the statistical information from a iteration + series is being ignored. ie. do a linear regression/fit + on all the values for a given target, and then + at the end, use a weighted blend of the best solution + and the fit. Weight by something like the number used + for the fit. vs. 1. + Try to improve calibration speed by using adaptive measurement set, rather than fixed resolution doubling ? (ie. just measure at troublesome points using a "divide in half" @@ -58,7 +73,7 @@ display gives about 18% output at 50% device input.] - The verify (-E) may not be being done correctly. + The verify (-z) may not be being done correctly. Like update, shouldn't it read the .cal file to set what's being calibrated aganist ? (This would fix missing ambient value too!) @@ -67,6 +82,13 @@ in "Figure out the black point target" - Yes they are !! Verify probably shouldn't work this way. + Add DICOM support: + + * 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 DICOM mode black point hue handling (? what policy ?) + * Add DICOM stats report (JND dE + mean + SD) to verify ?? */ #ifdef __MINGW32__ @@ -119,17 +141,22 @@ #define COMPORT 1 /* Default com port 1..4 */ #define OPTIMIZE_MODEL /* Adjust model for best fit */ -#define REFINE_GAIN 0.80 /* Refinement correction damping/gain */ -#define MAX_RPTS 12 /* Maximum tries at making a sample meet the current threshold */ +#define REFINE_GAIN 0.90 /* Refinement correction damping/gain */ #define VER_RES 100 /* Verification resolution */ #define NEUTRAL_BLEND_RATE 4.0 /* Default rate of transition for -k factor < 1.0 (power) */ #define ADJ_JACOBIAN /* Adjust the Jacobian predictor matrix each time */ -#define JAC_COR_FACT 0.4 /* Amount to correct Jacobian by (to filter noise) */ -#define REMEAS_JACOBIAN /* Re-measure Jacobian */ +#define JAC_COMP_FACT 0.4 /* Amount to compound Jacobian correction */ +#define JAC_COR_FACT 0.4 /* Amount to damp Jacobian by (to filter noise) */ +#define REMEAS_JACOBIAN /* Re-measure Jacobian if it is a poor predictor */ #define MOD_DIST_POW 1.6 /* Power used to distribute test samples for model building */ #define REFN_DIST_POW 1.6 /* Power used to distribute test samples for grey axis refinement */ #define CHECK_DIST_POW 1.6 /* Power used to distribute test samples for grey axis checking */ #define THRESH_SCALE_POW 0.5 /* Amount to loosen threshold for first itterations */ +#define ADJ_THRESH /* Adjust threshold to be half a step */ +#define MIN_THRESH 0.05 /* Minimum stopping threshold to allow in ADJ_THRESH */ +#define POWERR_THR 0.05 /* Point near black to start weighting +ve error */ +#define POWERR_WEIGHT 999.0 /* Weight to give +ve delta E at black */ +#define POWERR_WEIGHT_POW 4.0 /* Curve to plend from equal weight to +ve extra weight */ #define CAL_RES 256 /* Resolution of calibration table to produce. */ #define CLIP /* Clip RGB during refinement */ #define RDAC_SMOOTH 0.3 /* RAMDAC curve fitting smoothness */ @@ -190,6 +217,7 @@ typedef struct { double nwh[3]; /* Target white normalised XYZ value (Y = 1.0) */ double twh[3]; /* Target white absolute XYZ value */ + double twYxy[3]; /* Target white Yxy (informational) */ icmXYZNumber twN; /* Same as above as XYZNumber */ double tbk[3]; /* Target black point color */ @@ -847,14 +875,16 @@ typedef struct { double tXYZ[3]; /* Target XYZ */ double XYZ[3]; /* Read XYZ */ double deXYZ[3]; /* Delta XYZ wanted to target */ - double de; /* Delta Lab to neutral target */ - double dc; /* Delta XYZ to neutral target */ - double peqde; /* Delta Lab to previous point value (last pass) */ - double hde; /* Hybrid de composed of de and peqde */ + double _de; /* Non-weighted Delta Lab */ + double de; /* Weightd Delta Lab to neutral target */ + double dc; /* Weightd Delta XYZ to neutral target */ + double peqde; /* Weightd Delta Lab to last pass equivalent point value */ + double hde; /* Weightd Hybrid de composed of de and peqde */ + double prgb[3]; /* Previous measured RGB */ double pXYZ[3]; /* Previous measured XYZ */ double pdXYZ[3]; /* Delta XYZ intended from previous measure */ - double pdrgb[3]; /* Delta rgb made to previous */ + double pdrgb[3]; /* Delta rgb made to previous to acorrect XYZ */ double dXYZ[3]; /* Actual delta XYZ resulting from previous delta rgb */ @@ -863,11 +893,12 @@ typedef struct { double fb_ij[3][3]; /* Copy of initial inverse Jacobian, used as a fallback */ } csp; + /* All the sample points */ typedef struct { int no; /* Number of samples */ int _no; /* Allocation */ - csp *s; /* List of samples */ + csp *s; /* List of samples */ } csamp; static void free_alloc_csamp(csamp *p) { @@ -916,10 +947,13 @@ static void init_csamp_v(csamp *p, calx *x, int psrand) { } /* Initialise txyz values from v values */ -static void init_csamp_txyz(csamp *p, calx *x, int fixdev) { +static void init_csamp_txyz(csamp *p, calx *x, int fixdev, int verb) { int i, j; double tbL[3]; /* tbk as Lab */ + if (verb >= 3) + printf("init_csamp_txyz:\n"); + /* Convert target black from XYZ to Lab here, */ /* in case twN has changed at some point. */ icmXYZ2Lab(&x->twN, tbL, x->tbk); @@ -946,22 +980,22 @@ static void init_csamp_txyz(csamp *p, calx *x, int fixdev) { /* Compute blended neutral target a* b* */ bl = pow((1.0 - vv), x->nbrate); /* Crossover near the black */ - Lab[1] = bl * tbL[1]; - Lab[2] = bl * tbL[2]; + Lab[1] = (1.0 - bl) * 0.0 + bl * tbL[1]; + Lab[2] = (1.0 - bl) * 0.0 + bl * tbL[2]; icmAry2Ary(XYZ, p->s[i].tXYZ); /* Save the existing values */ icmLab2XYZ(&x->twN, p->s[i].tXYZ, Lab); /* New XYZ Value to aim for */ -#ifdef DEBUG - printf("%d: target XYZ %.2f %.2f %.2f, Lab %.2f %.2f %.2f\n",i, p->s[i].tXYZ[0],p->s[i].tXYZ[1],p->s[i].tXYZ[2], Lab[0],Lab[1],Lab[2]); -#endif + if (verb >= 3) { + printf("%d: target XYZ %.4f %.4f %.4f, Lab %.3f %.3f %.3f\n",i, p->s[i].tXYZ[0],p->s[i].tXYZ[1],p->s[i].tXYZ[2], Lab[0],Lab[1],Lab[2]); + } } } /* Allocate the sample points and initialise them with the */ /* target device and XYZ values, and first cut device values. */ -static void init_csamp(csamp *p, calx *x, int doupdate, int verify, int psrand, int no) { +static void init_csamp(csamp *p, calx *x, int doupdate, int verify, int psrand, int no, int verb) { int i, j; p->_no = p->no = no; @@ -971,7 +1005,7 @@ static void init_csamp(csamp *p, calx *x, int doupdate, int verify, int psrand, /* Compute v and txyz */ init_csamp_v(p, x, psrand); - init_csamp_txyz(p, x, 0); + init_csamp_txyz(p, x, 0, verb); /* Generate the sample points */ for (i = 0; i < no; i++) { @@ -1064,7 +1098,7 @@ static void csamp_interp(csamp *p, double xyz[3], double v) { /* Re-initialise a CSP with a new number of points. */ /* Interpolate the device values and jacobian. */ /* Set the current rgb from the current RAMDAC curves if not verifying */ -static void reinit_csamp(csamp *p, calx *x, int verify, int psrand, int no) { +static void reinit_csamp(csamp *p, calx *x, int verify, int psrand, int no, int verb) { csp *os; /* Old list of samples */ int ono; /* Old number of samples */ int i, j, k, m; @@ -1075,7 +1109,7 @@ static void reinit_csamp(csamp *p, calx *x, int verify, int psrand, int no) { os = p->s; /* Save the existing per point information */ ono = p->no; - init_csamp(p, x, 0, 2, psrand, no); + init_csamp(p, x, 0, 2, psrand, no, verb); p->_no = p->no = no; @@ -1151,7 +1185,7 @@ static void reinit_csamp(csamp *p, calx *x, int verify, int psrand, int no) { /* Compute expected delta XYZ using new Jacobian */ icmMulBy3x3(p->s[i].pdXYZ, p->s[i].j, p->s[i].pdrgb); - p->s[i].de = b * os[j+1].de + (1.0 - b) * os[j].de; + p->s[i]._de = p->s[i].de = b * os[j+1].de + (1.0 - b) * os[j].de; p->s[i].dc = b * os[j+1].dc + (1.0 - b) * os[j].dc; } @@ -1223,6 +1257,20 @@ static double comp_ct( /* =================================================================== */ +/* Return the normal Delta E given two XYZ values, but */ +/* exagerate the L* error if act L* > targ L* by a factor of fact */ +extern ICCLIB_API double bwXYZLabDE(icmXYZNumber *w, double *targ, double *act, double fact) { + double targlab[3], actlab[3], rv; + + icmXYZ2Lab(w, targlab, targ); + icmXYZ2Lab(w, actlab, act); + if (actlab[0] > targlab[0]) + actlab[0] = targlab[0] + fact * (actlab[0] - targlab[0]); + rv = icmLabDE(targlab, actlab); + return rv; +} +/* =================================================================== */ + /* Default gamma */ double g_def_gamma = 2.4; @@ -1232,7 +1280,7 @@ double g_def_gamma = 2.4; ABCDEFGHIJKLMNOPQRSTUVWXYZ upper .......... ....... . ... - lower ....... . ...... .... . + lower ....... . ...... .... .. */ @@ -1276,6 +1324,9 @@ void usage(char *diag, ...) { } free_disppaths(dp); fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); +#ifdef NT + 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," -c listno Set communication port from the following list (default %d)\n",COMPORT); if ((icmps = new_icompaths(g_log)) != NULL) { @@ -1323,7 +1374,7 @@ void usage(char *diag, ...) { fprintf(stderr," -A rate Rate of blending from neutral to black point. Default %.1f\n",NEUTRAL_BLEND_RATE); 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," -E Run only verify pass on installed calibration curves\n"); + fprintf(stderr," -z Run only verify pass on installed calibration curves\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"); @@ -1331,6 +1382,7 @@ void usage(char *diag, ...) { #if defined(UNIX_X11) 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," -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"); @@ -1343,7 +1395,9 @@ void usage(char *diag, ...) { fprintf(stderr," 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2, 1964_10c\n"); } fprintf(stderr," -I b|w Drift compensation, Black: -Ib, White: -Iw, Both: -Ibw\n"); + fprintf(stderr," -Y R:rate Override measured refresh rate with rate Hz\n"); fprintf(stderr," -Y A Use non-adaptive integration time mode (if available).\n"); + fprintf(stderr," -Y p Don't wait for the instrument to be placed on the display\n"); fprintf(stderr," -C \"command\" Invoke shell \"command\" each time a color is set\n"); fprintf(stderr," -M \"command\" Invoke shell \"command\" each time a color is measured\n"); fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n"); @@ -1360,6 +1414,7 @@ int main(int argc, char *argv[]) { 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 blackbg = 0; /* NZ if whole screen should be filled with black */ int verb = 0; int debug = 0; @@ -1382,7 +1437,9 @@ int main(int argc, char *argv[]) { int dtype = 0; /* Display type selection charater */ int tele = 0; /* nz if telephoto mode */ int nocal = 0; /* Disable auto calibration */ + int noplace = 0; /* Disable initial user placement check */ int highres = 0; /* Use high res mode if available */ + double refrate = 0.0; /* 0.0 = default, > 0.0 = override refresh rate */ int nadaptive = 0; /* Use non-adaptive mode if available */ int bdrift = 0; /* Flag, nz for black drift compensation */ int wdrift = 0; /* Flag, nz for white drift compensation */ @@ -1393,7 +1450,7 @@ int main(int argc, char *argv[]) { double tbright = 0.0; /* Target white brightness ( 0.0 == max) */ double gamma = 0.0; /* Advertised Gamma target */ double egamma = 0.0; /* Effective Gamma target, NZ if set */ - double ambient = 0.0; /* NZ if viewing cond. adjustment to be used (cd/m^2) */ + double ambient = 0.0; /* NZ if viewing cond. adjustment to be used (Lux) */ double bkcorrect = -1.0; /* Level of black point correction, < 0 = auto */ 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 */ @@ -1403,9 +1460,13 @@ int main(int argc, char *argv[]) { int thrfail = 0; /* Set to NZ if failed to meet threshold target */ double failerr = 0.0; /* Delta E of worst failed target */ int mxits = 3; /* maximum iterations (medium) */ + int mxrpts = 12; /* maximum repeats (medium) */ 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 */ +#ifdef NT + int madvrdisp = 0; /* NZ for madvr display */ +#endif char *ccallout = NULL; /* Change color Shell callout */ char *mcallout = NULL; /* Measure color Shell callout */ char outname[MAXNAMEL+1] = { 0 }; /* Output cgats file base name */ @@ -1421,9 +1482,12 @@ int main(int argc, char *argv[]) { int it; /* verify & refine iteration */ int rv; int fitord = 30; /* More seems to make curves smoother */ - int native = 1; /* 0 = use current or given calibration curve */ - /* 1 = set native linear op and use ramdac high prec'n */ + int native = 3; /* X0 = use current per channel calibration curve */ + /* X1 = set native linear output and use ramdac high prec */ + /* 0X = use current color management cLut (MadVR) */ + /* 1X = disable color management cLUT (MadVR) */ int noramdac = 0; /* Will be set to nz if can't set ramdac */ + int nocm = 0; /* Will be set to nz if can't set color managament */ int errc; /* Return value from new_disprd() */ calx x; /* Context for calibration solution */ @@ -1513,6 +1577,12 @@ int main(int argc, char *argv[]) { usage("Web port number must be in range 1..65535"); } fa = nfa; +#ifdef NT + } else if (strncmp(na,"madvr",5) == 0 + || strncmp(na,"MADVR",5) == 0) { + madvrdisp = 1; + fa = nfa; +#endif } else { #if defined(UNIX_X11) int ix, iv; @@ -1554,6 +1624,9 @@ int main(int argc, char *argv[]) { #endif } + } else if (argv[fa][1] == 'E') { + out_tvenc = 1; + } else if (argv[fa][1] == 'J') { docalib = 1; @@ -1673,7 +1746,7 @@ int main(int argc, char *argv[]) { fa = nfa; } - } else if (argv[fa][1] == 'E') { + } else if (argv[fa][1] == 'z') { verify = 2; mfa = 0; @@ -1834,7 +1907,6 @@ int main(int argc, char *argv[]) { ambient = atof(na); if (ambient < 0.0) usage("-a parameter %f out of range",ambient); - ambient /= 3.141592654; /* Convert from Lux to cd/m^2 */ /* Test patch offset and size */ } else if (argv[fa][1] == 'P') { @@ -1864,11 +1936,20 @@ int main(int argc, char *argv[]) { if (na == NULL) usage("Flag '-Y' expects extra flag"); - if (na[0] == 'A') { + if (na[0] == 'R') { + if (na[1] != ':') + usage("-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); + } else if (na[0] == 'A') { nadaptive = 1; + } else if (na[0] == 'p') { + noplace = 1; } else { usage("Flag '-Y %c' not recognised",na[0]); } + fa = nfa; } else usage("Flag '-%c' not recognised",argv[fa][1]); @@ -1932,7 +2013,7 @@ int main(int argc, char *argv[]) { } if (fake) - comport = -99; + comport = FAKE_DEVICE_PORT; if ((icmps = new_icompaths(g_log)) == NULL) error("Finding instrument paths failed"); if ((ipath = icmps->get_path(icmps, comport)) == NULL) @@ -1940,7 +2021,11 @@ int main(int argc, char *argv[]) { if (docalib) { if ((rv = disprd_calibration(ipath, fc, dtype, 0, tele, nadaptive, nocal, disp, - webdisp, blackbg, override, + webdisp, +#ifdef NT + madvrdisp, +#endif + out_tvenc, blackbg, override, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, g_log)) != 0) { error("docalibration failed with return value %d\n",rv); @@ -1973,12 +2058,16 @@ int main(int argc, char *argv[]) { /* Normally calibrate against native response */ if (verify == 2 || doreport == 2) - native = 0; /* But measure calibrated response of verify or report calibrated */ + 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, - highres, native, &noramdac, NULL, 0, 0, disp, blackbg, override, - webdisp, ccallout, mcallout, + if ((dr = new_disprd(&errc, ipath, fc, dtype, 0, tele, nadaptive, nocal, noplace, + highres, refrate, native, &noramdac, &nocm, NULL, 0, + disp, out_tvenc, blackbg, override, webdisp, +#ifdef NT + madvrdisp, +#endif + ccallout, mcallout, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, cmx != NULL ? cmx->matrix : NULL, ccs != NULL ? ccs->samples : NULL, ccs != NULL ? ccs->no_samp : 0, @@ -1986,11 +2075,11 @@ int main(int argc, char *argv[]) { "fake" ICC_FILE_EXT, g_log)) == NULL) error("new_disprd() failed with '%s'\n",disprd_err(errc)); - if (native != 0 && noramdac) { - warning("No access to VideoLUTs, so calibrating display as-is rather than native"); + if ((native & 1) && noramdac) { + warning("Unable to access to VideoLUTs so can't be sure colors are native"); if (doprofile) warning("Profile will reflect the as-is display response and not contain a 'vcgt' tag"); - native = 0; + native &= ~1; if (doupdate && doprofile) error("Can't update a profile that doesn't use the 'vcgt' tag for calibration"); @@ -2053,7 +2142,7 @@ int main(int argc, char *argv[]) { if (doreport == 1) { #define MAX_RES_SAMPS 24 col ttt[MAX_RES_SAMPS]; - int res_samps = 6; + int res_samps = 9; double a0, a1, a2, dd; int n; int issig = 0; @@ -2073,7 +2162,7 @@ int main(int argc, char *argv[]) { gcc_bug_fix(sigbits); #endif /* Notional test value */ - v = (5 << (sigbits-3))/((1 << sigbits) - 1.0); + v = (7 << (sigbits-3))/((1 << sigbits) - 1.0); /* And -1, 0 , +1 bit test values */ if ((n % 3) == 2) v += 1.0/((1 << sigbits) - 1.0); @@ -2102,6 +2191,7 @@ int main(int argc, char *argv[]) { a0 /= (res_samps / 3.0); a1 /= (res_samps / 3.0); a2 /= (res_samps / 3.0); + DBG((dbgo,"Bits %d: -1: %f 0: %f +1 %f\n",sigbits, a0, a1, a2)); /* Judge significance of any differences */ dd = 0.0; for (n = 0; n < res_samps; n++) { @@ -2120,13 +2210,13 @@ int main(int argc, char *argv[]) { issig = 1; /* Noticable difference */ else issig = 0; /* No noticable difference */ - DBG((dbgo,"Bits %d: Between = %f, %f within = %f, sig = %s\n",sigbits, fabs(a1 - a0), fabs(a2 - a1),dd, issig ? "yes" : "no")); + DBG((dbgo,"Bits %d: Between = %f, %f within = %f, sig = %s\n",sigbits, fabs(a1 - a0), fabs(a2 - a1), dd, issig ? "yes" : "no")); switch(sigbits) { case 8: /* Do another trial */ if (issig) { sigbits = 10; - res_samps = 6; + res_samps = 9; } else { sigbits = 6; } @@ -2134,6 +2224,7 @@ int main(int argc, char *argv[]) { case 6: /* Do another trial or give up */ if (issig) { sigbits = 7; + res_samps = 6; } else { sigbits = 0; issig = 2; /* Give up */ @@ -2147,7 +2238,7 @@ int main(int argc, char *argv[]) { case 10: /* Do another trial */ if (issig) { sigbits = 12; - res_samps = 9; + res_samps = 12; } else { sigbits = 9; } @@ -2181,7 +2272,8 @@ int main(int argc, char *argv[]) { printf("Current calibration response:\n"); else printf("Uncalibrated response:\n"); - printf("Black level = %.2f cd/m^2\n",tcols[0].XYZ[1]); + printf("Black level = %.4f cd/m^2\n",tcols[0].XYZ[1]); + printf("50%% level = %.2f cd/m^2\n",tcols[1].XYZ[1]); printf("White level = %.2f cd/m^2\n",tcols[2].XYZ[1]); printf("Aprox. gamma = %.2f\n",cgamma); printf("Contrast ratio = %.0f:1\n",tcols[2].XYZ[1]/tcols[0].XYZ[1]); @@ -2239,6 +2331,13 @@ int main(int argc, char *argv[]) { error("Can't update '%s' - there aren't two tables",outname); } + out_tvenc = 0; + if ((fi = icg->find_kword(icg, 0, "TV_OUTPUT_ENCODING")) >= 0) { + if (strcmp(icg->t[0].kdata[fi], "YES") == 0 + || strcmp(icg->t[0].kdata[fi], "yes") == 0) + out_tvenc = 1; + } + //printf("~1 reading previous cal, got 2 tables\n"); /* Read in the setup, user and model values */ @@ -2489,6 +2588,7 @@ int main(int argc, char *argv[]) { isteps = 3; rsteps = 9; mxits = 1; + mxrpts = 8; errthr = 2.0; break; case -2: /* Very low */ @@ -2499,6 +2599,7 @@ int main(int argc, char *argv[]) { mxits = 1; else mxits = 1; + mxrpts = 10; break; case -1: /* Low */ if (verify != 2 && doprofile && !doupdate) @@ -2511,6 +2612,7 @@ int main(int argc, char *argv[]) { mxits = 1; else mxits = 2; + mxrpts = 10; break; default: case 0: /* Medum */ @@ -2525,6 +2627,7 @@ int main(int argc, char *argv[]) { mxits = 1; else mxits = 3; + mxrpts = 12; break; case 1: /* High */ if (verify != 2 && doprofile && !doupdate) @@ -2537,6 +2640,7 @@ int main(int argc, char *argv[]) { mxits = 1; else mxits = 4; + mxrpts = 16; break; case 2: /* Ultra */ if (verify != 2 && doprofile && !doupdate) @@ -2549,6 +2653,7 @@ int main(int argc, char *argv[]) { mxits = 1; else mxits = 5; + mxrpts = 24; break; } @@ -2560,6 +2665,9 @@ int main(int argc, char *argv[]) { /* Say something about what we're doing */ if (verb) { + if (out_tvenc) + printf("Using TV encoding range of (16-235)/255\n"); + if (dtype > 0) printf("Display type is '%c'\n",dtype); @@ -2665,11 +2773,11 @@ int main(int argc, char *argv[]) { } if (verb) { - printf("Black = XYZ %6.2f %6.2f %6.2f\n",tcols[0].XYZ[0], + printf("Black = XYZ %6.4f %6.4f %6.4f\n",tcols[0].XYZ[0], tcols[0].XYZ[1], tcols[0].XYZ[2]); - printf("Grey = XYZ %6.2f %6.2f %6.2f\n",tcols[1].XYZ[0], + printf("Grey = XYZ %6.3f %6.3f %6.3f\n",tcols[1].XYZ[0], tcols[1].XYZ[1], tcols[1].XYZ[2]); - printf("White = XYZ %6.2f %6.2f %6.2f\n",tcols[2].XYZ[0], + printf("White = XYZ %6.3f %6.3f %6.3f\n",tcols[2].XYZ[0], tcols[2].XYZ[1], tcols[2].XYZ[2]); } @@ -2741,11 +2849,11 @@ int main(int argc, char *argv[]) { error("display read failed with '%s'\n",disprd_err(rv)); } if (verb) { - printf("Red = XYZ %6.2f %6.2f %6.2f\n",ccols[0].XYZ[0], + printf("Red = XYZ %6.3f %6.3f %6.3f\n",ccols[0].XYZ[0], ccols[0].XYZ[1], ccols[0].XYZ[2]); - printf("Green = XYZ %6.2f %6.2f %6.2f\n",ccols[1].XYZ[0], + printf("Green = XYZ %6.3f %6.3f %6.3f\n",ccols[1].XYZ[0], ccols[1].XYZ[1], ccols[1].XYZ[2]); - printf("Blue = XYZ %6.2f %6.2f %6.2f\n",ccols[2].XYZ[0], + printf("Blue = XYZ %6.3f %6.3f %6.3f\n",ccols[2].XYZ[0], ccols[2].XYZ[1], ccols[2].XYZ[2]); } for (i = 0; i < 3; i++) @@ -2758,7 +2866,7 @@ int main(int argc, char *argv[]) { error("display read failed with '%s'\n",disprd_err(rv)); } if (verb) { - printf("White = XYZ %6.2f %6.2f %6.2f\n",tcols[0].XYZ[0], + printf("White = XYZ %6.3f %6.3f %6.3f\n",tcols[0].XYZ[0], tcols[0].XYZ[1], tcols[0].XYZ[2]); } @@ -2939,7 +3047,7 @@ int main(int argc, char *argv[]) { error("display read failed with '%s'\n",disprd_err(rv)); } if (verb) { - printf("White = XYZ %6.2f %6.2f %6.2f\n",tcols[0].XYZ[0], + printf("White = XYZ %6.3f %6.3f %6.3f\n",tcols[0].XYZ[0], tcols[0].XYZ[1], tcols[0].XYZ[2]); } @@ -3012,11 +3120,11 @@ int main(int argc, char *argv[]) { error("display read failed with '%s'\n",disprd_err(rv)); } if (verb) { - printf("Red = XYZ %6.2f %6.2f %6.2f\n",ccols[0].XYZ[0], + printf("Red = XYZ %6.3f %6.3f %6.3f\n",ccols[0].XYZ[0], ccols[0].XYZ[1], ccols[0].XYZ[2]); - printf("Green = XYZ %6.2f %6.2f %6.2f\n",ccols[1].XYZ[0], + printf("Green = XYZ %6.3f %6.3f %6.3f\n",ccols[1].XYZ[0], ccols[1].XYZ[1], ccols[1].XYZ[2]); - printf("Blue = XYZ %6.2f %6.2f %6.2f\n",ccols[2].XYZ[0], + printf("Blue = XYZ %6.3f %6.3f %6.3f\n",ccols[2].XYZ[0], ccols[2].XYZ[1], ccols[2].XYZ[2]); } for (i = 0; i < 3; i++) @@ -3029,11 +3137,11 @@ int main(int argc, char *argv[]) { error("display read failed with '%s'\n",disprd_err(rv)); } if (verb) { - printf("Black = XYZ %6.2f %6.2f %6.2f\n",tcols[0].XYZ[0], + printf("Black = XYZ %6.4f %6.4f %6.4f\n",tcols[0].XYZ[0], tcols[0].XYZ[1], tcols[0].XYZ[2]); - printf("Grey = XYZ %6.2f %6.2f %6.2f\n",tcols[1].XYZ[0], + printf("Grey = XYZ %6.3f %6.3f %6.3f\n",tcols[1].XYZ[0], tcols[1].XYZ[1], tcols[1].XYZ[2]); - printf("White = XYZ %6.2f %6.2f %6.2f\n",tcols[2].XYZ[0], + printf("White = XYZ %6.3f %6.3f %6.3f\n",tcols[2].XYZ[0], tcols[2].XYZ[1], tcols[2].XYZ[2]); } @@ -3064,7 +3172,7 @@ int main(int argc, char *argv[]) { } printf("\nAdjust R,G & B offsets to get target x,y. Press space when done.\n"); - printf(" Target Br %.2f, x %.4f , y %.4f \n", tar1, tYxy[1],tYxy[2]); + printf(" Target Br %.4f, x %.4f , y %.4f \n", tar1, tYxy[1],tYxy[2]); for (ff = 0;; ff ^= 1) { double dir; /* Direction to adjust brightness */ double sv[3], val1; /* Scaled values */ @@ -3133,7 +3241,7 @@ int main(int argc, char *argv[]) { rgbdir[0] = rgbdir[1] = rgbdir[2] = 0.0; rgbxdir[bx] = rgbdir[bx]; - printf("%c%c Current Br %.2f, x %.4f%c, y %.4f%c DE %4.1f R%c%c G%c%c B%c%c ", + printf("%c%c Current Br %.4f, x %.4f%c, y %.4f%c DE %4.1f R%c%c G%c%c B%c%c ", cr_char, ff == 0 ? '/' : '\\', val1, @@ -3177,11 +3285,11 @@ int main(int argc, char *argv[]) { error("display read failed with '%s'\n",disprd_err(rv)); } if (verb) { - printf("Black = XYZ %6.2f %6.2f %6.2f\n",tcols[0].XYZ[0], + printf("Black = XYZ %6.4f %6.4f %6.4f\n",tcols[0].XYZ[0], tcols[0].XYZ[1], tcols[0].XYZ[2]); - printf("Grey = XYZ %6.2f %6.2f %6.2f\n",tcols[1].XYZ[0], + printf("Grey = XYZ %6.3f %6.3f %6.3f\n",tcols[1].XYZ[0], tcols[1].XYZ[1], tcols[1].XYZ[2]); - printf("White = XYZ %6.2f %6.2f %6.2f\n",tcols[2].XYZ[0], + printf("White = XYZ %6.3f %6.3f %6.3f\n",tcols[2].XYZ[0], tcols[2].XYZ[1], tcols[2].XYZ[2]); } @@ -3198,7 +3306,7 @@ int main(int argc, char *argv[]) { error("display read failed with '%s'\n",disprd_err(rv)); } if (verb) { - printf("1%% = XYZ %6.2f %6.2f %6.2f\n",tcols[3].XYZ[0], + printf("1%% = XYZ %6.3f %6.3f %6.3f\n",tcols[3].XYZ[0], tcols[3].XYZ[1], tcols[3].XYZ[2]); } @@ -3277,17 +3385,17 @@ int main(int argc, char *argv[]) { printf("\n"); if (tbright > 0.0) /* Given brightness */ - printf(" Target Brightness = %.2f, Current = %5.2f, error = % .1f%%\n", + printf(" Target Brightness = %.3f, Current = %.3f, error = % .1f%%\n", tarw, tcols[2].XYZ[1], 100.0 * (tcols[2].XYZ[1] - tarw)/tarw); else printf(" Current Brightness = %.2f\n", tcols[2].XYZ[1]); - printf(" Target 50%% Level = %.2f, Current = %5.2f, error = % .1f%%\n", + printf(" Target 50%% Level = %.3f, Current = %.3f, error = % .1f%%\n", tarh, tcols[1].XYZ[1], 100.0 * (tcols[1].XYZ[1] - tarh)/tarw); - printf(" Target Near Black = %5.2f, Current = %5.2f, error = % .1f%%\n", + printf(" Target Near Black = %.4f, Current = %.4f, error = % .1f%%\n", tar1, val1, 100.0 * (val1 - tar1)/tarw); @@ -3312,7 +3420,7 @@ int main(int argc, char *argv[]) { error("ambient measure failed with '%s'\n",disprd_err(rv)); } } else { - printf("Measured ambient level = %.1f Lux\n",ambient * 3.141592654); + printf("Measured ambient level = %.1f Lux\n",ambient); } } else if (c == '7') { @@ -3349,14 +3457,26 @@ int main(int argc, char *argv[]) { icmXYZNumber mwh; ramdac *or = NULL; - col base[6] = { /* Base set of test colors */ + col base[9] = { /* Base set of test colors */ { 0.0, 0.0, 0.0 }, /* 0 - Black */ { 1.0, 0.0, 0.0 }, /* 1 - Red */ - { 0.0, 1.0, 0.0 }, /* 2 - Green */ - { 0.0, 0.0, 1.0 }, /* 3 - Blue */ - { 1.0, 1.0, 1.0 }, /* 4 - White */ - { 0.0, 0.0, 0.0 } /* 5 - Black */ + { 1.0, 1.0, 1.0 }, /* 2 - White */ + { 0.0, 0.0, 0.0 }, /* 3 - Black */ + { 0.0, 1.0, 0.0 }, /* 4 - Green */ + { 0.0, 0.0, 0.0 }, /* 5 - Black */ + { 0.0, 0.0, 1.0 }, /* 6 - Blue */ + { 1.0, 1.0, 1.0 }, /* 7 - White */ + { 0.0, 0.0, 0.0 } /* 8 - Black */ }; + int ix_k1 = 0; + int ix_r = 1; + int ix_w1 = 2; + int ix_k2 = 3; + int ix_g = 4; + int ix_k3 = 5; + int ix_b = 6; + int ix_w2 = 7; + int ix_k4 = 8; if (verb) { if (verify == 2) @@ -3379,7 +3499,7 @@ int main(int argc, char *argv[]) { } /* Read the patches without clamping */ - if ((rv = dr->read(dr, base, 6, 1, 6, 1, 0, instNoClamp)) != 0) { + if ((rv = dr->read(dr, base, 9, 1, 9, 1, 0, instNoClamp)) != 0) { dr->del(dr); error("display read failed with '%s'\n",disprd_err(rv)); } @@ -3391,35 +3511,49 @@ int main(int argc, char *argv[]) { or->del(or); } - if (base[0].XYZ_v == 0) { + if (base[ix_k1].XYZ_v == 0) { dr->del(dr); error("Failed to get an XYZ value from the instrument!\n"); } - /* Average black relative from 2 readings */ - x.bk[0] = 0.5 * (base[0].XYZ[0] + base[5].XYZ[0]); - x.bk[1] = 0.5 * (base[0].XYZ[1] + base[5].XYZ[1]); - x.bk[2] = 0.5 * (base[0].XYZ[2] + base[5].XYZ[2]); + if (verb >= 3) { + for (i = 0; i < 9; i++) + printf("Meas %d XYZ = %f %f %f\n",i,base[i].XYZ[0], base[i].XYZ[1], base[i].XYZ[2]); + } + + /* Average black relative from 4 readings */ + x.bk[0] = 0.25 * (base[ix_k1].XYZ[0] + base[ix_k2].XYZ[0] + + base[ix_k3].XYZ[0] + base[ix_k4].XYZ[0]); + x.bk[1] = 0.25 * (base[ix_k1].XYZ[1] + base[ix_k2].XYZ[1] + + base[ix_k3].XYZ[1] + base[ix_k4].XYZ[1]); + x.bk[2] = 0.25 * (base[ix_k1].XYZ[2] + base[ix_k2].XYZ[2] + + base[ix_k3].XYZ[2] + base[ix_k4].XYZ[2]); icmClamp3(x.bk, x.bk); /* And clamp them */ - for (i = 1; i < 5; i++) + + /* Average white reading from 2 readings */ + base[ix_w1].XYZ[0] = 0.5 * (base[ix_w1].XYZ[0] + base[ix_w2].XYZ[0]); + base[ix_w1].XYZ[1] = 0.5 * (base[ix_w1].XYZ[1] + base[ix_w2].XYZ[1]); + base[ix_w1].XYZ[2] = 0.5 * (base[ix_w1].XYZ[2] + base[ix_w2].XYZ[2]); + + for (i = 0; i < 9; i++) icmClamp3(base[i].XYZ, base[i].XYZ); /* Copy other readings into place */ - dispLum = base[4].XYZ[1]; /* White Y */ - icmAry2Ary(x.wh, base[4].XYZ); + dispLum = base[ix_w1].XYZ[1]; /* White Y */ + 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[1].XYZ); - icmAry2XYZ(mgn, base[2].XYZ); - icmAry2XYZ(mbl, base[3].XYZ); - icmAry2XYZ(mwh, base[4].XYZ); + icmAry2XYZ(mrd, base[ix_r].XYZ); + icmAry2XYZ(mgn, base[ix_g].XYZ); + icmAry2XYZ(mbl, base[ix_b].XYZ); + icmAry2XYZ(mwh, base[ix_w1].XYZ); if (verb) { - printf("Black = XYZ %6.2f %6.2f %6.2f\n",x.bk[0],x.bk[1],x.bk[2]); - printf("Red = XYZ %6.2f %6.2f %6.2f\n",base[1].XYZ[0], base[1].XYZ[1], base[1].XYZ[2]); - printf("Green = XYZ %6.2f %6.2f %6.2f\n",base[2].XYZ[0], base[2].XYZ[1], base[2].XYZ[2]); - printf("Blue = XYZ %6.2f %6.2f %6.2f\n",base[3].XYZ[0], base[3].XYZ[1], base[3].XYZ[2]); - printf("White = XYZ %6.2f %6.2f %6.2f\n",base[4].XYZ[0], base[4].XYZ[1], base[4].XYZ[2]); + printf("Black = XYZ %6.4f %6.4f %6.4f\n",x.bk[0],x.bk[1],x.bk[2]); + printf("Red = XYZ %6.3f %6.3f %6.3f\n",base[ix_r].XYZ[0], base[ix_r].XYZ[1], base[ix_r].XYZ[2]); + printf("Green = XYZ %6.3f %6.3f %6.3f\n",base[ix_g].XYZ[0], base[ix_g].XYZ[1], base[ix_g].XYZ[2]); + printf("Blue = XYZ %6.3f %6.3f %6.3f\n",base[ix_b].XYZ[0], base[ix_b].XYZ[1], base[ix_b].XYZ[2]); + printf("White = XYZ %6.3f %6.3f %6.3f\n",base[ix_w1].XYZ[0], base[ix_w1].XYZ[1], base[ix_w1].XYZ[2]); } /* Setup forward matrix */ @@ -3427,6 +3561,7 @@ int main(int argc, char *argv[]) { 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) { @@ -3783,8 +3918,11 @@ int main(int argc, char *argv[]) { } } + icmXYZ2Yxy(x.twYxy, x.twh); /* For information */ + if (verb) - printf("Target white value is XYZ %f %f %f\n",x.twh[0],x.twh[1],x.twh[2]); + printf("Target white value is XYZ %f %f %f [xy %f %f]\n",x.twh[0],x.twh[1],x.twh[2], + x.twYxy[1], x.twYxy[2]); } /* Need this for Lab conversions */ @@ -3800,6 +3938,7 @@ int main(int argc, char *argv[]) { //printf("~1 black point Lab = %f %f %f\n", tbkLab[0], tbkLab[1], tbkLab[2]); /* Now blend the a* b* with that of the target white point */ + /* according to how much to try and correct the hue. */ tbL[0] = tbkLab[0]; tbL[1] = bkcorrect * 0.0 + (1.0 - bkcorrect) * tbkLab[1]; tbL[2] = bkcorrect * 0.0 + (1.0 - bkcorrect) * tbkLab[2]; @@ -3829,7 +3968,7 @@ int main(int argc, char *argv[]) { icmLab2XYZ(&x.twN, x.tbk, tbL); icmAry2XYZ(x.tbN, x.tbk); if (verb) - printf("Adjusted target black XYZ %.2f %.2f %.2f, Lab %.2f %.2f %.2f\n", + printf("Adjusted target black XYZ %.4f %.4f %.4f, Lab %.3f %.3f %.3f\n", x.tbk[0], x.tbk[1], x.tbk[2], tbL[0], tbL[1], tbL[2]); } @@ -3857,11 +3996,17 @@ int main(int argc, char *argv[]) { if (verb) { double tbp[3], tbplab[3]; - tbp[0] = x.tbk[0] * tby/x.tbk[1]; + if (fabs(x.tbk[1]) > 1e-9) + tbp[0] = x.tbk[0] * tby/x.tbk[1]; + else + tbp[0] = x.tbk[0]; tbp[1] = tby; - tbp[2] = x.tbk[2] * tby/x.tbk[1]; + if (fabs(x.tbk[1]) > 1e-9) + tbp[2] = x.tbk[2] * tby/x.tbk[1]; + else + tbp[2] = x.tbk[2]; icmXYZ2Lab(&x.twN, tbplab, tbp); - printf("Target black after min adjust: XYZ %.3f %.3f %.3f, Lab %.3f %.3f %.3f\n", + printf("Target black after min adjust: XYZ %.4f %.4f %.4f, Lab %.3f %.3f %.3f\n", tbp[0], tbp[1], tbp[2], tbplab[0], tbplab[1], tbplab[2]); } @@ -3896,7 +4041,7 @@ int main(int argc, char *argv[]) { 0.2 * 80.0, /* Adapting luminence, 20% of display 80 cd/m^2 */ 0.2, /* Background relative to reference white */ 80.0, /* Display is 80 cd/m^2 */ - 0.01, x.nwh, /* 1% flare same white point */ + 0.0, 0.01, x.nwh, /* 0% flare and 1% glare same white point */ 0); break; @@ -3907,7 +4052,7 @@ int main(int argc, char *argv[]) { 0.2 * 1000.0/3.1415, /* Adapting luminence, 20% of 1000 lux in cd/m^2 */ 0.2, /* Background relative to reference white */ 1000.0/3.1415, /* Luminance of white in the Image field (cd/m^2) */ - 0.01, x.nwh, /* 1% flare same white point */ + 0.0, 0.01, x.nwh, /* 0% flare and 1% glare same white point */ 0); break; @@ -3917,10 +4062,10 @@ int main(int argc, char *argv[]) { /* The display we're calibratings situation */ x.dvc->set_view(x.dvc, vc_none, x.nwh, /* Display normalised white point */ - 0.2 * ambient, /* Adapting luminence, 20% of ambient in cd/m^2 */ + 0.2 * ambient/3.1415, /* Adapting luminence, 20% of ambient in cd/m^2 */ 0.2, /* Background relative to reference white */ x.twh[1], /* Target white level (cd/m^2) */ - 0.01, x.nwh, /* 1% flare same white point */ + 0.0, 0.01, x.nwh, /* 0% flare and 1% glare same white point */ 0); /* Compute the normalisation values */ @@ -3984,7 +4129,7 @@ int main(int argc, char *argv[]) { } /* Setup the initial calibration test point values */ - init_csamp(&asgrey, &x, doupdate, verify, verify == 2 ? 1 : 0, rsteps); + init_csamp(&asgrey, &x, doupdate, verify, verify == 2 ? 1 : 0, rsteps, verb); /* Calculate the initial calibration curve values */ if (verify != 2 && !doupdate) { @@ -4069,13 +4214,13 @@ int main(int argc, char *argv[]) { /* Verify pass */ if (it >= mxits) - rsteps = VER_RES; /* Fixed verification resolution */ + rsteps = VER_RES; /* Fixed verification resolution */ else - thrfail = 0; /* Not verify pass */ + thrfail = 0; /* Not verify pass */ /* re-init asgrey if the number of test points has changed */ - reinit_csamp(&asgrey, &x, verify, (verify == 2 || it >= mxits) ? 1 : 0, rsteps); + reinit_csamp(&asgrey, &x, verify, (verify == 2 || it >= mxits) ? 1 : 0, rsteps, verb); if (verb) { if (it >= mxits) @@ -4084,41 +4229,67 @@ int main(int argc, char *argv[]) { printf("Doing iteration %d with %d sample points and repeat threshold of %f DE\n", it+1,rsteps, errthr); } + /* Read and adjust each step */ /* Do this white to black to track drift in native white point */ for (i = rsteps-1; i >= 0; i--) { - double rpt; + int rpt; double peqXYZ[3]; /* Previous steps equivalent aim point */ double bestrgb[3]; /* In case we fail */ double bestxyz[3]; double prevde = 1e7; + double best_de = 1e7; double bestde = 1e7; double bestdc = 1e7; double bestpeqde = 1e7; double besthde = 1e7; double rgain = REFINE_GAIN; /* Scale down if lots of repeats */ int mjac = 0; /* We measured the Jacobian */ + double ierrth = errthr; /* This points error threshold */ + + icmCpy3(asgrey.s[i].prgb, asgrey.s[i].rgb); /* Init previous */ - /* Setup a second termination threshold chriteria based on */ + /* Setup a second termination threshold criteria based on */ /* the delta E to the previous step point for the last pass. */ + /* This is to try and steer towards neutral axis consistency ? */ if (i == (rsteps-1) || it < (mxits-1)) { icmAry2Ary(peqXYZ, asgrey.s[i].tXYZ); /* Its own aim point */ } else { double Lab1[3], Lab2[3], Lab3[3]; icmXYZ2Lab(&x.twN, Lab1, asgrey.s[i+1].XYZ); icmXYZ2Lab(&x.twN, Lab2, asgrey.s[i].tXYZ); - Lab3[0] = Lab2[0]; - Lab3[1] = 0.5 * (Lab1[1] + Lab2[1]); /* Compute aim point between actual target */ - Lab3[2] = 0.5 * (Lab1[2] + Lab2[2]); /* and previous point. */ - icmLab2XYZ(&x.twN, asgrey.s[i].tXYZ, Lab3); Lab1[0] = Lab2[0]; /* L of current target with ab of previous as 2nd threshold */ - icmLab2XYZ(&x.twN, peqXYZ, Lab1); + icmLab2XYZ(&x.twN, peqXYZ, Lab1); /* Previous equivalent */ } +#ifdef ADJ_THRESH + /* Adjust the termination threshold to make sure it is less than */ + /* half a step */ + { + double de; + if (i < (rsteps-1)) { + de = 0.5 * icmXYZLabDE(&x.twN, asgrey.s[i].tXYZ, asgrey.s[i+1].tXYZ); + if (de < MIN_THRESH) /* Don't be silly */ + de = MIN_THRESH; + if (de < ierrth) + ierrth = de; + } + if (i > 0) { + de = 0.5 * icmXYZLabDE(&x.twN, asgrey.s[i].tXYZ, asgrey.s[i-1].tXYZ); + if (de < MIN_THRESH) /* Don't be silly */ + de = MIN_THRESH; + if (de < ierrth) + ierrth = de; + } + } +#endif /* ADJ_THRESH */ + /* Until we meet the necessary accuracy or give up */ - for (rpt = 0;rpt < MAX_RPTS; rpt++) { + for (rpt = 0; rpt < mxrpts; rpt++) { + double hlew = 1.0; /* high L* error weight */ int gworse = 0; /* information flag */ - double wde; /* informational */ + double w_de, wde; /* informational */ + double pjadj[3][3] = { 0.0 }; /* Previous jacobian adjustment */ set[0].r = asgrey.s[i].rgb[0]; set[0].g = asgrey.s[i].rgb[1]; @@ -4142,7 +4313,7 @@ int main(int argc, char *argv[]) { 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); /* Recompute txyz's */ + init_csamp_txyz(&asgrey, &x, 1, verb); /* Recompute txyz's */ icmAry2Ary(peqXYZ, asgrey.s[i].tXYZ); /* Fix peqXYZ */ //printf("~1 Just reset target white to native white\n"); if (wdrift) { /* Make sure white drift is reset on next read. */ @@ -4150,35 +4321,48 @@ int main(int argc, char *argv[]) { } } - /* Compute the current change wanted to hit target */ + /* Compute the next change wanted to hit target */ icmSub3(asgrey.s[i].deXYZ, asgrey.s[i].tXYZ, asgrey.s[i].XYZ); - asgrey.s[i].de = icmXYZLabDE(&x.twN, asgrey.s[i].tXYZ, asgrey.s[i].XYZ); - asgrey.s[i].peqde = icmXYZLabDE(&x.twN, peqXYZ, asgrey.s[i].XYZ); + + /* For the darkest 5% of targets, weight the L* delta E so that */ + /* we err on the darker side */ + if (asgrey.s[i].v < POWERR_THR) + hlew = 1.0 + POWERR_WEIGHT * pow((POWERR_THR - asgrey.s[i].v)/POWERR_THR, + POWERR_WEIGHT_POW); + else + hlew = 1.0; +//printf("~1 i %d, v %f, hlew %f\n",i,asgrey.s[i].v,hlew); + asgrey.s[i]._de = icmXYZLabDE(&x.twN, asgrey.s[i].tXYZ, asgrey.s[i].XYZ); + asgrey.s[i].de = bwXYZLabDE(&x.twN, asgrey.s[i].tXYZ, asgrey.s[i].XYZ, hlew); + asgrey.s[i].peqde = bwXYZLabDE(&x.twN, peqXYZ, asgrey.s[i].XYZ, hlew); asgrey.s[i].hde = 0.8 * asgrey.s[i].de + 0.2 * asgrey.s[i].peqde; /* Eudclidian difference of XYZ, because this doesn't always track Lab */ asgrey.s[i].dc = icmLabDE(asgrey.s[i].tXYZ, asgrey.s[i].XYZ); - /* Compute change from last XYZ */ + /* Compute actual change from last XYZ */ icmSub3(asgrey.s[i].dXYZ, asgrey.s[i].XYZ, asgrey.s[i].pXYZ); -#ifdef DEBUG - printf("\n\nTest point %d, v = %f\n",rsteps - i,asgrey.s[i].v); - printf("Current rgb %f %f %f -> XYZ %f %f %f, de %f, dc %f\n", - asgrey.s[i].rgb[0], asgrey.s[i].rgb[1], asgrey.s[i].rgb[2], - asgrey.s[i].XYZ[0], asgrey.s[i].XYZ[1], asgrey.s[i].XYZ[2], - asgrey.s[i].de, asgrey.s[i].dc); - printf("Target XYZ %f %f %f, delta needed %f %f %f\n", - asgrey.s[i].tXYZ[0], asgrey.s[i].tXYZ[1], asgrey.s[i].tXYZ[2], - asgrey.s[i].deXYZ[0], asgrey.s[i].deXYZ[1], asgrey.s[i].deXYZ[2]); - if (rpt > 0) { - printf("Intended XYZ change %f %f %f, actual change %f %f %f\n", - asgrey.s[i].pdXYZ[0], asgrey.s[i].pdXYZ[1], asgrey.s[i].pdXYZ[2], - asgrey.s[i].dXYZ[0], asgrey.s[i].dXYZ[1], asgrey.s[i].dXYZ[2]); + w_de = asgrey.s[i]._de; + wde = asgrey.s[i].de; + + if (verb >= 3) { + printf("\n\nTest point %d, v = %f, rpt %d\n",rsteps - i,asgrey.s[i].v,rpt); + printf("Current rgb %f %f %f -> XYZ %f %f %f, de %f, dc %f\n", + asgrey.s[i].rgb[0], asgrey.s[i].rgb[1], asgrey.s[i].rgb[2], + asgrey.s[i].XYZ[0], asgrey.s[i].XYZ[1], asgrey.s[i].XYZ[2], + asgrey.s[i]._de, asgrey.s[i].dc); + printf("Target XYZ %f %f %f, delta needed %f %f %f\n", + asgrey.s[i].tXYZ[0], asgrey.s[i].tXYZ[1], asgrey.s[i].tXYZ[2], + asgrey.s[i].deXYZ[0], asgrey.s[i].deXYZ[1], asgrey.s[i].deXYZ[2]); + if (rpt > 0) { + printf("Last intended XYZ change %f %f %f, actual change %f %f %f\n", + asgrey.s[i].pdXYZ[0], asgrey.s[i].pdXYZ[1], asgrey.s[i].pdXYZ[2], + asgrey.s[i].dXYZ[0], asgrey.s[i].dXYZ[1], asgrey.s[i].dXYZ[2]); + } } -#endif if (it < mxits) { /* Not verify, apply correction */ - int impj = 0; /* We improved the Jacobian */ + int impj = 0; /* We adjusted the Jacobian */ int dclip = 0; /* We clipped the new RGB */ #ifdef ADJ_JACOBIAN int isclipped = 0; @@ -4197,7 +4381,9 @@ int main(int argc, char *argv[]) { #ifdef REMEAS_JACOBIAN /* If the de hasn't improved, try and measure the Jacobian */ - if (it < (rsteps-1) && mjac == 0 && asgrey.s[i].de > (0.8 * prevde)) { +// if (it < (rsteps-1) && mjac == 0 && asgrey.s[i].de > (0.8 * prevde)) + if (mjac == 0 && asgrey.s[i].de > (0.8 * prevde)) + { double dd; if (asgrey.s[i].v < 0.5) dd = 0.05; @@ -4224,18 +4410,20 @@ int main(int argc, char *argv[]) { } totmeas += 3; +//printf("\n~1 remeasured jacobian\n"); /* Matrix organization is J[XYZ][RGB] for del RGB->del XYZ*/ for (j = 0; j < 3; j++) { asgrey.s[i].j[0][j] = (set[j].XYZ[0] - asgrey.s[i].XYZ[0]) / dd; asgrey.s[i].j[1][j] = (set[j].XYZ[1] - asgrey.s[i].XYZ[1]) / dd; asgrey.s[i].j[2][j] = (set[j].XYZ[2] - asgrey.s[i].XYZ[2]) / dd; } + + /* Clear pjadj */ + for (j = 0; j < 3; j++) + pjadj[3][0] = pjadj[3][1] = pjadj[3][2] = 0.0; + if (icmInverse3x3(asgrey.s[i].ij, asgrey.s[i].j)) { /* Should repeat with bigger dd ? */ -//printf("~1 matrix =\n"); -//printf("~1 %f %f %f\n", asgrey.s[i].j[0][0], asgrey.s[i].j[0][1], asgrey.s[i].j[0][2]); -//printf("~1 %f %f %f\n", asgrey.s[i].j[1][0], asgrey.s[i].j[1][1], asgrey.s[i].j[1][2]); -//printf("~1 %f %f %f\n", asgrey.s[i].j[2][0], asgrey.s[i].j[2][1], asgrey.s[i].j[2][2]); if (verb) printf("dispcal: inverting Jacobian failed (3) - falling back\n"); @@ -4244,6 +4432,7 @@ int main(int argc, char *argv[]) { } /* Restart at the best we've had */ if (asgrey.s[i].hde > besthde) { + asgrey.s[i]._de = best_de; asgrey.s[i].de = bestde; asgrey.s[i].dc = bestdc; asgrey.s[i].peqde = bestpeqde; @@ -4257,13 +4446,13 @@ int main(int argc, char *argv[]) { icmSub3(asgrey.s[i].deXYZ, asgrey.s[i].tXYZ, asgrey.s[i].XYZ); } mjac = 1; - impj = 1; + impj = 1; /* Have remeasured */ } #endif /* REMEAS_JACOBIAN */ /* Compute a correction to the Jacobian if we can. */ /* (Don't do this unless we have a solid previous */ - /* reading for this patch) */ + /* reading for this patch, and we haven't remeasured it) */ if (impj == 0 && rpt > 0 && isclipped == 0) { double nsdrgb; /* Norm squared of pdrgb */ double spdrgb[3]; /* Scaled previous delta rgb */ @@ -4272,6 +4461,10 @@ int main(int argc, char *argv[]) { double tj[3][3]; /* Temp Jacobian */ double itj[3][3]; /* Temp inverse Jacobian */ +//printf("~1 Jacobian was: %f %f %f\n", asgrey.s[i].j[0][0], asgrey.s[i].j[0][1], asgrey.s[i].j[0][2]); +//printf("~1 %f %f %f\n", asgrey.s[i].j[1][0], asgrey.s[i].j[1][1], asgrey.s[i].j[1][2]); +//printf("~1 %f %f %f\n", asgrey.s[i].j[2][0], asgrey.s[i].j[2][1], asgrey.s[i].j[2][2]); + /* Use Broyden's Formula */ icmSub3(dXYZerr, asgrey.s[i].dXYZ, asgrey.s[i].pdXYZ); //printf("~1 Jacobian error = %f %f %f\n", dXYZerr[0], dXYZerr[1], dXYZerr[2]); @@ -4287,31 +4480,68 @@ int main(int argc, char *argv[]) { { double eXYZ[3]; +//printf("~1 del RGB %f %f %f got del XYZ %f %f %f\n", asgrey.s[i].pdrgb[0], asgrey.s[i].pdrgb[1], asgrey.s[i].pdrgb[2], asgrey.s[i].dXYZ[0], asgrey.s[i].dXYZ[1], asgrey.s[i].dXYZ[2]); + /* Make a full adjustment to temporary Jac */ icmAdd3x3(tj, asgrey.s[i].j, jadj); + +//printf("~1 Full Jacobian: %f %f %f\n", tj[0][0], tj[0][1], tj[0][2]); +//printf("~1 %f %f %f\n", tj[1][0], tj[1][1], tj[1][2]); +//printf("~1 %f %f %f\n", tj[2][0], tj[2][1], tj[2][2]); + icmMulBy3x3(eXYZ, tj, asgrey.s[i].pdrgb); icmSub3(eXYZ, eXYZ, asgrey.s[i].dXYZ); printf("Jac check resid %f %f %f\n", eXYZ[0], eXYZ[1], eXYZ[2]); } #endif /* DEBUG */ + /* Add to portion of previous adjustment */ + /* to counteract undershoot & overshoot */ + icmScale3x3(pjadj, pjadj, JAC_COMP_FACT); + icmAdd3x3(jadj, jadj, pjadj); + icmCpy3x3(pjadj, jadj); + /* Add part of our correction to actual Jacobian */ + /* to smooth out correction to counteract noise */ icmScale3x3(jadj, jadj, JAC_COR_FACT); icmAdd3x3(tj, asgrey.s[i].j, jadj); + if (icmInverse3x3(itj, tj) == 0) { /* Invert OK */ icmCpy3x3(asgrey.s[i].j, tj); /* Use adjusted */ icmCpy3x3(asgrey.s[i].ij, itj); impj = 1; + +#ifdef NEVER + /* Check how close new Jacobian predicts previous delta XYZ */ + { + double eXYZ[3]; + double ergb[3]; + + icmMulBy3x3(eXYZ, asgrey.s[i].j, asgrey.s[i].pdrgb); + icmSub3(eXYZ, eXYZ, asgrey.s[i].dXYZ); + printf("Jac check2 resid %f %f %f\n", eXYZ[0], eXYZ[1], eXYZ[2]); + + icmMulBy3x3(ergb, asgrey.s[i].ij, asgrey.s[i].pdXYZ); + printf("Jac check2 drgb would have been %f %f %f\n", ergb[0], ergb[1], ergb[2]); + icmAdd3(ergb, ergb, asgrey.s[i].prgb); + printf("Jac check2 rgb would have been %f %f %f\n", ergb[0], ergb[1], ergb[2]); + } +#endif } -//else printf("~1 ij failed\n"); +//else printf("~1 ij failed - reverted\n"); } //else printf("~1 nsdrgb was below threshold\n"); } //else if (isclipped) printf("~1 no j update: rgb is clipped\n"); +//printf("~1 Jacobian now: %f %f %f\n", asgrey.s[i].j[0][0], asgrey.s[i].j[0][1], asgrey.s[i].j[0][2]); +//printf("~1 %f %f %f\n", asgrey.s[i].j[1][0], asgrey.s[i].j[1][1], asgrey.s[i].j[1][2]); +//printf("~1 %f %f %f\n", asgrey.s[i].j[2][0], asgrey.s[i].j[2][1], asgrey.s[i].j[2][2]); + #endif /* ADJ_JACOBIAN */ /* Track the best solution we've found */ if (asgrey.s[i].hde <= besthde) { + best_de = asgrey.s[i]._de; bestde = asgrey.s[i].de; bestdc = asgrey.s[i].dc; bestpeqde = asgrey.s[i].peqde; @@ -4323,13 +4553,14 @@ int main(int argc, char *argv[]) { bestxyz[1] = asgrey.s[i].XYZ[1]; bestxyz[2] = asgrey.s[i].XYZ[2]; +//printf("~1 new best\n"); } else if (asgrey.s[i].dc > bestdc) { /* we got worse in Lab and XYZ ! */ - wde = asgrey.s[i].de; - /* If we've wandered too far, return to best we found */ if (asgrey.s[i].hde > (3.0 * besthde)) { +//printf("~1 resetting to last best\n"); + asgrey.s[i]._de = best_de; asgrey.s[i].de = bestde; asgrey.s[i].dc = bestdc; asgrey.s[i].peqde = bestpeqde; @@ -4344,22 +4575,26 @@ int main(int argc, char *argv[]) { } /* If the Jacobian hasn't changed, moderate the gain */ - if (impj == 0) + if (impj == 0) { rgain *= 0.8; /* We might be overshooting */ +//printf("~1 reducing rgain to %f\n",rgain); + } gworse = 1; } /* See if we need to repeat */ - if (asgrey.s[i].de <= errthr && asgrey.s[i].peqde < errthr) { - if (verb > 1) + if (asgrey.s[i].de <= ierrth && asgrey.s[i].peqde < ierrth) { + if (verb > 1) { if (it < (mxits-1)) - printf("Point %d Delta E %f, OK\n",rsteps - i,asgrey.s[i].de); + printf("Point %d DE %f, W.DE %f, OK ( < %f)\n",rsteps - i,asgrey.s[i]._de, asgrey.s[i].de, ierrth); else - printf("Point %d Delta E %f, peqDE %f, OK\n",rsteps - i,asgrey.s[i].de, asgrey.s[i].peqde); + printf("Point %d DE %f, W.DE %f, W.peqDE %f, OK ( < %f)\n",rsteps - i,asgrey.s[i]._de,asgrey.s[i].de, asgrey.s[i].peqde, ierrth); + } break; /* No more retries */ } - if ((rpt+1) >= MAX_RPTS) { - asgrey.s[i].de = bestde; /* Restore to best we found */ + if ((rpt+1) >= mxrpts) { + asgrey.s[i]._de = best_de; /* Restore to best we found */ + asgrey.s[i].de = bestde; asgrey.s[i].dc = bestdc; asgrey.s[i].peqde = bestpeqde; /* Restore to best we found */ asgrey.s[i].hde = besthde; /* Restore to best we found */ @@ -4369,11 +4604,12 @@ int main(int argc, char *argv[]) { asgrey.s[i].XYZ[0] = bestxyz[0]; asgrey.s[i].XYZ[1] = bestxyz[1]; asgrey.s[i].XYZ[2] = bestxyz[2]; - if (verb > 1) + if (verb > 1) { if (it < (mxits-1)) - printf("Point %d Delta E %f, Fail\n",rsteps - i,asgrey.s[i].de); + printf("Point %d DE %f, W.DE %f, Fail ( > %f)\n",rsteps - i,asgrey.s[i]._de, asgrey.s[i].de, ierrth); else - printf("Point %d Delta E %f, peqDE %f, Fail\n",rsteps - i,asgrey.s[i].de,asgrey.s[i].peqde); + printf("Point %d DE %f, W.DE %f, W.peqDE %f, Fail ( > %f)\n",rsteps - i,asgrey.s[i]._de,asgrey.s[i].de,asgrey.s[i].peqde,ierrth); + } thrfail = 1; /* Failed to meet target */ if (bestde > failerr) failerr = bestde; /* Worst failed delta E */ @@ -4382,26 +4618,28 @@ int main(int argc, char *argv[]) { if (verb > 1) { if (gworse) if (it < (mxits-1)) - printf("Point %d Delta E %f, Repeat (got worse)\n", rsteps - i, wde); + printf("Point %d DE %f, W.DE %f, Repeat (got worse)\n", rsteps - i, w_de, wde); else - printf("Point %d Delta E %f, peqDE %f, Repeat (got worse)\n", rsteps - i, wde,asgrey.s[i].peqde); + printf("Point %d DE %f, W.DE %f, peqDE %f, Repeat (got worse)\n", rsteps - i, w_de, wde,asgrey.s[i].peqde); else if (it < (mxits-1)) - printf("Point %d Delta E %f, Repeat\n", rsteps - i,asgrey.s[i].de); + printf("Point %d DE %f, W.DE %f, Repeat\n", rsteps - i,asgrey.s[i]._de,asgrey.s[i].de); else - printf("Point %d Delta E %f, peqDE %f, Repeat\n", rsteps - i,asgrey.s[i].de,asgrey.s[i].peqde); + printf("Point %d DE %f, W.DE %f, peqDE %f, Repeat\n", rsteps - i,asgrey.s[i]._de,asgrey.s[i].de,asgrey.s[i].peqde); } +//printf("~1 RGB Jacobian: %f %f %f\n", asgrey.s[i].j[0][0], asgrey.s[i].j[0][1], asgrey.s[i].j[0][2]); +//printf("~1 %f %f %f\n", asgrey.s[i].j[1][0], asgrey.s[i].j[1][1], asgrey.s[i].j[1][2]); +//printf("~1 %f %f %f\n", asgrey.s[i].j[2][0], asgrey.s[i].j[2][1], asgrey.s[i].j[2][2]); /* Compute refinement of rgb */ icmMulBy3x3(asgrey.s[i].pdrgb, asgrey.s[i].ij, asgrey.s[i].deXYZ); -//printf("~1 delta needed %f %f %f -> delta RGB %f %f %f\n", +//printf("~1 XYZ delta needed %f %f %f -> delta RGB %f %f %f\n", //asgrey.s[i].deXYZ[0], asgrey.s[i].deXYZ[1], asgrey.s[i].deXYZ[2], //asgrey.s[i].pdrgb[0], asgrey.s[i].pdrgb[1], asgrey.s[i].pdrgb[2]); /* Gain scale */ icmScale3(asgrey.s[i].pdrgb, asgrey.s[i].pdrgb, rgain); -//printf("~1 delta RGB after gain scale %f %f %f\n", -//asgrey.s[i].pdrgb[0], asgrey.s[i].pdrgb[1], asgrey.s[i].pdrgb[2]); +//printf("~1 delta RGB after gain scale %f %f %f\n", asgrey.s[i].pdrgb[0], asgrey.s[i].pdrgb[1], asgrey.s[i].pdrgb[2]); #ifdef CLIP /* Component wise clip */ @@ -4415,28 +4653,39 @@ int main(int argc, char *argv[]) { dclip = 1; } } -#ifdef DEBUG - if (dclip) printf("delta RGB after clip %f %f %f\n", + if (verb >= 3 && dclip) printf("delta RGB after clip %f %f %f\n", asgrey.s[i].pdrgb[0], asgrey.s[i].pdrgb[1], asgrey.s[i].pdrgb[2]); -#endif /* DEBUG */ #endif /* CLIP */ /* Compute next on the basis of this one RGB */ + icmCpy3(asgrey.s[i].prgb, asgrey.s[i].rgb); /* Save previous */ icmAdd3(asgrey.s[i].rgb, asgrey.s[i].rgb, asgrey.s[i].pdrgb); /* Save expected change in XYZ */ icmMulBy3x3(asgrey.s[i].pdXYZ, asgrey.s[i].j, asgrey.s[i].pdrgb); -#ifdef DEBUG - printf("New rgb %f %f %f from expected del XYZ %f %f %f\n", - asgrey.s[i].rgb[0], asgrey.s[i].rgb[1], asgrey.s[i].rgb[2], - asgrey.s[i].pdXYZ[0], asgrey.s[i].pdXYZ[1], asgrey.s[i].pdXYZ[2]); -#endif + if (verb >= 3) { + printf("New rgb %f %f %f from expected del XYZ %f %f %f\n", + asgrey.s[i].rgb[0], asgrey.s[i].rgb[1], asgrey.s[i].rgb[2], + asgrey.s[i].pdXYZ[0], asgrey.s[i].pdXYZ[1], asgrey.s[i].pdXYZ[2]); + } } else { /* Verification, so no repeat */ break; } prevde = asgrey.s[i].de; } /* Next repeat */ - } /* Next resolution step */ + + if (verb >= 3) { + printf("After adjustment:\n"); + printf("Current rgb %f %f %f -> XYZ %f %f %f, de %f, dc %f\n", + asgrey.s[i].rgb[0], asgrey.s[i].rgb[1], asgrey.s[i].rgb[2], + asgrey.s[i].XYZ[0], asgrey.s[i].XYZ[1], asgrey.s[i].XYZ[2], + asgrey.s[i].de, asgrey.s[i].dc); + printf("Target XYZ %f %f %f, delta needed %f %f %f\n", + asgrey.s[i].tXYZ[0], asgrey.s[i].tXYZ[1], asgrey.s[i].tXYZ[2], + asgrey.s[i].deXYZ[0], asgrey.s[i].deXYZ[1], asgrey.s[i].deXYZ[2]); + } + + } /* Next patch/step */ if (verb) printf("\n"); /* Final return for patch count */ @@ -4487,7 +4736,7 @@ int main(int argc, char *argv[]) { /* We're checking against our given brightness and */ /* white point target. */ mnerr = anerr = 0.0; - init_csamp_txyz(&asgrey, &x, 0); /* In case the targets were tweaked */ + init_csamp_txyz(&asgrey, &x, 0, verb); /* In case the targets were tweaked */ for (i = 0; i < asgrey.no; i++) { double err; @@ -4518,7 +4767,7 @@ int main(int argc, char *argv[]) { } /* Verify loop exit */ - if (it >= (mxits + nver -1)) { + if (nver > 0 && it >= (mxits + nver -1)) { break; } @@ -4546,7 +4795,6 @@ int main(int argc, char *argv[]) { sdv[j][i].p = asgrey.s[i].v; sdv[j][i].v = asgrey.s[i].rgb[j]; sdv[j][i].w = 1.0; -// ~~999 #ifdef NEVER printf("rdac %d point %d = %f, %f\n",j,i,sdv[j][i].p,sdv[j][i].v); #endif @@ -4623,6 +4871,8 @@ int main(int argc, char *argv[]) { /* Tell downstream whether they can expect that this calibration */ /* will be applied in hardware or not. */ ocg->add_kword(ocg, 0, "VIDEO_LUT_CALIBRATION_POSSIBLE",noramdac ? "NO" : "YES", NULL); + /* Tell downstream whether the device range was actually (16-235)/255 */ + ocg->add_kword(ocg, 0, "TV_OUTPUT_ENCODING",out_tvenc ? "YES" : "NO", NULL); /* Put the target parameters in the CGATS file too */ if (dtype != 0) { @@ -4833,6 +5083,16 @@ int main(int argc, char *argv[]) { cc = 0.0; else if (cc > 1.0) cc = 1.0; + if (out_tvenc) { + cc = (cc * (235.0-16.0) + 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. */ + /* We assume the precision is the vcgt table size = 16 */ + /* ~~99 ideally we should tag the fact that this is video encoded, so that */ + /* the vcgt loaded can adjust for a different bit precision ~~~~ */ + cc = (cc * 255 * (1 << (16 - 8)))/((1 << 16) - 1.0); + } ((unsigned short*)wo->u.table.data)[CAL_RES * j + i] = (int)(cc * 65535.0 + 0.5); } } @@ -5174,6 +5434,15 @@ int main(int argc, char *argv[]) { cc = 0.0; else if (cc > 1.0) cc = 1.0; + if (out_tvenc) { + cc = (cc * (235.0-16.0) + 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. We assume the precision is the vcgt table size = 16 */ + /* ~~99 ideally we should tag the fact that this is video encoded, so */ + /* that the vcgt loaded can adjust for a different bit precision ~~~~ */ + cc = (cc * 255 * (1 << (16 - 8)))/((1 << 16) - 1.0); + } ((unsigned short*)wo->u.table.data)[CAL_RES * j + i] = (int)(cc * 65535.0 + 0.5); } } @@ -5197,9 +5466,13 @@ int main(int argc, char *argv[]) { wog->allocate((icmBase *)wog); wob->allocate((icmBase *)wob); - wor->data[0].X = mat[0][0]; wor->data[0].Y = mat[1][0]; wor->data[0].Z = mat[2][0]; - wog->data[0].X = mat[0][1]; wog->data[0].Y = mat[1][1]; wog->data[0].Z = mat[2][1]; - wob->data[0].X = mat[0][2]; wob->data[0].Y = mat[1][2]; wob->data[0].Z = mat[2][2]; + /* Make sure rounding doesn't wreck white point */ + icmTranspose3x3(mat, mat); /* Convert [XYZ][RGB] to [RGB][XYZ] */ + quantizeRGBprimsS15Fixed16(mat); + + wor->data[0].X = mat[0][0]; wor->data[0].Y = mat[0][1]; wor->data[0].Z = mat[0][2]; + wog->data[0].X = mat[1][0]; wog->data[0].Y = mat[1][1]; wog->data[0].Z = mat[1][2]; + wob->data[0].X = mat[2][0]; wob->data[0].Y = mat[2][1]; wob->data[0].Z = mat[2][2]; } /* Red, Green and Blue Tone Reproduction Curve Tags: */ diff --git a/spectro/dispread.c b/spectro/dispread.c index 4a8d364..990501f 100644 --- a/spectro/dispread.c +++ b/spectro/dispread.c @@ -84,7 +84,7 @@ static int gcc_bug_fix(int i) { Flags used: ABCDEFGHIJKLMNOPQRSTUVWXYZ - upper .. . .... .. .. ... + upper .... .... .. .. ... lower .. . . . . .. . */ @@ -129,6 +129,9 @@ void usage(char *diag, ...) { } free_disppaths(dp); fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); +#ifdef NT + 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," -c listno Set communication port from the following list (default %d)\n",COMPORT); if ((icmps = new_icompaths(g_log)) != NULL) { @@ -150,6 +153,9 @@ void usage(char *diag, ...) { cap2 = inst_show_disptype_options(stderr, " -y ", icmps, 0); fprintf(stderr," -k file.cal Load calibration file into display while reading\n"); fprintf(stderr," -K file.cal Apply calibration file to test values while reading\n"); +#ifdef NT + fprintf(stderr," -V Enable MadVR color management (3dLut)\n"); +#endif fprintf(stderr," -s Save spectral information (default don't save)\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"); @@ -158,6 +164,7 @@ void usage(char *diag, ...) { #if defined(UNIX_X11) 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," -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"); @@ -171,7 +178,9 @@ void usage(char *diag, ...) { fprintf(stderr," 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2, 1964_10c\n"); } fprintf(stderr," -I b|w Drift compensation, Black: -Ib, White: -Iw, Both: -Ibw\n"); + fprintf(stderr," -Y R:rate Override measured refresh rate with rate Hz\n"); fprintf(stderr," -Y A Use non-adaptive integration time mode (if available).\n"); + fprintf(stderr," -Y p Don't wait for the instrument to be placed on the display\n"); fprintf(stderr," -C \"command\" Invoke shell \"command\" each time a color is set\n"); fprintf(stderr," -M \"command\" Invoke shell \"command\" each time a color is measured\n"); fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n"); @@ -188,6 +197,7 @@ int main(int argc, char *argv[]) { 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 blackbg = 0; /* NZ if whole screen should be filled with black */ int verb = 0; int debug = 0; @@ -199,12 +209,14 @@ int main(int argc, char *argv[]) { flow_control fc = fc_nc; /* Default flow control */ int docalib = 0; /* Do a calibration */ int highres = 0; /* Use high res mode if available */ + double refrate = 0.0; /* 0.0 = default, > 0.0 = override refresh rate */ int nadaptive = 0; /* Use non-adaptive mode if available */ int bdrift = 0; /* Flag, nz for black drift compensation */ int wdrift = 0; /* Flag, nz for white drift compensation */ int dtype = 0; /* Display type selection charater */ 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 */ char ccxxname[MAXNAMEL+1] = "\000"; /* Colorimeter Correction Matrix name */ ccmx *cmx = NULL; /* Colorimeter Correction Matrix */ @@ -212,12 +224,18 @@ int main(int argc, char *argv[]) { int spec = 0; /* Don't save spectral information */ icxObserverType obType = icxOT_default; int webdisp = 0; /* NZ for web display, == port number */ +#ifdef NT + int madvrdisp = 0; /* NZ for MadVR display */ +#endif char *ccallout = NULL; /* Change color Shell callout */ char *mcallout = NULL; /* Measure color Shell callout */ char inname[MAXNAMEL+1] = "\000"; /* Input cgats file base name */ char outname[MAXNAMEL+1] = "\000"; /* Output cgats file base name */ char calname[MAXNAMEL+1] = "\000"; /* Calibration file name (if any) */ - int softcal = 0; /* nz if cal applied to values rather than hardware */ + int native = 2; /* X0 = use current per channel calibration curve */ + /* X1 = set native linear output and use ramdac high prec */ + /* 0X = use current color management cLut (MadVR) */ + /* 1X = disable color management cLUT (MadVR) */ double cal[3][MAX_CAL_ENT]; /* Display calibration */ int ncal = 256; /* number of cal entries used */ cgats *icg; /* input cgats structure */ @@ -237,6 +255,7 @@ int main(int argc, char *argv[]) { cgats_set_elem *setel; /* Array of set value elements */ disprd *dr; /* Display patch read object */ int noramdac = 0; /* Will be set to nz if can't set ramdac */ + int nocm = 0; /* Will be set to nz if can't set color management */ int errc; /* Return value from new_disprd() */ int rv; @@ -295,6 +314,12 @@ int main(int argc, char *argv[]) { usage("Web port number must be in range 1..65535"); } fa = nfa; +#ifdef NT + } else if (strncmp(na,"madvr",5) == 0 + || strncmp(na,"MADVR",5) == 0) { + madvrdisp = 1; + fa = nfa; +#endif } else { #if defined(UNIX_X11) int ix, iv; @@ -358,17 +383,24 @@ int main(int argc, char *argv[]) { dtype = na[0]; /* Calibration file */ - } else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { - fa = nfa; - if (na == NULL) usage("Parameter expected after -k/-K"); + } else if (argv[fa][1] == 'k' + || argv[fa][1] == 'K') { + if (na == NULL) usage("Parameter expected after -%c",argv[fa][1]); strncpy(calname,na,MAXNAMEL); calname[MAXNAMEL] = '\000'; - softcal = 0; if (argv[fa][1] == 'K') - softcal = 1; - } + native |= 1; /* Use native linear & soft cal */ + else + native &= ~1; /* Use HW cal */ + fa = nfa; + +#ifdef NT + /* MadVR verify mode */ + } else if (argv[fa][1] == 'V') { + native &= ~2; +#endif /* Save spectral data */ - else if (argv[fa][1] == 's') { + } else if (argv[fa][1] == 's') { spec = 1; /* Test patch offset and size */ @@ -394,6 +426,10 @@ int main(int argc, char *argv[]) { } else if (argv[fa][1] == 'F') { blackbg = 1; + /* Video encoded output */ + } else if (argv[fa][1] == 'E') { + out_tvenc = 1; + /* Force calibration */ } else if (argv[fa][1] == 'J') { docalib = 1; @@ -406,10 +442,6 @@ int main(int argc, char *argv[]) { } else if (argv[fa][1] == 'H') { highres = 1; - /* Adaptive mode - default, so flag is deprecated */ - } else if (argv[fa][1] == 'V') { - warning("dispread -V flag is deprecated"); - /* No normalisation */ } else if (argv[fa][1] == 'w') { nonorm = 1; @@ -495,11 +527,20 @@ int main(int argc, char *argv[]) { if (na == NULL) usage("Flag '-Y' expects extra flag"); - if (na[0] == 'A') { + if (na[0] == 'R') { + if (na[1] != ':') + usage("-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); + } else if (na[0] == 'p') { + noplace = 1; + } else if (na[0] == 'A') { nadaptive = 1; } else { usage("Flag '-Y %c' not recognised",na[0]); } + fa = nfa; } else usage("Flag '-%c' not recognised",argv[fa][1]); @@ -560,15 +601,19 @@ int main(int argc, char *argv[]) { } if (fake) - comport = -99; + comport = FAKE_DEVICE_PORT; if ((icmps = new_icompaths(g_log)) == NULL) error("Finding instrument paths failed"); if ((ipath = icmps->get_path(icmps, comport)) == NULL) error("No instrument at port %d",comport); if (docalib) { - if ((rv = disprd_calibration(ipath, fc, dtype, 0, tele, nadaptive, noautocal, disp, - webdisp, blackbg, override, + if ((rv = disprd_calibration(ipath, fc, dtype, 0, tele, nadaptive, noautocal, + disp, webdisp, +#ifdef NT + madvrdisp, +#endif + out_tvenc, blackbg, override, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, g_log)) != 0) { error("docalibration failed with return value %d\n",rv); @@ -619,6 +664,9 @@ int main(int argc, char *argv[]) { if ((ti = icg->find_kword(icg, 0, "FULL_SPREAD_PATCHES")) >= 0) ocg->add_kword(ocg, 0, "FULL_SPREAD_PATCHES",icg->t[0].kdata[ti], NULL); + if ((ti = icg->find_kword(icg, 0, "DARK_REGION_EMPHASIS")) >= 0) + ocg->add_kword(ocg, 0, "DARK_REGION_EMPHASIS",icg->t[0].kdata[ti], NULL); + if (verb) { printf("Number of patches = %d\n",npat); } @@ -717,11 +765,19 @@ int main(int argc, char *argv[]) { if ((fi = ccg->find_kword(ccg, 0, "VIDEO_LUT_CALIBRATION_POSSIBLE")) >= 0) { if (stricmp(ccg->t[0].kdata[fi],"NO") == 0) { - softcal = 1; + native = 1; if (verb) printf("Switching to soft cal because there is no access to VideoLUTs\n"); } } + if ((fi = ccg->find_kword(ccg, 0, "TV_OUTPUT_ENCODING")) >= 0) { + if (!out_tvenc && (strcmp(ccg->t[0].kdata[fi], "YES") == 0 + || strcmp(ccg->t[0].kdata[fi], "yes")) == 0) { + if (verb) printf("Using Video range (16-235)/255 range encoding because cal. used it\n"); + out_tvenc = 1; + } + } + if ((fi = ccg->find_kword(ccg, 0, "COLOR_REP")) < 0) error ("Calibration file '%s' doesn't contain keyword COLOR_REP",calname); if (strcmp(ccg->t[0].kdata[fi],"RGB") != 0) @@ -753,9 +809,14 @@ 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, - highres, 0, &noramdac, cal, ncal, softcal, disp, blackbg, override, - webdisp, ccallout, mcallout, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, + if ((dr = new_disprd(&errc, ipath, fc, dtype, 0, tele, nadaptive, noautocal, noplace, + highres, refrate, native, &noramdac, &nocm, cal, ncal, disp, + out_tvenc, blackbg, override, webdisp, +#ifdef NT + madvrdisp, +#endif + ccallout, mcallout, + 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, cmx != NULL ? cmx->matrix : NULL, ccs != NULL ? ccs->samples : NULL, ccs != NULL ? ccs->no_samp : 0, spec, obType, NULL, bdrift, wdrift, @@ -768,7 +829,7 @@ int main(int argc, char *argv[]) { ccs->del(ccs); /* Test the CRT with all of the test points */ - if ((rv = dr->read(dr, cols, npat + xpat, 1, npat + xpat, 1, 0, instClamp)) != 0) { + 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); } @@ -824,7 +885,7 @@ int main(int argc, char *argv[]) { nn = 1.0; else { if (cols[wpat].XYZ_v == 0) - error("XYZ of white patch is not valid!",i); + 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 */ } @@ -832,7 +893,7 @@ int main(int argc, char *argv[]) { for (i = 0; i < npat; i++) { if (cols[i].XYZ_v == 0) - error("XYZ %d is not valid!",i); + warning("XYZ patch %d is not valid!",i+1); for (j = 0; j < 3; j++) cols[i].XYZ[j] = nn * cols[i].XYZ[j]; @@ -881,6 +942,11 @@ int main(int argc, char *argv[]) { for (i = 0; i < npat; i++) { int k = 0; + if (cols[i].XYZ_v == 0) { + warning("Omitting patch %d from .ti3 file!",i+1); + continue; + } + setel[k++].c = cols[i].id; setel[k++].d = 100.0 * cols[i].r; setel[k++].d = 100.0 * cols[i].g; diff --git a/spectro/dispsup.c b/spectro/dispsup.c index 0dc29ad..a27dab0 100644 --- a/spectro/dispsup.c +++ b/spectro/dispsup.c @@ -44,6 +44,9 @@ #include "dispwin.h" #include "dispsup.h" #include "webwin.h" +#ifdef NT +# include "madvrwin.h" +#endif #include "instappsup.h" #undef SIMPLE_MODEL /* Make fake device well behaved */ @@ -56,9 +59,11 @@ #ifdef SIMPLE_MODEL # undef FAKE_NOISE /* Add noise to _fake_ devices XYZ */ +# undef FAKE_UNPREDIC /* Initialise random unpredictably */ # undef FAKE_BITS /* Number of bits of significance of fake device */ #else # define FAKE_NOISE 0.01 /* Add noise to _fake_ devices XYZ */ +# define FAKE_UNPREDIC /* Initialise random unpredictably */ # define FAKE_BITS 9 /* Number of bits of significance of fake device */ #endif @@ -75,7 +80,7 @@ inst_code setup_display_calibrate( ) { inst_code rv = inst_ok, ev; dispwin *dw; /* Display window to display test patches on, NULL if none. */ - a1logd(p->log,1,"setup_display_calibrate called\n"); + a1logd(p->log,1,"setup_display_calibrate called with calc = 0x%x\n",calc); switch (calc) { case inst_calc_none: /* Use this as a cleanup flag */ @@ -86,75 +91,70 @@ inst_code setup_display_calibrate( break; case inst_calc_emis_white: - if (dwi->dw == NULL) { - if (dwi->webdisp != 0) { - if ((dwi->_dw = new_webwin(dwi->webdisp, dwi->hpatsize, dwi->vpatsize, - dwi->ho, dwi->vo, 0, 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; - } - } else { - if ((dwi->_dw = new_dispwin(dwi->disp, dwi->hpatsize, dwi->vpatsize, - dwi->ho, dwi->vo, 0, 0, NULL, dwi->blackbg, - dwi->override, 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; - } - } - printf("Frequency calibration, Place instrument on test window.\n"); - printf(" Hit any key to continue,\n"); - printf(" or hit Esc or Q to abort:"); - } else { - dwi->_dw = dwi->dw; - } - p->cal_gy_level = 1.0; - dwi->_dw->set_color(dwi->_dw, 1.0, 1.0, 1.0); - break; - + case inst_calc_emis_80pc: case inst_calc_emis_grey: case inst_calc_emis_grey_darker: case inst_calc_emis_grey_ligher: if (dwi->dw == NULL) { if (dwi->webdisp != 0) { if ((dwi->_dw = new_webwin(dwi->webdisp, dwi->hpatsize, dwi->vpatsize, - dwi->ho, dwi->vo, 0, dwi->blackbg, - p->log->verb, p->log->debug)) == NULL) { + 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, + 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; + } +#endif /* NT */ } else { if ((dwi->_dw = new_dispwin(dwi->disp, dwi->hpatsize, dwi->vpatsize, - dwi->ho, dwi->vo, 0, 0, NULL, dwi->blackbg, - dwi->override, p->log->debug)) == NULL) { + dwi->ho, dwi->vo, 0, 0, NULL, NULL, dwi->out_tvenc, dwi->blackbg, + dwi->override, 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; } } - printf("Cell ratio calibration, Place instrument on test window.\n"); + printf("Calibration: Place instrument on test window.\n"); printf(" Hit any key to continue,\n"); printf(" or hit Esc or Q to abort:"); } else { dwi->_dw = dwi->dw; } - if (calc == inst_calc_emis_grey) { - p->cal_gy_level = 0.6; - p->cal_gy_count = 0; - } else if (calc == inst_calc_emis_grey_darker) { - p->cal_gy_level *= 0.7; - p->cal_gy_count++; - } else if (calc == inst_calc_emis_grey_ligher) { - p->cal_gy_level *= 1.4; - if (p->cal_gy_level > 1.0) - p->cal_gy_level = 1.0; - p->cal_gy_count++; - } - if (p->cal_gy_count > 4) { - printf("Cell ratio calibration failed - too many tries at setting grey level.\n"); - a1logd(p->log,1,"inst_handle_calibrate too many tries at setting grey level 0x%x\n",inst_internal_error); - return inst_internal_error; + + if (calc == inst_calc_emis_white) { + p->cal_gy_level = 1.0; + dwi->_dw->set_color(dwi->_dw, 1.0, 1.0, 1.0); + + } else if (calc == inst_calc_emis_80pc) { + p->cal_gy_level = 0.8; + dwi->_dw->set_color(dwi->_dw, 0.8, 0.8, 0.8); + + } else { + if (calc == inst_calc_emis_grey) { + p->cal_gy_level = 0.6; + p->cal_gy_count = 0; + } else if (calc == inst_calc_emis_grey_darker) { + p->cal_gy_level *= 0.7; + p->cal_gy_count++; + } else if (calc == inst_calc_emis_grey_ligher) { + p->cal_gy_level *= 1.4; + if (p->cal_gy_level > 1.0) + p->cal_gy_level = 1.0; + p->cal_gy_count++; + } + if (p->cal_gy_count > 4) { + printf("Cell ratio calibration failed - too many tries at setting grey level.\n"); + a1logd(p->log,1,"inst_handle_calibrate too many tries at setting grey level 0x%x\n",inst_internal_error); + return inst_internal_error; + } + dwi->_dw->set_color(dwi->_dw, p->cal_gy_level, p->cal_gy_level, p->cal_gy_level); } - dwi->_dw->set_color(dwi->_dw, p->cal_gy_level, p->cal_gy_level, p->cal_gy_level); break; default: @@ -180,6 +180,10 @@ 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 */ +#ifdef NT +int madvrdisp, /* NZ for MadVR display */ +#endif +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 */ double hpatsize, /* Size of dispwin */ @@ -201,7 +205,11 @@ a1log *log /* Verb, debug & error log */ memset((void *)&dwi, 0, sizeof(disp_win_info)); dwi.webdisp = webdisp; +#ifdef NT + dwi.madvrdisp = madvrdisp; +#endif dwi.disp = disp; + dwi.out_tvenc = out_tvenc; dwi.blackbg = blackbg; dwi.override = override; dwi.hpatsize = hpatsize; @@ -209,8 +217,6 @@ a1log *log /* Verb, debug & error log */ dwi.ho = ho; dwi.vo = vo; - p->log = new_a1log_d(log); - a1logv(log, 1, "Setting up the instrument\n"); if ((p = new_inst(ipath, 0, log, DUIH_FUNC_AND_CONTEXT)) == NULL) { @@ -218,6 +224,8 @@ a1log *log /* Verb, debug & error log */ return -1; } + p->log = new_a1log_d(log); + /* Establish communications */ if ((rv = p->init_coms(p, br, fc, 15.0)) != inst_ok) { a1logd(p->log, 1, "init_coms returned '%s' (%s)\n", @@ -234,6 +242,9 @@ a1log *log /* Verb, debug & error log */ return -1; } +// ~~~~9999 should we call config_inst_displ(p) instead of badly duplicating +// the instrument setup here ??? + itype = p->get_itype(p); /* Actual type */ p->capabilities(p, &cap, &cap2, &cap3); @@ -243,6 +254,12 @@ a1log *log /* Verb, debug & error log */ tele = 0; } + if (!tele && !IMODETST(cap, inst_mode_emis_spot)) { + printf("Want emissive spot measurement capability but instrument doesn't support it\n"); + printf("so switching to telephoto spot mode.\n"); + tele = 1; + } + /* Set to emission mode to read a display */ if (tele) mode = inst_mode_emis_tele; @@ -251,6 +268,9 @@ a1log *log /* Verb, debug & error log */ if (nadaptive) mode |= inst_mode_emis_nonadaptive; +// if (p->highres) +// mode |= inst_mode_highres; + /* (We're assuming spectral doesn't affect calibration ?) */ if ((rv = p->set_mode(p, mode)) != inst_ok) { @@ -308,13 +328,21 @@ a1log *log /* Verb, debug & error log */ return 0; } +/* Set color to black after 50msec delay */ +int del_set_black(void *cx) { + disprd *p = (disprd *)cx; + int rv; + + msec_sleep(100); + 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); + return 3; + } + return 0; +} + /* Take a series of readings from the display - implementation */ -/* Return nz on fail/abort */ -/* 1 = user aborted */ -/* 2 = instrument access failed */ -/* 3 = window access failed */ -/* 4 = user hit terminate key */ -/* 5 = system error */ +/* Return nz on fail/abort - see dispsup.h */ /* Use disprd_err() to interpret it */ static int disprd_read_imp( disprd *p, @@ -350,19 +378,20 @@ 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; /* Set white with a normal delay */ if ((rv = p->dw->set_color(p->dw, 1.0, 1.0, 1.0)) != 0) { - a1logd(p->log,1,"set_color() returned %s\n",rv); + a1logd(p->log,1,"set_color() returned %d\n",rv); return 3; } - /* Set a zero delay */ + /* Set a zero return delay */ cdelay = p->dw->set_update_delay(p->dw, 0); - /* Set black with zero delay */ - if ((rv = p->dw->set_color(p->dw, 0.0, 0.0, 0.0)) != 0) { - a1logd(p->log,1,"set_color() returned %s\n",rv); + /* Set black 50msec after this call */ + if ((th = new_athread(del_set_black, (void *)p)) == NULL) { + a1logd(p->log,1,"failed to create thread to set_color()\n"); return 3; } @@ -372,24 +401,29 @@ static int disprd_read_imp( 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 */ } 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 + 50; + mdelay += mdelay/2 + 100; p->dw->set_update_delay(p->dw, mdelay); a1logv(p->log, 1, ", using delay of %d msec\n",mdelay); } p->update_delay_set = 1; + th->del(th); } /* See if we should do a frequency calibration or display integration time cal. first */ if (p->it->needs_calibration(p->it) & inst_calt_ref_freq && npat > 0 - && (cols[0].r != 1.0 || cols[0].g != 1.0 || cols[0].b != 1.0)) { + && (cols[0].r != 0.8 || cols[0].g != 0.8 || cols[0].b != 0.8)) { col tc; inst_cal_type calt = inst_calt_ref_freq; - inst_cal_cond calc = inst_calc_emis_white; + inst_cal_cond calc = inst_calc_emis_80pc; - if ((rv = p->dw->set_color(p->dw, 1.0, 1.0, 1.0)) != 0) { - a1logd(p->log,1,"set_color() returned %s\n",rv); + /* Hmm. Should really ask the instrument what sort of calc it needs !!! */ + if ((rv = p->dw->set_color(p->dw, 0.8, 0.8, 0.8)) != 0) { + a1logd(p->log,1,"set_color() returned %d\n",rv); return 3; } /* Do calibrate, but ignore return code. Press on regardless. */ @@ -406,7 +440,7 @@ static int disprd_read_imp( inst_cal_cond calc = inst_calc_emis_white; if ((rv = p->dw->set_color(p->dw, 1.0, 1.0, 1.0)) != 0) { - a1logd(p->log,1,"set_color() returned %s\n",rv); + a1logd(p->log,1,"set_color() returned %d\n",rv); return 3; } /* Do calibrate, but ignore return code. Press on regardless. */ @@ -425,8 +459,11 @@ static int disprd_read_imp( scb->sp.spec_n = 0; scb->duration = 0.0; - if (spat != 0 && tpat != 0) + if (spat != 0 && tpat != 0) { a1logv(p->log, 1, "%cpatch %d of %d",cr_char,spat + (noinc != 0 ? 0 : patch),tpat); + if (p->dw->set_pinfo != NULL) + p->dw->set_pinfo(p->dw, spat + (noinc != 0 ? 0 : patch),tpat); + } a1logd(p->log,1,"About to read patch %d\n",patch); rgb[0] = scb->r; @@ -434,7 +471,8 @@ static int disprd_read_imp( rgb[2] = scb->b; /* If we are doing a soft cal, apply it to the test color */ - if (p->softcal && p->cal[0][0] >= 0.0) { + /* (dispwin will apply any tvenc needed) */ + if ((p->native & 1) && p->cal[0][0] >= 0.0) { int j; double inputEnt_1 = (double)(p->ncal-1); @@ -456,7 +494,7 @@ static int disprd_read_imp( } } if ((rv = p->dw->set_color(p->dw, rgb[0], rgb[1], rgb[2])) != 0) { - a1logd(p->log,1,"set_color() returned %s\n",rv); + a1logd(p->log,1,"set_color() returned %d\n",rv); return 3; } @@ -599,12 +637,7 @@ typedef struct { } dsamples; /* Take a series of readings from the display - drift compensation */ -/* Return nz on fail/abort */ -/* 1 = user aborted */ -/* 2 = instrument access failed */ -/* 3 = window access failed */ -/* 4 = user hit terminate key */ -/* 5 = system error */ +/* Return nz on fail/abort - see dispsup.h */ /* Use disprd_err() to interpret it */ static int disprd_read_drift( disprd *p, @@ -627,7 +660,7 @@ static int disprd_read_drift( if (p->bdrift == 0) { /* Must be just wdrift */ boff = eoff = 1; dno = 1; - } else if (p->bdrift == 0) { /* Must be just bdrift */ + } else if (p->wdrift == 0) { /* Must be just bdrift */ boff = eoff = 0; dno = 1; } else { /* Must be both */ @@ -979,12 +1012,7 @@ static void disprd_change_drift_comp(disprd *p, } /* Take a series of readings from the display */ -/* Return nz on fail/abort */ -/* 1 = user aborted */ -/* 2 = instrument access failed */ -/* 3 = window access failed */ -/* 4 = user hit terminate key */ -/* 5 = system error */ +/* Return nz on fail/abort - see dispsup.h */ /* Use disprd_err() to interpret it */ static int disprd_read( disprd *p, @@ -1035,12 +1063,7 @@ static void disprd_get_disptype(disprd *p, int *refrmode, int *cbid) { static int config_inst_displ(disprd *p); /* Take an ambient reading if the instrument has the capability. */ -/* return nz on fail/abort */ -/* 1 = user aborted */ -/* 2 = instrument access failed */ -/* 4 = user hit terminate key */ -/* 5 = system error */ -/* 8 = no ambient capability */ +/* return nz on fail/abort - see dispsup.h */ /* Use disprd_err() to interpret it */ int disprd_ambient( struct _disprd *p, @@ -1246,12 +1269,7 @@ int disprd_ambient( /* Test without a spectrometer using a fake device */ -/* Return nz on fail/abort */ -/* 1 = user aborted */ -/* 2 = instrument access failed */ -/* 3 = window access failed */ -/* 4 = user hit terminate key */ -/* 5 = system error */ +/* Return nz on fail/abort - see dispsup.h */ /* Use disprd_err() to interpret it */ static int disprd_fake_read( disprd *p, @@ -1301,9 +1319,9 @@ static int disprd_fake_read( ooff[0] = ooff[1] = ooff[2] = 0.0; /* Output offset */ #else /* Input offset, equivalent to RGB offsets having various values */ - doff[0] = 0.10; + doff[0] = 0.05; doff[1] = 0.06; - doff[2] = 0.08; + doff[2] = 0.07; /* Output offset - equivalent to flare [range 0.0 - 1.0] */ ooff[0] = 0.03; ooff[1] = 0.04; @@ -1313,6 +1331,8 @@ static int disprd_fake_read( if (icmRGBprim2matrix(white, red, green, blue, mat)) error("Fake read unexpectedly got singular matrix\n"); + icmTranspose3x3(mat, mat); /* Convert [RGB][XYZ] to [XYZ][RGB] */ + icmSetUnity3x3(xmat); if (p->fake2 == 1) { @@ -1390,14 +1410,14 @@ static int disprd_fake_read( /* If we have a test window, display the patch color */ if (p->dw) { inst_code rv; - if (p->softcal) { /* Display value with calibration */ + if (p->native & 1) { /* Apply soft calibration to display value */ if ((rv = p->dw->set_color(p->dw, crgb[0], crgb[1], crgb[2])) != 0) { - a1logd(p->log,1,"set_color() returned %s\n",rv); + a1logd(p->log,1,"set_color() returned %d\n",rv); return 3; } } else { /* Hardware will apply calibration */ if ((rv = p->dw->set_color(p->dw, rgb[0], rgb[1], rgb[2])) != 0) { - a1logd(p->log,1,"set_color() returned %s\n",rv); + a1logd(p->log,1,"set_color() returned %d\n",rv); return 3; } } @@ -1461,12 +1481,7 @@ static int disprd_fake_read( } /* Test without a spectrometer using a fake ICC profile device */ -/* Return nz on fail/abort */ -/* 1 = user aborted */ -/* 2 = instrument access failed */ -/* 3 = window access failed */ -/* 4 = user hit terminate key */ -/* 5 = system error */ +/* Return nz on fail/abort - see dispsup.h */ /* Use disprd_err() to interpret it */ static int disprd_fake_read_lu( disprd *p, @@ -1533,7 +1548,7 @@ static int disprd_fake_read_lu( if (p->dw) { inst_code rv; if ((rv = p->dw->set_color(p->dw, rgb[0], rgb[1], rgb[2])) != 0) { - a1logd(p->log,1,"set_color() returned %s\n",rv); + a1logd(p->log,1,"set_color() returned %d\n",rv); return 3; } } @@ -1581,12 +1596,7 @@ static int disprd_fake_read_lu( } /* Use without a direct connect spectrometer using shell callout */ -/* Return nz on fail/abort */ -/* 1 = user aborted */ -/* 2 = instrument access failed */ -/* 3 = window access failed */ -/* 4 = user hit terminate key */ -/* 5 = system error */ +/* Return nz on fail/abort - see duspsup.h */ /* Use disprd_err() to interpret it */ static int disprd_fake_read_co(disprd *p, col *cols, /* Array of patch colors to be tested */ @@ -1655,7 +1665,7 @@ static int disprd_fake_read_co(disprd *p, if (p->dw) { inst_code rv; if ((rv = p->dw->set_color(p->dw, rgb[0], rgb[1], rgb[2])) != 0) { - a1logd(p->log,1,"set_color() returned %s\n",rv); + a1logd(p->log,1,"set_color() returned %d\n",rv); return 3; } } @@ -1726,7 +1736,7 @@ char *disprd_err(int en) { case 2: return "Instrument Access Failed"; case 22: - return "Instrument Access Failed (No PLD Pattern - have you run spyd2en ?)"; + return "Instrument Access Failed (No PLD Pattern - have you run oeminst ?)"; case 3: return "Window Access Failed"; case 4: @@ -1746,7 +1756,15 @@ char *disprd_err(int en) { case 11: return "Instrument has no CCSS capability"; case 12: - return "Internal: trying to set calibration when not using calibration"; + return "Video encoding requested using nonlinear current calibration curves"; + case 13: + return "Video encoding requested for MadVR display - use MadVR to set video encoding"; + case 14: + return "Instrument has no set refresh rate capability"; + case 15: + return "Unknown calibration display type selection"; + case 16: + return "Must use BASE calibration display type selection"; } return "Unknown"; } @@ -1782,8 +1800,6 @@ static void disprd_del(disprd *p) { if (p->it != NULL) p->it->del(p->it); if (p->dw != NULL) { - if (p->or != NULL) - p->dw->set_ramdac(p->dw,p->or, 0); p->dw->del(p->dw); } if (p->sp2cie != NULL) @@ -1811,6 +1827,12 @@ static int config_inst_displ(disprd *p) { p->tele = 0; } + if (!p->tele && !IMODETST(cap, inst_mode_emis_spot)) { + printf("Want emissive spot measurement capability but instrument doesn't support it\n"); + printf("so switching to telephoto spot mode.\n"); + p->tele = 1; + } + if (( p->tele && !IMODETST(cap, inst_mode_emis_tele)) || (!p->tele && !IMODETST(cap, inst_mode_emis_spot))) { printf("Need %s emissive measurement capability,\n", p->tele ? "telephoto" : "spot"); @@ -1866,18 +1888,19 @@ static int config_inst_displ(disprd *p) { } /* Set the display type */ - if (p->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); - return 2; + if (p->docbid) + return 16; + return 15; } if ((rv = p->it->set_disptype(p->it, ix)) != inst_ok) { - a1logd(p->log,1,"Setting display type failed failed with '%s' (%s)\n", + a1logd(p->log,1,"Setting display type failed with '%s' (%s)\n", p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv)); - return 2; + return 15; } } else printf("Display type ignored - instrument doesn't support display type\n"); @@ -1899,12 +1922,7 @@ static int config_inst_displ(disprd *p) { if (p->highres) { if (IMODETST(cap, inst_mode_highres)) { - inst_code ev; - if ((ev = p->it->get_set_opt(p->it, inst_opt_highres)) != inst_ok) { - a1logd(p->log,1,"\nSetting high res mode failed with error :'%s' (%s)\n", - p->it->inst_interp_error(p->it, ev), p->it->interp_error(p->it, ev)); - return 2; - } + mode |= inst_mode_highres; } else { a1logv(p->log, 1, "high resolution ignored - instrument doesn't support high res. mode\n"); } @@ -1928,19 +1946,29 @@ static int config_inst_displ(disprd *p) { } } - if (p->sets != NULL - || ((cap2 & inst2_ccss) != 0 && p->obType != icxOT_default)) { + /* Observer */ + if ((cap2 & inst2_ccss) != 0 && p->obType != icxOT_default) { if ((cap2 & inst2_ccss) == 0) { - a1logd(p->log,1,"Instrument doesn't support ccss calibration\n"); + a1logd(p->log,1,"Instrument doesn't support ccss calibration and we need it\n"); return 11; } if ((rv = p->it->get_set_opt(p->it, inst_opt_set_ccss_obs, p->obType, p->custObserver)) - != inst_ok) { + != inst_ok) { a1logd(p->log,1,"inst_opt_set_ccss_obs returned '%s' (%s)\n", p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv)); return 2; } + } + + /* Custom CCSS */ + if (p->sets != NULL) { + + if ((cap2 & inst2_ccss) == 0) { + a1logd(p->log,1,"Instrument doesn't support ccss calibration and we need it\n"); + return 11; + } + if ((rv = p->it->col_cal_spec_set(p->it, 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)); @@ -1948,6 +1976,19 @@ static int config_inst_displ(disprd *p) { } } + if (p->refrate > 0.0) { + if (!(cap2 & inst2_set_refresh_rate)) { + a1logd(p->log,1,"Instrument doesn't support setting refresh rate\n"); + return 11; + } else { + if ((rv = p->it->set_refr_rate(p->it, p->refrate)) != inst_ok) { + a1logd(p->log,1,"set_refr_rate %f Hz returned '%s' (%s)\n", + p->refrate, p->it->inst_interp_error(p->it, rv), p->it->interp_error(p->it, rv)); + return 2; + } + } + } + /* Set the trigger mode to program triggered */ if ((rv = p->it->get_set_opt(p->it,inst_opt_trig_prog)) != inst_ok) { a1logd(p->log,1,"Setting program trigger mode failed failed with '%s' (%s)\n", @@ -1964,20 +2005,7 @@ static int config_inst_displ(disprd *p) { /* Create a display reading object. */ /* Return NULL if error */ -/* Set *errc to code: */ -/* 0 = no error */ -/* 1 = user aborted */ -/* 2 = instrument access failed */ -/* 22 = instrument access failed - no PLD pattern */ -/* 3 = window access failed */ -/* 4 = RAMDAC access failed */ -/* 5 = user hit terminate key */ -/* 6 = system error */ -/* 7 = CRT or LCD must be selected */ -/* 9 = spectral conversion failed */ -/* 10 = no ccmx support */ -/* 11 = no ccss support */ -/* 12 = cal to set but native != 0 */ +/* Set *errc to code. See dispsup.h */ /* Use disprd_err() to interpret *errc */ disprd *new_disprd( int *errc, /* Error code. May be NULL (could use log for this instead?) */ @@ -1988,18 +2016,25 @@ 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 */ int noinitcal, /* No initial instrument calibration */ +int noinitplace, /* Don't wait for user to place instrument on screen */ int highres, /* Use high res mode if available */ -int native, /* 0 = use current current or given calibration curve */ - /* 1 = use native linear out & high precision */ -int *noramdac, /* Return nz if no ramdac access. native is set to 0 */ -double cal[3][MAX_CAL_ENT], /* Calibration set/return (cal[0][0] < 0.0 or NULL if not used) */ - /* native must be 0 if cal is set */ +double refrate, /* If != 0.0, set display refresh rate calibration */ +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 */ +double cal[3][MAX_CAL_ENT], /* Calibration (cal == NULL or cal[0][0] < 0.0 if not valid) */ int ncal, /* Number of cal[] entries */ -int softcal, /* NZ if apply cal to readings rather than hardware */ disppath *disp, /* Display to calibrate. NULL if fake and no dispwin */ +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 */ +#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) */ double hpatsize, /* Size of dispwin */ @@ -2020,14 +2055,10 @@ a1log *log /* Verb, debug & error log */ disprd *p = NULL; int ch; inst_code rv; + int uout_tvenc = out_tvenc; /* tvenc to use with dispwin value setting */ if (errc != NULL) *errc = 0; /* default return code = no error */ - if (cal != NULL && cal[0][0] >= 0.0 && native != 0) { - if (errc != NULL) *errc = 12; - return NULL; - } - /* Allocate a disprd */ if ((p = (disprd *)calloc(sizeof(disprd), 1)) == NULL) { a1logd(log, 1, "new_disprd failed due to malloc failure\n"); @@ -2058,7 +2089,9 @@ a1log *log /* Verb, debug & error log */ p->tele = tele; p->nadaptive = nadaptive; p->noinitcal = noinitcal; + p->noinitplace = noinitplace; p->highres = highres; + p->refrate = refrate; if (mcallout != NULL) ipath = &icomFakeDevice; /* Force fake device */ p->mcallout = mcallout; @@ -2066,7 +2099,16 @@ a1log *log /* Verb, debug & error log */ p->br = baud_19200; p->fc = fc; - /* Save this in case we are using a fake device */ + p->native = native; + +#ifdef FAKE_NOISE +# ifdef FAKE_UNPREDIC + rand32(time(NULL)); +# endif +#endif + + /* Save this so we can return current cal, or */ + /* in case we are using a fake device */ if (cal != NULL && cal[0][0] >= 0.0) { int j, i; for (j = 0; j < 3; j++) { @@ -2075,11 +2117,9 @@ a1log *log /* Verb, debug & error log */ } } p->ncal = ncal; - p->softcal = softcal; } else { p->cal[0][0] = -1.0; p->ncal = 0; - p->softcal = 0; } /* If non-real instrument */ @@ -2179,28 +2219,103 @@ a1log *log /* Verb, debug & error log */ if (webdisp != 0) { /* Open web display */ - if ((p->dw = new_webwin(webdisp, hpatsize, vpatsize, ho, vo, 0, 0, - p->log->verb, p->log->debug)) == NULL) { + 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"); p->del(p); if (errc != NULL) *errc = 3; return NULL; } - if (noramdac != NULL) - *noramdac = 1; +#ifdef NT + } else if (madvrdisp != 0) { + if (out_tvenc) { + a1logd(log,1,"new_disprd failed because tv_enc & MadVR window\n"); + p->del(p); + if (errc != NULL) *errc = 13; + return NULL; + } + + if ((p->dw = new_madvrwin(hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm, out_tvenc, + blackbg, 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; + return NULL; + } +#endif } else { + /* Don't do tvenc on test values if we are going to do it with RAMDAC curve */ + if (out_tvenc && (p->native & 1) == 0 && p->cal[0][0] >= 0.0) { + uout_tvenc = 0; + } + /* Open display window for positioning (no blackbg) */ - if ((p->dw = new_dispwin(disp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, 0, - override, p->log->debug)) == NULL) { + if ((p->dw = new_dispwin(disp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, nocm, + uout_tvenc, 0, override, p->log->debug)) == NULL) { a1logd(log,1,"new_disprd failed because new_dispwin failed\n"); p->del(p); if (errc != NULL) *errc = 3; return NULL; } + + /* If TV encoding using existing RAMDAC and it is not linear, */ + /* error out, as TV encoding probably won't work properly. */ + if (out_tvenc && (native & 1) == 0 && p->cal[0][0] < 0.0) { + ramdac *cr; + if ((cr = p->dw->get_ramdac(p->dw)) != NULL) { + int i, j; + for (i = 0; i < cr->nent; i++) { + double val = i/(cr->nent-1.0); + for (j = 0; j < 3; j++) { + if (fabs(val - cr->v[j][i]) > 1e-5) + break; + } + if (j < 3) + break; + } + if (i < cr->nent) { + a1logd(log,1,"new_disprd failed because tvenc and nonlinear RAMDAC"); + cr->del(cr); + p->del(p); + if (errc != NULL) *errc = 12; + return NULL; + } + cr->del(cr); + } + } + +#ifdef NEVER // Don't do this - dispread doesn't save calibration when using existing. + /* If using exiting RAMDAC, return it */ + if ((native & 1) == 0 && p->cal[0][0] < 0.0 && cal != NULL) { + ramdac *r; + + if ((r = p->dw->get_ramdac(p->dw)) != NULL) { + int j, i; + + /* Get the ramdac contents. */ + /* We linearly interpolate from RAMDAC[nent] to cal[ncal] resolution */ + for (i = 0; i < ncal; i++) { + double val, w; + unsigned int ix; + + val = (r->nent-1.0) * i/(ncal-1.0); + ix = (unsigned int)floor(val); /* Coordinate */ + if (ix > (r->nent-2)) + ix = (r->nent-2); + w = val - (double)ix; /* weight */ + for (j = 0; j < 3; j++) { + val = r->v[j][ix]; + cal[j][i] = val + w * (r->v[j][ix+1] - val); + } + } + r->del(r); + } + } +#endif } if (p->it != NULL) { - /* Do a calibration up front, so as not to get in the users way, */ + /* Do an instrument calibration up front, so as not to get in the users way, */ /* but ignore a CRT frequency or display integration calibration, */ /* since these will be done automatically. */ if (p->it->needs_calibration(p->it) & inst_calt_n_dfrble_mask) { @@ -2223,30 +2338,50 @@ a1log *log /* Verb, debug & error log */ } } - /* Ask user to put instrument on screen */ - empty_con_chars(); - printf("Place instrument 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') { + if (!p->noinitplace) { + inst2_capability cap2; + + p->it->capabilities(p->it, NULL, &cap2, NULL); + + /* Turn target on */ + if (cap2 & inst2_has_target) + p->it->get_set_opt(p->it, inst_opt_set_target_state, 1); + + /* Ask user to put instrument on screen */ + empty_con_chars(); + + printf("Place instrument 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"); + a1logd(log,1,"new_disprd failed because user aborted when placing device\n"); + if (cap2 & inst2_has_target) /* Turn target off */ + p->it->get_set_opt(p->it, inst_opt_set_target_state, 0); + p->del(p); + if (errc != NULL) *errc = 1; + return NULL; + } + + /* Turn target off */ + if (cap2 & inst2_has_target) + p->it->get_set_opt(p->it, inst_opt_set_target_state, 0); + printf("\n"); - a1logd(log,1,"new_disprd failed because user aborted when placing device\n"); - p->del(p); - if (errc != NULL) *errc = 1; - return NULL; } - printf("\n"); - if (webdisp == 0) { + if (webdisp == 0 +#ifdef NT + && madvrdisp == 0 +#endif + ) { /* Close the positioning window */ if (p->dw != NULL) { - if (p->or != NULL) - p->dw->set_ramdac(p->dw,p->or, 0); p->dw->del(p->dw); } /* Open display window again for measurement */ - if ((p->dw = new_dispwin(disp, hpatsize, vpatsize, ho, vo, 0, native, noramdac, blackbg, - override, p->log->debug)) == NULL) { + 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; @@ -2259,84 +2394,57 @@ a1log *log /* Verb, debug & error log */ p->dw->set_callout(p->dw, ccallout); } - /* If we have a calibration to set */ + /* If we have a calibration to set using the RAMDAC */ /* (This is only typically the case for disread) */ - if (!p->softcal && cal != NULL && cal[0][0] >= 0.0) { + if ((p->native & 1) == 0 && p->cal[0][0] >= 0.0) { /* Save current RAMDAC so that we can restore it */ - p->or = NULL; - if ((p->or = p->dw->get_ramdac(p->dw)) == NULL) { + if (p->dw->r == NULL) { warning("Unable to read or set display RAMDAC - switching to softcal"); - p->softcal = softcal = 1; - } + p->native |= 1; /* Set the given RAMDAC so we can characterise through it */ - if (p->or != NULL) { - ramdac *r; + } else { int j, i; - r = p->or->clone(p->or); - /* Set the ramdac contents. */ /* We linearly interpolate from cal[ncal] to RAMDAC[nent] resolution */ - for (i = 0; i < r->nent; i++) { + for (i = 0; i < p->dw->r->nent; i++) { double val, w; unsigned int ix; - val = (ncal-1.0) * i/(r->nent-1.0); + val = (ncal-1.0) * i/(p->dw->r->nent-1.0); ix = (unsigned int)floor(val); /* Coordinate */ if (ix > (ncal-2)) ix = (ncal-2); w = val - (double)ix; /* weight */ for (j = 0; j < 3; j++) { - val = cal[j][ix]; - r->v[j][i] = val + w * (cal[j][ix+1] - val); + val = p->cal[j][ix]; + p->dw->r->v[j][i] = val + w * (p->cal[j][ix+1] - val); + /* Implement tvenc here rather than dispwin, so that the order is correct */ + if (out_tvenc) { + p->dw->r->v[j][i] = ((235.0 - 16.0) * p->dw->r->v[j][i] + 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->dw->edepth > 8) + p->dw->r->v[j][i] = + (p->dw->r->v[j][i] * 255 * (1 << (p->dw->edepth - 8))) + /((1 << p->dw->edepth) - 1.0); + } } } - if (p->dw->set_ramdac(p->dw, r, 0)) { + if (p->dw->set_ramdac(p->dw, p->dw->r, 0)) { a1logd(log,1,"new_disprd failed becayse set_ramdac failed\n"); a1logv(p->log, 1, "Failed to set RAMDAC to desired calibration.\n"); a1logv(p->log, 1, "Perhaps the operating system is being fussy ?\n"); - r->del(r); - p->del(p); if (errc != NULL) *errc = 4; return NULL; } - r->del(r); } } - /* Return the ramdac being used */ - if (p->or != NULL && cal != NULL) { - ramdac *r; - int j, i; - - if ((r = p->dw->get_ramdac(p->dw)) == NULL) { - a1logd(log,1,"new_disprd failed becayse get_ramdac failed\n"); - a1logv(p->log, 1, "Failed to read current RAMDAC\n"); - p->del(p); - if (errc != NULL) *errc = 4; - return NULL; - } - /* Get the ramdac contents. */ - /* We linearly interpolate from RAMDAC[nent] to cal[ncal] resolution */ - for (i = 0; i < ncal; i++) { - double val, w; - unsigned int ix; - - val = (r->nent-1.0) * i/(ncal-1.0); - ix = (unsigned int)floor(val); /* Coordinate */ - if (ix > (r->nent-2)) - ix = (r->nent-2); - w = val - (double)ix; /* weight */ - for (j = 0; j < 3; j++) { - val = r->v[j][ix]; - cal[j][i] = val + w * (r->v[j][ix+1] - val); - } - } - r->del(r); - } - a1logd(log,1,"new_disprd succeeded\n"); return p; } diff --git a/spectro/dispsup.h b/spectro/dispsup.h index 837e16a..1d291af 100644 --- a/spectro/dispsup.h +++ b/spectro/dispsup.h @@ -18,7 +18,11 @@ /* A helper function to handle presenting a display test patch */ struct _disp_win_info { int webdisp; /* nz if web display is to be used */ +#ifdef NT + int madvrdisp; /* NZ for MadVR display */ +#endif disppath *disp; /* display to calibrate. */ + 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 */ double hpatsize; /* Size of dispwin */ @@ -49,6 +53,10 @@ 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 */ +#ifdef NT +int madvrdisp, /* NZ for MadVR display */ +#endif +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 */ double hpatsize, /* Size of dispwin */ @@ -91,9 +99,12 @@ struct _disprd { char *fake_name; /* Fake profile name */ icmFile *fake_fp; icc *fake_icc; /* NZ if ICC profile is being used for fake */ + 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) */ double cal[3][MAX_CAL_ENT]; /* Calibration being worked through (cal[0][0] < 0.0 or NULL if not used) */ int ncal; /* Number of entries used in cal[] */ - int softcal; /* NZ if apply cal to readings rather than hardware */ icmLuBase *fake_lu; char *mcallout; /* fake instrument shell callout */ icompath *ipath; /* Instrument path to open, &icomFakeDevice == fake */ @@ -107,6 +118,7 @@ struct _disprd { 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 */ double (*ccmtx)[3]; /* Colorimeter Correction Matrix, NULL if none */ icxObserverType obType; /* CCSS Observer */ @@ -119,8 +131,8 @@ struct _disprd { int bdrift; /* Flag, nz for black drift compensation */ int wdrift; /* Flag, nz for white drift compensation */ int noinitcal; /* No initial instrument calibration */ + int noinitplace; /* Don't wait for user to place instrument on screen */ dispwin *dw; /* Window */ - ramdac *or; /* Original ramdac if we set one */ int serno; /* Reading serial number */ col ref_bw[2]; /* Reference black and white readings for drift comp. */ @@ -199,7 +211,11 @@ struct _disprd { /* 9 = spectral conversion failed */ /* 10 = no ccmx support */ /* 11 = no ccss support */ -/* 12 = cal to set but native != 0 */ +/* 12 = out_tvenc & native = 0 && no cal but current RAMDAC is not linear */ +/* 13 = out_tvenc for MadVR window */ +/* 14 = no set refresh rate support */ +/* 15 = unknown calibration/display type */ +/* 16 = non based calibration/display type */ /* Use disprd_err() to interpret errc */ disprd *new_disprd( int *errc, /* Error code. May be NULL */ @@ -210,18 +226,25 @@ int docbid, /* NZ to only allow cbid dtypes */ int tele, /* NZ for tele mode */ int nadaptive, /* NZ for non-adaptive mode */ int noinitcal, /* No initial instrument calibration */ +int noinitplace, /* Don't wait for user to place instrument on screen */ int highres, /* Use high res mode if available */ -int native, /* 0 = use current current or given calibration curve */ - /* 1 = use native linear out & high precision */ -int *noramdac, /* Return nz if no ramdac access. native is set to 0 */ -double cal[3][MAX_CAL_ENT], /* Calibration set/return (cal[0][0] < 0.0 if can't/not to be used) */ - /* native must be 0 if cal is set */ +double refrate, /* If != 0.0, set display refresh rate calibration */ +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 */ +double cal[3][MAX_CAL_ENT], /* Calibration set (cal = NULL or cal[0][0] < 0.0 if not to be used) */ int ncal, /* number of entries use in cal */ -int softcal, /* NZ if apply cal to readings rather than hardware */ disppath *screen, /* Screen to calibrate. */ +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 */ +#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) */ double hpatsize, /* Size of dispwin */ diff --git a/spectro/dispwin.c b/spectro/dispwin.c index 4a6acd6..74186cf 100644 --- a/spectro/dispwin.c +++ b/spectro/dispwin.c @@ -17,8 +17,6 @@ /* TTBD * - * Nice to have option to create non-square test window ? - * * Should probably check the display attributes (like visual depth) * and complain if we aren't using 24 bit color or better. * @@ -30,6 +28,9 @@ * * Is there a >8 bit way of getting/setting RAMDAC indexes ? * + * Should add dithering support to overcome 8 bit limitations of + * non-RAMDAC access or limited RAMDAC depth. (How do we easily + * determine the latter ??) */ #include <stdio.h> @@ -53,12 +54,34 @@ #include "conv.h" #include "dispwin.h" #include "webwin.h" -#if defined(UNIX_X11) && defined(USE_UCMM) -#include "ucmm.h" +#ifdef NT +# include "madvrwin.h" +#endif +#if defined(UNIX_X11) +# include <dlfcn.h> +# if defined(USE_UCMM) +# include "ucmm.h" +# endif #endif #ifdef __APPLE__ +/* + Note that the new ColorSync API is defined in + /System/Library/Frameworks/ApplicationServices.framework/Frameworks/ColorSync.framework/Headers + + in + ColorSync.h + ColorSyncBase.h + ColorSyncCMM.h + ColorSyncDeprecated.h + ColorSyncDevice.h + ColorSyncProfile.h + ColorSyncTransform.h + + and isn't documented by Apple anywhere else. + */ + #include <Foundation/Foundation.h> #include <AppKit/AppKit.h> @@ -68,13 +91,23 @@ # endif #ifndef CGFLOAT_DEFINED -#ifdef __LP64__ +#ifdef __LP64__ || NS_BUILD_32_LIKE_64 typedef double CGFloat; #else typedef float CGFloat; #endif /* defined(__LP64__) */ #endif /* !CGFLOAT_DEFINED */ +#ifndef NSINTEGER_DEFINED +#if __LP64__ || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; +#else + typedef int NSInteger; + typedef unsigned int NSUInteger; +#endif +#endif /* !NSINTEGER_DEFINED */ + #include <IOKit/Graphics/IOGraphicsLib.h> #if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1060 @@ -572,10 +605,24 @@ disppath **get_displays() { /* Go through all the screens */ for (i = 0; i < dcount; i++) { + static void *xrr_found = NULL; /* .so handle */ + static XRRScreenResources *(*_XRRGetScreenResourcesCurrent) + (Display *dpy, Window window) = NULL; XRRScreenResources *scrnres; int jj; /* Screen index */ - if ((scrnres = XRRGetScreenResources(mydisplay, RootWindow(mydisplay,i))) == NULL) { + if (minv >= 3 && xrr_found == NULL) { + if ((xrr_found = dlopen("libXrandr.so", RTLD_LAZY)) != NULL) + _XRRGetScreenResourcesCurrent = dlsym(xrr_found, "XRRGetScreenResourcesCurrent"); + } + + if (minv >= 3 && _XRRGetScreenResourcesCurrent != NULL) { + scrnres = _XRRGetScreenResourcesCurrent(mydisplay, RootWindow(mydisplay,i)); + + } else { + scrnres = XRRGetScreenResources(mydisplay, RootWindow(mydisplay,i)); + } + if (scrnres == NULL) { debugrr("XRRGetScreenResources failed\n"); XCloseDisplay(mydisplay); free_disppaths(disps); @@ -1084,10 +1131,6 @@ void free_a_disppath(disppath *path) { /* ----------------------------------------------- */ -static ramdac *dispwin_clone_ramdac(ramdac *r); -static void dispwin_setlin_ramdac(ramdac *r); -static void dispwin_del_ramdac(ramdac *r); - /* For VideoLUT/RAMDAC use, we assume that the number of entries in the RAMDAC */ /* meshes perfectly with the display raster depth, so that we can */ /* figure out how to apportion device values. We fail if they don't */ @@ -1122,9 +1165,9 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } r->pdepth = p->pdepth; r->nent = (1 << p->pdepth); - r->clone = dispwin_clone_ramdac; + r->clone = dispwin_clone_ramdac; r->setlin = dispwin_setlin_ramdac; - r->del = dispwin_del_ramdac; + r->del = dispwin_del_ramdac; for (j = 0; j < 3; j++) { @@ -1139,11 +1182,13 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { /* GetDeviceGammaRamp() is hard coded for 3 x 256 entries (Quantize) */ if (r->nent != 256) { + free(r); debugr2((errout,"GetDeviceGammaRamp() is hard coded for nent == 256, and we've got nent = %d!\n",r->nent)); return NULL; } if (GetDeviceGammaRamp(p->hdc, vals) == 0) { + free(r); debugr("dispwin_get_ramdac failed on GetDeviceGammaRamp()\n"); return NULL; } @@ -1171,7 +1216,7 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } if (nent != (1 << p->pdepth)) { - debugr("CGGetDisplayTransferByTable number of entries mismatches screen depth\n"); + debugr2((errout,"CGGetDisplayTransferByTable number of entries %d mismatches screen depth %d\n",nent,p->pdepth)); return NULL; } @@ -1183,9 +1228,9 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { r->pdepth = p->pdepth; r->nent = (1 << p->pdepth); - r->clone = dispwin_clone_ramdac; + r->clone = dispwin_clone_ramdac; r->setlin = dispwin_setlin_ramdac; - r->del = dispwin_del_ramdac; + r->del = dispwin_del_ramdac; for (j = 0; j < 3; j++) { if ((r->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) { @@ -1231,7 +1276,7 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } if (nent != (1 << p->pdepth)) { - debugr2((errout,"XRRGetCrtcGammaSize number of entries %d mismatches screen depth %d\n",nent,(1 << p->pdepth))); + debugr2((errout,"XRRGetCrtcGammaSize number of entries %d mismatches screen depth %d bits\n",nent,(1 << p->pdepth))); return NULL; } @@ -1292,7 +1337,7 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } if (nent != (1 << p->pdepth)) { - debugr2((errout,"CGGetDisplayTransferByTable number of entries %d mismatches screen depth %d\n",nent,(1 << p->pdepth))); + debugr2((errout,"CGGetDisplayTransferByTable number of entries %d mismatches screen depth %d bits\n",nent,(1 << p->pdepth))); return NULL; } } @@ -1429,7 +1474,7 @@ typedef struct { CFURLRef url; /* URL to return */ } diter_cntx_t; -bool diter_callback(CFDictionaryRef dict, void *cntx) { +static bool diter_callback(CFDictionaryRef dict, void *cntx) { diter_cntx_t *cx = (diter_cntx_t *)cntx; CFStringRef str; CFUUIDRef uuid; @@ -1475,7 +1520,7 @@ bool diter_callback(CFDictionaryRef dict, void *cntx) { /* Return the url to the given displays current profile. */ /* Return NULL on error. CFRelease when done. */ /* Optionally return the ProfileID string */ -CFURLRef cur_profile_url(CFStringRef *idp, dispwin *p) { +static CFURLRef cur_profile_url(CFStringRef *idp, dispwin *p) { diter_cntx_t cx; if ((cx.dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) { @@ -1498,7 +1543,7 @@ CFURLRef cur_profile_url(CFStringRef *idp, dispwin *p) { /* Convert a URL into a local POSIX path string */ /* Return NULL on error. Free returned string when done. */ -char *url_to_path(CFURLRef url) { +static char *url_to_path(CFURLRef url) { CFStringRef urlstr; CFIndex bufSize; char *dpath = NULL; /* return value */ @@ -1521,9 +1566,9 @@ char *url_to_path(CFURLRef url) { return dpath; } -/* Return information about the given displays current profile */ +/* Return the local POSIX path to the given displays current profile */ /* Return NULL on error. Free returned string when done. */ -char *cur_profile(dispwin *p) { +static char *cur_profile(dispwin *p) { CFURLRef url; char *dpath = NULL; /* return value */ @@ -1541,6 +1586,58 @@ char *cur_profile(dispwin *p) { #endif /* >= 10.6 */ +/* Return a CMProfileRef/ColorSyncProfileRef for the */ +/* displays profile. Return NULL on error */ +static void *cur_colorsync_ref(dispwin *p) { + void *cspr = NULL; + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + CFURLRef url; + ColorSyncProfileRef ref; + CFErrorRef ev; + + if ((url = cur_profile_url(NULL, p)) == NULL) { + debugr2((errout,"cur_colorsync_ref got NULL URL\n")); + return NULL; + } + + if ((ref = ColorSyncProfileCreateWithURL(url, &ev)) == NULL) { + debugr2((errout,"ColorSyncProfileCreateWithURL failed\n")); + return NULL; + } + CFRelease(url); + + cspr = (void *)ref; + +#else /* 10.5 and prior */ + CMError ev; + CMDeviceProfileID curID; /* Current Device Default profile ID */ + CMProfileLocation cploc; /* Current profile location */ + CMProfileRef prof; + + /* Get the default ID for the display */ + if ((ev = CMGetDeviceDefaultProfileID(cmDisplayDeviceClass, (CMDeviceID)p->ddid, &curID)) != noErr) { + debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev)); + return NULL; + } + + /* Get the displays profile */ + if ((ev = CMGetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, curID, &cploc)) != noErr) { + debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev)); + return NULL; + } + + if ((ev = CMOpenProfile(&prof, &cploc)) != noErr) { + debugr2((errout,"CMOpenProfile() failed with error %d\n",ev)); + return NULL; + } + cspr = (void *)prof; + +#endif + + return cspr; +} + #endif /* __APPLE__ */ /* Set the RAMDAC values. */ @@ -1576,6 +1673,7 @@ static int dispwin_set_ramdac(dispwin *p, ramdac *r, int persist) { debugr2((errout,"dispwin_set_ramdac failed on SetDeviceGammaRamp() with error %d\n",GetLastError())); return 1; } + GdiFlush(); #endif /* NT */ #ifdef __APPLE__ @@ -2071,13 +2169,14 @@ static int dispwin_set_ramdac(dispwin *p, ramdac *r, int persist) { } #endif /* UNXI X11 */ - debugr("XF86VidModeSetGammaRamp returning OK\n"); + debugr("dispwin_set_ramdac returning OK\n"); + return 0; } /* Clone ourselves */ -static ramdac *dispwin_clone_ramdac(ramdac *r) { +ramdac *dispwin_clone_ramdac(ramdac *r) { ramdac *nr; int i, j; @@ -2111,7 +2210,7 @@ static ramdac *dispwin_clone_ramdac(ramdac *r) { } /* Set the ramdac values to linear */ -static void dispwin_setlin_ramdac(ramdac *r) { +void dispwin_setlin_ramdac(ramdac *r) { int i, j; debug("dispwin_setlin_ramdac called\n"); @@ -2125,7 +2224,7 @@ static void dispwin_setlin_ramdac(ramdac *r) { } /* We're done with a ramdac structure */ -static void dispwin_del_ramdac(ramdac *r) { +void dispwin_del_ramdac(ramdac *r) { int j; debug("dispwin_del_ramdac called\n"); @@ -2138,7 +2237,7 @@ static void dispwin_del_ramdac(ramdac *r) { } /* ----------------------------------------------- */ -/* Useful function for X11 profie atom settings */ +/* Useful function for X11 profile atom settings */ #if defined(UNIX_X11) /* Return NZ on error */ @@ -2216,6 +2315,50 @@ static int set_X11_atom(dispwin *p, char *fname) { #endif /* UNXI X11 */ /* ----------------------------------------------- */ +/* See if colord is available */ + +#if defined(UNIX_X11) && defined(USE_UCMM) + +/* colord libcolordcompat.so shim functions */ + +ucmm_error (*cd_edid_install_profile)(unsigned char *edid, int edid_len, + ucmm_scope scope, char *profile_fn) = NULL; +ucmm_error (*cd_edid_remove_profile)(unsigned char *edid, int edid_len, char *profile_fn) = NULL; +ucmm_error (*cd_edid_get_profile)(unsigned char *edid, int edid_len, char **profile_fn) = NULL; +int cd_init = 0; /* nz if we've looked for colord */ +void *cd_found = NULL; /* .so handle if we've found colord */ + +/* Return nz if found colord functions */ +int dispwin_checkfor_colord() { + + if (cd_init) + return (cd_found != NULL); + + cd_found = NULL; + + if ((cd_found = dlopen("libcolordcompat.so", RTLD_LAZY)) != NULL) { + + cd_edid_install_profile = dlsym(cd_found, "cd_edid_install_profile"); + cd_edid_remove_profile = dlsym(cd_found, "cd_edid_remove_profile"); + cd_edid_get_profile = dlsym(cd_found, "cd_edid_get_profile"); + + if (cd_edid_install_profile == NULL + || cd_edid_remove_profile == NULL + || cd_edid_get_profile == NULL) { + cd_found = NULL; + } + } + + cd_init = 1; + + return (cd_found != NULL); +} + +#endif + + + +/* ----------------------------------------------- */ /* Install a display profile and make */ /* it the default for this display. */ /* Set the display to the calibration in the profile */ @@ -2223,6 +2366,7 @@ static int set_X11_atom(dispwin *p, char *fname) { /* (We assume that the caller has checked that it's an ICC profile) */ /* Return nz if failed */ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { + debugr2((errout,"dispwin_install_profile '%s'\n",fname)); #ifdef NT { char *fullpath; @@ -2232,8 +2376,6 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { WCS_PROFILE_MANAGEMENT_SCOPE wcssc; unsigned short *wpath, *wbname, *wmonid; - debugr2((errout,"dispwin_install_profile got '%s'\n",fname)); - if (GetColorDirectory(NULL, colpath, &colpathlen) == 0) { debugr2((errout,"Getting color directory failed\n")); return 1; @@ -2555,7 +2697,12 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { else sc = ucmm_user; - if ((ev = ucmm_install_monitor_profile(sc, p->edid, p->edid_len, p->name, fname)) != ucmm_ok) { + if (cd_found) + ev = cd_edid_install_profile(p->edid, p->edid_len, sc, fname); + else + ev = ucmm_install_monitor_profile(sc, p->edid, p->edid_len, p->name, fname); + + if (ev != ucmm_ok) { debugr2((errout,"Installing profile '%s' failed with error %d '%s'\n",fname,ev,ucmm_error_string(ev))); return 1; } @@ -2583,6 +2730,7 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { /* 1 if not sucessfully deleted */ /* 2 if profile not found */ int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { + debugr2((errout,"dispwin_uninstall_profile '%s'\n", fname)); #ifdef NT { char *fullpath; @@ -2592,8 +2740,6 @@ int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { WCS_PROFILE_MANAGEMENT_SCOPE wcssc; unsigned short *wbname, *wmonid; - debugr2((errout,"Uninstalling '%s'\n", fname)); - if (GetColorDirectory(NULL, colpath, &colpathlen) == 0) { debugr2((errout,"Getting color directory failed\n")); return 1; @@ -2841,7 +2987,12 @@ int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { else sc = ucmm_user; - if ((ev = ucmm_uninstall_monitor_profile(sc, p->edid, p->edid_len, p->name, fname)) != ucmm_ok) { + if (cd_found) + ev = cd_edid_remove_profile(p->edid, p->edid_len, fname); + else + ev = ucmm_uninstall_monitor_profile(sc, p->edid, p->edid_len, p->name, fname); + + if (ev != ucmm_ok) { debugr2((errout,"Installing profile '%s' failed with error %d '%s'\n",fname,ev,ucmm_error_string(ev))); return 1; } @@ -3039,7 +3190,12 @@ icmFile *dispwin_get_profile(dispwin *p, char *name, int mxlen) { debugr2((errout,"dispwin_get_profile called\n")); - if ((ev = ucmm_get_monitor_profile(p->edid, p->edid_len, p->name, &profile)) == ucmm_ok) { + if (cd_found) + ev = cd_edid_get_profile(p->edid, p->edid_len, &profile); + else + ev = ucmm_get_monitor_profile(p->edid, p->edid_len, p->name, &profile); + + if (ev == ucmm_ok) { if (name != NULL) { strncpy(name, profile, mxlen); @@ -3143,12 +3299,17 @@ icmFile *dispwin_get_profile(dispwin *p, char *name, int mxlen) { /* ----------------------------------------------- */ -/* Restore the display state */ +/* Restore the display state and free ramdacs */ static void restore_display(dispwin *p) { + if (p->oor != NULL) { + p->oor->del(p->oor); + p->oor = NULL; + } /* Restore the ramdac */ if (p->or != NULL) { p->set_ramdac(p, p->or, 0); + p->set_ramdac(p, p->or, 0); /* Hmm. To be sure to be sure... */ p->or->del(p->or); p->or = NULL; debugr("Restored original ramdac\n"); @@ -3288,6 +3449,9 @@ typedef struct { dispwin *p; DWWin *window; /* Our NSWindow */ DWView *view; /* Our NSView */ +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 + NSColorSpace *nscs; /* Colorspace from profile */ +#endif } osx_cntx_t; static void OSX_ProcessEvents(dispwin *p); @@ -3316,9 +3480,10 @@ unsigned char emptyCursor[43] = { /* Make cursor invisible over our window */ /* * This doesn't work very well. The only way to work it properly - * is to create a CGEventTap and do a hide/unkid cursor when + * is to create a CGEventTap and do a hide/unhide cursor when * the mouse enters the window. This needs the main thread - * to be dedicated to running the event loop. + * to be dedicated to running the event loop, so would involve + * some trickiness after main() in every program. */ - (void)resetCursorRects { [super resetCursorRects]; @@ -3339,10 +3504,28 @@ unsigned char emptyCursor[43] = { frect = NSMakeRect(p->tx, p->ty, (1.0 + p->tw), (1.0 + p->th)); - [[NSColor colorWithDeviceRed: p->r_rgb[0] - green: p->r_rgb[1] - blue: p->r_rgb[2] - alpha: 1.0] setFill]; +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 + /* Use matching profile to (hopefully) trigger null color transform */ + /* This doesn't work on < 10.6 though. */ + if (cx->nscs != NULL) { + CGFloat rgb[4]; + NSInteger cnt = 4; + rgb[0] = p->r_rgb[0]; + rgb[1] = p->r_rgb[1]; + rgb[2] = p->r_rgb[2]; + rgb[3] = 1.0; + cnt = [ cx->nscs numberOfColorComponents ] + 1; + [[NSColor colorWithColorSpace: cx->nscs components: rgb count: cnt] setFill]; + + /* This works for < 10.6, but not for >= 10.6 on non-primary display */ + } else +#endif + { + [[NSColor colorWithDeviceRed: p->r_rgb[0] + green: p->r_rgb[1] + blue: p->r_rgb[2] + alpha: 1.0] setFill]; + } [aPath appendBezierPathWithRect:frect]; [aPath fill]; } @@ -3388,6 +3571,8 @@ unsigned char emptyCursor[43] = { /* Create our window */ static void create_my_win(NSRect rect, osx_cntx_t *cx) { dispwin *p = cx->p; + SInt32 MacVers; + void *cspr = NULL; /* ColorSync profile ref. */ int i; /* We need to locate the NSScreen that corresponds to the */ @@ -3427,6 +3612,61 @@ static void create_my_win(NSRect rect, osx_cntx_t *cx) { [cx->window setContentView: cx->view]; [cx->window makeKeyAndOrderFront: nil]; + + /* Use a null color transform to ensure device values */ + /* on non-primary display for 10.6+ */ +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + + /* Get the ColorSync profile for this display */ + if ((cspr = cur_colorsync_ref(p)) == NULL) { + debugr2((errout,"cur_colorsync_ref failed\n")); + + } else { + +#ifdef NEVER + /* This is buggy on 10.6 (returns gray space). it does work on 10.4-10.5 & 10.7+ */ + cx->nscs = [[NSColorSpace alloc] initWithColorSyncProfile: cspr]; +#else + CGColorSpaceRef cgref; + + /* This works on 10.6+ */ + if ((cgref = CGColorSpaceCreateWithPlatformColorSpace(cspr)) == NULL) { + debugr2((errout,"CGColorSpaceCreateWithPlatformColorSpace failed\n")); + } else { + /* 10.4 doesn't declare initWithCGColorSpace, but does implement it */ + cx->nscs = [[NSColorSpace alloc] initWithCGColorSpace: cgref]; + } + CFRelease(cgref); +#endif + if (cx->nscs == NULL) { + debugr2((errout,"initWithColorSyncProfile failed\n")); + } else { + if ([ cx->nscs numberOfColorComponents ] != 3) { + [cx->nscs release]; + cx->nscs = NULL; + } + } + + if (cspr != NULL) { +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + CFRelease((ColorSyncProfileRef)cspr); +#else + CMCloseProfile((CMProfileRef)cspr); +#endif + } + } +#endif /* >= 10.6 */ + +#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 + && cx->nscs == NULL) { + warning("Unable to create null color transform - test colors may be wrong!"); + } +#endif + } #endif /* __APPLE__ */ @@ -3440,40 +3680,70 @@ dispwin *p, 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 */ debugr("dispwin_set_color called\n"); if (p->nowin) return 1; - p->rgb[0] = r; - p->rgb[1] = g; - p->rgb[2] = b; + 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; + +//printf("\n set rgb %f %f %f\n",p->rgb[0], p->rgb[1], p->rgb[2]); 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->rgb[j]; + 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); + } } +//if (p->out_tvenc) { +//printf(" %d: 8 bit tv = s_rgb %f %f %f\n",j, p->s_rgb[0], p->s_rgb[1], p->s_rgb[2]); +//printf(" %d: %d bitraster r_rgb %f %f %f\n",j, p->pdepth,p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]); +//} + /* Use ramdac for high precision native output. */ /* The ramdac is used to hold the lsb that the frame buffer */ /* doesn't hold. */ - if (p->native == 1) { + if ((p->native & 1) == 1) { double prange = p->r->nent - 1.0; + p->r->setlin(p->r); /* In case something else altered this */ + for (j = 0; j < 3; j++) { int tt; + double vv; -//printf("~1 %d: in %f, ",j,p->rgb[j]); - tt = (int)(p->rgb[j] * prange); - p->r->v[j][tt] = p->rgb[j]; + vv = p->s_rgb[j]; + + /* 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->out_tvenc && p->edepth > 8) + vv = (vv * 255 * (1 << (p->edepth - 8)))/((1 << p->edepth) - 1.0); + + tt = (int)(vv * prange + 0.5); p->r_rgb[j] = (double)tt/prange; /* RAMDAC output Quantized value */ -//printf(" cell[%d], val %f, rast val %f\n",tt, p->rgb[j], p->r_rgb[j]); + p->r->v[j][tt] = vv; + +//printf(" cell[%d] = r_rgb %f, cell val %f\n",tt, p->r_rgb[j], vv); } - if (p->set_ramdac(p,p->r, 0)) { + if (p->set_ramdac(p, p->r, 0)) { debugr("set_ramdac() failed\n"); return 1; } @@ -3487,10 +3757,15 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ /* Stop the system going to sleep */ - /* This used to work OK in reseting the screen saver, but not in Vista :-( */ + /* This used to work OK in reseting the screen saver on */ + /* all versions of MSWin. */ SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); - /* So we use a fake mouse non-movement reset the Vista screensaver. */ +#ifdef NEVER + /* (Some people use ES_CONTINUOUS | ES_AWAYMODE_REQUIRED as well ?? */ + /* See also setting SPI_SETSCREENSAVEACTIVE? ) */ + + /* Another approach is a fake mouse non-movementr. */ SystemParametersInfo(SPI_SETBLOCKSENDINPUTRESETS, FALSE, NULL, 0); fip.type = INPUT_MOUSE; fip.mi.dx = 0; @@ -3499,19 +3774,24 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ fip.mi.time = 0; fip.mi.dwExtraInfo = 0; SendInput(1, &fip, sizeof(INPUT)); +#endif p->colupd++; +//printf("~1 set color %f %f %f\n", p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]); /* Trigger a WM_PAINT */ if (!InvalidateRect(p->hwnd, NULL, FALSE)) { debugr2((errout,"InvalidateRect failed, lasterr = %d\n",GetLastError())); return 1; } + UpdateWindow(p->hwnd); +//printf("~1 waiting for paint\n"); /* Wait for WM_PAINT to be executed */ - while (p->colupd != p->colupde) { - msec_sleep(20); + while (p->colupd != p->colupde && p->cberror == 0) { + msec_sleep(10); } +//printf("~1 paint done\n"); } #endif /* NT */ @@ -3523,6 +3803,10 @@ 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]; + /* Stop the system going to sleep */ UpdateSystemActivity(OverallAct); @@ -3552,6 +3836,8 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ /* Process events */ OSX_ProcessEvents(p); + [tpool release]; + #endif /* __APPLE__ */ /* - - - - - - - - - - - - - - */ @@ -3597,11 +3883,48 @@ 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; + } + /* 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(p->update_delay); + msec_sleep(update_delay); + + if (p->cberror) { /* Callback routine failed */ + return 1; + } return 0; } @@ -3633,7 +3956,7 @@ char *callout /* ----------------------------------------------- */ /* Destroy ourselves */ -static void dispwin_del( +void dispwin_del( dispwin *p ) { @@ -3682,6 +4005,12 @@ dispwin *p p->winclose = 1; [cx->window release]; + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 + if (cx->nscs != NULL) + [cx->nscs release]; +#endif + free(p->osx_cntx); p->osx_cntx = NULL; } @@ -3730,6 +4059,11 @@ dispwin *p #define SWP_STATECHANGED 0x8000 #endif +/* MingW doesn't seem to have this, even though it's been there sine Win2k ... */ +#ifndef ICM_DONE_OUTSIDEDC +# define ICM_DONE_OUTSIDEDC 4 +#endif + static LRESULT CALLBACK MainWndProc( HWND hwnd, UINT message, @@ -3776,29 +4110,50 @@ static LRESULT CALLBACK MainWndProc( vali[j] = (int)(255.0 * p->r_rgb[j] + 0.5); } - hdc = BeginPaint(hwnd, &ps); + if ((hdc = BeginPaint(hwnd, &ps)) == NULL) { + debugrr2l(4, (stderr,"BeginPaint failed\n")); + EndPaint(hwnd, &ps); + p->cberror = 2; + return 0; + } - SaveDC(hdc); + if (SaveDC(hdc) == 0) { + debugrr2l(4, (stderr,"SaveDC failed\n")); + EndPaint(hwnd, &ps); + p->cberror = 3; + return 0; + } - /* Try and turn ICM off */ -#ifdef ICM_DONE_OUTSIDEDC + /* Try and turn ICM & WCS off */ if (!SetICMMode(hdc, ICM_DONE_OUTSIDEDC)) { + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + osver.dwMajorVersion = 5; + GetVersionEx(&osver); /* This seems to fail with "invalid handle" under NT4 */ - /* Does it work under Win98 or Win2K ? */ - printf("SetICMMode failed, lasterr = %d\n",GetLastError()); + /* so only report an error if Win2K or more */ + if (osver.dwMajorVersion >= 5) + printf("SetICMMode failed, lasterr = %d\n",GetLastError()); } -#endif - - hbr = CreateSolidBrush(RGB(vali[0],vali[1],vali[2])); - SelectObject(hdc,hbr); - SetRect(&rect, p->tx, p->ty, p->tx + p->tw, p->ty + p->th); - FillRect(hdc, &rect, hbr); + if ((hbr = CreateSolidBrush(RGB(vali[0],vali[1],vali[2]))) == NULL) { + debugrr2l(4, (stderr,"CreateSolidBrush failed\n")); + RestoreDC(hdc,-1); + EndPaint(hwnd, &ps); + p->cberror = 4; + return 0; + } + if (SelectObject(hdc, hbr) == NULL + || SetRect(&rect, p->tx, p->ty, p->tx + p->tw, p->ty + p->th) == 0 + || FillRect(hdc, &rect, hbr) == 0) { + debugrr2l(4, (stderr,"SelectObject/SetRect/FillRect failed\n")); + p->cberror = 5; + } + DeleteObject(hbr); RestoreDC(hdc,-1); - DeleteDC(hdc); - EndPaint(hwnd, &ps); + GdiFlush(); p->colupde = p->colupd; /* We're updated to this color */ @@ -3847,7 +4202,7 @@ static void OSX_ProcessEvents(dispwin *p) { /* 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]; +// NSAutoreleasePool *tpool = [NSAutoreleasePool new]; /* Wait until the events are done */ to = [NSDate dateWithTimeIntervalSinceNow:0.01]; /* autorelease ? */ @@ -3860,7 +4215,7 @@ static void OSX_ProcessEvents(dispwin *p) { break; } } - [tpool release]; +// [tpool release]; } #endif /* __APPLE__ */ @@ -3890,7 +4245,7 @@ int win_message_thread(void *pp) { wc.hInstance = NULL; /* Application that owns the class. */ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_CROSS); - wc.hbrBackground = GetStockObject(BLACK_BRUSH); + wc.hbrBackground = GetStockObject(BLACK_BRUSH); /* So full screen black works */ wc.lpszMenuName = NULL; wc.lpszClassName = p->AppName; @@ -3913,7 +4268,9 @@ int win_message_thread(void *pp) { p->AppName, "Argyll Display Calibration Window", WS_VISIBLE | WS_DISABLED | WS_POPUP, -// WS_OVERLAPPEDWINDOW | WS_VISIBLE, +// WS_EX_TOPMOST +// WS_EX_PALETTEWINDOW +// WS_OVERLAPPEDWINDOW | WS_VISIBLE // WS_POPUPWINDOW | WS_VISIBLE, p->xo, p->yo, /* Location */ p->wi, p->he, /* Size */ @@ -3978,9 +4335,13 @@ disppath *disp, /* Display to calibrate. */ double width, double height, /* Width and height in mm */ 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, /* 0 = use current current or given calibration curve */ - /* 1 = use native linear out & high precision */ -int *noramdac, /* Return nz if no ramdac access. native is set to 0 */ +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 override, /* NZ if override_redirect is to be used on X11 */ int ddebug /* >0 to print debug statements to stderr */ @@ -3990,6 +4351,16 @@ int ddebug /* >0 to print debug statements to stderr */ debug("new_dispwin called\n"); +#if defined(UNIX_X11) && defined(USE_UCMM) + dispwin_checkfor_colord(); /* Make colord functions available */ + if (ddebug) { + if (cd_found) + fprintf(stderr,"using colord for profile installation\n"); + else + fprintf(stderr,"using ucmm for profile installation\n"); + } +#endif + if ((p = (dispwin *)calloc(sizeof(dispwin), 1)) == NULL) { if (ddebug) fprintf(stderr,"new_dispwin failed because malloc failed\n"); return NULL; @@ -3998,6 +4369,7 @@ int ddebug /* >0 to print debug statements to stderr */ /* !!!! Make changes in webwin.c as well !!!! */ p->nowin = nowin; p->native = native; + p->out_tvenc = out_tvenc; p->blackbg = blackbg; p->ddebug = ddebug; p->get_ramdac = dispwin_get_ramdac; @@ -4027,6 +4399,8 @@ int ddebug /* >0 to print debug statements to stderr */ 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 */ + /* Basic object is initialised, so create a window */ /* -------------------------------------------------- */ @@ -4119,6 +4493,8 @@ int ddebug /* >0 to print debug statements to stderr */ else p->pdepth = bpp/3; + p->edepth = 16; + if (nowin == 0) { /* We use a thread to process the window messages, so that */ @@ -4167,12 +4543,30 @@ int ddebug /* >0 to print debug statements to stderr */ } p->ddid = disp->ddid; /* Display we're working on */ + /* Hmm. Could we use CGDisplayGammaTableCapacity() instead ? */ #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { CGDisplayModeRef dispmode; CFStringRef pixenc; + int cap = CGDisplayGammaTableCapacity(p->ddid); + int fbdepth = 0; - p->pdepth = 0; + debugr2((errout,"new_dispwin: CGDisplayGammaTableCapacity = %d\n",cap)); + + /* Compute GammaTable depth */ + { + for (p->pdepth = 1; p->pdepth < 17; p->pdepth++) { + if ((1 << p->pdepth) == cap) + break; + } + if (p->pdepth >= 17) { + debugr2((errout,"new_dispwin: failed to extract depth from GammaTableCapacity %d\n",cap)); + dispwin_del(p); + return NULL; + } + debugr2((errout,"new_dispwin: found pixel depth %d bits\n",p->pdepth)); + } + /* Get frame buffer depth for sanity check */ dispmode = CGDisplayCopyDisplayMode(p->ddid); pixenc = CGDisplayModeCopyPixelEncoding(dispmode); @@ -4180,23 +4574,42 @@ int ddebug /* >0 to print debug statements to stderr */ /* Hmm. Don't know what to do with kIO16BitFloatPixels or kIO32BitFloatPixels */ if (CFStringCompare(pixenc, CFSTR(kIO64BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - p->pdepth = 16; + fbdepth = 16; else if (CFStringCompare(pixenc, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - p->pdepth = 10; + fbdepth = 10; else if (CFStringCompare(pixenc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - p->pdepth = 8; + fbdepth = 8; else if (CFStringCompare(pixenc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - p->pdepth = 5; + fbdepth = 5; +#ifndef DEBUG + if (p->ddebug) +#endif + { + char buf[200]; + CFStringGetCString(pixenc, buf, 200, kCFStringEncodingUTF8); + debugr2((errout,"new_dispwin: CGDisplayModePixelEncoding = '%s'\n",buf)); + } + CFRelease(pixenc); CGDisplayModeRelease(dispmode); + + if (p->pdepth != fbdepth) { + static int warned = 0; + if (!warned) { + warning("new_dispwin: frame buffer depth %d != GammaTable depth %d\n",fbdepth, p->pdepth); + warned = 1; + } + } } #else p->pdepth = CGDisplayBitsPerSample(p->ddid); #endif + p->edepth = 16; /* By experiment it seems to be 16 bits too */ + if (nowin == 0) { /* Create a window */ osx_cntx_t *cx; CGSize sz; /* Display size in mm */ @@ -4206,13 +4619,17 @@ 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 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 (NSApp == nil) { - static NSAutoreleasePool *pool; /* Pool used for NSApp */ - pool = [NSAutoreleasePool new]; +// 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 */ @@ -4220,6 +4637,7 @@ int ddebug /* >0 to print debug statements to stderr */ } if ((cx = (osx_cntx_t *)calloc(sizeof(osx_cntx_t), 1)) == NULL) { + [tpool release]; debugr2((errout,"new_dispwin: Malloc failed (osx_cntx_t)\n")); dispwin_del(p); return NULL; @@ -4268,6 +4686,8 @@ int ddebug /* >0 to print debug statements to stderr */ OSX_ProcessEvents(p); + [tpool release]; + p->winclose = 0; } @@ -4362,6 +4782,7 @@ int ddebug /* >0 to print debug statements to stderr */ //p->pdepth = DefaultDepth(p->mydisplay, p->myscreen)/3; myvisual = DefaultVisual(p->mydisplay, p->myscreen); p->pdepth = myvisual->bits_per_rgb; + p->edepth = 16; if (nowin == 0) { /* Create a window */ rootwindow = RootWindow(p->mydisplay, p->myscreen); @@ -4625,32 +5046,47 @@ int ddebug /* >0 to print debug statements to stderr */ #endif /* UNIX X11 */ /* -------------------------------------------------- */ - if (!p->nowin) { - /* Setup for native mode */ - if (p->native) { - debug("About to setup native mode\n"); - if ((p->or = p->get_ramdac(p)) == NULL - || (p->r = p->or->clone(p->or)) == NULL) { - if (noramdac != NULL) - *noramdac = 1; - debugr("new_dispwin: Accessing VideoLUT failed, so no way to guarantee that calibration is turned off!!\n"); - warning("new_dispwin: Accessing VideoLUT failed, so no way to guarantee that calibration is turned off!!"); - p->native = 0; - } else { - p->r->setlin(p->r); - if (noramdac != NULL) - *noramdac = 0; - debug("Saved original VideoLUT\n"); - } - } else { - if (p->get_ramdac(p) == NULL) { - if (noramdac != NULL) - *noramdac = 1; - } + /* Save the original ramdac, which gets restored on exit */ + if ((p->or = p->get_ramdac(p)) != NULL) { + + if (noramdac != NULL) + *noramdac = 0; + + debugr("Saved original VideoLUT\n"); + + /* Copy original ramdac that never gets altered */ + if ((p->oor = p->or->clone(p->or)) == NULL) { + dispwin_del(p); + debugr("ramdac clone failed - memory ?\n"); + return NULL; } - + + /* Create a working ramdac for native or other use */ + if ((p->r = p->or->clone(p->or)) == NULL) { + dispwin_del(p); + debugr("ramdac clone failed - memory ?\n"); + return NULL; + } + + } else { + debugr("Unable to access VideoLUT\n"); + if (noramdac != NULL) + *noramdac = 1; + p->oor = p->or = p->r = NULL; + } + + if (!p->nowin) { + /* Make sure initial test color is displayed */ dispwin_set_color(p, p->rgb[0], p->rgb[1], p->rgb[2]); + + /* Hmm. Could we add this ?? */ + /* Hard to know whether OS CM is active though. By default */ + /* dispwin disables it. */ + if (nocm != NULL) + *nocm = 1; + + p->native &= ~2; } debugr("new_dispwin: return sucessfully\n"); @@ -4705,6 +5141,16 @@ int x11_daemon_mode(disppath *disp, int verb, int ddebug) { && XRRQueryExtension(mydisplay, &evb, &erb) != 0 && XRRQueryVersion(mydisplay, &majv, &minv) && majv == 1 && minv >= 2) { + static void *xrr_found = NULL; /* .so handle */ + static XRRScreenResources *(*_XRRGetScreenResourcesCurrent) + (Display *dpy, Window window) = NULL; + XRRScreenResources *scrnres; + + if (minv >= 3 && xrr_found == NULL) { + if ((xrr_found = dlopen("libXrandr.so", RTLD_LAZY)) != NULL) + _XRRGetScreenResourcesCurrent = dlsym(xrr_found, "XRRGetScreenResourcesCurrent"); + } + if (verb) printf("Found XRandR 1.2 or latter\n"); XRRSelectInput(mydisplay,RootWindow(mydisplay,0), @@ -4721,7 +5167,12 @@ int x11_daemon_mode(disppath *disp, int verb, int ddebug) { if (update_profiles == 0) { if (dopoll) { for (;;) { - XRRGetScreenResources(mydisplay, RootWindow(mydisplay,0)); + if (minv >= 3 && _XRRGetScreenResourcesCurrent != NULL) { + _XRRGetScreenResourcesCurrent(mydisplay, RootWindow(mydisplay,0)); + + } else { + XRRGetScreenResources(mydisplay, RootWindow(mydisplay,0)); + } if(XPending(mydisplay) > 0) break; sleep(2); @@ -4777,7 +5228,7 @@ int x11_daemon_mode(disppath *disp, int verb, int ddebug) { break; if (verb) printf("Updating display %d = '%s'\n",i+1,dp[i]->description); - if ((dw = new_dispwin(dp[i], 0.0, 0.0, 0.0, 0.0, 1, 0, NULL, 0, 0, ddebug)) == NULL) { + if ((dw = new_dispwin(dp[i], 0.0, 0.0, 0.0, 0.0, 1, 0, NULL, NULL, 0, 0, 0, ddebug)) == NULL) { if (verb) printf("Failed to access screen %d of display '%s'\n",i+1,dnbuf); continue; } @@ -4814,7 +5265,7 @@ int x11_daemon_mode(disppath *disp, int verb, int ddebug) { } if ((wo = (icmVideoCardGamma *)icco->read_tag(icco, icSigVideoCardGammaTag)) == NULL) { - if (verb) printf("Failed to fined vcgt tagd in profile for screen %d for display '%s' so setting linear\n",i+1,dnbuf); + if (verb) printf("Failed to find vcgt tagd in profile for screen %d for display '%s' so setting linear\n",i+1,dnbuf); for (j = 0; j < r->nent; j++) { iv = j/(r->nent-1.0); r->v[0][j] = iv; @@ -4927,14 +5378,19 @@ static void usage(char *diag, ...) { } free_disppaths(dp); fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); +#ifdef NT + fprintf(stderr," -dmadvr Display via MadVR Video Renderer\n"); +#endif + 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," -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"); fprintf(stderr," -r Test just Video LUT loading & Beeps\n"); - fprintf(stderr," -n Test native output (rather than through Video LUT)\n"); + fprintf(stderr," -n Test native output (rather than through Video LUT and C.M.)\n"); fprintf(stderr," -s filename Save the currently loaded Video LUT to 'filename'\n"); fprintf(stderr," -c Load a linear display calibration\n"); fprintf(stderr," -V Verify that calfile/profile cal. is currently loaded in LUT\n"); @@ -4944,7 +5400,7 @@ static void usage(char *diag, ...) { fprintf(stderr," d is one of: n = network, l = local system, u = user (default)\n"); fprintf(stderr," -L Load installed profiles cal. into Video LUT\n"); #if defined(UNIX_X11) - fprintf(stderr," -E Run in daemon loader mode for given X11 server\n"); + fprintf(stderr," -X Run in daemon loader mode for given X11 server\n"); #endif /* X11 */ fprintf(stderr," -D [level] Print debug diagnostics to stderr\n"); fprintf(stderr," calfile Load calibration (.cal or %s) into Video LUT\n",ICC_FILE_EXT); @@ -4961,19 +5417,27 @@ main(int argc, char *argv[]) { int verb = 0; /* Verbose flag */ int ddebug = 0; /* debug level */ int webdisp = 0; /* NZ for web display, == port number */ +#ifdef NT + int madvrdisp = 0; /* NZ for MadVR display */ +#endif 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 blackbg = 0; /* NZ if whole screen should be filled with black */ int nowin = 0; /* Don't create test window */ int ramd = 0; /* Just test ramdac */ int fade = 0; /* Test greyramp fade */ - int native = 0; /* 0 = use current current or given calibration curve */ - /* 1 = set native linear output and use ramdac high prec'n */ - /* 2 = set native linear output */ - int noramdac = 0; /* Set to nz if there is no VideoLUT access */ + int native = 0; /* 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 = 0; /* nz if no ramdac access. native is set to X0 */ + int nocm = 0; /* nz if no CM cLUT access. native is set to 0X */ int inf = 0; /* Infnite/manual patches flag */ char pcname[MAXNAMEL+1] = "\000"; /* CGATS patch color name */ + int nmrgb = 0; /* Number of manual RGB values */ + double mrgb[10][3]; /* Manual RGB values */ int clear = 0; /* Clear any display calibration (any calname is ignored) */ char sname[MAXNAMEL+1] = "\000"; /* Current cal save name */ int verify = 0; /* Verify that calname is currently loaded */ @@ -4986,7 +5450,7 @@ main(int argc, char *argv[]) { dispwin *dw; unsigned int seed = 0x56781234; int i, j; - ramdac *or = NULL, *r = NULL; +// ramdac *r = NULL; int is_ok_icc = 0; /* The profile is OK */ error_program = "Dispwin"; @@ -5038,6 +5502,12 @@ main(int argc, char *argv[]) { usage("Web port number must be in range 1..65535"); } fa = nfa; +#ifdef NT + } else if (strncmp(na,"madvr",5) == 0 + || strncmp(na,"MADVR",5) == 0) { + madvrdisp = 1; + fa = nfa; +#endif } else { #if defined(UNIX_X11) int ix, iv; @@ -5096,10 +5566,14 @@ main(int argc, char *argv[]) { } else if (argv[fa][1] == 'F') { blackbg = 1; + /* Video mode encoding */ + } else if (argv[fa][1] == 'E') { + out_tvenc = 1; + } else if (argv[fa][1] == 'i') inf = 1; - else if (argv[fa][1] == 'm' || argv[fa][1] == 'M') + else if (argv[fa][1] == 'm') inf = 2; /* CGATS patch color file */ @@ -5108,16 +5582,28 @@ main(int argc, char *argv[]) { if (na == NULL) usage("-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"); + if (sscanf(na, "%lf,%lf,%lf ",&mrgb[nmrgb][0],&mrgb[nmrgb][1],&mrgb[nmrgb][2]) != 3) + usage("-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]); + nmrgb++; + } else if (argv[fa][1] == 'f') fade = 1; - else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') + else if (argv[fa][1] == 'r') ramd = 1; - else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') { - native = 1; - if (argv[fa][1] == 'N') - native = 2; + else if (argv[fa][1] == 'n') { + native = 3; /* Disable cal & any CM */ } else if (argv[fa][1] == 's') { @@ -5141,7 +5627,7 @@ main(int argc, char *argv[]) { else if (argv[fa][1] == 'L') loadprofile = 1; - else if (argv[fa][1] == 'E') + else if (argv[fa][1] == 'X') daemonmode = 1; else if (argv[fa][1] == 'S') { @@ -5162,7 +5648,11 @@ main(int argc, char *argv[]) { } /* No explicit display has been set */ - if (webdisp == 0 && disp == NULL) { + if (disp == NULL +#ifdef NT + && madvrdisp == 0 +#endif + && webdisp == 0) { int ix = 0; #if defined(UNIX_X11) char *dn, *pp; @@ -5212,22 +5702,36 @@ main(int argc, char *argv[]) { if (webdisp != 0) { - if ((dw = new_webwin(webdisp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, blackbg, verb, ddebug)) == NULL) { - printf("Error - new_webpwin failed!\n"); + if ((dw = new_webwin(webdisp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, + &noramdac, &nocm, out_tvenc, blackbg, verb, ddebug)) == NULL) { + printf("Error - new_webwin failed!\n"); return -1; } - noramdac = 1; +#ifdef NT + } else if (madvrdisp != 0) { + if (out_tvenc) { + printf("Error - Set TV encodfing in MadVR\n"); + return -1; + } + + 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"); + return -1; + } +#endif } else { if (verb) printf("About to open dispwin object on the display\n"); - if ((dw = new_dispwin(disp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, &noramdac, blackbg, 1, ddebug)) == NULL) { + if ((dw = new_dispwin(disp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, + &noramdac, &nocm, out_tvenc, blackbg, 1, ddebug)) == NULL) { printf("Error - new_dispwin failed!\n"); return -1; } } - if (native != 0 && noramdac) { - error("We don't have access to the VideoLUT so can't display native colors\n"); + if ((native & 1) != 0 && noramdac) { + warning("Unable to access to VideoLUTs so can't be sure colors are native"); } /* Save the current Video LUT to the calfile */ @@ -5242,7 +5746,7 @@ main(int argc, char *argv[]) { if (verb) printf("About to save current loaded calibration to file '%s'\n",sname); - if ((r = dw->get_ramdac(dw)) == NULL) { + if (dw->oor == NULL) { error("We don't have access to the VideoLUT"); } @@ -5267,16 +5771,16 @@ main(int argc, char *argv[]) { error("Malloc failed!"); /* Write the video lut curve values */ - for (i = 0; i < r->nent; i++) { - double iv = i/(r->nent-1.0); + for (i = 0; i < dw->oor->nent; i++) { + double iv = i/(dw->oor->nent-1.0); #if defined(__APPLE__) && defined(__POWERPC__) gcc_bug_fix(i); #endif setel[0].d = iv; - setel[1].d = r->v[0][i]; - setel[2].d = r->v[1][i]; - setel[3].d = r->v[2][i]; + setel[1].d = dw->oor->v[0][i]; + setel[2].d = dw->oor->v[1][i]; + setel[3].d = dw->oor->v[2][i]; ocg->add_setarr(ocg, 0, setel); } @@ -5287,8 +5791,6 @@ main(int argc, char *argv[]) { error("Write error to '%s' : %s",sname,ocg->err); ocg->del(ocg); /* Clean up */ - r->del(r); - r = NULL; /* Fall through, as we may want to do other stuff too */ } @@ -5297,28 +5799,24 @@ main(int argc, char *argv[]) { if (clear != 0) { int rv; - if ((r = dw->get_ramdac(dw)) == NULL) { - error("We don't have access to the VideoLUT"); - } + if (dw->or == NULL) + error("We don't have access to the VideoLUT for clearing"); - for (i = 0; i < r->nent; i++) { - double iv = i/(r->nent-1.0); - r->v[0][i] = iv; - r->v[1][i] = iv; - r->v[2][i] = iv; + for (i = 0; i < dw->or->nent; i++) { + double iv = i/(dw->or->nent-1.0); + dw->or->v[0][i] = iv; + dw->or->v[1][i] = iv; + dw->or->v[2][i] = iv; } if (verb) printf("About to clear the calibration\n"); - if ((rv = dw->set_ramdac(dw,r,1)) != 0) { + if ((rv = dw->set_ramdac(dw,dw->or,1)) != 0) { if (rv == 2) warning("Failed to set VideoLUTs persistently because current System Profile can't be renamed"); else error("Failed to set VideoLUTs"); } - r->del(r); - r = NULL; - /* Fall through, as we may want to do other stuff too */ } @@ -5338,15 +5836,14 @@ main(int argc, char *argv[]) { /* Get any calibration from the provided .cal file or .profile, */ /* or calibration from the current default display profile, */ - /* and put it in r */ + /* and put it in dw->or */ if (loadfile != 0 || verify != 0 || loadprofile != 0 || installprofile == 1) { icmFile *rd_fp = NULL; icc *icco = NULL; cgats *ccg = NULL; /* calibration cgats structure */ - /* Get a calibration that's compatible with the display. */ - /* This can fail and return NULL - error if we later need it */ - r = dw->get_ramdac(dw); + if (dw->r == NULL) + error("We don't have access to the VideoLUT for loading"); /* Should we load calfile instead of installed profile if it's present ??? */ if (loadprofile) { @@ -5377,46 +5874,41 @@ main(int argc, char *argv[]) { if ((wo = (icmVideoCardGamma *)icco->read_tag(icco, icSigVideoCardGammaTag)) == NULL) { warning("No vcgt tag found in profile - assuming linear\n"); - if (r != NULL) { - for (i = 0; i < r->nent; i++) { - iv = i/(r->nent-1.0); - r->v[0][i] = iv; - r->v[1][i] = iv; - r->v[2][i] = iv; - } + for (i = 0; i < dw->r->nent; i++) { + iv = i/(dw->r->nent-1.0); + dw->r->v[0][i] = iv; + dw->r->v[1][i] = iv; + dw->r->v[2][i] = iv; } } else { - /* Hmm. Perhaps we should ignore this if the vcgt is linear ?? */ - if (r == NULL) - error("We don't have access to the VideoLUT"); - if (wo->u.table.channels == 3) { - for (i = 0; i < r->nent; i++) { - iv = i/(r->nent-1.0); - r->v[0][i] = wo->lookup(wo, 0, iv); - r->v[1][i] = wo->lookup(wo, 1, iv); - r->v[2][i] = wo->lookup(wo, 2, iv); -//printf("~1 entry %d = %f %f %f\n",i,r->v[0][i],r->v[1][i],r->v[2][i]); + for (i = 0; i < dw->r->nent; i++) { + iv = i/(dw->r->nent-1.0); + dw->r->v[0][i] = wo->lookup(wo, 0, iv); + dw->r->v[1][i] = wo->lookup(wo, 1, iv); + dw->r->v[2][i] = wo->lookup(wo, 2, iv); +//printf("~1 entry %d = %f %f %f\n",i,dw->r->v[0][i],dw->r->v[1][i],dw->r->v[2][i]); } debug("Got color vcgt calibration\n"); } else if (wo->u.table.channels == 1) { - for (i = 0; i < r->nent; i++) { - iv = i/(r->nent-1.0); - r->v[0][i] = - r->v[1][i] = - r->v[2][i] = wo->lookup(wo, 0, iv); + for (i = 0; i < dw->r->nent; i++) { + iv = i/(dw->r->nent-1.0); + dw->r->v[0][i] = + dw->r->v[1][i] = + dw->r->v[2][i] = wo->lookup(wo, 0, iv); } debug("Got monochrom vcgt calibration\n"); - } else { - r->del(r); - r = NULL; } + /* ~~~ Ideally the vcgt should have been tagged if it is TV encoded, so */ + /* that the scaling can be adjusted here if the RAMDAC depth differs from */ + /* the vcgt depth. ~~~ */ } } else { /* See if it's a .cal file */ int ncal; int ii, fi, ri, gi, bi; double cal[3][256]; + int out_tvenc = 0; /* nz to use (16-235)/255 video encoding */ icco->del(icco); /* Don't need these now */ icco = NULL; @@ -5447,6 +5939,12 @@ main(int argc, char *argv[]) { if (strcmp(ccg->t[0].kdata[fi],"DISPLAY") != 0) error("Calibration file '%s' doesn't have DEVICE_CLASS of DISPLAY",calname); + if ((fi = ccg->find_kword(ccg, 0, "TV_OUTPUT_ENCODING")) >= 0) { + if (strcmp(ccg->t[0].kdata[fi], "YES") == 0 + || strcmp(ccg->t[0].kdata[fi], "yes") == 0) + out_tvenc = 1; + } + if ((ii = ccg->find_field(ccg, 0, "RGB_I")) < 0) error("Calibration file '%s' doesn't contain field RGB_I",calname); if (ccg->t[0].ftype[ii] != r_t) @@ -5469,24 +5967,38 @@ main(int argc, char *argv[]) { cal[2][i] = *((double *)ccg->t[0].fdata[i][bi]); } - if (r == NULL) - error("We don't have access to the VideoLUT"); - /* Interpolate from cal value to RAMDAC entries */ - for (i = 0; i < r->nent; i++) { + for (i = 0; i < dw->r->nent; i++) { double val, w; unsigned int ix; - val = (ncal-1.0) * i/(r->nent-1.0); + val = (ncal-1.0) * i/(dw->r->nent-1.0); ix = (unsigned int)floor(val); /* Coordinate */ if (ix > (ncal-2)) ix = (ncal-2); w = val - (double)ix; /* weight */ for (j = 0; j < 3; j++) { val = cal[j][ix]; - r->v[j][i] = val + w * (cal[j][ix+1] - val); + dw->r->v[j][i] = val + w * (cal[j][ix+1] - val); } } + /* If the calibration was created with a restricted range video encoding, */ + /* ensure that the installed calibration applies this encoding. */ + if (out_tvenc) { + for (i = 0; i < dw->r->nent; i++) { + for (j = 0; j < 3; j++) { + dw->r->v[j][i] = (dw->r->v[j][i] * (235.0-16.0) + 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 (dw->edepth > 8) + dw->r->v[j][i] = (dw->r->v[j][i] * 255 * (1 << (dw->edepth - 8))) + /((1 << dw->edepth) - 1.0); + } + } + } + debug("Got cal file calibration\n"); } if (ccg != NULL) @@ -5502,27 +6014,35 @@ main(int argc, char *argv[]) { if (is_ok_icc == 0) error("File '%s' doesn't seem to be an ICC profile!",calname); - if (verb) - printf("About to install '%s' as display's default profile\n",calname); - if (dw->install_profile(dw, calname, r, scope)) { - error("Failed to install profile '%s'!",calname); - } - if (verb) { - printf("Installed '%s' and made it the default\n",calname); + if (dw->r== NULL) { + warning("Unable to access VideoLUT so can't install calibration"); + } else { + if (verb) + printf("About to install '%s' as display's default profile\n",calname); + if (dw->or) + dw->or->del(dw->or); + if ((dw->or = dw->r->clone(dw->r)) == NULL) + error("Failed to clone VideoLUT - memory ?"); + if (dw->install_profile(dw, calname, dw->or, scope)) + error("Failed to install profile '%s'!",calname); + if (verb) + printf("Installed '%s' and made it the default\n",calname); } /* load the LUT with the calibration from the given file or the current display profile. */ - /* (But don't load profile calibration if we're verifying against it) */ - } else if (loadfile != 0 || (loadprofile != 0 && verify == 0)) { + } else if (loadfile != 0 || loadprofile != 0) { int rv; - /* r == NULL if no VideoLUT access and ICC profile without vcgt */ - if (r == NULL) { - warning("No linear calibration loaded because there is no access to the VideoLUT"); + if (dw->or == NULL) { + warning("Calibration not loaded because there is no access to the VideoLUT"); } else { if (verb) printf("About to set display to given calibration\n"); - if ((rv = dw->set_ramdac(dw,r,1)) != 0) { + if (dw->or) + dw->or->del(dw->or); + if ((dw->or = dw->r->clone(dw->r)) == NULL) + error("Failed to clone VideoLUT - memory ?"); + if ((rv = dw->set_ramdac(dw,dw->or,1)) != 0) { if (rv == 2) error("Failed to set VideoLUTs persistently because current System Profile can't be renamed"); else @@ -5536,16 +6056,20 @@ main(int argc, char *argv[]) { if (verify != 0) { int ver = 1; double berr = 0.0; - if ((or = dw->get_ramdac(dw)) == NULL) - error("Unable to get current VideoLUT for verify"); + + if (dw->oor == NULL) + error("Unable to get original VideoLUT for verify"); - if (r == NULL) + if (dw->r == NULL) error("No calibration to verify against"); + if (dw->r->nent != dw->oor->nent) + error("VideoLUTs have different size"); + for (j = 0; j < 3; j++) { - for (i = 0; i < r->nent; i++) { + for (i = 0; i < dw->oor->nent; i++) { double err; - err = fabs(r->v[j][i] - or->v[j][i]); + err = fabs(dw->oor->v[j][i] - dw->r->v[j][i]); if (err > berr) berr = err; if (err > VERIFY_TOL) { @@ -5557,12 +6081,6 @@ main(int argc, char *argv[]) { printf("Verify: '%s' IS loaded (discrepancy %.1f%%)\n", calname, berr * 100); else printf("Verify: '%s' is NOT loaded (discrepancy %.1f%%)\n", calname, berr * 100); - or->del(or); - or = NULL; - } - if (r != NULL) { - r->del(r); - r = NULL; } /* If no other command selected, do a Window or VideoLUT test */ @@ -5637,7 +6155,9 @@ main(int argc, char *argv[]) { printf("Patch no %d",i+1); printf(" color %f %f %f\n",r,g,b); - dw->set_color(dw, r, g, b); + if (dw->set_color(dw, r, g, b) != 0) { + error ("set_color failed"); + } if (inf == 2) getchar(); @@ -5646,6 +6166,37 @@ main(int argc, char *argv[]) { } icg->del(icg); + /* Manually define patche colors */ + } else if (nmrgb > 0) { + int i; + int ri, gi, bi; + + if (inf == 2) + printf("\nHit return to advance each color\n"); + + if (inf == 2) { + printf("\nHit return to start\n"); + getchar(); + } + for (i = 0; i < nmrgb; i++) { + double r, g, b; + r = mrgb[i][0]; + g = mrgb[i][1]; + b = mrgb[i][2]; + + printf("Patch no %d",i+1); + printf(" color %f %f %f\n",r,g,b); + + if (dw->set_color(dw, r, g, b) != 0) { + error ("set_color failed"); + } + + if (inf == 2) + getchar(); + else + sleep(2); + } + /* Preset and random patch colors */ } else { @@ -5796,32 +6347,30 @@ main(int argc, char *argv[]) { if (inf != 2) { /* Test out the VideoLUT access */ - if ((dw->or = dw->get_ramdac(dw)) != NULL) { /* Use dw->or so signal will restore */ + if (dw->r != NULL) { /* Working ramdac to use */ - r = dw->or->clone(dw->or); - /* Try darkening it */ for (j = 0; j < 3; j++) { - for (i = 0; i < r->nent; i++) { - r->v[j][i] = pow(dw->or->v[j][i], 2.0); + for (i = 0; i < dw->r->nent; i++) { + dw->r->v[j][i] = pow(dw->or->v[j][i], 2.0); } } printf("Darkening screen\n"); - if (dw->set_ramdac(dw,r,0)) { - dw->set_ramdac(dw,or,0); + if (dw->set_ramdac(dw, dw->r, 0)) { + dw->set_ramdac(dw, dw->or, 0); /* is this needed ? */ error("Failed to set VideoLUTs"); } sleep(1); /* Try lightening it */ for (j = 0; j < 3; j++) { - for (i = 0; i < r->nent; i++) { - r->v[j][i] = pow(dw->or->v[j][i], 0.5); + for (i = 0; i < dw->r->nent; i++) { + dw->r->v[j][i] = pow(dw->or->v[j][i], 0.5); } } printf("Lightening screen\n"); - if (dw->set_ramdac(dw,r,0)) { - dw->set_ramdac(dw,or,0); + if (dw->set_ramdac(dw,dw->r,0)) { + dw->set_ramdac(dw,dw->or,0); /* is this needed ? */ error("Failed to set VideoLUTs"); } sleep(1); @@ -5832,10 +6381,6 @@ main(int argc, char *argv[]) { error("Failed to set VideoLUTs"); } - r->del(r); - dw->or->del(dw->or); - dw->or = NULL; - } else { printf("We don't have access to the VideoLUT\n"); } diff --git a/spectro/dispwin.h b/spectro/dispwin.h index 096547f..1206661 100644 --- a/spectro/dispwin.h +++ b/spectro/dispwin.h @@ -15,7 +15,13 @@ * see the License.txt file for licencing details. */ -#define DISPLAY_UPDATE_DELAY 200 /* default display update delay allowance */ +#define DISPLAY_UPDATE_DELAY 200 /* default minimum display update delay allowance */ + +/* 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 */ int do_plot(double *x, double *y1, double *y2, double *y3, int n); @@ -145,7 +151,11 @@ extern int callback_ddebug; /* Diagnostic global for get_displays() and get_a_d /* - - - - - - - - - - - - - - - - - - - - - - - */ /* Structure to handle RAMDAC values */ struct _ramdac { - int pdepth; /* Plane depth, usually 8 */ + + /* Should have separate frame buffer depth + representation to account */ + /* for floating point frame buffers, even though this isn't currently used. */ + + int pdepth; /* Frame buffer depth into RAMDAC, usually 8 */ int nent; /* Number of entries, = 2^pdepth */ double *v[3]; /* 2^pdepth entries for RGB, values 0.0 - 1.0 */ @@ -163,7 +173,7 @@ struct _ramdac { /* - - - - - - - - - - - - - - - - - - - - - - - */ /* Dispwin object */ -/* !!!! Make changes in dispwin.c and webwin.c !!!! */ +/* !!!! Make changes in dispwin.c, webwin.c & madvrwin.c !!!! */ struct _dispwin { @@ -179,15 +189,21 @@ struct _dispwin { int tx,ty; /* Test area within window offset in pixels */ int tw,th; /* Test area width and height in pixels */ - double rgb[3]; /* Current color (full resolution) */ + double rgb[3]; /* Current color (full resolution, full range) */ + double s_rgb[3]; /* Current color (possibly scaled range) */ double r_rgb[3]; /* Current color (raster value) */ - int update_delay; /* Update delay in msec, default 200 */ - int min_update_delay; /* Minimum update delay, default 20, overriden by EV */ + 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 do_resp_time_del; /* NZ to compute and use expected display response time */ int nowin; /* Don't create a test window */ - int native; /* 0 = use current current or given calibration curve */ - /* 1 = set native linear output and use ramdac high precision */ - ramdac *or; /* Original ramdac contents, NULL if none */ - ramdac *r; /* Ramdac in use for native mode */ + int native; /* X0 = use current per channel calibration curve */ + /* X1 = set native linear output and use ramdac high precision */ + /* 0X = use current color management cLut (MadVR) */ + /* 1X = disable color management cLUT (MadVR) */ + 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 */ int blackbg; /* NZ if black full screen background */ char *callout; /* if not NULL - set color Shell callout routine */ @@ -210,8 +226,8 @@ struct _dispwin { int inited; int quit; /* Request to quit */ - int colupd; /* Color update count */ - int colupde; /* Color update count echo */ + volatile int colupd; /* Color update count */ + volatile int colupde; /* Color update count echo */ #endif /* NT */ @@ -265,10 +281,13 @@ struct _dispwin { volatile unsigned int ncix, ccix; /* Counters to trigger webwin colorchange */ volatile int mg_stop; /* Stop flag */ + volatile int cberror; /* NZ if error detected in a callback routine */ int ddebug; /* >0 to print debug to stderr */ /* public: */ - int pdepth; /* Plane depth of display */ + int pdepth; /* Frame buffer plane depth of display */ + int edepth; /* Notional ramdac entry size in bits. (Bits actually used may be less) */ + /* This is used to scale out_tvenc appropriately */ /* Get RAMDAC values. ->del() when finished. */ /* Return NULL if not possible */ @@ -296,7 +315,14 @@ struct _dispwin { /* Return nz on error */ int (*set_color)(struct _dispwin *p, double r, double g, double b); + /* 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 shell set color callout command line */ @@ -313,14 +339,23 @@ dispwin *new_dispwin( double width, double height, /* Width and height in mm */ double hoff, double voff, /* Offset from c. in fraction of screen, range -1.0 .. 1.0 */ int nowin, /* NZ if no window should be created - RAMDAC access only */ - int native, /* 0 = use current current or given calibration curve */ - /* 1 = use native linear out & high precision */ - int *noramdac, /* Return nz if no ramdac access. native is set to 0 */ + 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 override, /* NZ if override_redirect is to be used on X11 */ int ddebug /* >0 to print debug statements to stderr */ ); +/* Shared implementation */ +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 84c4f2b..b378f50 100644 --- a/spectro/dtp20.c +++ b/spectro/dtp20.c @@ -7,7 +7,7 @@ * Author: Graeme W. Gill * Date: 10/3/2001 * - * Copyright 1996 - 2013, Graeme W. Gill + * Copyright 1996 - 2014, Graeme W. Gill * All rights reserved. * * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- @@ -108,7 +108,7 @@ char *in, /* In string */ char *out, /* Out string buffer */ int bsize, /* Out buffer size */ double to) { /* Timout in seconts */ - char tc = '>'; /* Terminating character */ + char *tc = ">"; /* Terminating character */ int ntc = 1; /* Number of terminating characters */ int rv, se, insize; @@ -128,7 +128,7 @@ double to) { /* Timout in seconts */ } rv = DTP20_OK; - if (tc == '>' && ntc == 1) { /* Expecting DTP type error code */ + if (tc[0] == '>' && ntc == 1) { /* Expecting DTP type error code */ rv = extract_ec(out); if (rv > 0) { rv &= inst_imask; @@ -221,8 +221,8 @@ dtp20_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { dtp20_command(p, "0PR\r", buf, MAX_MES_SIZE, 0.5); } else { - a1logd(p->log, 1, "dtp20: Failed to find connection to instrument\n"); - return inst_coms_fail; + a1logd(p->log, 1, "dtp20: wrong communications type for device\n"); + return inst_internal_error; } /* Check instrument is responding */ @@ -551,6 +551,7 @@ ipatch *vals) { /* Pointer to array of values */ if ((ev = dtp20_command(p, "0518CF\r", buf, MAX_RD_SIZE, 0.5)) != inst_ok) return ev; } + } a1logv(p->log, 1, "All saved strips read\n"); @@ -779,6 +780,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ return ev; } + /* Wait for status to change */ for (;;) { if ((ev = dtp20_command(p, "CS\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) { @@ -1006,6 +1008,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ return ev; } + if (!IMODETST(p->mode, inst_mode_s_ref_spot)) { /* Clear the spot database so our reading doesn't appear as a stored reading */ diff --git a/spectro/dtp22.c b/spectro/dtp22.c index 5831a1d..927d833 100644 --- a/spectro/dtp22.c +++ b/spectro/dtp22.c @@ -126,7 +126,7 @@ dtp22_fcommand( char *in, /* In string */ char *out, /* Out string buffer */ int bsize, /* Out buffer size */ - char tc, /* Terminating character */ + char *tc, /* Terminating characters */ int ntc, /* Number of terminating characters */ double to) { /* Timout in seconds */ int se, rv = DTP22_OK; @@ -135,7 +135,7 @@ dtp22_fcommand( a1logd(p->log, 1, "dtp22_fcommand: serial i/o failure on write_read '%s'\n",icoms_fix(in)); return icoms2dtp22_err(se); } - if (tc == '>' && ntc == 1) { + if (tc[0] == '>' && ntc == 1) { rv = extract_ec(out); #ifdef NEVER /* Simulate an error ?? */ if (strcmp(in, "0PR\r") == 0) @@ -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", buf, MAX_MES_SIZE, ">", 1, 0.5); } } } @@ -158,7 +158,7 @@ dtp22_fcommand( /* Return the dtp error code */ static inst_code dtp22_command(dtp22 *p, char *in, char *out, int bsize, double to) { - int rv = dtp22_fcommand(p, in, out, bsize, '>', 1, to); + int rv = dtp22_fcommand(p, in, out, bsize, ">", 1, to); return dtp22_interp_code((inst *)p, rv); } @@ -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], buf, MAX_MES_SIZE, ">", 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", buf, MAX_MES_SIZE, ">", 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, ">", 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); @@ -708,7 +708,7 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ /* 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, ">", 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 4487124..4717ed4 100644 --- a/spectro/dtp41.c +++ b/spectro/dtp41.c @@ -110,7 +110,7 @@ dtp41 *p, char *in, /* In string */ char *out, /* Out string buffer */ int bsize, /* Out buffer size */ -char tc, /* Terminating character */ +char *tc, /* Terminating characters */ int ntc, /* Number of terminating characters */ double to) { /* Timout in seconts */ int rv, se; @@ -120,13 +120,13 @@ double to) { /* Timout in seconts */ return icoms2dtp41_err(se); } rv = DTP41_OK; - if (tc == '>' && ntc == 1) { + if (tc[0] == '>' && ntc == 1) { rv = extract_ec(out); if (rv > 0) { 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", buf, MAX_MES_SIZE, ">", 1, 0.5); } } } @@ -139,7 +139,7 @@ double to) { /* Timout in seconts */ /* Return the instrument error code */ static inst_code dtp41_command(dtp41 *p, char *in, char *out, int bsize, double to) { - int rv = dtp41_fcommand(p, in, out, bsize, '>', 1, to); + int rv = dtp41_fcommand(p, in, out, bsize, ">", 1, to); return dtp41_interp_code((inst *)p, rv); } @@ -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, buf, MAX_MES_SIZE, ">", 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], buf, MAX_MES_SIZE, ">", 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", buf, MAX_MES_SIZE, ">", 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 7e49f3e..ac497d9 100644 --- a/spectro/dtp51.c +++ b/spectro/dtp51.c @@ -113,7 +113,7 @@ dtp51_fcommand( char *in, /* In string */ char *out, /* Out string buffer */ int bsize, /* Out buffer size */ - char tc, /* Terminating character */ + char *tc, /* Terminating character */ int ntc, /* Number of terminating characters */ double to) { /* Timout in seconts */ int rv, se; @@ -123,13 +123,13 @@ dtp51_fcommand( return icoms2dtp51_err(se); } rv = DTP51_OK; - if (tc == '>' && ntc == 1) { + if (tc[0] == '>' && ntc == 1) { rv = extract_ec(out); if (rv > 0) { 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", buf, MAX_MES_SIZE, ">", 1, 0.5); } } } @@ -142,7 +142,7 @@ dtp51_fcommand( /* Return the dtp error code */ static inst_code dtp51_command(dtp51 *p, char *in, char *out, int bsize, double to) { - int rv = dtp51_fcommand(p, in, out, bsize, '>', 1, to); + int rv = dtp51_fcommand(p, in, out, bsize, ">", 1, to); return dtp51_interp_code((inst *)p, rv); } @@ -154,7 +154,7 @@ struct _dtp51 *p, char *out, /* Out string buffer */ int bsize, /* Out buffer size */ double to) { /* Timout in seconts */ - char tc = '>'; /* Terminating character */ + char *tc = ">"; /* Terminating character */ int ntc = 1; /* Number of terminating characters */ int rv, se; @@ -163,13 +163,13 @@ double to) { /* Timout in seconts */ return icoms2dtp51_err(se); } rv = DTP51_OK; - if (tc == '>' && ntc == 1) { + if (tc[0] == '>' && ntc == 1) { rv = extract_ec(out); if (rv > 0) { 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", buf, MAX_MES_SIZE, ">", 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], buf, MAX_MES_SIZE, ">", 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", buf, MAX_MES_SIZE, ">", 1, 0.5); /* Check instrument is responding */ if ((ev = dtp51_command(p, "\r", buf, MAX_MES_SIZE, 1.5)) != inst_ok) @@ -440,7 +440,7 @@ dtp51_init_inst(inst *pp) { /* Set a strip length of 1, to ensure parsing is invalidated */ build_strip(tbuf, " ", 1, " ", 30); - if ((rv = dtp51_fcommand(p, "0105DS\r", buf, MAX_MES_SIZE, '*', 1, 0.5)) != DTP51_OK) + if ((rv = dtp51_fcommand(p, "0105DS\r", buf, MAX_MES_SIZE, "*", 1, 0.5)) != DTP51_OK) return dtp51_interp_code(pp, rv); /* Expect '*' as response */ @@ -486,7 +486,7 @@ ipatch *vals) { /* Pointer to array of instrument patch values */ build_strip(tbuf, name, npatch, pname, sguide); - if ((rv = dtp51_fcommand(p, "0105DS\r", buf, MAX_RD_SIZE, '*', 1, 0.5)) != DTP51_OK) + if ((rv = dtp51_fcommand(p, "0105DS\r", buf, MAX_RD_SIZE, "*", 1, 0.5)) != DTP51_OK) return dtp51_interp_code(pp, rv); /* Expect '*' as response */ diff --git a/spectro/dtp92.c b/spectro/dtp92.c index 441c0eb..f86672e 100644 --- a/spectro/dtp92.c +++ b/spectro/dtp92.c @@ -6,7 +6,7 @@ * Author: Graeme W. Gill * Date: 5/10/1996 * - * Copyright 1996 - 2013, Graeme W. Gill + * Copyright 1996 - 2014, Graeme W. Gill * All rights reserved. * * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- @@ -60,6 +60,8 @@ /* Default flow control */ #define DEFFC fc_none +#define DEF_TIMEOUT 0.5 + #define IGNORE_NEEDS_OFFSET_DRIFT_CAL_ERR static inst_code dtp92_interp_code(inst *pp, int ec); @@ -113,7 +115,7 @@ dtp92_fcommand( char *in, /* In string */ char *out, /* Out string buffer */ int bsize, /* Out buffer size */ - char tc, /* Terminating character */ + char *tc, /* Terminating characters */ int ntc, /* Number of terminating characters */ double to) { /* Timout in seconds */ int rv, se; @@ -123,7 +125,7 @@ dtp92_fcommand( return icoms2dtp92_err(se); } rv = DTP92_OK; - if (tc == '>' && ntc == 1) { + if (tc != NULL && tc[0] == '>' && ntc >= 1) { rv = extract_ec(out); #ifdef NEVER @@ -135,7 +137,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", buf, MAX_MES_SIZE, ">", 1, 0.5); } } } @@ -159,7 +161,7 @@ dtp92_fcommand( /* Return the dtp error code */ static inst_code dtp92_command(dtp92 *p, char *in, char *out, int bsize, double to) { - int rv = dtp92_fcommand(p, in, out, bsize, '>', 1, to); + int rv = dtp92_fcommand(p, in, out, bsize, ">", 1, to); return dtp92_interp_code((inst *)p, rv); } @@ -210,11 +212,11 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, 0.5); dtp92_command(p, "0PR\r", buf, MAX_MES_SIZE, 0.5); #else /* !ENABLE_USB */ - a1logd(p->log, 1, "dtp20: Failed to find USB connection to instrument\n"); + a1logd(p->log, 1, "dtp92: Failed to find USB connection to instrument\n"); return inst_coms_fail; #endif /* !ENABLE_USB */ - } else { + } else if (p->icom->port_type(p->icom) == icomt_serial) { #ifdef ENABLE_SERIAL a1logd(p->log, 2, "dtp92_init_coms: About to init Serial I/O\n"); @@ -287,11 +289,11 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } /* Set the handshaking */ - if ((ev = dtp92_command(p, fcc, buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, fcc, buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) 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], buf, MAX_MES_SIZE, ">", 1, .2)) != 0) { if (extract_ec(buf) != DTP92_OK) return inst_coms_fail; } @@ -304,15 +306,18 @@ 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", buf, MAX_MES_SIZE, ">", 1, 0.1); #else /* !ENABLE_SERIAL */ - a1logd(p->log, 1, "dtp20: Failed to find serial connection to instrument\n"); + a1logd(p->log, 1, "dtp92: Failed to find serial connection to instrument\n"); return inst_coms_fail; #endif /* !ENABLE_SERIAL */ + } else { + a1logd(p->log, 1, "dtp92: wrong communications type for device!\n"); + return inst_coms_fail; } /* Check instrument is responding, and reset it again. */ - if ((ev = dtp92_command(p, "\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok + 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) { a1logd(p->log, 1, "dtp92_init_coms: failed with ICOM 0x%x\n",ev); @@ -348,8 +353,8 @@ dtp92_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } #endif /* NEVER */ - p->icom->del(p->icom); /* Since caller may not clean up */ - p->icom = NULL; +// p->icom->del(p->icom); /* Since caller may not clean up */ +// p->icom = NULL; return inst_coms_fail; } @@ -379,7 +384,7 @@ dtp92_init_inst(inst *pp) { return ev; /* Get the model and version number */ - if ((ev = dtp92_command(p, "SV\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "SV\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Check that it is a DTP92, DTP92Q or DTP94 */ @@ -395,36 +400,36 @@ dtp92_init_inst(inst *pp) { if (p->itype == instDTP92) { /* Turn echoing of characters off */ - if ((ev = dtp92_command(p, "DEC\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "DEC\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; } if (p->itype == instDTP92) { /* Set decimal point on */ - if ((ev = dtp92_command(p, "0106CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0106CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; } /* Set color data separator to TAB */ - if ((ev = dtp92_command(p, "0207CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0207CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Set delimeter to CR */ - if ((ev = dtp92_command(p, "0008CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0008CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Set extra digit resolution (X10) */ - if ((ev = dtp92_command(p, "010ACF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "010ACF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; if (p->itype == instDTP92) { /* Set absolute (luminance) calibration */ - if ((ev = dtp92_command(p, "0118CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0118CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; } /* Set no black point subtraction */ - if ((ev = dtp92_command(p, "0019CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0019CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Set to factory calibration */ @@ -439,43 +444,43 @@ dtp92_init_inst(inst *pp) { if (p->itype == instDTP92) { /* Enable ABS mode (in case firmware doesn't default to this after EFC) */ - if ((ev = dtp92_command(p, "0118CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0118CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Disable -BLK mode (in case firmware doesn't default to this after EFC) */ - if ((ev = dtp92_command(p, "0019CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0019CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Set to transmit just the colorimetric data */ - if ((ev = dtp92_command(p, "0120CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0120CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Transmit colorimetric data as XYZ */ - if ((ev = dtp92_command(p, "0221CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0221CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Disable Luminance group */ - if ((ev = dtp92_command(p, "0022CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0022CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Disable Frequency group */ - if ((ev = dtp92_command(p, "0023CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0023CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Disable color temperature group */ - if ((ev = dtp92_command(p, "0024CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0024CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Disable RGB group */ - if ((ev = dtp92_command(p, "0025CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0025CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Disable pushbutton */ - if ((ev = dtp92_command(p, "DPB\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "DPB\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; /* Set sample size to 16 (default is 16) */ - if ((ev = dtp92_command(p, "10SS\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "10SS\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; } p->trig = inst_opt_trig_user; @@ -493,7 +498,7 @@ dtp92_init_inst(inst *pp) { for (i = 0; i < 0x800; i++) { sprintf(tb,"%04xRN\r",i); - if ((ev = dtp92_command(p, tb, buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, tb, buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; if (sscanf(buf,"%x",&val) != 1) return inst_coms_fail; @@ -506,8 +511,8 @@ dtp92_init_inst(inst *pp) { if (p->log->verb) { int i, j; - if ((ev = dtp92_command(p, "GI\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) { - a1logd(p->log, 1, "dtp20: GI command failed with ICOM err 0x%x\n",ev); + if ((ev = dtp92_command(p, "GI\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) { + a1logd(p->log, 1, "dtp92: GI command failed with ICOM err 0x%x\n",ev); return ev; } for (j = i = 0; ;i++) { @@ -555,7 +560,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ #ifdef NEVER if (p->itype == instDTP92) { /* Set sample size to 31 (default is 16) for low level readings */ - if ((rv = dtp92_command(p, "1fSS\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((rv = dtp92_command(p, "1fSS\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return rv; } #endif @@ -628,7 +633,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ #ifdef NEVER if (p->itype == instDTP92) { /* Set sample size back to 16 */ - if ((rv = dtp92_command(p, "10SS\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + if ((rv = dtp92_command(p, "10SS\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return rv; } #endif @@ -850,18 +855,6 @@ double *ref_rate return inst_ok; } -/* 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 dtp92_set_refr_rate(inst *pp, -double ref_rate -) { - dtp92 *p = (dtp92 *)pp; - - /* The DTP92 can't have a refresh rate set */ - return inst_unsupported; -} - /* Error codes interpretation */ static char * dtp92_interp_error(inst *pp, int ec) { @@ -952,6 +945,7 @@ dtp92_interp_code(inst *pp, int ec) { case DTP92_INTERNAL_ERROR: return inst_internal_error | ec; + case DTP92_TIMEOUT: case DTP92_COMS_FAIL: return inst_coms_fail | ec; @@ -1013,7 +1007,7 @@ inst3_capability *pcap3) { if (p->itype == instDTP94) { cap2 |= inst2_disptype; } else { - cap2 |= inst2_refresh_rate; + cap2 |= inst2_get_refresh_rate; cap2 |= inst2_emis_refr_meas; } @@ -1163,13 +1157,13 @@ static inst_code set_disp_type(dtp92 *p, inst_disptypesel *dentry) { inst_code ev; if (p->icx == 0) { /* Generic/Non-specific */ - if ((ev = dtp92_command(p, "0016CF\r", buf, MAX_MES_SIZE, 0.2)) != inst_ok) + 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, 0.2)) != inst_ok) + 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, 0.2)) != inst_ok) + if ((ev = dtp92_command(p, "0216CF\r", buf, MAX_MES_SIZE, DEF_TIMEOUT)) != inst_ok) return ev; } else { return inst_unsupported; @@ -1310,7 +1304,6 @@ extern dtp92 *new_dtp92(icoms *icom, instType itype) { p->calibrate = dtp92_calibrate; p->col_cor_mat = dtp92_col_cor_mat; p->get_refr_rate = dtp92_get_refr_rate; - p->set_refr_rate = dtp92_set_refr_rate; p->interp_error = dtp92_interp_error; p->del = dtp92_del; diff --git a/spectro/fakeread.c b/spectro/fakeread.c index df77930..ce06e93 100644 --- a/spectro/fakeread.c +++ b/spectro/fakeread.c @@ -1,4 +1,4 @@ -/* + /* * Argyll Color Correction System * Fake print target chart reader - use ICC or MPP profile rather than instrument * @@ -15,6 +15,10 @@ /* * TTBD: + This utility should really be merged with cctiff ? + 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 ?? */ @@ -46,21 +50,36 @@ void usage(char *diag, ...) { va_end(args); fprintf(stderr,"\n"); } - fprintf(stderr,"usage: fakeread [-v] [-s] [separation.icm] profile.[%s|mpp|ti3] outfile\n",ICC_FILE_EXT_ND); - fprintf(stderr," -v Verbose mode\n"); - fprintf(stderr," -s Lookup MPP spectral values\n"); - fprintf(stderr," -p Use separation profile\n"); + /* Keep options in order of processing */ + fprintf(stderr,"usage: fakeread [-options] profile.[%s|mpp|ti3] outfile\n",ICC_FILE_EXT_ND); + fprintf(stderr," -v [n] Verbose mode [level]\n"); + fprintf(stderr," -e flag Video encode device input to sepration as:\n"); + fprintf(stderr," n normal 0..1 full range RGB levels (default)\n"); + fprintf(stderr," t (16-235)/255 \"TV\" RGB levels\n"); + fprintf(stderr," 6 Rec601 YCbCr SD (16-235,240)/255 \"TV\" levels\n"); + fprintf(stderr," 7 Rec709 1125/60Hz YCbCr HD (16-235,240)/255 \"TV\" levels\n"); + fprintf(stderr," 5 Rec709 1250/50Hz YCbCr HD (16-235,240)/255 \"TV\" levels\n"); + fprintf(stderr," 2 Rec2020 YCbCr UHD (16-235,240)/255 \"TV\" levels\n"); + 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," -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," -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"); - fprintf(stderr," -k file.cal Apply calibration (after sep.) and include in .ti3\n"); - fprintf(stderr," -i file.cal Include calibration in .ti3 (but don't apply it)\n"); - fprintf(stderr," -r level Add average random deviation of <level>%% to input device values (after sep. & cal.)\n"); - fprintf(stderr," -0 pow Apply power to input device chanel 0-9 (after sep. cal. & rand)\n"); + fprintf(stderr," -s Lookup MPP spectral values\n"); 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," -b L,a,b Scale black point to target Lab value\n"); - fprintf(stderr," -I intent r = relative colorimetric, a = absolute (default)\n"); - fprintf(stderr," [separation.%s] Device link separation profile\n",ICC_FILE_EXT_ND); 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); @@ -69,9 +88,14 @@ void usage(char *diag, ...) { int main(int argc, char *argv[]) { int j; - int fa,nfa; /* current argument we're looking at */ + int fa, nfa, mfa; /* current argument we're looking at */ int verb = 0; /* Verbose flag */ 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 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 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 }; @@ -80,12 +104,13 @@ int main(int argc, char *argv[]) int unidist = 0; /* Use uniform distribution of errors */ unsigned int seed = time(NULL); /* Random seed value */ double tbp[3] = { -1.0, 0.0, 0.0 }; /* Target black point */ - int applycal = 0; /* NZ to apply calibration */ + int applycal = 0; /* 1 to apply calibration, 2 to apply inverse calibration */ static char sepname[MAXNAMEL+1] = { 0 }; /* ICC separation profile */ 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 */ cgats *icg; /* input cgats structure */ cgats *ocg; /* output cgats structure */ int nmask = 0; /* Test chart device colorant mask */ @@ -96,7 +121,11 @@ int main(int argc, char *argv[]) int fi; /* Colorspace index */ int inti3 = 0; /* Input is a renamed .ti3 file rather than .ti1 */ - /* ICC separation device link profile */ + /* 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. */ + + /* ICC separation/calibration device link profile */ icmFile *sep_fp = NULL; /* Color profile file */ icc *sep_icco = NULL; /* Profile object */ icmLuBase *sep_luo = NULL; /* Conversion object */ @@ -114,6 +143,7 @@ int main(int argc, char *argv[]) icmFile *icc_fp = NULL; /* Color profile file */ icc *icc_icco = NULL; /* Profile object */ icmLuBase *icc_luo = NULL; /* Conversion object */ + icmLuAlgType alg; /* Algorithm */ /* MPP profile based */ mpp *mlu = NULL; /* Conversion object */ @@ -146,6 +176,7 @@ int main(int argc, char *argv[]) usage("Too few arguments"); /* Process the arguments */ + mfa = 2; /* Minimum final arguments */ for(fa = 1;fa < argc;fa++) { nfa = fa; /* skip to nfa if next argument is used */ if (argv[fa][0] == '-') { /* Look for any flags */ @@ -155,7 +186,7 @@ int main(int argc, char *argv[]) na = &argv[fa][2]; /* next is directly after flag */ else { - if ((fa+1) < argc) + if ((fa+1+mfa) < argc) { if (argv[fa+1][0] != '-') { @@ -169,17 +200,93 @@ int main(int argc, char *argv[]) usage("Usage requested"); /* Verbose */ - else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') + else if (argv[fa][1] == 'v') { verb = 1; + if (na != NULL && na[0] >= '0' && na[0] <= '9') { + verb = atoi(na); + fa = nfa; + } + } + /* Spectral MPP lookup */ else if (argv[fa][1] == 's') dospec = 1; + /* Video RGB encoding */ + else if (argv[fa][1] == 'e' + || argv[fa][1] == 'E') { + int enc; + if (na == NULL) usage("Video encodong flag (-e/E) needs an argument"); + switch (na[0]) { + case 'n': /* Normal */ + enc = 0; + break; + case 't': /* TV 16 .. 235 */ + enc = 1; + break; + case '6': /* Rec601 YCbCr */ + enc = 2; + break; + case '7': /* Rec709 1150/60/2:1 YCbCr */ + enc = 3; + break; + case '5': /* Rec709 1250/50/2:1 YCbCr (HD) */ + enc = 4; + break; + case '2': /* Rec2020 Non-constant Luminance YCbCr (UHD) */ + enc = 5; + break; + case 'C': /* Rec2020 Constant Luminance YCbCr (UHD) */ + enc = 6; + break; + default: + usage("Video encoding (-E) argument not recognised"); + } + if (argv[fa][1] == 'e') + in_tvenc = enc; + else + out_tvenc = enc; + fa = nfa; + } + /* Separation */ - else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') + else if (argv[fa][1] == 'p') { dosep = 1; + fa = nfa; + if (na == NULL) usage("Expected an argument to -p"); + strncpy(sepname,na,MAXNAMEL); sepname[MAXNAMEL] = '\000'; + } + + /* BT.1886 modifier */ + else if (argv[fa][1] == 'b' + || argv[fa][1] == 'B') { + 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 ((cp = strchr(na, ':')) != NULL) { + double gamma = 0.0; + *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; + } + strncpy(oprofname,cp,MAXNAMEL); oprofname[MAXNAMEL] = '\000'; + } + /* Lab */ else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') dolab = 1; @@ -197,8 +304,12 @@ int main(int argc, char *argv[]) } /* calibration to device values */ - else if (argv[fa][1] == 'k' || argv[fa][1] == 'i') { - if (argv[fa][1] == 'k') + else if (argv[fa][1] == 'k' + || argv[fa][1] == 'K' + || argv[fa][1] == 'i') { + if (argv[fa][1] == 'K') + applycal = 2; + else if (argv[fa][1] == 'k') applycal = 1; else applycal = 0; @@ -233,8 +344,8 @@ int main(int argc, char *argv[]) } /* Black point scale */ - else if (argv[fa][1] == 'b' || argv[fa][1] == 'B') { - if (na == NULL) usage("Expect argument to -b"); + else if (argv[fa][1] == 'A') { + if (na == NULL) usage("Expect argument to -A"); fa = nfa; if (sscanf(na, " %lf , %lf , %lf ",&tbp[0], &tbp[1], &tbp[2]) != 3) usage("Couldn't parse argument to -b"); @@ -266,12 +377,6 @@ int main(int argc, char *argv[]) break; } - /* Get the file name argument */ - if (dosep) { - if (fa >= argc || argv[fa][0] == '-') usage("Missing separation profile filename argument"); - strncpy(sepname,argv[fa++],MAXNAMEL); sepname[MAXNAMEL] = '\000'; - } - if (fa >= argc || argv[fa][0] == '-') usage("Missing profile filename argument"); strncpy(profname,argv[fa++],MAXNAMEL); profname[MAXNAMEL] = '\000'; @@ -302,6 +407,13 @@ int main(int argc, char *argv[]) sep_luo->spaces(sep_luo, &sep_ins, &sep_inn, &sep_outs, NULL, NULL, NULL, NULL, NULL, NULL); sep_nmask = icx_icc_to_colorant_comb(sep_ins, sep_icco->header->deviceClass); } + if (in_tvenc && sep_ins != icSigRgbData) + error("Can only use TV encoding on sepration RGB input"); + if (out_tvenc && sep_outs != icSigRgbData) + error("Can only use TV decoding on sepration RGB output"); + } else { + if (in_tvenc || out_tvenc) + warning("TV encoding ignored because there is no sepration file"); } /* Deal with calibration */ @@ -310,11 +422,14 @@ int main(int argc, char *argv[]) error("new_xcal failed"); if ((cal->read(cal, calname)) != 0) error("%s",cal->err); - if (verb) - if (applycal) + if (verb) { + if (applycal == 1) printf("Applying calibration curves from '%s'\n",calname); + else if (applycal == 2) + printf("Applying inverse calibration curves from '%s'\n",calname); else printf("Embedding calibration curves from '%s' in output\n",calname); + } } /* Deal with ICC profile */ @@ -345,7 +460,8 @@ int main(int argc, char *argv[]) } /* Get details of conversion */ - icc_luo->spaces(icc_luo, &ins, &inn, &outs, &outn, NULL, NULL, NULL, NULL, NULL); + icc_luo->spaces(icc_luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL, NULL); + cnv_nmask = icx_icc_to_colorant_comb(ins, icc_icco->header->deviceClass); if (dospec) @@ -531,6 +647,105 @@ int main(int argc, char *argv[]) free(ti3_bident); } + /* Dealy with BT.1886 processing */ + if (bt1886) { + icmFile *ofp = NULL; /* Output Color profile file */ + icc *oicco = NULL; /* Output Profile object */ + icmLuBase *oluo = NULL; /* Output Conversion object */ + icmLuMatrix *lu; /* Input profile lookup */ + double bp[3], rgb[3]; + + if (icc_luo == NULL) + error ("Can only use BT.1886 with an ICC profile"); + + /* Check input profile is an RGB matrix profile */ + if (alg != icmMatrixFwdType + || ins != icSigRgbData) + error("BT.1886 mode only works with an RGB matrix input profile"); + + 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 ((oicco = new_icc()) == NULL) + error ("Creation of ICC object failed"); + + if (oicco->read(oicco,ofp,0)) + error ("Unable to read '%s'",oprofname); + + if ((oluo = oicco->get_luobj(oicco, icmFwd, icRelativeColorimetric, + icSigXYZData, icmLuOrdNorm)) == NULL) + error("get output ICC lookup object failed: %d, %s",oicco->errc,oicco->err); + + /* We're assuming that the input space has a perfect black point... */ + + /* Lookup the ouput black point in XYZ PCS. We're assuming monotonicity.. */ + bp[0] = bp[1] = bp[2] = 0.0; + oluo->lookup(oluo, bp, bp); + + /* Done with output profile */ + oluo->del(oluo); + oicco->del(oicco); + ofp->del(ofp); + + 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); + } else { + if (verb) + printf("BT.1886: Using technical gamma %f\n",tgamma); + } + + bt1886_setup(&bt, bp, tgamma); + + if (verb) { + printf("BT.1886: 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); + } + + /* 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); + lu->fwd_matrix(lu, rgb, rgb); + bt1886_apply(&bt, lu, 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; + + /* Overral rendering curve from video in to output target */ + printf("BT.1886: overall rendering\n"); + for (i = 0; i < no; i++) { + double v = i/(no-1.0), vv; + 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 */ + + icmXYZ2Lab(&icmD50, Lab, vo); + + if (v > 1e-9 && vo[1] > 1e-9 && fabs(v - 1.0) > 1e-9) + loglog = log(vo[1])/log(v); + + printf(" In %5.1f%% -> XYZ in %f -> bt.1886 %f, log/log %.3f, Lab %f %f %f \n",v * 100.0,vi[1],vo[1], loglog, Lab[0], Lab[1], Lab[2]); + } + } + } + /* Some sanity checking */ if (tbp[0] >= 0.0 && icc_luo == NULL) error("Black scaling only works with ICC profile"); @@ -642,6 +857,9 @@ int main(int argc, char *argv[]) if ((ti = icg->find_kword(icg, 0, "FULL_SPREAD_PATCHES")) >= 0) ocg->add_kword(ocg, 0, "FULL_SPREAD_PATCHES",icg->t[0].kdata[ti], NULL); + if ((ti = icg->find_kword(icg, 0, "DARK_REGION_EMPHASIS")) >= 0) + ocg->add_kword(ocg, 0, "DARK_REGION_EMPHASIS",icg->t[0].kdata[ti], NULL); + if (icc_luo == NULL) { /* If MPP profile, we know what the target instrument is */ ocg->add_kword(ocg, 0, "TARGET_INSTRUMENT", inst_name(itype) , NULL); } @@ -842,13 +1060,62 @@ int main(int argc, char *argv[]) } } - if (dosep) + 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); + } + } + 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); + } + } + } + /* Do calibration */ - if (applycal && cal != NULL) - cal->interp(cal, sep, sep); + 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. */ @@ -875,8 +1142,21 @@ int main(int argc, char *argv[]) /* Do color conversion */ if (icc_luo != NULL) { - if (icc_luo->lookup(icc_luo, PCS, sep) > 1) - error ("%d, %s",icc_icco->errc,icc_icco->err); + 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) + error ("%d, %s",icc_icco->errc,icc_icco->err); + } if (tbp[0] >= 0) { /* Doing black point scaling */ @@ -913,7 +1193,7 @@ int main(int argc, char *argv[]) } } /* Copy best value over */ - if (!dosep) /* Doesn't make sense for separation */ + 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; } @@ -949,8 +1229,12 @@ int main(int argc, char *argv[]) /* 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]; + 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; @@ -959,7 +1243,7 @@ int main(int argc, char *argv[]) rr = 100.0 * d_rand(-2.0 * rplevel, 2.0 * rplevel); else rr = 100.0 * 1.2533 * rplevel * norm_rand(); - dv += rr; + dv += ll * rr; /* Don't let L*, X, Y or Z go negative */ if ((!dolab || j == 0) && dv < 0.0) diff --git a/spectro/hcfr.c b/spectro/hcfr.c index 9ec6be9..c67a419 100644 --- a/spectro/hcfr.c +++ b/spectro/hcfr.c @@ -78,7 +78,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, out, bsize, "\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)); @@ -370,6 +370,9 @@ 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 */ @@ -530,8 +533,8 @@ double mtx[3][3] 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"); - inst_wrong_setup; + a1loge(p->log, 1, "hcfr: can't set col_cor_mat over non-base display type\n"); + return inst_wrong_setup; } icmCpy3x3(p->ccmat, mtx); } diff --git a/spectro/hidio.c b/spectro/hidio.c index 595b707..918bb83 100644 --- a/spectro/hidio.c +++ b/spectro/hidio.c @@ -18,11 +18,38 @@ /* * TTBD: - * As usual, Apple seem to have deprecated the routines used here. + * As usual, Apple seem to have deprecated the routines used here, + * and reccommend a new API for 10.5 and latter, so we should + * switch to using this if compiled on 10.6 or latter. * See IOHIDDeviceGetReportWithCallback & IOHIDDeviceSetReportWithCallback + * <https://developer.apple.com/library/mac/technotes/tn2187/_index.html> * for info on the replacements. */ +/* + New 10.5 and later code is not completely developed/debugged + - the read and write routines simply error out. + + Perhaps we should try using + + IOHIDDeviceRegisterInputReportCallback() + + Handle_IOHIDDeviceIOHIDReportCallback() + + what triggers it ?? How is this related to GetReport ? + + + OHIDDeviceGetReportWithCallback() + + Handle_IOHIDDeviceGetReportCallback() + + + IOHIDDeviceSetReportWithCallback() + + Handle_IOHIDDeviceSetReportCallback() + + Plus IOHIDQueueScheduleWithRunLoop() + IOHIDQueueUnscheduleFromRunLoop() + + + we probably have to call the run loop ? + +*/ +#undef USE_NEW_OSX_CODE + /* These routines supliement the class code in ntio.c and unixio.c */ /* with HID specific access routines for devices running on operating */ /* systems where this is desirable. */ @@ -234,6 +261,88 @@ int hid_get_paths(icompaths *p) { #endif /* NT */ #ifdef __APPLE__ +# if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + { + CFAllocatorContext alocctx; + IOHIDManagerRef mref; + CFSetRef setref; + CFIndex count, i; + IOHIDDeviceRef *values; + + /* Create HID Manager reference */ + if ((mref = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone)) == NULL) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() IOHIDManagerCreate returned NULL\n"); + return ICOM_SYS; + } + + /* Set to match all HID devices */ + IOHIDManagerSetDeviceMatching(mref, NULL); + + /* Enumerate the devices (doesn't seem to have to open) */ + if ((setref = IOHIDManagerCopyDevices(mref)) == NULL) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() IOHIDManagerCopyDevices failed\n"); + CFRelease(mref); + return ICOM_SYS; + } + + count = CFSetGetCount(setref); + if ((values = (IOHIDDeviceRef *)calloc(sizeof(IOHIDDeviceRef), count)) == NULL) { + a1loge(p->log, ICOM_SYS, "hid_get_paths() calloc failed!\n"); + CFRelease(setref); + CFRelease(mref); + return ICOM_SYS; + } + + CFSetGetValues(setref, (const void **)values); + + for (i = 0; i < count; i++) { + CFNumberRef vref, pref; /* HID Vendor and Product ID propeties */ + CFNumberRef lidpref; /* Location ID properties */ + unsigned int vid = 0, pid = 0, lid = 0; + instType itype; + IOHIDDeviceRef ioob = values[i]; /* HID object found */ + + if ((vref = IOHIDDeviceGetProperty(ioob, CFSTR(kIOHIDVendorIDKey))) != NULL) { + CFNumberGetValue(vref, kCFNumberSInt32Type, &vid); + CFRelease(vref); + } + if ((pref = IOHIDDeviceGetProperty(ioob, CFSTR(kIOHIDProductIDKey))) != NULL) { + CFNumberGetValue(pref, kCFNumberSInt32Type, &pid); + CFRelease(vref); + } + if ((lidpref = IOHIDDeviceGetProperty(ioob, CFSTR("LocationID"))) != NULL) { + CFNumberGetValue(lidpref, kCFNumberIntType, &lid); + CFRelease(lidpref); + } + + /* If it's a device we're looking for */ + if ((itype = inst_usb_match(vid, pid, 0)) != instUnknown) { + struct hid_idevice *hidd; + char pname[400]; + + a1logd(p->log, 2, "found HID device '%s' lid 0x%x that we're looking for\n",inst_name(itype), lid); + + /* Create human readable path/identification */ + sprintf(pname,"hid%d: (%s)", lid >> 20, inst_name(itype)); + + if ((hidd = (struct hid_idevice *)calloc(sizeof(struct hid_idevice), 1)) == NULL) { + a1loge(p->log, ICOM_SYS, "hid_get_paths calloc failed!\n"); + return ICOM_SYS; + } + hidd->lid = lid; + CFRetain(ioob); /* We're retaining it */ + hidd->ioob = ioob; + + /* Add the path to the list */ + p->add_hid(p, pname, vid, pid, 0, hidd, itype); + } + } + /* Clean up */ + free(values); + CFRelease(setref); + CFRelease(mref); + } +#else /* < 1060 */ { kern_return_t kstat; CFMutableDictionaryRef sdict; /* HID Device dictionary */ @@ -307,6 +416,7 @@ int hid_get_paths(icompaths *p) { } IOObjectRelease(mit); /* Release the itterator */ } +#endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ #endif /* __APPLE__ */ #if defined(UNIX_X11) @@ -371,47 +481,35 @@ int hid_get_paths(icompaths *p) { /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* Close an open HID port */ -/* If we don't do this, the port and/or the device may be left in an unusable state. */ -void hid_close_port(icoms *p) { - - a1logd(p->log, 8, "hid_close_port: called\n"); - - if (p->is_open && p->hidd != NULL) { - -#if defined(NT) - CloseHandle(p->hidd->ols.hEvent); - CloseHandle(p->hidd->fh); -#endif /* NT */ - #ifdef __APPLE__ - IOObjectRelease(p->hidd->port); - p->hidd->port = 0; - - if (p->hidd->evsrc != NULL) - CFRelease(p->hidd->evsrc); - p->hidd->evsrc = NULL; - p->hidd->rlr = NULL; +/* HID Interrupt callback for OS X */ +/* This seems to only get called when the run loop is active. */ +static void hid_read_callback( +void *target, +IOReturn result, +void *refcon, +void *sender, +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 +uint32_t size +#else +UInt32 size +#endif +) { + icoms *p = (icoms *)target; - if ((*p->hidd->device)->close(p->hidd->device) != kIOReturnSuccess) { - a1loge(p->log, ICOM_SYS, "hid_close_port: closing HID port '%s' failed",p->name); - return; - } + a1logd(p->log, 8, "HID callback called with size %d, result 0x%x\n",size,result); + p->hidd->result = result; + p->hidd->bread = size; + if (p->hidd->rlr != NULL) + CFRunLoopStop(p->hidd->rlr); /* We're done */ + else + a1logd(p->log, 8, "HID callback has no run loop\n"); +} - if ((*p->hidd->device)->Release(p->hidd->device) != kIOReturnSuccess) { - a1loge(p->log, ICOM_SYS, "hid_close_port: Releasing HID port '%s' failed",p->name); - } - p->hidd->device = NULL; #endif /* __APPLE__ */ - p->is_open = 0; - a1logd(p->log, 8, "hid_close_port: has been released and closed\n"); - } - - /* Find it and delete it from our static cleanup list */ - usb_delete_from_cleanup_list(p); -} +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Open an HID port for all our uses. */ @@ -461,6 +559,15 @@ char **pnames /* List of process names to try and kill before opening */ #endif /* NT */ #ifdef __APPLE__ +# if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + { + /* Open the device */ + if (IOHIDDeviceOpen(p->hidd->ioob, kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "hid_open_port: Opening HID device '%s' failed",p->name); + return ICOM_SYS; + } + } +#else /* < 1060 */ { IOCFPlugInInterface **piif = NULL; IOReturn result; @@ -505,7 +612,25 @@ char **pnames /* List of process names to try and kill before opening */ return ICOM_SYS; } + /* Setup for read callback */ + p->hidd->result = -1; + p->hidd->bread = 0; + + /* And set read callback routine */ + if ((*(p->hidd->device))->setInterruptReportHandlerCallback(p->hidd->device, + p->hidd->rbuf, 256, hid_read_callback, (void *)p, NULL) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: Setting callback handler for " + "HID '%s' failed\n", p->name); + return ICOM_SYS; + } + + if ((*(p->hidd->device))->startAllQueues(p->hidd->device) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: Starting queues for " + "HID '%s' failed\n", p->name); + return ICOM_SYS; + } } +#endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ #endif /* __APPLE__ */ p->is_open = 1; @@ -518,32 +643,64 @@ char **pnames /* List of process names to try and kill before opening */ return ICOM_OK; } -/* ========================================================= */ +/* Close an open HID port */ +/* If we don't do this, the port and/or the device may be left in an unusable state. */ +void hid_close_port(icoms *p) { + + a1logd(p->log, 8, "hid_close_port: called\n"); + + if (p->is_open && p->hidd != NULL) { + +#if defined(NT) + CloseHandle(p->hidd->ols.hEvent); + CloseHandle(p->hidd->fh); +#endif /* NT */ #ifdef __APPLE__ +# if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + if (IOHIDDeviceClose(p->hidd->ioob, kIOHIDOptionsTypeNone) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "hid_close_port: closing HID port '%s' failed",p->name); + return; + } +#else /* < 1060 */ + if ((*(p->hidd->device))->stopAllQueues(p->hidd->device) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: Stopping queues for " + "HID '%s' failed\n", p->name); + return; + } -/* HID Interrupt callback for OS X */ -static void hid_read_callback( -void *target, -IOReturn result, -void *refcon, -void *sender, -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 -uint32_t size -#else -UInt32 size -#endif -) { - icoms *p = (icoms *)target; + IOObjectRelease(p->hidd->port); + p->hidd->port = 0; - a1logd(p->log, 8, "HID callback called with size %d, result 0x%x\n",size,result); - p->hidd->result = result; - p->hidd->bread = size; - CFRunLoopStop(p->hidd->rlr); /* We're done */ -} + if (p->hidd->evsrc != NULL) + CFRelease(p->hidd->evsrc); + p->hidd->evsrc = NULL; + + p->hidd->rlr = NULL; + if ((*p->hidd->device)->close(p->hidd->device) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "hid_close_port: closing HID port '%s' failed",p->name); + return; + } + + if ((*p->hidd->device)->Release(p->hidd->device) != kIOReturnSuccess) { + a1loge(p->log, ICOM_SYS, "hid_close_port: Releasing HID port '%s' failed",p->name); + } + p->hidd->device = NULL; +#endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ #endif /* __APPLE__ */ + p->is_open = 0; + a1logd(p->log, 8, "hid_close_port: has been released and closed\n"); + } + + /* Find it and delete it from our static cleanup list */ + usb_delete_from_cleanup_list(p); +} + + +/* ========================================================= */ + /* HID Interrupt pipe Read */ /* Don't retry on a short read, return ICOM_SHORT. */ /* [Probably uses the control pipe. We need a different */ @@ -558,6 +715,8 @@ icoms_hid_read(icoms *p, int retrv = ICOM_OK; /* Returned error value */ int bread = 0; + a1logd(p->log, 8, "icoms_hid_read: %d bytes, tout %f\n",bsize,tout); + if (!p->is_open) { a1loge(p->log, ICOM_SYS, "icoms_hid_read: device not initialised\n"); return ICOM_SYS; @@ -599,52 +758,108 @@ icoms_hid_read(icoms *p, #endif /* NT */ #ifdef __APPLE__ -#ifndef NEVER +# if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { IOReturn result; + CFIndex lbsize = bsize; + + /* Normally the _write should have set the ->rlr */ + /* but this may not be so if we are doing a flush ? */ + if (p->hidd->rlr == NULL) { + p->hidd->rlr = CFRunLoopGetCurrent(); + p->hidd->result = -1; + p->hidd->bread = 0; + } - /* Setup for callback */ - p->hidd->result = -1; - p->hidd->bread = 0; + IOHIDDeviceScheduleWithRunLoop(p->hidd->ioob, p->hidd->rlr, kCFRunLoopDefaultMode); - if ((*(p->hidd->device))->setInterruptReportHandlerCallback(p->hidd->device, - rbuf, bsize, hid_read_callback, (void *)p, NULL) != kIOReturnSuccess) { - a1loge(p->log, ICOM_SYS, "icoms_hid_read: Setting callback handler for " - "HID '%s' failed\n", p->name); - return ICOM_SYS; +#ifndef NEVER + /* This doesn't work. Don't know why */ + /* Returns 0xe000404f = kIOUSBPipeStalled, Pipe has stalled, error needs to be cleared */ + if ((result = IOHIDDeviceGetReportWithCallback( + p->hidd->ioob, + kIOHIDReportTypeInput, + 9, /* Bulk or Interrupt transfer ??? - or wbuf[0] ?? */ + (uint8_t *)rbuf, + &lbsize, + tout, + NULL, NULL)) != kIOReturnSuccess) { +printf("~1 IOHIDDeviceGetReportWithCallback returned 0x%x\n",result); + /* (We could detect other error codes and translate them to ICOM) */ + if (result == kIOReturnTimeout) + retrv = ICOM_TO; + else + retrv = ICOM_USBR; + } else { + bsize = lbsize; + bread = bsize; } - /* Call runloop, but exit after handling one callback */ - p->hidd->rlr = CFRunLoopGetCurrent(); - CFRunLoopAddSource(p->hidd->rlr, p->hidd->evsrc, kCFRunLoopDefaultMode); +#else + /* This doesn't work. Don't know why */ + /* Returns 0xe000404f = kIOUSBPipeStalled, Pipe has stalled, error needs to be cleared */ + if ((result = IOHIDDeviceGetReport( + p->hidd->ioob, + kIOHIDReportTypeInput, + 9, /* Bulk or Interrupt transfer ??? - or wbuf[0] ?? */ + (uint8_t *)rbuf, + &lbsize)) != kIOReturnSuccess) { +printf("~1 IOHIDDeviceGet returned 0x%x\n",result); + /* (We could detect other error codes and translate them to ICOM) */ + if (result == kIOReturnTimeout) + retrv = ICOM_TO; + else + retrv = ICOM_USBR; + } else { + bsize = lbsize; + bread = bsize; + } +#endif + + IOHIDDeviceUnscheduleFromRunLoop(p->hidd->ioob, p->hidd->rlr, kCFRunLoopDefaultMode); + p->hidd->rlr = NULL; + } +#else /* < 1060 */ +#ifndef NEVER /* This works */ + { + IOReturn result; - if ((*(p->hidd->device))->startAllQueues(p->hidd->device) != kIOReturnSuccess) { - a1loge(p->log, ICOM_SYS, "icoms_hid_read: Starting queues for " - "HID '%s' failed\n", p->name); + if (bsize > HID_RBUF_SIZE) { + a1loge(p->log, ICOM_SYS, "icoms_hid_read: Requested more bytes that HID_RBUF_SIZE\n", p->name); return ICOM_SYS; } + /* Normally the _write should have set the ->rlr */ + /* but this may not be so if we are doing a flush ? */ + if (p->hidd->rlr == NULL) { + p->hidd->rlr = CFRunLoopGetCurrent(); + p->hidd->result = -1; + p->hidd->bread = 0; + } + + CFRunLoopAddSource(p->hidd->rlr, p->hidd->evsrc, kCFRunLoopDefaultMode); + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, tout, false); - if ((*(p->hidd->device))->stopAllQueues(p->hidd->device) != kIOReturnSuccess) { - a1loge(p->log, ICOM_SYS, "icoms_hid_read: Stopping queues for " - "HID '%s' failed\n", p->name); - return ICOM_SYS; - } if (result == kCFRunLoopRunTimedOut) { retrv = ICOM_TO; } else if (result != kCFRunLoopRunStopped) { retrv = ICOM_USBR; } CFRunLoopRemoveSource(p->hidd->rlr, p->hidd->evsrc, kCFRunLoopDefaultMode); + p->hidd->rlr = NULL; /* Callback is invalid now */ if (p->hidd->result == -1) { /* Callback wasn't called */ retrv = ICOM_TO; } else if (p->hidd->result != kIOReturnSuccess) { a1loge(p->log, ICOM_SYS, "icoms_hid_read: Callback for " "HID '%s' got unexpected return value\n", p->name); + p->hidd->result = -1; /* Result is now invalid */ + p->hidd->bread = 0; return ICOM_SYS; } bread = p->hidd->bread; + if (bread > 0) + memcpy(rbuf, p->hidd->rbuf, bread > HID_RBUF_SIZE ? HID_RBUF_SIZE : bread); } #else // NEVER /* This doesn't work. Don't know why */ @@ -670,6 +885,7 @@ icoms_hid_read(icoms *p, } } #endif // NEVER +#endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ #endif /* __APPLE__ */ if (breadp != NULL) @@ -695,6 +911,8 @@ icoms_hid_write(icoms *p, int retrv = ICOM_OK; /* Returned error value */ int bwritten = 0; + a1logd(p->log, 8, "icoms_hid_write: %d bytes, tout %f\n",bsize,tout); + if (!p->is_open) { a1loge(p->log, ICOM_SYS, "icoms_hid_write: device not initialised\n"); return ICOM_SYS; @@ -738,7 +956,57 @@ icoms_hid_write(icoms *p, #endif /* NT */ #ifdef __APPLE__ +# if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + { + IOReturn result; +#ifdef NEVER + /* This doesn't work. Don't know why */ + /* Returns 0xe000404f = kIOUSBPipeStalled, Pipe has stalled, error needs to be cleared */ + /* if we did a read that has timed out */ + /* Returns 0xe00002c7 = kIOReturnUnsupported, without the failed read + if ((result = IOHIDDeviceSetReportWithCallback( + p->hidd->ioob, + kIOHIDReportTypeOutput, + 9, /* Bulk or Interrupt transfer ??? - or wbuf[0] ?? */ + (uint8_t *)wbuf, + bsize, + tout, + NULL, NULL)) != kIOReturnSuccess) { +printf("~1 IOHIDDeviceSetReportWithCallback returned 0x%x\n",result); + /* (We could detect other error codes and translate them to ICOM) */ + if (result == kIOReturnTimeout) + retrv = ICOM_TO; + else + retrv = ICOM_USBW; + } else { + bwritten = bsize; + } +#else + /* This works, but has no timeout */ + if ((result = IOHIDDeviceSetReport( + p->hidd->ioob, + kIOHIDReportTypeOutput, + 9, /* Bulk or Interrupt transfer ??? - or wbuf[0] ?? */ + (uint8_t *)wbuf, + bsize)) != kIOReturnSuccess) { + /* (We could detect other error codes and translate them to ICOM) */ + if (result == kIOReturnTimeout) + retrv = ICOM_TO; + else + retrv = ICOM_USBW; + } else { + bwritten = bsize; + } +#endif + } +#else /* < 1060 */ { + /* Clear any existing read message */ + /* (We're assuming it's all write then read measages !!) */ + p->hidd->rlr = CFRunLoopGetCurrent(); // Our thread's run loop + p->hidd->result = -1; + p->hidd->bread = 0; + IOReturn result; if ((result = (*p->hidd->device)->setReport( p->hidd->device, @@ -757,6 +1025,7 @@ icoms_hid_write(icoms *p, bwritten = bsize; } } +#endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ #endif /* __APPLE__ */ if (bwrittenp != NULL) @@ -820,9 +1089,14 @@ int hid_copy_hid_idevice(icoms *d, icompath *s) { } #endif #if defined(__APPLE__) +# if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + d->hidd->ioob = s->hidd->ioob; + CFRetain(d->hidd->ioob); +#else d->hidd->ioob = s->hidd->ioob; IOObjectRetain(d->hidd->ioob); -#endif +#endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ +#endif /* __APPLE__ */ #if defined (UNIX_X11) #endif return ICOM_OK; @@ -838,9 +1112,14 @@ void hid_del_hid_idevice(struct hid_idevice *hidd) { free(hidd->dpath); #endif #if defined(__APPLE__) +# if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + if (hidd->ioob != 0) + CFRelease(hidd->ioob); +#else if (hidd->ioob != 0) IOObjectRelease(hidd->ioob); -#endif +#endif /* __MAC_OS_X_VERSION_MAX_ALLOWED < 1060 */ +#endif /* __APPLE__ */ #if defined (UNIX_X11) #endif free(hidd); diff --git a/spectro/hidio.h b/spectro/hidio.h index b25fe03..a7e94c8 100644 --- a/spectro/hidio.h +++ b/spectro/hidio.h @@ -60,6 +60,12 @@ struct hid_idevice { OVERLAPPED ols; /* Overlapped structure for write/read */ #endif #if defined(__APPLE__) +# if defined(USE_NEW_OSX_CODE) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + int lid; /* Location ID */ + IOHIDDeviceRef ioob; /* Object to open */ + /* Stuff setup when device is open: */ + CFRunLoopRef rlr; +#else int lid; /* Location ID */ io_object_t ioob; /* Object to open */ /* Stuff setup when device is open: */ @@ -68,7 +74,10 @@ struct hid_idevice { CFRunLoopSourceRef evsrc; CFRunLoopRef rlr; IOReturn result; +# define HID_RBUF_SIZE 1024 + 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 */ diff --git a/spectro/huey.c b/spectro/huey.c index f8d8308..8c307ac 100644 --- a/spectro/huey.c +++ b/spectro/huey.c @@ -7,7 +7,7 @@ * Author: Graeme W. Gill * Date: 18/10/2006 * - * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2006 - 2014, Graeme W. Gill * All rights reserved. * * (Based on i1disp.c) @@ -61,7 +61,8 @@ static inst_code huey_interp_code(inst *pp, int ec); static inst_code huey_check_unlock(huey *p); #define CALFACTOR 3.428 /* Emissive magic calibration factor */ -#define AMB_SCALE_FACTOR 5.772e-3 /* Ambient mode scale factor */ +//#define AMB_SCALE_FACTOR 5.772e-3 /* Ambient mode scale factor Lux/pi*/ +#define AMB_SCALE_FACTOR 18.1333e-3 /* Ambient mode scale factor (Lux) */ /* This is only approximate, and were derived */ /* by matching readings from the i1pro. */ @@ -392,7 +393,7 @@ huey_rdreg_float( if ((ev = huey_rdreg_word(p, &val, addr)) != inst_ok) return ev; - if (ev == 0xffffffff) { + if (val == 0xffffffff) { return inst_ok; } @@ -823,7 +824,7 @@ huey_take_XYZ_measurement( if (IMODETST(p->mode, inst_mode_emis_ambient)) { if ((ev = huey_take_amb_measurement(p, 0, &XYZ[1])) != inst_ok) return ev; - XYZ[1] *= AMB_SCALE_FACTOR; /* Times aproximate fudge factor */ + XYZ[1] *= AMB_SCALE_FACTOR; /* Times aproximate fudge factor to Lux */ XYZ[0] = icmD50.X * XYZ[1]; /* Convert to D50 neutral */ XYZ[2] = icmD50.Z * XYZ[1]; } else { @@ -1048,7 +1049,7 @@ huey_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } else { a1logd(p->log, 1, "huey_init_coms: wrong communications type for device!\n"); - return huey_interp_code((inst *)p, HUEY_UNKNOWN_MODEL); + return inst_coms_fail; } if ((p->icom->vid == 0x0765 && p->icom->pid == 0x5001) @@ -1146,7 +1147,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ int user_trig = 0; int rv = inst_protocol_error; -a1logd(p->log, 1, "huey: huey_read_sample called\n"); + a1logd(p->log, 1, "huey: huey_read_sample called\n"); if (!p->gotcoms) return inst_no_coms; @@ -1160,7 +1161,6 @@ a1logd(p->log, 1, "huey: huey_read_sample called\n"); return inst_unsupported; } -a1logd(p->log, 1, "huey: about to wait for user trigger\n"); for (;;) { if ((rv = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) { if (rv == inst_user_abort) @@ -1178,14 +1178,12 @@ a1logd(p->log, 1, "huey: about to wait for user trigger\n"); /* Progromatic Trigger */ } else { -a1logd(p->log, 1, "huey: checking for abort\n"); /* Check for abort */ if (p->uicallback != NULL && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) return rv; /* Abort */ } -a1logd(p->log, 1, "huey: about to call huey_take_XYZ_measurement\n"); /* Read the XYZ value */ if ((rv = huey_take_XYZ_measurement(p, val->XYZ)) != inst_ok) { return rv; @@ -1226,8 +1224,8 @@ double mtx[3][3] 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"); - inst_wrong_setup; + a1loge(p->log, 1, "huey: can't set col_cor_mat over non-base display type\n"); + return inst_wrong_setup; } icmCpy3x3(p->ccmat, mtx); } diff --git a/spectro/i1d3.c b/spectro/i1d3.c index 53b2f0c..cf0fc08 100644 --- a/spectro/i1d3.c +++ b/spectro/i1d3.c @@ -7,7 +7,7 @@ * Author: Graeme W. Gill * Date: 28/7/2011 * - * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2006 - 2014, Graeme W. Gill * All rights reserved. * * (Based on huey.c) @@ -58,6 +58,14 @@ #undef PLOT_SPECTRA /* Plot the sensor senitivity spectra */ #undef SAVE_SPECTRA /* Save the sensor senitivity spectra to "sensors.cmf" */ #undef PLOT_REFRESH /* Plot data used to determine refresh rate */ +#undef PLOT_UPDELAY /* Plot data used to determine display update delay */ + +#undef DEBUG_TWEAKS /* Allow environment variable tweaks to int time etc. */ +/* I1D3_MIN_REF_QUANT_TIME in seconds. Default 0.05 */ +/* I1D3_MIN_INT_TIME in seconds. Default 0.4 for refresh displays */ + +#define I1D3_MEAS_TIMEOUT 40.0 /* Longest reading timeout in seconds */ + /* Typically 20.0 is the maximum needed. */ static inst_code i1d3_interp_code(inst *pp, int ec); static inst_code i1d3_check_unlock(i1d3 *p); @@ -145,7 +153,7 @@ static char *inst_desc(i1Disp3CC cc) { case i1d3_rd_sensor: return "ReadAnalogSensor"; case i1d3_get_diff: - return "GetDiffuserPositio"; + return "GetDiffuserPosition"; case i1d3_lockchal: return "GetLockChallenge"; case i1d3_lockresp: @@ -186,8 +194,14 @@ i1d3_command( if (cmd == 0x00) send[1] = (cc & 0xff); /* Minor command */ - if (!nd) a1logd(p->log, 4, "i1d3_command: Sending cmd '%s' args '%s'\n", - inst_desc(cc), icoms_tohex(send, 8)); + if (!nd) { + if (cc == i1d3_lockresp) + a1logd(p->log, 4, "i1d3_command: Sending cmd '%s' args '%s'\n", + inst_desc(cc), icoms_tohex(send, 64)); + else + a1logd(p->log, 4, "i1d3_command: Sending cmd '%s' args '%s'\n", + inst_desc(cc), icoms_tohex(send, 8)); + } if (p->icom->port_type(p->icom) == icomt_hid) { se = p->icom->hid_write(p->icom, send, 64, &wbytes, to); @@ -196,6 +210,12 @@ i1d3_command( } if (se != 0) { if (!nd) a1logd(p->log, 1, "i1d3_command: Command send failed with ICOM err 0x%x\n",se); + /* Flush any response */ + if (ishid) { + p->icom->hid_read(p->icom, recv, 64, &rbytes, to); + } else { + p->icom->usb_read(p->icom, NULL, 0x81, recv, 64, &rbytes, to); + } amutex_unlock(p->lock); return i1d3_interp_code((inst *)p, I1D3_COMS_FAIL); } @@ -213,7 +233,7 @@ i1d3_command( p->icom->hid_read(p->icom, recv, 64, &rbytes, to); } else { p->icom->usb_read(p->icom, NULL, 0x81, recv, 64, &rbytes, to); - } + } amutex_unlock(p->lock); return rv; } @@ -228,6 +248,12 @@ i1d3_command( } if (se != 0) { if (!nd) a1logd(p->log, 1, "i1d3_command: response read failed with ICOM err 0x%x\n",se); + /* Flush any extra response, in case responses are out of sync */ + if (ishid) { + p->icom->hid_read(p->icom, recv, 64, &rbytes, 0.2); + } else { + p->icom->usb_read(p->icom, NULL, 0x81, recv, 64, &rbytes, 0.2); + } amutex_unlock(p->lock); return i1d3_interp_code((inst *)p, I1D3_COMS_FAIL); } @@ -237,12 +263,13 @@ i1d3_command( } /* The first byte returned seems to be a command result error code. */ - /* The second byte is usually the command code being echo'd back, but not always. */ if (rv == inst_ok && recv[0] != 0x00) { if (!nd) a1logd(p->log, 1, "i1d3_command: status byte != 00 = 0x%x\n",recv[0]); rv = i1d3_interp_code((inst *)p, I1D3_BAD_RET_STAT); } + /* The second byte is usually the command code being echo'd back, but not always. */ + /* ie., get i1d3_get_diff returns the status instead. */ if (rv == inst_ok) { if (cc != i1d3_get_diff) { if (recv[1] != cmd) { @@ -250,31 +277,35 @@ i1d3_command( cmd,recv[1]); rv = i1d3_interp_code((inst *)p, I1D3_BAD_RET_CMD); } + } else { /* i1d3_get_diff as special case */ + int i; + for (i = 2; i < 64; i++) { + if (recv[i] != 0x00) { + if (!nd) a1logd(p->log, 1, "i1d3_command: i1d3_get_diff not zero filled\n"); + rv = i1d3_interp_code((inst *)p, I1D3_BAD_RET_CMD); + break; + } + } } } - if (!nd) a1logd(p->log, 4, "i1d3_command: got '%s' ICOM err 0x%x\n",icoms_tohex(recv, 14),ua); - - amutex_unlock(p->lock); - return rv; -} - -/* Read a packet and time out or throw it away */ -static inst_code -i1d3_dummy_read( - i1d3 *p /* i1d3 object */ -) { - unsigned char buf[64]; - int rbytes; /* bytes read from ep */ - int se, rv = inst_ok; - int ishid = p->icom->port_type(p->icom) == icomt_hid; + if (!nd) { + if (cc == i1d3_lockchal) + a1logd(p->log, 4, "i1d3_command: got '%s' ICOM err 0x%x\n",icoms_tohex(recv, 64),ua); + else + a1logd(p->log, 4, "i1d3_command: got '%s' ICOM err 0x%x\n",icoms_tohex(recv, 14),ua); + } - if (ishid) { - se = p->icom->hid_read(p->icom, buf, 64, &rbytes, 0.1); - } else { - se = p->icom->usb_read(p->icom, NULL, 0x81, buf, 64, &rbytes, 0.1); - } + if (rv != inst_ok) { + /* Flush any extra response, in case responses are out of sync */ + if (ishid) { + p->icom->hid_read(p->icom, recv, 64, &rbytes, 0.2); + } else { + p->icom->usb_read(p->icom, NULL, 0x81, recv, 64, &rbytes, 0.2); + } + } + amutex_unlock(p->lock); return rv; } @@ -534,19 +565,26 @@ i1d3_unlock( { "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 }, { NULL } }; inst_code ev; - int ix; + int ix, nix; a1logd(p->log, 2, "i1d3_unlock: called\n"); + /* Count the keys */ + for (nix = 0;;nix++) { + if (codes[nix].pname == NULL) + break; + } + /* Until we give up */ for (ix = 0;;ix++) { /* If we've run out of unlock keys */ if (codes[ix].pname == NULL) { - a1logd(p->log, 1, "i1d3: Unknown lock code. Please contact ArgyllCMS for help\n"); + a1logw(p->log, "i1d3: Unknown lock code. Please contact ArgyllCMS for help\n"); return i1d3_interp_code((inst *)p, I1D3_UNKNOWN_UNLOCK); } @@ -559,6 +597,7 @@ i1d3_unlock( // a1logd(p->log, 3, "i1d3_unlock: Trying unlock key 0x%08x 0x%08x\n", // codes[ix].key[0], codes[ix].key[1]); + a1logd(p->log, 3, "i1d3_unlock: Trying unlock key %d/%d\n", ix+1, nix); p->dtype = codes[ix].dtype; p->stype = codes[ix].stype; @@ -734,7 +773,7 @@ i1d3_freq_measure( todev[23] = 0; /* Unknown parameter, always 0 */ - if ((ev = i1d3_command(p, i1d3_measure1, todev, fromdev, 20.0, 0)) != inst_ok) + if ((ev = i1d3_command(p, i1d3_measure1, todev, fromdev, I1D3_MEAS_TIMEOUT, 0)) != inst_ok) return ev; rgb[0] = (double)buf2uint(fromdev + 2); @@ -771,7 +810,7 @@ i1d3_period_measure( todev[7] = (unsigned char)mask; todev[8] = 0; /* Unknown parameter, always 0 */ - if ((ev = i1d3_command(p, i1d3_measure2, todev, fromdev, 20.0, 0)) != inst_ok) + if ((ev = i1d3_command(p, i1d3_measure2, todev, fromdev, I1D3_MEAS_TIMEOUT, 0)) != inst_ok) return ev; rgb[0] = (double)buf2uint(fromdev + 2); @@ -911,6 +950,7 @@ i1d3_imp_measure_refresh( int npeaks = 0; /* Number of peaks */ double pval; /* Period value */ int isdeb; + int isth; if (prefrate != NULL) *prefrate = 0.0; @@ -922,14 +962,17 @@ i1d3_imp_measure_refresh( return inst_internal_error; } - /* Turn debug off so that it doesn't intefere with measurement timing */ + /* Turn debug and thread off so that they doesn't intefere with measurement timing */ isdeb = p->log->debug; p->icom->log->debug = 0; + isth = p->th_en; + p->th_en = 0; /* Do some measurement and throw them away, to make sure the code is in cache. */ for (i = 0; i < 5; i++) { if ((ev = i1d3_freq_measure(p, &inttimeh, samp[i].rgb)) != inst_ok) { p->log->debug = isdeb; + p->th_en = isth; return ev; } } @@ -945,6 +988,7 @@ i1d3_imp_measure_refresh( if ((ev = i1d3_freq_measure(p, &inttime1, samp[0].rgb)) != inst_ok) { p->log->debug = isdeb; + p->th_en = isth; return ev; } @@ -952,6 +996,7 @@ i1d3_imp_measure_refresh( if ((ev = i1d3_freq_measure(p, &inttime2, samp[0].rgb)) != inst_ok) { p->log->debug = isdeb; + p->th_en = isth; return ev; } @@ -977,10 +1022,10 @@ i1d3_imp_measure_refresh( if ((ev = i1d3_freq_measure(p, &samp[i].itime, samp[i].rgb)) != inst_ok) { p->log->debug = isdeb; + p->th_en = isth; return ev; } cutime = (usec_time() - sutime) / 1000000.0; -// ~~999 samp[i].sec = 0.5 * (putime + cutime); /* Mean of before and after stamp */ //samp[i].sec *= 85.0/20.0; /* Test 20 Hz */ //samp[i].sec *= 85.0/100.0; /* Test 100 Hz */ @@ -988,7 +1033,9 @@ i1d3_imp_measure_refresh( if (cutime > NFMXTIME) break; } + /* Restore debug & thread */ p->log->debug = isdeb; + p->th_en = isth; nfsamps = i; if (nfsamps < 100) { @@ -1354,6 +1401,7 @@ i1d3_imp_measure_refresh( } else { int mul; double refrate; + double pval; pval = avg/ano; pval /= 1000.0; /* Convert to seconds */ @@ -1365,10 +1413,25 @@ i1d3_imp_measure_refresh( /* Error against my 85Hz CRT - GWG */ // a1logd(p->log, 1, "Refresh rate error = %.4f%%\n",100.0 * fabs(refrate - 85.0)/(85.0)); - /* Scale to just above 20 Hz */ - mul = floor((1.0/20) / pval); - if (mul > 1) + /* Scale to just above 20 Hz, but make it multiple of 2 or 4 */ +#ifdef DEBUG_TWEAKS + { + double quanttime = 1.0/20.0; + char *cp; + if ((cp = getenv("I1D3_MIN_REF_QUANT_TIME")) != NULL) + quanttime = atof(cp); + mul = (int)floor(quanttime / pval); + } +#else + mul = (int)floor((1.0/20.0) / pval); +#endif + if (mul > 1) { + if (mul >= 8) + mul = (mul + 3) & ~3; /* Round up to mult of 4 */ + else + mul = (mul + 1) & ~1; /* Round up to mult of 2 */ pval *= mul; + } a1logd(p->log, 1, "Refresh rate = %f Hz, quantizing to %f msec\n",refrate,pval); a1logv(p->log, 1, "Refresh rate = %f Hz, quantizing to %f msec\n",refrate,pval); @@ -1411,6 +1474,8 @@ i1d3_measure_set_refresh( /* - - - - - - - - - - - - - - - - - - - - - - */ +#ifdef NEVER /* No longer used - use general measurement instead */ + /* Take an ambient measurement and return the cooked reading */ /* The cooked reading is the frequency of the L2V */ static inst_code @@ -1451,11 +1516,11 @@ i1d3_take_amb_measurement( return inst_ok; } - +#endif /* - - - - - - - - - - - - - - - - - - - - - - */ -/* Take an display measurement and return the cooked reading */ +/* Take a general measurement and return the cooked reading */ /* The cooked reading is the frequency of the L2V */ static inst_code i1d3_take_emis_measurement( @@ -1464,26 +1529,22 @@ i1d3_take_emis_measurement( double *rgb /* Return the cooked emsissive RGB values */ ) { int i, k; - int pos; inst_code ev; double rmeas[3] = { -1.0, -1.0, -1.0 }; /* raw measurement */ int edgec[3] = {2,2,2}; /* Measurement edge count for each channel (not counting start edge) */ int mask = 0x7; /* Period measure mask */ int msecstart = msec_time(); /* Debug */ double rgb2[3] = { 0.0, 0.0, 0.0 }; /* Trial measurement RGB values */ + int isth; if (p->inited == 0) return i1d3_interp_code((inst *)p, I1D3_NOT_INITED); a1logd(p->log,3,"\ntake_emis_measurement called\n"); - /* Check that the ambient filter is not in place */ - if ((ev = i1d3_get_diffpos(p, &pos, 0)) != inst_ok) - return ev; - - if (pos != 0) - return i1d3_interp_code((inst *)p, I1D3_SPOS_EMIS); - + /* Suspend thread so that it doesn't intefere with measurement timing */ + isth = p->th_en; + p->th_en = 0; /* If we should take a frequency measurement first */ if (mode == i1d3_adaptive || mode == i1d3_frequency) { @@ -1492,8 +1553,10 @@ i1d3_take_emis_measurement( a1logd(p->log,3,"Doing fixed period frequency measurement over %f secs\n",p->inttime); /* Take a frequency measurement over a fixed period */ - if ((ev = i1d3_freq_measure(p, &p->inttime, rmeas)) != inst_ok) + if ((ev = i1d3_freq_measure(p, &p->inttime, rmeas)) != inst_ok) { + p->th_en = isth; return ev; + } /* Convert to frequency (assume raw meas is both edges count over integration time) */ for (i = 0; i < 3; i++) { @@ -1555,8 +1618,10 @@ i1d3_take_emis_measurement( a1logd(p->log,3,"Doing 1st period pre-measurement mask 0x%x, edgec %d %d %d\n",mask2,edgec[0],edgec[1],edgec[2]); /* Take an initial period pre-measurement over 2 edges */ - if ((ev = i1d3_period_measure(p, edgec, mask2, rmeas2)) != inst_ok) + if ((ev = i1d3_period_measure(p, edgec, mask2, rmeas2)) != inst_ok) { + p->th_en = isth; return ev; + } a1logd(p->log,3,"Got %f %f %f raw %f %f %f Hz\n",rmeas2[0],rmeas2[1],rmeas2[2], 0.5 * edgec[0] * p->clk_freq/rmeas2[0], @@ -1627,8 +1692,10 @@ i1d3_take_emis_measurement( a1logd(p->log,3,"Doing 2nd initial period measurement mask 0x%x, edgec %d %d %d\n",mask2,edgec[0],edgec[1],edgec[2]); /* Take a 2nd initial period measurement */ - if ((ev = i1d3_period_measure(p, edgec, mask3, rmeas2)) != inst_ok) + if ((ev = i1d3_period_measure(p, edgec, mask3, rmeas2)) != inst_ok) { + p->th_en = isth; return ev; + } a1logd(p->log,3,"Got %f %f %f raw %f %f %f Hz\n",rmeas2[0],rmeas2[1],rmeas2[2], 0.5 * edgec[0] * p->clk_freq/rmeas2[0], @@ -1659,7 +1726,7 @@ i1d3_take_emis_measurement( continue; /* Compute number of edges needed for a clock count */ - /* of p->inttime (0.2 secs) */ + /* of p->inttime (0.2/0.4 secs) */ nedgec = edgec[i] * p->inttime * p->clk_freq/rmeas[i]; /* If we will get less than 200 edges, raise the target integration */ @@ -1780,8 +1847,10 @@ i1d3_take_emis_measurement( a1logd(p->log,3,"Doing freq re-measure inttime %f\n",tinttime); /* Take a frequency measurement over a fixed period */ - if ((ev = i1d3_freq_measure(p, &tinttime, rmeas)) != inst_ok) + if ((ev = i1d3_freq_measure(p, &tinttime, rmeas)) != inst_ok) { + p->th_en = isth; return ev; + } /* Convert raw measurement to frequency */ for (i = 0; i < 3; i++) { @@ -1799,8 +1868,10 @@ i1d3_take_emis_measurement( a1logd(p->log,3,"Doing period re-measure mask 0x%x, edgec %d %d %d\n",mask,edgec[0],edgec[1],edgec[2]); /* Measure again with desired precision, taking up to 0.4/0.8 secs */ - if ((ev = i1d3_period_measure(p, edgec, mask, rmeas)) != inst_ok) + if ((ev = i1d3_period_measure(p, edgec, mask, rmeas)) != inst_ok) { + p->th_en = isth; return ev; + } for (i = 0; i < 3; i++) { double tt; @@ -1820,6 +1891,7 @@ i1d3_take_emis_measurement( } } } + p->th_en = isth; a1logd(p->log,3,"Took %d msec to measure\n", msec_time() - msecstart); @@ -1843,27 +1915,35 @@ i1d3_take_XYZ_measurement( i1d3 *p, /* Object */ double XYZ[3] /* Return the XYZ values */ ) { + int pos; inst_code ev; if (IMODETST(p->mode, inst_mode_emis_ambient)) { - if ((ev = i1d3_take_amb_measurement(p, XYZ)) != inst_ok) + + /* Check that the ambient filter is in place */ + if ((ev = i1d3_get_diffpos(p, &pos, 0)) != inst_ok) + return ev; + + if (pos != 1) + 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) return ev; /* Multiply by ambient calibration matrix */ - icmMulBy3x3(XYZ, p->ambi_cal, XYZ); - icmScale3(XYZ, XYZ, 1/3.141592654); /* Convert from Lux to cd/m^2 */ + icmMulBy3x3(XYZ, p->ambi_cal, XYZ); /* Values in Lux */ } else { - /* Constant fast speed, poor accuracy for black */ -// if ((ev = i1d3_take_emis_measurement(p, i1d3_frequency, XYZ)) != inst_ok) -// return ev; - - /* Most accurate ? */ -// if ((ev = i1d3_take_emis_measurement(p, i1d3_period, XYZ)) != inst_ok) -// return ev; + /* Check that the ambient filter is not in place */ + if ((ev = i1d3_get_diffpos(p, &pos, 0)) != inst_ok) + return ev; + + if (pos != 0) + return i1d3_interp_code((inst *)p, I1D3_SPOS_EMIS); - /* Best combination */ + /* Best type of reading, including refresh support */ if ((ev = i1d3_take_emis_measurement(p, i1d3_adaptive, XYZ)) != inst_ok) return ev; @@ -1892,6 +1972,9 @@ static inst_code i1d3_decode_intEE( strncpy(p->serial_no, (char *)buf + 0x10, 20); p->serial_no[20] = '\000'; + strncpy(p->vers_no, (char *)buf + 0x2C, 10); + p->serial_no[10] = '\000'; + /* Read the black level offset */ for (i = 0; i < 3; i++) { t1 = buf2uint(buf + 0x0004 + 4 * i); @@ -1915,16 +1998,22 @@ static inst_code i1d3_decode_extEE( unsigned int chsum, rchsum; xspect tmp; - for (chsum = 0, i = 4; i < 6042; i++) - chsum += buf[i]; + rchsum = buf2short(buf + 2); - chsum &= 0xffff; /* 16 bit sum */ + /* For the "A-01" revsions the checksum is from 4 to 0x179a */ + /* The "A-02" seems to have abandoned reliable checksums ?? */ + for (chsum = 0, i = 4; i < 0x179a; i++) { + chsum += buf[i]; + } - rchsum = buf2short(buf + 2); + chsum &= 0xffff; if (rchsum != chsum) { - a1logd(p->log, 3, "i1d3_decode_extEE: checksum failed\n"); - return i1d3_interp_code((inst *)p, I1D3_BAD_EX_CHSUM); + a1logd(p->log, 3, "i1d3_decode_extEE: checksum failed, is 0x%x, should be 0x%x\n",chsum,rchsum); + if (strcmp(p->vers_no, "A-01") == 0) + return i1d3_interp_code((inst *)p, I1D3_BAD_EX_CHSUM); + + /* Else ignore checksum error */ } /* Read 3 x sensor spectral sensitivits */ @@ -2116,6 +2205,108 @@ i1d3_comp_calmat( return inst_ok; } +/* Preset the calibration to a spectral sample type. */ +/* ccmat[][] is set to unity */ +static inst_code +i1d3_set_speccal( + i1d3 *p, + xspect *samples, /* Array of nsamp spectral samples, or RGBcmfs for MIbLSr */ + int nsamp /* Number of samples */ +) { + int i; + + /* Save a the spectral samples to the current state */ + if (p->samples != NULL) + free(p->samples); + p->nsamp = 0; + if ((p->samples = (xspect *)calloc(sizeof(xspect), nsamp)) == NULL) { + a1loge(p->log, inst_internal_error, "i1d3_set_speccal: malloc failed\n"); + return inst_internal_error; + } + for (i = 0; i < nsamp; i++ ) + p->samples[i] = samples[i]; /* Struct copy */ + p->nsamp = nsamp; + + icmSetUnity3x3(p->ccmat); /* No matrix */ + + 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 (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; + } + icmCpy3x3(p->ccmat, mtx); + } + return inst_ok; +} + + +/* Set the calibration to the currently preset type */ +static inst_code +i1d3_set_cal(i1d3 *p) { + inst_code ev = inst_ok; + + if (p->samples != NULL && p->nsamp > 0) { + + /* Create matrix for specified samples */ + if ((ev = i1d3_comp_calmat(p, p->emis_cal, p->obType, p->custObserver, + p->sens, p->samples, p->nsamp)) != inst_ok) { + a1logd(p->log, 1, "i1d3_set_cal: comp_calmat ccss 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; + + icmSetUnity3x3(p->ccmat); /* to be sure to be sure... */ + + } else { /* Assume 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); + 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; + } + + 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]); + 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,"\n"); + } + + return inst_ok; +} /* ------------------------------------------------------------------------ */ @@ -2175,15 +2366,10 @@ i1d3_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { } } else { - a1logd(p->log, 1, "i1d3_init_coms: wrong sort of coms!\n"); - return i1d3_interp_code((inst *)p, I1D3_UNKNOWN_MODEL); + a1logd(p->log, 1, "i1d3_init_coms: wrong communications type for device!\n"); + return inst_coms_fail; } -#if defined(__APPLE__) - /* We seem to have to clear any pending messages for OS X HID */ - i1d3_dummy_read(p); -#endif - /* Check instrument is responding */ if ((ev = i1d3_check_status(p,&stat)) != inst_ok) { a1logd(p->log, 1, "i1d3_init_coms: failed with rv = 0x%x\n",ev); @@ -2214,7 +2400,7 @@ static void dump_bytes(a1log *log, char *pfx, unsigned char *buf, int len) { bp += sprintf(bp,"."); } bp += sprintf(bp,"\n"); - a1logd(log,0,oline); + a1logd(log,0, "%s", oline); bp = oline; } } @@ -2230,20 +2416,25 @@ int i1d3_diff_thread(void *pp) { for (nfailed = 0; nfailed < 5;) { int pos; - rv = i1d3_get_diffpos(p, &pos, 1); - if (p->th_term) { - p->th_termed = 1; - break; - } - if (rv != inst_ok) { - nfailed++; - a1logd(p->log,3,"Diffuser thread failed with 0x%x\n",rv); - continue; - } - if (pos != p->dpos) { - p->dpos = pos; - if (p->eventcallback != NULL) { - p->eventcallback(p->event_cntx, inst_event_mconf); + /* Don't get diffpos if we're doing something else that */ + /* is timing critical */ + if (p->th_en) { +//a1logd(p->log,3,"Diffuser thread loop debug = %d\n",p->log->debug); + rv = i1d3_get_diffpos(p, &pos, p->log->debug < 8 ? 1 : 0); + if (p->th_term) { + p->th_termed = 1; + break; + } + if (rv != inst_ok) { + nfailed++; + a1logd(p->log,3,"Diffuser thread failed with 0x%x\n",rv); + continue; + } + if (pos != p->dpos) { + p->dpos = pos; + if (p->eventcallback != NULL) { + p->eventcallback(p->event_cntx, inst_event_mconf); + } } } msec_sleep(100); @@ -2262,7 +2453,7 @@ i1d3_init_inst(inst *pp) { int i, stat; unsigned char buf[8192]; - a1logd(p->log, 2, "i1d3_init_inst: called\n"); + a1logd(p->log, 2, "i1d3_init_inst: called, debug = %d\n",p->log->debug); p->rrset = 0; @@ -2329,9 +2520,11 @@ i1d3_init_inst(inst *pp) { return ev; /* Set known constants */ - p->clk_freq = 12e6; /* 12 Mhz */ - p->dinttime = 0.2; /* 0.2 second integration time default */ - p->inttime = p->dinttime; /* Start in non-refresh mode */ + p->clk_freq = 12e6; /* 12 Mhz */ + p->omininttime = 0.0; /* No override */ + p->dinttime = 0.2; /* 0.2 second integration time default */ + p->inttime = p->dinttime; /* Start in non-refresh mode */ + p->mininttime = p->inttime; /* Current value */ /* Create the default calibrations */ @@ -2353,29 +2546,32 @@ i1d3_init_inst(inst *pp) { if (p->log->debug >= 4) { a1logd(p->log,4,"Default calibration:\n"); - 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]); 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", 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]); a1logd(p->log,4,"\n"); } /* Start the diffuser monitoring thread */ + p->th_en = 1; if ((p->th = new_athread(i1d3_diff_thread, (void *)p)) == NULL) - return I1D3_INT_THREADFAILED; + return i1d3_interp_code((inst *)p, I1D3_INT_THREADFAILED); /* Flash the LED, just cos we can! */ if ((ev = i1d3_set_LEDs(p, i1d3_flash, 0.2, 0.05, 2)) != inst_ok) return ev; + a1logd(p->log, 2, "i1d3_init_inst: done\n"); + return ev; } @@ -2422,26 +2618,40 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } else { /* Check for abort */ if (p->uicallback != NULL - && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) + && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) { return rv; /* Abort */ + } } /* Attempt a refresh display frame rate calibration if needed */ if (p->dtype != i1d3_munkdisp && p->refrmode != 0 && p->rrset == 0) { inst_code ev = inst_ok; + p->mininttime = 2.0 * p->dinttime; + + if (p->omininttime != 0.0) + p->mininttime = p->omininttime; /* Override */ + +#ifdef DEBUG_TWEAKS + { + char *cp; + if ((cp = getenv("I1D3_MIN_INT_TIME")) != NULL) + p->mininttime = atof(cp); + } +#endif + if ((ev = i1d3_measure_set_refresh(p)) != inst_ok) return ev; /* Quantize the sample time */ if (p->refperiod > 0.0) { /* If we have a refresh period */ int n; - n = (int)ceil(p->dinttime/p->refperiod); - p->inttime = 2.0 * n * p->refperiod; + n = (int)ceil(p->mininttime/p->refperiod); + p->inttime = n * p->refperiod; a1logd(p->log, 3, "i1d3: integration time quantize to %f secs\n",p->inttime); - } else { /* We don't have a period, so simply double the default */ - p->inttime = 2.0 * p->dinttime; + } else { /* We don't have a period, so simply use the double default */ + p->inttime = p->mininttime; a1logd(p->log, 3, "i1d3: integration time integration time doubled to %f secs\n",p->inttime); } } @@ -2463,6 +2673,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ val->sp.spec_n = 0; val->duration = 0.0; + if (user_trig) return inst_user_trig; @@ -2491,6 +2702,7 @@ double *ref_rate) { if (*ref_rate == 0.0) return inst_misread; + return inst_ok; } @@ -2502,23 +2714,19 @@ inst *pp, double mtx[3][3] ) { i1d3 *p = (i1d3 *)pp; + inst_code ev = inst_ok; + + a1logd(p->log, 4, "i1d3_col_cor_mat%s\n",mtx == NULL ? " (noop)": ""); if (!p->gotcoms) return inst_no_coms; if (!p->inited) return inst_no_init; - 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"); - inst_wrong_setup; - } - icmCpy3x3(p->ccmat, mtx); - } - - return inst_ok; + if ((ev = i1d3_set_matcal(p, mtx)) != inst_ok) + return ev; + + return i1d3_set_cal(p); } /* Use a Colorimeter Calibration Spectral Set to set the */ @@ -2533,6 +2741,8 @@ int no_sets i1d3 *p = (i1d3 *)pp; inst_code ev = inst_ok; + a1logd(p->log, 4, "i1d3_col_cal_spec_set%s\n",sets == NULL ? " (default)": ""); + if (!p->gotcoms) return inst_no_coms; if (!p->inited) @@ -2543,31 +2753,12 @@ int no_sets return ev; } } else { - /* Use given spectral samples */ - if ((ev = i1d3_comp_calmat(p, p->emis_cal, p->obType, p->custObserver, p->sens, - sets, no_sets)) != inst_ok) - return ev; - /* Use MIbLSr */ - if ((ev = i1d3_comp_calmat(p, p->ambi_cal, p->obType, p->custObserver, p->ambi, - p->ambi, 3)) != inst_ok) - return ev; - } - if (p->log->debug >= 4) { - a1logd(p->log,4,"CCSS update calibration:\n"); - 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]); - 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", - p->emis_cal[2][0], p->emis_cal[2][1], p->emis_cal[2][2]); - a1logd(p->log,4,"\n"); + if ((ev = i1d3_set_speccal(p, sets, no_sets)) != inst_ok) + return ev; + + ev = i1d3_set_cal(p); } + return ev; } @@ -2638,11 +2829,24 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if ((*calt & inst_calt_ref_freq) && p->dtype != i1d3_munkdisp && p->refrmode != 0) { inst_code ev = inst_ok; - if (*calc != inst_calc_emis_white) { - *calc = inst_calc_emis_white; + p->mininttime = 2.0 * p->dinttime; + + if (*calc != inst_calc_emis_80pc) { + *calc = inst_calc_emis_80pc; return inst_cal_setup; } + if (p->omininttime != 0.0) + p->mininttime = p->omininttime; /* Override */ + +#ifdef DEBUG_TWEAKS + { + char *cp; + if ((cp = getenv("I1D3_MIN_INT_TIME")) != NULL) + p->mininttime = atof(cp); + } +#endif + /* Do refresh display rate calibration */ if ((ev = i1d3_measure_set_refresh(p)) != inst_ok) return ev; @@ -2650,11 +2854,11 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ /* Quantize the sample time */ if (p->refperiod > 0.0) { int n; - n = (int)ceil(p->dinttime/p->refperiod); - p->inttime = 2.0 * n * p->refperiod; + n = (int)ceil(p->mininttime/p->refperiod); + p->inttime = n * p->refperiod; a1logd(p->log, 3, "i1d3: integration time quantize to %f secs\n",p->inttime); } else { - p->inttime = 2.0 * p->dinttime; /* Double default integration time */ + p->inttime = p->mininttime; /* Double default integration time */ a1logd(p->log, 3, "i1d3: integration time integration time doubled to %f secs\n",p->inttime); } *calt &= ~inst_calt_ref_freq; @@ -2665,11 +2869,11 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference 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 0.5 seconds. */ +/* be noticed by the instrument, up to 0.6 seconds. */ /* inst_misread will be returned on failure to find a transition to black. */ -#define NDSAMPS 60 -#define DINTT 0.010 -#define NDMXTIME 0.6 /* Maximum time to take */ +#define NDSAMPS 200 +#define DINTT 0.005 /* Too short hits blanking */ +#define NDMXTIME 1.0 /* Maximum time to take */ inst_code i1d3_meas_delay( inst *pp, @@ -2681,21 +2885,25 @@ int *msecdelay) { /* Return the number of msec */ struct { double sec; double rgb[3]; + double tot; } samp[NDSAMPS]; int ndsamps; double inttime = DINTT; - double rgb[3]; + double stot, etot, del, thr; double etime; int isdeb; + int isth; if (usec_time() < 0.0) { a1loge(p->log, inst_internal_error, "i1d3_meas_delay: No high resolution timers\n"); return inst_internal_error; } - /* Turn debug off so that it doesn't intefere with measurement timing */ + /* Turn debug and thread off so that they doesn't intefere with measurement timing */ isdeb = p->log->debug; p->icom->log->debug = 0; + isth = p->th_en; + p->th_en = 0; /* Read the samples */ sutime = usec_time(); @@ -2704,71 +2912,85 @@ int *msecdelay) { /* Return the number of msec */ if ((ev = i1d3_freq_measure(p, &inttime, samp[i].rgb)) != inst_ok) { a1logd(p->log, 1, "i1d3_meas_delay: measurement failed\n"); p->log->debug = isdeb; + p->th_en = isth; return ev; } cutime = (usec_time() - sutime) / 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]; if (cutime > NDMXTIME) break; } ndsamps = i; - /* Restore debugging */ + /* Restore debugging & thread */ p->log->debug = isdeb; + p->th_en = isth; if (ndsamps == 0) { a1logd(p->log, 1, "i1d3_meas_delay: No measurement samples returned in time\n"); return inst_internal_error; } -#ifdef NEVER +#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]; - //printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].rgb[0]); + 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, NULL, NULL, NULL, ndsamps); + do_plot6(xx, y1, y2, y3, y4, NULL, NULL, ndsamps); } #endif + /* Over the first 100msec, locate the maximum value */ + etime = samp[ndsamps-1].sec; + stot = -1e9; + for (i = 0; i < ndsamps; i++) { + if (samp[i].tot > stot) + stot = samp[i].tot; + if (samp[i].sec > 0.1) + break; + } + /* Over the last 100msec, locate the maximum value */ etime = samp[ndsamps-1].sec; - for (j = 0; j < 3; j++) - rgb[j] = 0.0; + etot = -1e9; for (i = ndsamps-1; i >= 0; i--) { - for (j = 0; j < 3; j++) { - if (samp[i].rgb[j] > rgb[j]) - rgb[j] = samp[i].rgb[j]; - } + if (samp[i].tot > etot) + etot = samp[i].tot; if ((etime - samp[i].sec) > 0.1) break; } -// a1logd(p->log, 3, "i1d3_meas_delay: end rgb = %f %f %f stopped at %d\n", rgb[0], rgb[1], rgb[2], i); + del = stot - etot; + thr = etot + 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 - if (rgb[0] > 10.0 || rgb[1] > 10.0 || rgb[2] > 10.0) { - a1logd(p->log, 1, "i1d3_meas_delay: measurement delay doesn't seem to be black\n"); + /* 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"); return inst_misread; } - /* Locate the time at which the values are above this */ + /* Working from the end, locate the time at which the level was above the threshold */ for (i = ndsamps-1; i >= 0; i--) { - for (j = 0; j < 3; j++) { - if (samp[i].rgb[j] > (1.5 * rgb[j])) - break; - } - if (j < 3) + if (samp[i].tot > thr) break; } if (i < 0) /* Assume the update was so fast that we missed it */ @@ -2778,6 +3000,10 @@ int *msecdelay) { /* Return the number of msec */ *msecdelay = (int)(samp[i].sec * 1000.0 + 0.5); +#ifdef PLOT_UPDELAY + a1logd(p->log, 0, "i1d3_meas_delay: returning %d msec\n",*msecdelay); +#endif + return inst_ok; } #undef NDSAMPS @@ -2819,13 +3045,27 @@ double ref_rate int mul; double pval; - /* Scale to just above 20 Hz */ + /* Scale to just above 20 Hz, but make it multiple of 2 or 4 */ pval = 1.0/ref_rate; - mul = floor((1.0/20) / pval); - if (mul > 1) +#ifdef DEBUG_TWEAKS + { + double quanttime = 1.0/20.0; + char *cp; + if ((cp = getenv("I1D3_MIN_REF_QUANT_TIME")) != NULL) + quanttime = atof(cp); + mul = (int)floor(quanttime / pval); + } +#else + mul = (int)floor((1.0/20.0) / pval); +#endif + if (mul > 1) { + if (mul >= 8) + mul = (mul + 3) & ~3; /* Round up to mult of 4 */ + else + mul = (mul + 1) & ~1; /* Round up to mult of 2 */ pval *= mul; + } p->refperiod = pval; - p->refrvalid = 1; } p->rrset = 1; @@ -2994,6 +3234,8 @@ i1d3_del(inst *pp) { if (p->icom != NULL) p->icom->del(p->icom); inst_del_disptype_list(p->dtlist, p->ndtlist); + if (p->samples != NULL) + free(p->samples); amutex_del(p->lock); free(p); } @@ -3011,7 +3253,7 @@ inst3_capability *pcap3) { cap1 |= inst_mode_emis_spot | inst_mode_emis_tele | inst_mode_emis_ambient - | inst_mode_emis_refresh_ovd + | inst_mode_emis_refresh_ovd /* (allow override ccmx & ccss mode) */ | inst_mode_emis_norefresh_ovd | inst_mode_colorimeter ; @@ -3023,11 +3265,14 @@ inst3_capability *pcap3) { | inst2_disptype | inst2_ccmx | inst2_ccss + | inst2_get_min_int_time + | inst2_set_min_int_time ; if (p->dtype != i1d3_munkdisp) { cap2 |= inst2_meas_disp_update; - cap2 |= inst2_refresh_rate; + cap2 |= inst2_get_refresh_rate; + cap2 |= inst2_set_refresh_rate; cap2 |= inst2_emis_refr_meas; } if (pcap1 != NULL) @@ -3075,8 +3320,8 @@ int *conf_ix /* Add the extra dependent and independent modes */ mval |= inst_mode_emis_refresh_ovd - | inst_mode_emis_norefresh_ovd - | inst_mode_colorimeter; + | inst_mode_emis_norefresh_ovd + | inst_mode_colorimeter; if (mmodes != NULL) *mmodes = mval; @@ -3139,11 +3384,15 @@ inst_code i1d3_set_mode(inst *pp, inst_mode m) { } 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 */ return inst_ok; } @@ -3152,7 +3401,7 @@ inst_disptypesel i1d3_disptypesel[3] = { { inst_dtflags_default, 1, - "n", + "nl", "Non-Refresh display", 0, 0 @@ -3160,7 +3409,7 @@ inst_disptypesel i1d3_disptypesel[3] = { { inst_dtflags_none, /* flags */ 2, /* cbid */ - "r", /* sel */ + "rc", /* sel */ "Refresh display", /* desc */ 1, /* refr */ 1 /* ix */ @@ -3186,7 +3435,7 @@ int recreate /* nz to re-check for new ccmx & ccss files */ i1d3 *p = (i1d3 *)pp; inst_code rv = inst_ok; - if (!allconfig && p->dpos) { /* If set to Ambient */ + if (!allconfig && p->dpos) { /* If set to Ambient, there are no display types ? */ if (pnsels != NULL) *pnsels = 0; @@ -3233,59 +3482,31 @@ static inst_code set_disp_type(i1d3 *p, inst_disptypesel *dentry) { p->rrset = 0; /* This is a hint we may have swapped displays */ p->refrmode = refrmode; -// if (p->refrmode && p->dtype == i1d3_munkdisp) { 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 */ - if (dentry->flags & inst_dtflags_ccss) { + if (dentry->flags & inst_dtflags_ccss) { /* Spectral sample */ - if ((ev = i1d3_comp_calmat(p, p->emis_cal, p->obType, p->custObserver, - p->sens, dentry->sets, dentry->no_sets)) != inst_ok) { - a1logd(p->log, 1, "i1d3_set_disp_type: comp_calmat ccss failed with rv = 0x%x\n",ev); + if ((ev = i1d3_set_speccal(p, dentry->sets, dentry->no_sets)) != inst_ok) 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; - - icmSetUnity3x3(p->ccmat); - if (p->log->debug >= 4) { - a1logd(p->log,4,"Display type set CCSS:\n"); - 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", - p->emis_cal[2][0], p->emis_cal[2][1], p->emis_cal[2][2]); - a1logd(p->log,4,"\n"); - } - - } else { /* Assume matrix */ - - /* Create the default 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); - 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; + } else { /* Matrix */ if (dentry->flags & inst_dtflags_ccmx) { - icmCpy3x3(p->ccmat, dentry->mat); + if ((ev = i1d3_set_matcal(p, dentry->mat)) != inst_ok) + return ev; } else { - icmSetUnity3x3(p->ccmat); + if ((ev = i1d3_set_matcal(p, NULL)) != inst_ok) /* Noop */ + return ev; } } - - return inst_ok; + return i1d3_set_cal(p); } /* Setup the default display type */ @@ -3386,6 +3607,66 @@ i1d3_get_set_opt(inst *pp, inst_opt_type m, ...) return inst_ok; } + /* Get the current minimum integration time */ + if (m == inst_opt_get_min_int_time) { + va_list args; + double *dpoint; + + va_start(args, m); + dpoint = va_arg(args, double *); + va_end(args); + + if (dpoint != NULL) + *dpoint = p->mininttime; + + return inst_ok; + } + + /* Set the minimum integration time */ + if (m == inst_opt_set_min_int_time) { + va_list args; + double dval; + + va_start(args, m); + dval = va_arg(args, double); + va_end(args); + + p->omininttime = dval; + + /* Hmm. This code is duplicated a lot.. */ + if (p->dtype != i1d3_munkdisp && p->refrmode != 0) { + inst_code ev = inst_ok; + + p->mininttime = 2.0 * p->dinttime; + + if (p->omininttime != 0.0) + p->mininttime = p->omininttime; /* Override */ + +#ifdef DEBUG_TWEAKS + { + char *cp; + if ((cp = getenv("I1D3_MIN_INT_TIME")) != NULL) + p->mininttime = atof(cp); + } +#endif + + /* Quantize the sample time if we have a refresh rate */ + if (p->rrset && p->refperiod > 0.0) { /* If we have a refresh period */ + int n; + n = (int)ceil(p->mininttime/p->refperiod); + p->inttime = n * p->refperiod; + a1logd(p->log, 3, "i1d3: integration time quantize to %f secs\n",p->inttime); + + } else { /* We don't have a period, so simply use the double default */ + p->inttime = p->mininttime; + a1logd(p->log, 3, "i1d3: integration time integration time doubled to %f secs\n",p->inttime); + } + } + + return inst_ok; + } + + /* Set the ccss observer type */ if (m == inst_opt_set_ccss_obs) { va_list args; @@ -3406,7 +3687,9 @@ i1d3_get_set_opt(inst *pp, inst_opt_type m, ...) p->custObserver[2] = custObserver[2]; } - return inst_ok; + a1logd(p->log, 4, "inst_opt_set_ccss_obs\n"); + + return i1d3_set_cal(p); /* Recompute calibration */ } /* Operate the LEDS */ @@ -3436,6 +3719,7 @@ i1d3_get_set_opt(inst *pp, inst_opt_type m, ...) mask = va_arg(args, int); va_end(args); + p->led_state = mask; if (p->led_state & 0x1) return i1d3_set_LEDs(p, i1d3_flash, 0.0, 100.0, 0x80); else @@ -3595,9 +3879,9 @@ static void create_unlock_response(unsigned int *k, unsigned char *c, unsigned c /* Minus the two key values as bytes */ sum += (0xff & -k[0]) + (0xff & (-k[0] >> 8)) - + (0xff & (-k[0] >> 16)) + (0xff & (-k[0] >> 24)); + + (0xff & (-k[0] >> 16)) + (0xff & (-k[0] >> 24)); sum += (0xff & -k[1]) + (0xff & (-k[1] >> 8)) - + (0xff & (-k[1] >> 16)) + (0xff & (-k[1] >> 24)); + + (0xff & (-k[1] >> 16)) + (0xff & (-k[1] >> 24)); /* Convert sum to bytes. Only need 2, because sum of 16 bytes can't exceed 16 bits. */ s0 = sum & 0xff; diff --git a/spectro/i1d3.h b/spectro/i1d3.h index 96de478..6d0eb85 100644 --- a/spectro/i1d3.h +++ b/spectro/i1d3.h @@ -80,7 +80,8 @@ typedef enum { i1d3_munkdisp = 1, /* ColorMunki Display */ i1d3_oem = 2, /* OEM */ i1d3_nec_ssp = 3, /* NEC SpectraSensor Pro */ - i1d3_quato_sh3 = 4 /* Quato Silver Haze 3 */ + i1d3_quato_sh3 = 4, /* Quato Silver Haze 3 */ + i1d3_hp_dreamc = 5 /* HP DreameColor */ } i1d3_dtype; /* Measurement mode */ @@ -106,6 +107,7 @@ struct _i1d3 { /* (Only accurate if it needed unlocking). */ int status; /* 0 if status is ok (not sure what this is) */ char serial_no[21]; /* "I1-11.A-01.100999.02" or "CM-11.A-01.100999.02" */ + char vers_no[11]; /* "A-01", "A-02" */ char prod_name[32]; /* "i1Display3 " or "ColorMunki Display" */ int prod_type; /* 16 bit product type number. i1d3_disppro = 0x0001, */ /* i1d3_munkdisp = 0x0002 */ @@ -126,9 +128,11 @@ struct _i1d3 { int icx; /* Internal calibration matrix index, 11 = Raw */ int cbid; /* calibration base ID, 0 if not a base */ int refrmode; /* nz if in refresh display mode/double int. time */ - double ccmat[3][3]; /* Optional colorimeter correction matrix */ icxObserverType obType; /* ccss observer to use */ xspect custObserver[3]; /* Custom ccss observer to use */ + double ccmat[3][3]; /* Optional colorimeter correction matrix, unity if none. */ + xspect *samples; /* Copy of current calibration spectral samples, NULL if none */ + int nsamp; /* Number of samples, 0 if none */ /* Computed factors and state */ int rrset; /* Flag, nz if the refresh rate has been determined */ @@ -136,8 +140,10 @@ struct _i1d3 { double refrate; /* Measured refresh rate in Hz */ int refrvalid; /* nz if refrate is valid */ double clk_freq; /* Clock frequency (12Mhz) */ + double omininttime; /* Override minimum integration time = 0.0 = none */ double dinttime; /* default integration time = 0.2 seconds */ - double inttime; /* current integration time = 0.2 seconds */ + double mininttime; /* current minimum integration time (doubled for refresh) */ + double inttime; /* current (quantized, doubled) integration time = 0.2 seconds */ double transblend; /* Blend between fixed and adaptive integration */ /* at low light levels */ @@ -147,6 +153,7 @@ struct _i1d3 { double led_period, led_on_time_prop, led_trans_time_prop; /* Pulse state */ athread *th; /* Diffuser position monitoring thread */ + volatile int th_en; /* Enable updating diffuser possition */ volatile int th_term; /* nz to terminate thread */ volatile int th_termed; /* nz when thread terminated */ int dpos; /* Diffuser position, 0 = display, 1 = ambient */ diff --git a/spectro/i1disp.c b/spectro/i1disp.c index b9da1e1..cb5472b 100644 --- a/spectro/i1disp.c +++ b/spectro/i1disp.c @@ -7,7 +7,7 @@ * Author: Graeme W. Gill * Date: 18/10/2006 * - * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2006 - 2014, Graeme W. Gill * All rights reserved. * * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- @@ -368,7 +368,7 @@ i1disp_rdreg_float( if ((ev = i1disp_rdreg_word(p, &val, addr)) != inst_ok) return ev; - if (ev == 0xffffffff) { + if (val == 0xffffffff) { return I1DISP_FLOAT_NOT_SET; } @@ -1233,13 +1233,8 @@ i1disp_take_XYZ_measurement( if ((ev = i1d1_take_measurement(p, 0, rgb)) != inst_ok) return ev; } else { /* i1 disp 2 or ColorMunki Smile */ - int refreshm = 0; /* Assume non-refresh mode */ - if (p->refrmode && !IMODETST(p->mode, inst_mode_emis_ambient)) { - refreshm = 1; - } - - if ((ev = i1d2_take_measurement(p, refreshm, rgb)) != inst_ok) + if ((ev = i1d2_take_measurement(p, p->refrmode, rgb)) != inst_ok) return ev; } @@ -1703,9 +1698,9 @@ i1disp_compute_factors( if (p->reg90_W == 0xffffffff) return i1disp_interp_code((inst *)p, I1DISP_BAD_CRT_CALIBRATION); - /* Compute ambient matrix */ + /* Compute ambient matrix for Lux */ for (i = 0; i < 9; i++) - p->amb[i] = p->reg144_F[i % 3] * 0.5 * (p->reg4_F[i] + p->reg54_F[i]); + p->amb[i] = 3.141592654 * p->reg144_F[i % 3] * 0.5 * (p->reg4_F[i] + p->reg54_F[i]); /* Integration clock frequency */ p->iclk_freq = 1.0/(p->reg40_S * 1e-9); @@ -1746,8 +1741,8 @@ i1disp_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { a1logd(p->log, 2, "i1disp: About to init coms\n"); if (p->icom->port_type(p->icom) != icomt_usb) { - a1logd(p->log, 1, "i1disp_init_coms: coms is not the right type!\n"); - return i1disp_interp_code((inst *)p, I1DISP_UNKNOWN_MODEL); + a1logd(p->log, 1, "i1disp_init_coms: wrong communications type for device!\n"); + return inst_coms_fail; } /* Set config, interface, write end point, read end point */ @@ -1904,6 +1899,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ val->sp.spec_n = 0; val->duration = 0.0; + if (user_trig) return inst_user_trig; return rv; @@ -1927,8 +1923,8 @@ double mtx[3][3] 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"); - inst_wrong_setup; + a1loge(p->log, 1, "spyd2: can't set col_cor_mat over non-base display type\n"); + return inst_wrong_setup; } icmCpy3x3(p->ccmat, mtx); } @@ -2029,8 +2025,8 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ } else { /* Eye-One Display 2 */ if ((*calt & inst_calt_ref_freq) && p->refrmode != 0) { - if (*calc != inst_calc_emis_white) { - *calc = inst_calc_emis_white; + if (*calc != inst_calc_emis_80pc) { + *calc = inst_calc_emis_80pc; return inst_cal_setup; } @@ -2243,7 +2239,8 @@ inst3_capability *pcap3) { | inst_mode_emis_norefresh_ovd ; - cap2 |= inst2_refresh_rate + cap2 |= inst2_get_refresh_rate + | inst2_set_refresh_rate | inst2_emis_refr_meas ; } @@ -2331,7 +2328,7 @@ inst_disptypesel smile_disptypesel[3] = { { inst_dtflags_default, /* flags */ 1, /* cbix */ - "f", /* sel */ + "fl", /* sel */ "LCD with CCFL backlight", /* desc */ 0, /* refr */ 1 /* ix */ @@ -2580,7 +2577,7 @@ static void dump_bytes(a1log *log, char *pfx, unsigned char *buf, int base, int bp += sprintf(bp,"."); } bp += sprintf(bp,"\n"); - a1logd(log,0,oline); + a1logd(log,0,"%s",oline); bp = oline; } } diff --git a/spectro/i1pro.c b/spectro/i1pro.c index 99dd61b..2cac518 100644 --- a/spectro/i1pro.c +++ b/spectro/i1pro.c @@ -7,7 +7,7 @@ * Author: Graeme W. Gill * Date: 24/11/2006 * - * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2006 - 2014, Graeme W. Gill * All rights reserved. * * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- @@ -100,8 +100,8 @@ i1pro_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { a1logd(p->log, 2, "i1pro_init_coms: called\n"); if (p->icom->port_type(p->icom) != icomt_usb) { - a1logd(p->log, 1, "i1pro_init_coms: wrong sort of coms!\n"); - return i1pro_interp_code(p, I1PRO_UNKNOWN_MODEL); + a1logd(p->log, 1, "i1pro_init_coms: wrong communications type for device!\n"); + return inst_coms_fail; } a1logd(p->log, 2, "i1pro_init_coms: about to init USB\n"); @@ -168,8 +168,10 @@ i1pro_determine_capabilities(i1pro *p) { if (p->m != NULL) { i1proimp *m = (i1proimp *)p->m; i1pro_state *s = &m->ms[m->mmode]; - if (s->emiss) + if (s->emiss) { + p->cap2 |= inst2_meas_disp_update; p->cap2 |= inst2_emis_refr_meas; + } } p->cap3 = inst3_none; @@ -273,6 +275,25 @@ double *ref_rate) { rv = i1pro_imp_meas_refrate(p, ref_rate); + + return i1pro_interp_code(p, rv); +} + +/* Read the display update delay */ +static inst_code +i1pro_meas_delay( +inst *pp, +int *msecdelay) { + i1pro *p = (i1pro *)pp; + i1pro_code rv; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + rv = i1pro_imp_meas_delay(p, msecdelay); + return i1pro_interp_code(p, rv); } @@ -417,6 +438,8 @@ i1pro_interp_error(inst *pp, i1pro_code ec) { return "No ambient found before first flash"; case I1PRO_RD_NOREFR_FOUND: return "No refresh rate detected or failed to measure it"; + case I1PRO_RD_NOTRANS_FOUND: + return "No delay calibration transition found"; case I1PRO_INT_NO_COMS: return "Communications hasn't been established"; @@ -538,6 +561,7 @@ i1pro_interp_code(i1pro *p, i1pro_code ec) { case I1PRO_RD_NOFLASHES: case I1PRO_RD_NOAMBB4FLASHES: case I1PRO_RD_NOREFR_FOUND: + case I1PRO_RD_NOTRANS_FOUND: return inst_misread | ec; case I1PRO_RD_NEEDS_CAL: @@ -790,6 +814,7 @@ extern i1pro *new_i1pro(icoms *icom, instType itype) { p->read_refrate = i1pro_read_refrate; p->get_n_a_cals = i1pro_get_n_a_cals; p->calibrate = i1pro_calibrate; + p->meas_delay = i1pro_meas_delay; p->interp_error = i1pro_interp_error; p->del = i1pro_del; diff --git a/spectro/i1pro_imp.c b/spectro/i1pro_imp.c index 2211bd4..b6d6747 100644 --- a/spectro/i1pro_imp.c +++ b/spectro/i1pro_imp.c @@ -7,7 +7,7 @@ * Author: Graeme W. Gill * Date: 24/11/2006 * - * Copyright 2006 - 2013 Graeme W. Gill + * Copyright 2006 - 2014 Graeme W. Gill * All rights reserved. * * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- @@ -100,21 +100,25 @@ #undef ENABLE_WRITE /* [Und] Enable writing of calibration and log data to the EEProm */ #define ENABLE_NONVCAL /* [Def] Enable saving calibration state between program runs in a file */ #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 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 15.0 /* [15] Maximum scan time 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 */ #define SINGLE_READ /* [Def] Use a single USB read for scan to eliminate latency issues. */ #define HIGH_RES /* [Def] Enable high resolution spectral mode code. Dissable */ /* to break dependency on rspl library. */ +# undef FAST_HIGH_RES_SETUP /* Slightly better accuracy ? */ /* Debug [Und] */ #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 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" */ @@ -124,6 +128,7 @@ #undef IGNORE_WHITE_INCONS /* Ignore define reference reading inconsistency */ #undef HIGH_RES_DEBUG #undef HIGH_RES_PLOT +#undef ANALIZE_EXISTING /* Analize the manufacturers existing filter shape */ #undef PLOT_BLACK_SUBTRACT /* Plot temperature corrected black subtraction */ #undef FAKE_AMBIENT /* Fake the ambient mode for a Rev A */ @@ -143,18 +148,18 @@ #define ADARKINT_MAX2 4.0 /* Max cal time for adaptive dark cal Rev E or no high gain */ #define EMIS_SCALE_FACTOR 1.0 /* Emission mode scale factor */ -#define AMB_SCALE_FACTOR (1.0/3.141592654) /* Ambient mode scale factor - convert */ - /* from Lux to Lux/PI */ - /* These factors get the same behaviour as the GMB drivers. */ +#define AMB_SCALE_FACTOR 1.0 /* Ambient mode scale factor for Lux */ +//#define AMB_SCALE_FACTOR (1.0/3.141592654) /* Ambient mode scale factor - convert */ +// /* from Lux to Lux/PI */ +// /* These factors get the same behaviour as the GMB drivers. */ #define NSEN_MAX 140 /* Maximum nsen value we can cope with */ /* High res mode settings */ -#define HIGHRES_SHORT 350 -#define HIGHRES_LONG 740 +#define HIGHRES_SHORT 370.0 /* i1pro2 uses more of the CCD, */ +#define HIGHRES_LONG 730.0 /* leaving less scope for extenion */ #define HIGHRES_WIDTH (10.0/3.0) /* (The 3.3333 spacing and lanczos2 seems a good combination) */ #define HIGHRES_REF_MIN 375.0 /* Too much stray light below this in reflective mode */ - /* (i1pro2 could go lower with different correction) */ #include "i1pro.h" #include "i1pro_imp.h" @@ -162,6 +167,8 @@ /* - - - - - - - - - - - - - - - - - - */ #define LAMP_OFF_TIME 1500 /* msec to make sure lamp is dark for dark measurement */ #define PATCH_CONS_THR 0.1 /* Dark measurement consistency threshold */ + +#define USE_RD_SYNC /* Use mutex syncronisation, else depend on TRIG_DELAY */ #define TRIG_DELAY 10 /* Measure trigger delay to allow pending read, msec */ /* - - - - - - - - - - - - - - - - - - */ @@ -260,6 +267,58 @@ void plot_wav_2(i1proimp *m, int hires, double *data1, double *data2) { /* ============================================================ */ +/* Return a linear interpolated spectral value. Clip to ends */ +static double wav_lerp(i1proimp *m, int hires, double *ary, double wl) { + int jj; + double wl0, wl1, bl; + double rv; + + jj = (int)floor(XSPECT_DIX(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], wl)); + if (jj < 0) + jj = 0; + else if (jj > (m->nwav[hires]-2)) + jj = m->nwav[hires]-2; + + wl0 = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], jj); + wl1 = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], jj+1); + + bl = (wl - wl0)/(wl1 - wl0); + if (bl < 0.0) + bl = 0; + else if (bl > 1.0) + bl = 1.0; + + rv = (1.0 - bl) * ary[jj] + bl * ary[jj+1]; + + return rv; +} + +/* Same as above, but return cv value on clip */ +static double wav_lerp_cv(i1proimp *m, int hires, double *ary, double wl, double cv) { + int jj; + double wl0, wl1, bl; + double rv; + + jj = (int)floor(XSPECT_DIX(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], wl)); + if (jj < 0) + jj = 0; + else if (jj > (m->nwav[hires]-2)) + jj = m->nwav[hires]-2; + + wl0 = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], jj); + wl1 = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], jj+1); + + bl = (wl - wl0)/(wl1 - wl0); + if (bl < 0.0 || bl > 1.0) + return cv; + + rv = (1.0 - bl) * ary[jj] + bl * ary[jj+1]; + + return rv; +} + +/* ============================================================ */ + /* Implementation struct */ /* Add an implementation structure */ @@ -316,7 +375,8 @@ void del_i1proimp(i1pro *p) { a1logd(p->log,5,"i1pro switch thread termination failed\n"); } m->th->del(m->th); - usb_uninit_cancel(&m->cancelt); /* Don't need cancel token now */ + 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"); @@ -403,7 +463,8 @@ i1pro_code i1pro_imp_init(i1pro *p) { if ((ev = i1pro_reset(p, 0x1f)) != I1PRO_OK) return ev; - usb_init_cancel(&m->cancelt); /* Init cancel token */ + usb_init_cancel(&m->sw_cancel); /* Init switch cancel token */ + usb_init_cancel(&m->rd_sync); /* Init reading sync token */ #ifdef USE_THREAD /* Setup the switch monitoring thread */ @@ -1246,7 +1307,7 @@ i1pro_code i1pro_imp_set_mode( ) { i1proimp *m = (i1proimp *)p->m; - a1logd(p->log,2,"i1pro_imp_set_mode called with %d\n",mmode); + a1logd(p->log,2,"i1pro_imp_set_mode called with mode no %d and mask 0x%x\n",mmode,m); switch(mmode) { case i1p_refl_spot: case i1p_refl_scan: @@ -1266,6 +1327,15 @@ i1pro_code i1pro_imp_set_mode( return I1PRO_INT_ILLEGALMODE; } m->spec_en = (mode & inst_mode_spectral) != 0; + + if ((mode & inst_mode_highres) != 0) { + i1pro_code rv; + if ((rv = i1pro_set_highres(p)) != I1PRO_OK) + return rv; + } else { + i1pro_set_stdres(p); /* Ignore any error */ + } + m->uv_en = 0; if (mmode == i1p_refl_spot @@ -1356,6 +1426,19 @@ i1pro_code i1pro_imp_get_n_a_cals(i1pro *p, inst_cal_type *pn_cals, inst_cal_typ a_cals |= inst_calt_emis_int_time; } + /* Special case high res. emissive cal fine calibration, */ + /* needs reflective cal. */ + /* Hmmm. Should we do this every time for emission, in case */ + /* we switch to hires mode ??? */ + if ((cs->emiss || cs->trans) /* We're in an emissive mode */ + && m->hr_inited /* and hi-res has been setup */ + && (!m->emis_hr_cal || (n_cals & inst_calt_em_dark)) /* and the emis cal hasn't been */ + /* fine tuned or we will be doing a dark cal */ + && p->itype != instI1Monitor) { /* i1Monitor doesn't have reflective cal capability */ + n_cals |= inst_calt_ref_white; /* Need a reflective white calibration */ + a_cals |= inst_calt_ref_white; + } + if (pn_cals != NULL) *pn_cals = n_cals; @@ -1380,7 +1463,7 @@ i1pro_code i1pro_imp_calibrate( i1proimp *m = (i1proimp *)p->m; int mmode = m->mmode; i1pro_state *cs = &m->ms[m->mmode]; - int sx1, sx2, sx; + int sx1, sx2, sx3, sx; time_t cdate = time(NULL); int nummeas = 0; int ltocmode = 0; /* 1 = Lamp turn on compensation mode */ @@ -1403,7 +1486,7 @@ i1pro_code i1pro_imp_calibrate( else if (*calt == inst_calt_available) *calt = available & inst_calt_n_dfrble_mask; - a1logd(p->log,4,"i1pro_imp_calibrate: doing calt 0x%x\n",calt); + a1logd(p->log,4,"i1pro_imp_calibrate: doing calt 0x%x\n",*calt); if ((*calt & inst_calt_n_dfrble_mask) == 0) /* Nothing todo */ return I1PRO_OK; @@ -1411,17 +1494,26 @@ i1pro_code i1pro_imp_calibrate( /* See if it's a calibration we understand */ if (*calt & ~available & inst_calt_all_mask) { + a1logd(p->log,4,"i1pro_imp_calibrate: unsupported, calt 0x%x, available 0x%x\n",*calt,available); return I1PRO_UNSUPPORTED; } if (*calt & inst_calt_ap_flag) { - sx1 = 0; sx2 = i1p_no_modes; /* Go through all the modes */ + sx1 = 0; sx2 = sx3 = i1p_no_modes; /* Go through all the modes */ } else { - sx1 = m->mmode; sx2 = sx1 + 1; /* Just current mode */ + /* Special case - doing reflective cal. to fix emiss hires */ + if ((cs->emiss || cs->trans) /* We're in an emissive mode */ + && (*calt & inst_calt_ref_white)) { /* but we're asked for a ref white cal */ + sx1 = m->mmode; sx2 = sx1 + 1; /* Just current mode */ + sx3 = i1p_refl_spot; /* no extra mode */ + } else { + sx1 = m->mmode; sx2 = sx1 + 1; /* Just current mode */ + sx3 = i1p_no_modes; /* no extra mode */ + } } /* Go through the modes we are going to cover */ - for (sx = sx1; sx < sx2; sx++) { + for (sx = sx1; sx < sx2; (++sx >= sx2 && sx3 != i1p_no_modes) ? sx = sx3, sx2 = sx+1, sx3 = i1p_no_modes : 0) { i1pro_state *s = &m->ms[sx]; m->mmode = sx; /* A lot of functions we call rely on this */ @@ -1459,15 +1551,15 @@ i1pro_code i1pro_imp_calibrate( free_dvector(wlraw, -1, m->nraw-1); - /* Compute normal res. reflective wavelength corrected filters */ - if ((ev = i1pro2_compute_wav_filters(p, 1)) != I1PRO_OK) { - a1logd(p->log,2,"i1pro2_compute_wav_filters() failed\n"); + /* Compute normal res. emissive/transmissive wavelength corrected filters */ + if ((ev = i1pro_compute_wav_filters(p, 0, 0)) != I1PRO_OK) { + a1logd(p->log,2,"i1pro_compute_wav_filters() failed\n"); return ev; } - /* Compute normal res. emissive/transmissive wavelength corrected filters */ - if ((ev = i1pro2_compute_wav_filters(p, 0)) != I1PRO_OK) { - a1logd(p->log,2,"i1pro2_compute_wav_filters() failed\n"); + /* Compute normal res. reflective wavelength corrected filters */ + if ((ev = i1pro_compute_wav_filters(p, 0, 1)) != I1PRO_OK) { + a1logd(p->log,2,"i1pro_compute_wav_filters() failed\n"); return ev; } @@ -2112,13 +2204,28 @@ i1pro_code i1pro_imp_calibrate( return ev; } /* Compute a calibration factor given the reading of the white reference. */ - i1pro_compute_white_cal(p, s->cal_factor[0], m->white_ref[0], s->cal_factor[0], - s->cal_factor[1], m->white_ref[1], s->cal_factor[1]); + ev = i1pro_compute_white_cal(p, + 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) { + m->mmode = mmode; /* Restore actual mode */ + return ev; + } - } else { + } else { /* transmissive */ /* Compute a calibration factor given the reading of the white reference. */ - m->transwarn |= i1pro_compute_white_cal(p, s->cal_factor[0], NULL, s->cal_factor[0], - s->cal_factor[1], NULL, s->cal_factor[1]); + 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) { + m->transwarn |= 1; + ev = I1PRO_OK; + } + if (ev != I1PRO_OK) { + m->mmode = mmode; /* Restore actual mode */ + return ev; + } } s->cal_valid = 1; s->cfdate = cdate; @@ -2195,8 +2302,16 @@ i1pro_code i1pro_imp_calibrate( } /* Look at next mode */ m->mmode = mmode; /* Restore actual mode */ - /* Make sure there's the right condition for any remaining calibrations */ - if (*calt & inst_calt_wavelength) { /* Wavelength calibration */ + /* Make sure there's the right condition for any remaining calibrations. */ + /* Do ref_white first in case we are doing a high res fine tune. */ + + if (*calt & (inst_calt_ref_dark | inst_calt_ref_white)) { + sprintf(id, "Serial no. %d",m->serno); + if (*calc != inst_calc_man_ref_white) { + *calc = inst_calc_man_ref_white; /* Calibrate using white tile */ + return I1PRO_CAL_SETUP; + } + } else if (*calt & inst_calt_wavelength) { /* Wavelength calibration */ if (cs->emiss && cs->ambient) { id[0] = '\000'; if (*calc != inst_calc_man_am_dark) { @@ -2210,12 +2325,6 @@ i1pro_code i1pro_imp_calibrate( return I1PRO_CAL_SETUP; } } - } else if (*calt & (inst_calt_ref_dark | inst_calt_ref_white)) { - sprintf(id, "Serial no. %d",m->serno); - if (*calc != inst_calc_man_ref_white) { - *calc = inst_calc_man_ref_white; /* Calibrate using white tile */ - return I1PRO_CAL_SETUP; - } } else if (*calt & inst_calt_em_dark) { /* Emissive Dark calib */ id[0] = '\000'; if (*calc != inst_calc_man_em_dark) { @@ -2286,6 +2395,154 @@ int icoms2i1pro_err(int se) { } /* - - - - - - - - - - - - - - - - */ +/* 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 0.6 seconds. */ +/* inst_misread will be returned on failure to find a transition to black. */ +#define NDMXTIME 0.7 /* Maximum time to take */ +#define NDSAMPS 500 /* Debug samples */ + +typedef struct { + double sec; + double rgb[3]; + double tot; +} i1rgbdsamp; + +i1pro_code i1pro_imp_meas_delay( +i1pro *p, +int *msecdelay) { /* Return the number of msec */ + i1pro_code ev = I1PRO_OK; + i1proimp *m = (i1proimp *)p->m; + i1pro_state *s = &m->ms[m->mmode]; + int i, j, k, mm; + 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; + i1rgbdsamp *samp; + double stot, etot, del, thr; + double etime; + int isdeb; + + /* Read the samples */ + inttime = m->min_int_time; + nummeas = (int)(NDMXTIME/inttime + 0.5); + multimeas = dmatrix(0, nummeas-1, -1, m->nwav[m->highres]-1); + if ((samp = (i1rgbdsamp *)calloc(sizeof(i1rgbdsamp), nummeas)) == NULL) { + a1logd(p->log, 1, "i1pro_meas_delay: malloc failed\n"); + return I1PRO_INT_MALLOC; + } + +//printf("~1 %d samples at %f int\n",nummeas,inttime); + 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; + } + + /* Convert the samples to RGB */ + for (i = 0; i < nummeas; i++) { + samp[i].sec = i * inttime; + 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); + +//printf("~1 multimeas %d %d = %f\n",i, j, multimeas[i][j]); + for (k = 0; k < 3; k++) { + double tt = (double)(wl - rgbw[k]); + tt = (50.0 - fabs(tt))/50.0; + if (tt < 0.0) + tt = 0.0; + samp[i].rgb[k] += sqrt(tt) * multimeas[i][j]; + } + } + samp[i].tot = samp[i].rgb[0] + samp[i].rgb[1] + samp[i].rgb[2]; + } + 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 + + /* Over the first 100msec, locate the maximum value */ + etime = samp[nummeas-1].sec; + stot = -1e9; + for (i = 0; i < nummeas; i++) { + if (samp[i].tot > stot) + stot = samp[i].tot; + if (samp[i].sec > 0.1) + break; + } + + /* Over the last 100msec, locate the maximum value */ + etime = samp[nummeas-1].sec; + etot = -1e9; + for (i = nummeas-1; i >= 0; i--) { + if (samp[i].tot > etot) + etot = samp[i].tot; + if ((etime - samp[i].sec) > 0.1) + break; + } + + del = stot - etot; + thr = etot + 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 + + /* 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"); + return I1PRO_RD_NOTRANS_FOUND; + } + + /* Locate the time at which the values are above the end values */ + for (i = nummeas-1; i >= 0; 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); + +#ifdef PLOT_UPDELAY + a1logd(p->log, 0, "i1pro_meas_delay: returning %d msec\n",*msecdelay); +#endif + free(samp); + + return I1PRO_OK; +} +#undef NDSAMPS +#undef NDMXTIME + +/* - - - - - - - - - - - - - - - - */ /* Measure a patch or strip in the current mode. */ /* To try and speed up the reaction time between */ /* triggering a scan measurement and being able to */ @@ -2799,10 +3056,11 @@ i1pro_code i1pro_imp_meas_refrate( tt = (40.0 - fabs(tt))/40.0; if (tt < 0.0) tt = 0.0; - samp[i].rgb[k] += tt * multimeas[i][j]; + samp[i].rgb[k] += sqrt(tt) * multimeas[i][j]; } } } + 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); @@ -3325,6 +3583,7 @@ i1pro_code i1pro_imp_meas_refrate( if (brange > 0.05) { a1logd(p->log, 3, "Readings are too inconsistent (brange %.1f%%) - should retry ?\n",brange * 100.0); } else { + if (ref_rate != NULL) *ref_rate = brate; @@ -3399,7 +3658,7 @@ i1pro_code i1pro_restore_refspot_cal(i1pro *p) { } else s->gainmode = 0; - /* Get the calibration integrattion time */ + /* Get the calibration integration time */ if ((dp = m->data->get_doubles(m->data, &count, key_inttime + offst)) == NULL || count < 1) { a1logd(p->log,2,"Failed to read calibration integration time from EEPRom\n"); return I1PRO_OK; @@ -3470,8 +3729,12 @@ i1pro_code i1pro_restore_refspot_cal(i1pro *p) { return I1PRO_OK; } /* Compute a calibration factor given the reading of the white reference. */ - i1pro_compute_white_cal(p, s->cal_factor[0], m->white_ref[0], s->cal_factor[0], - s->cal_factor[1], m->white_ref[1], s->cal_factor[1]); + ev = i1pro_compute_white_cal(p, s->cal_factor[0], m->white_ref[0], s->cal_factor[0], + s->cal_factor[1], m->white_ref[1], s->cal_factor[1], 1); + if (ev != I1PRO_RD_TRANSWHITEWARN && ev != I1PRO_OK) { + a1logd(p->log,2,"i1pro_compute_white_cal failed to convert EEProm data to calibration\n"); + return I1PRO_OK; + } /* We've sucessfully restored the calibration */ s->cal_valid = 1; @@ -4300,6 +4563,7 @@ i1pro_code i1pro_dark_measure_2( /* 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); return ev; } @@ -5459,6 +5723,7 @@ i1pro_trigger_one_measure( } /* Trigger a measurement */ + usb_reinit_cancel(&m->rd_sync); /* Prepare to sync rd and trigger */ if (p->itype != instI1Pro2) { if ((ev = i1pro_triggermeasure(p, TRIG_DELAY)) != I1PRO_OK) return ev; @@ -6819,14 +7084,21 @@ void i1pro_sub_absraw( a1logd(p->log,2,"Black shielded value = %f, Reading shielded value = %f\n",sub[-1], avgscell); /* Compute the adjusted black */ + /* [ Unlike the ColorMunki, using the black drift comp. for reflective */ + /* seems to be OK and even beneficial. ] */ for (j = 0; j < m->nraw; j++) { -#ifdef NEVER +#ifdef ENABLE_BKDRIFTC +# ifdef HEURISTIC_BKDRIFTC + /* heuristic scaled correction */ + asub[j] = zero - (zero - sub[j]) * (zero - avgscell)/(zero - sub[-1]); +# else /* simple additive correction */ -# pragma message("######### i1pro2 Simple shielded cell temperature correction! ########") +# pragma message("######### i1pro2 Simple shielded cell temperature correction! ########") asub[j] = sub[j] + avgscell - sub[-1]; +# endif #else - /* heuristic scaled correction */ - asub[j] = zero - (zero - sub[j]) * (zero - avgscell)/(zero - sub[-1]); +# pragma message("######### i1pro2 No shielded cell temperature correction! ########") + asub[j] = sub[j]; /* Just use the calibration dark data */ #endif } @@ -7448,9 +7720,11 @@ i1pro_code i1pro2_match_wl_meas(i1pro *p, double *pled_off, double *wlraw) { return ev; } -/* Compute standard res. downsampling filters for the given mode */ -/* given the current wl_led_off, and set them as current. */ -i1pro_code i1pro2_compute_wav_filters(i1pro *p, int refl) { +/* Compute standard/high res. downsampling filters for the given mode */ +/* given the current wl_led_off, and set them as current, */ +/* using triangular filters of the lagrange interpolation of the */ +/* CCD values. */ +i1pro_code i1pro_compute_wav_filters(i1pro *p, int hr, int refl) { i1proimp *m = (i1proimp *)p->m; i1pro_state *s = &m->ms[m->mmode]; i1pro_code ev = I1PRO_OK; @@ -7461,28 +7735,28 @@ i1pro_code i1pro2_compute_wav_filters(i1pro *p, int refl) { double trh, trx; /* Triangle height and triangle equation x weighting */ int i, j, k; - a1logd(p->log,2,"i1pro2_compute_wav_filters called with correction %f raw\n",s->wl_led_off - m->wl_led_ref_off); + a1logd(p->log,2,"i1pro_compute_wav_filters called with correction %f raw\n",s->wl_led_off - m->wl_led_ref_off); - twidth = (m->wl_long[0] - m->wl_short[0])/(m->nwav[0] - 1.0); /* Filter width */ + twidth = (m->wl_long[hr] - m->wl_short[hr])/(m->nwav[hr] - 1.0); /* Filter width */ trh = 1.0/twidth; /* Triangle height */ trx = trh/twidth; /* Triangle equation x weighting */ /* Allocate separate space for the calibrated versions, so that the */ /* original eeprom values are preserved */ - if (m->mtx_c[0][refl].index == NULL) { + if (m->mtx_c[hr][refl].index == NULL) { - if ((m->mtx_c[0][refl].index = (int *)calloc(m->nwav[0], sizeof(int))) == NULL) { + if ((m->mtx_c[hr][refl].index = (int *)calloc(m->nwav[hr], sizeof(int))) == NULL) { a1logd(p->log,1,"i1pro: malloc ndex1 failed!\n"); return I1PRO_INT_MALLOC; } - if ((m->mtx_c[0][refl].nocoef = (int *)calloc(m->nwav[0], sizeof(int))) == NULL) { + if ((m->mtx_c[hr][refl].nocoef = (int *)calloc(m->nwav[hr], sizeof(int))) == NULL) { a1logd(p->log,1,"i1pro: malloc nocoef failed!\n"); return I1PRO_INT_MALLOC; } - if ((m->mtx_c[0][refl].coef = (double *)calloc(16 * m->nwav[0], sizeof(double))) + if ((m->mtx_c[hr][refl].coef = (double *)calloc(16 * m->nwav[hr], sizeof(double))) == NULL) { a1logd(p->log,1,"i1pro: malloc coef failed!\n"); return I1PRO_INT_MALLOC; @@ -7490,9 +7764,9 @@ i1pro_code i1pro2_compute_wav_filters(i1pro *p, int refl) { } /* For each output wavelength */ - wlcop = m->mtx_c[0][refl].coef; - for (wlix = 0; wlix < m->nwav[0]; wlix++) { - double owl = wlix/(m->nwav[0]-1.0) * (m->wl_long[0] - m->wl_short[0]) + m->wl_short[0]; + wlcop = m->mtx_c[hr][refl].coef; + for (wlix = 0; wlix < m->nwav[hr]; wlix++) { + double owl = wlix/(m->nwav[hr]-1.0) * (m->wl_long[hr] - m->wl_short[hr]) + m->wl_short[hr]; int lip; /* Lagrange interpolation position */ // printf("Generating filter for %.1f nm width %.1f nm\n",owl, twidth); @@ -7503,11 +7777,14 @@ i1pro_code i1pro2_compute_wav_filters(i1pro *p, int refl) { /* Do a dumb search from high to low nm */ for (six = 0; six < m->nraw; six++) { +//printf("~1 (raw2wav (six %d) %f <? (owl %f + twidth %f) %f\n",six,i1pro_raw2wav(p, refl, (double)six),owl,twidth,owl + twidth); + if (i1pro_raw2wav(p, refl, (double)six) < (owl + twidth)) break; } + if (six < 2 || six >= m->nraw) { - a1loge(p->log,1,"i1pro: compute_wav_filters() six %d out of raw range to cover output filter %.1f nm width %.1f nm\n",six, owl, twidth); + a1loge(p->log,1,"i1pro: compute_wav_filters() six %d, exceeds raw range to cover output filter %.1f nm width %.1f nm\n",six, owl, twidth); return I1PRO_INT_ASSERT; } eix = six; @@ -7518,30 +7795,30 @@ i1pro_code i1pro2_compute_wav_filters(i1pro *p, int refl) { break; } if (eix > (m->nraw - 2) ) { - a1loge(p->log,1,"i1pro: compute_wav_filters() eix %d out of raw range to cover output filter %.1f nm width %.1f nm\n",eix, owl, twidth); + a1loge(p->log,1,"i1pro: compute_wav_filters() eix %d, exceeds raw range to cover output filter %.1f nm width %.1f nm\n",eix, owl, twidth); return I1PRO_INT_ASSERT; } - eix += 2; + eix += 2; /* Outside */ // for (j = six; j < eix; j++) // printf("Using raw %d @ %.1f nm\n",j, i1pro_raw2wav(p, refl, (double)j)); /* Set start index for this wavelength */ - m->mtx_c[0][refl].index[wlix] = six; + m->mtx_c[hr][refl].index[wlix] = six; /* Set number of filter coefficients */ - m->mtx_c[0][refl].nocoef[wlix] = eix - six; + m->mtx_c[hr][refl].nocoef[wlix] = eix - six; - if (m->mtx_c[0][refl].nocoef[wlix] > 16) { - a1loge(p->log,1,"i1pro: compute_wav_filters() too many filter %d\n",m->mtx_c[0][refl].nocoef[wlix]); + if (m->mtx_c[hr][refl].nocoef[wlix] > 16) { + a1loge(p->log,1,"i1pro: compute_wav_filters() too many filter %d\n",m->mtx_c[hr][refl].nocoef[wlix]); return I1PRO_INT_ASSERT; } /* Start with zero filter weightings */ - for (i = 0; i < m->mtx_c[0][refl].nocoef[wlix]; i++) + for (i = 0; i < m->mtx_c[hr][refl].nocoef[wlix]; i++) wlcop[i] = 0.0; - /* for each Lagrange interpolation position */ + /* for each Lagrange interpolation position (adjacent CCD locations) */ for (lip = six; (lip + 3) < eix; lip++) { double rwav[4]; /* Relative wavelength of these Lagrange points */ double den[4]; /* Denominator values for points */ @@ -7656,14 +7933,14 @@ i1pro_code i1pro2_compute_wav_filters(i1pro *p, int refl) { } } // printf("~1 Weightings for for %.1f nm are:\n",owl); -// for (i = 0; i < m->mtx_c[0][refl].nocoef[wlix]; i++) +// for (i = 0; i < m->mtx_c[hr][refl].nocoef[wlix]; i++) // printf("~1 comp %d weight %e\n",i,wlcop[i]); - wlcop += m->mtx_c[0][refl].nocoef[wlix]; /* Next group of weightings */ + wlcop += m->mtx_c[hr][refl].nocoef[wlix]; /* Next group of weightings */ } #ifdef DEBUG /* Check against orginal filters */ - { + if (!hr) { int ix1, ix1c; double aerr = 0.0; @@ -7712,7 +7989,7 @@ i1pro_code i1pro2_compute_wav_filters(i1pro *p, int refl) { #endif /* DEBUG */ /* Switch normal res. to use wavelength calibrated version */ - m->mtx[0][refl] = m->mtx_c[0][refl]; + m->mtx[hr][refl] = m->mtx_c[hr][refl]; return ev; } @@ -7721,16 +7998,34 @@ i1pro_code i1pro2_compute_wav_filters(i1pro *p, int refl) { /* =============================================== */ #ifdef HIGH_RES -#undef ANALIZE_EXISTING /* Analize the manufacturers existing filter shape */ +/* + It turns out that using the sharpest possible resampling filter + may make accuracy worse (particularly on the RevE), because it + enhances bumps in the raw response that mightn't be there + after calibrating for the instrument spectral sensitivity. + A better scheme (which we could sythesise using the hi-res + emissive calibration logic) would be to calibrate the raw CCD + values and then resample with possible sharpening. + Another approach would be to sharpen after filtering with + non-sharpening resampling filters. + The bottom line is that it's best to use a gausian hi-res + filter to avoid sharpening in non calibrated spectral space. + */ /* High res congiguration */ /* Pick one of these: */ -#define USE_LANCZOS2 /* [def] Use lanczos2 filter shape */ +#undef USE_TRI_LAGRANGE /* [und] Use normal res filter shape */ +#undef USE_LANCZOS2 /* [und] Use lanczos2 filter shape */ +#undef USE_LANCZOS3 /* [und] Use lanczos3 filter shape */ #undef USE_DECONV /* [und] Use deconvolution curve */ -#undef USE_GAUSSIAN /* [und] Use gaussian filter shape*/ #undef USE_BLACKMAN /* [und] Use Blackman windowed sinc shape */ +#define USE_GAUSSIAN /* [def] Use gaussian filter shape*/ #undef USE_CUBIC /* [und] Use cubic spline filter */ +#define DO_CCDNORM /* [def] Normalise CCD values to original */ +#define DO_CCDNORMAVG /* [und] Normalise averages rather than per CCD bin */ + /* (We relly on fine cal & white cal to fix it) */ + #undef COMPUTE_DISPERSION /* Compute slit & optics dispersion from red laser data */ #ifdef NEVER @@ -7766,7 +8061,7 @@ static void i1pro_debug_plot_mtx_coef(i1pro *p) { free_dvector(xx, -1, m->nraw-1); free_dmatrix(yy, 0, 2, -1, m->nraw-1); } -#endif +#endif /* NEVER */ #ifdef COMPUTE_DISPERSION @@ -7876,7 +8171,7 @@ static double lin_fshape(i1pro_fs *fsh, int n, double x) { /* Generate a sample from a lanczos2 filter shape */ /* wi is the width of the filter */ static double lanczos2(double wi, double x) { - double y; + double y = 0.0; #ifdef USE_DECONV /* For 3.333, created by i1deconv.c */ @@ -7948,11 +8243,21 @@ static double lanczos2(double wi, double x) { x = fabs(1.0 * x/wi); if (x >= 2.0) return 0.0; - if (x < 1e-5) + if (x < 1e-6) return 1.0; y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/2.0)/(DBL_PI * x/2.0); #endif +#ifdef USE_LANCZOS3 + /* lanczos3 */ + x = fabs(1.0 * x/wi); + if (x >= 3.0) + return 0.0; + if (x < 1e-6) + return 1.0; + y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/3.0)/(DBL_PI * x/3.0); +#endif + #ifdef USE_BLACKMAN /* Use Blackman windowed sinc shape */ double xx = x, w; double a0, a1, a2, a3; @@ -8034,16 +8339,126 @@ static int gcc_bug_fix(int i) { } #endif /* APPLE */ +/* Re-create calibration factors for hi-res */ +/* Set emisonly to only recompute emissive factors */ +i1pro_code i1pro_create_hr_calfactors(i1pro *p, int eonly) { + i1proimp *m = (i1proimp *)p->m; + i1pro_code ev = I1PRO_OK; + int i, j; + + /* Generate high res. per mode calibration factors. */ + if (m->hr_inited) { + + for (i = 0; i < i1p_no_modes; i++) { + i1pro_state *s = &m->ms[i]; + + if (s->cal_factor[1] == NULL) + s->cal_factor[1] = dvectorz(0, m->nwav[1]-1); + + switch(i) { + case i1p_refl_spot: + case i1p_refl_scan: + if (eonly) + continue; + if (s->cal_valid) { + /* (Using cal_factor[] as temp. for i1pro_absraw_to_abswav()) */ +#ifdef NEVER + printf("~1 regenerating calibration for reflection\n"); + printf("~1 raw white data:\n"); + plot_raw(s->white_data); +#endif /* NEVER */ + i1pro_absraw_to_abswav(p, 0, s->reflective, 1, &s->cal_factor[0], &s->white_data); +#ifdef NEVER + printf("~1 Std res intmd. cal_factor:\n"); + plot_wav(m, 0, s->cal_factor[0]); +#endif /* NEVER */ + i1pro_absraw_to_abswav(p, 1, s->reflective, 1, &s->cal_factor[1], &s->white_data); +#ifdef NEVER + printf("~1 High intmd. cal_factor:\n"); + plot_wav(m, 1, s->cal_factor[1]); + printf("~1 Std res white ref:\n"); + plot_wav(m, 0, m->white_ref[0]); + printf("~1 High res white ref:\n"); + plot_wav(m, 1, m->white_ref[1]); +#endif /* NEVER */ + ev = i1pro_compute_white_cal(p, + 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) { + return ev; + } +#ifdef NEVER + printf("~1 Std res final cal_factor:\n"); + plot_wav(m, 0, s->cal_factor[0]); + printf("~1 High final cal_factor:\n"); + plot_wav(m, 1, s->cal_factor[1]); +#endif /* NEVER */ + } + break; + + case i1p_emiss_spot_na: + case i1p_emiss_spot: + case i1p_emiss_scan: + for (j = 0; j < m->nwav[1]; j++) + s->cal_factor[1][j] = EMIS_SCALE_FACTOR * m->emis_coef[1][j]; + break; + + case i1p_amb_spot: + case i1p_amb_flash: +#ifdef FAKE_AMBIENT + for (j = 0; j < m->nwav[1]; j++) + s->cal_factor[1][j] = EMIS_SCALE_FACTOR * m->emis_coef[1][j]; + s->cal_valid = 1; +#else + + if (m->amb_coef[0] != NULL) { + for (j = 0; j < m->nwav[1]; j++) + s->cal_factor[1][j] = AMB_SCALE_FACTOR * m->emis_coef[1][j] * m->amb_coef[1][j]; + s->cal_valid = 1; + } +#endif + break; + case i1p_trans_spot: + case i1p_trans_scan: + if (eonly) + continue; + if (s->cal_valid) { + /* (Using cal_factor[] as temp. for i1pro_absraw_to_abswav()) */ + i1pro_absraw_to_abswav(p, 0, s->reflective, 1, &s->cal_factor[0], &s->white_data); + 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) { + return ev; + } + } + break; + } + } + } + return ev; +} + +#ifdef SALONEINSTLIB +# define ONEDSTRAYLIGHTUS +#endif + /* Create or re-create high resolution mode references */ i1pro_code i1pro_create_hr(i1pro *p) { i1proimp *m = (i1proimp *)p->m; i1pro_code ev = I1PRO_OK; int refl; + double twidth = HIGHRES_WIDTH; int i, j, k, cx, sx; /* If we don't have any way of converting raw2wav (ie. RevE polinomial equations), */ /* use the orginal filters to figure this out. */ - if (p->itype != instI1Pro2 && m->raw2wav == NULL) { + if (m->raw2wav == NULL +#ifndef ANALIZE_EXISTING + && p->itype != instI1Pro2 +#endif + ) { i1pro_fc coeff[100][16]; /* Existing filter cooefficients */ i1pro_xp xp[101]; /* Crossover points each side of filter */ i1pro_fs fshape[100 * 16]; /* Existing filter shape */ @@ -8101,51 +8516,55 @@ i1pro_code i1pro_create_hr(i1pro *p) { } #endif /* HIGH_RES_PLOT */ +// a1logd(p->log,3,"computing crossover points\n"); /* Compute the crossover points between each filter */ for (i = 0; i < (m->nwav[0]-1); i++) { double den, y1, y2, y3, y4, yn, xn; /* Location of intersection */ + double eps = 1e-6; /* Numerical tollerance */ + double besty = -1e6; /* between filter i and i+1, we want to find the two */ /* raw indexes where the weighting values cross over */ /* Do a brute force search to avoid making assumptions */ - /* about the raw order */ + /* about the raw order. */ for (j = 0; j < (m->mtx_o.nocoef[i]-1); j++) { for (k = 0; k < (m->mtx_o.nocoef[i+1]-1); k++) { -// printf("~1 checking %d, %d: %d = %d, %d = %d\n",j,k, coeff[i][j].ix, coeff[i+1][k].ix, coeff[i][j+1].ix, coeff[i+1][k+1].ix); if (coeff[i][j].ix == coeff[i+1][k].ix - && coeff[i][j+1].ix == coeff[i+1][k+1].ix - && coeff[i][j].we > 0.0 && coeff[i][j+1].we > 0.0 - && coeff[i][k].we > 0.0 && coeff[i][k+1].we > 0.0 - && (( coeff[i][j].we >= coeff[i+1][k].we - && coeff[i][j+1].we <= coeff[i+1][k+1].we) - || ( coeff[i][j].we <= coeff[i+1][k].we - && coeff[i][j+1].we >= coeff[i+1][k+1].we))) { -// printf("~1 got it at %d, %d: %d = %d, %d = %d\n",j,k, coeff[i][j].ix, coeff[i+1][k].ix, coeff[i][j+1].ix, coeff[i+1][k+1].ix); - goto gotit; + && coeff[i][j+1].ix == coeff[i+1][k+1].ix) { + +// a1logd(p->log,3,"got it at %d, %d: %d = %d, %d = %d\n",j,k, coeff[i][j].ix, coeff[i+1][k].ix, coeff[i][j+1].ix, coeff[i+1][k+1].ix); + + /* Compute the intersection of the two line segments */ + y1 = coeff[i][j].we; + y2 = coeff[i][j+1].we; + y3 = coeff[i+1][k].we; + y4 = coeff[i+1][k+1].we; +// a1logd(p->log,3,"y1 %f, y2 %f, y3 %f, y4 %f\n",y1, y2, y3, y4); + den = -y4 + y3 + y2 - y1; + if (fabs(den) < eps) + continue; + yn = (y2 * y3 - y1 * y4)/den; + xn = (y3 - y1)/den; + if (xn < -eps || xn > (1.0 + eps)) + continue; +// a1logd(p->log,3,"den = %f, yn = %f, xn = %f\n",den,yn,xn); + if (yn > besty) { + xp[i+1].wav = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i + 0.5); + xp[i+1].raw = (1.0 - xn) * coeff[i][j].ix + xn * coeff[i][j+1].ix; + xp[i+1].wei = yn; + besty = yn; +// a1logd(p->log,3,"Intersection %d: wav %f, raw %f, wei %f\n",i+1,xp[i+1].wav,xp[i+1].raw,xp[i+1].wei); +// a1logd(p->log,3,"Found new best y %f\n",yn); + } +// a1logd(p->log,3,"\n"); } } } - gotit:; - if (j >= m->mtx_o.nocoef[i]) { /* Assert */ - a1loge(p->log,1,"i1pro: failed to locate crossover between resampling filters\n"); + if (besty < 0.0) { /* Assert */ + a1logw(p->log,"i1pro: failed to locate crossover between resampling filters\n"); return I1PRO_INT_ASSERT; } -// printf("~1 %d: overlap at %d, %d: %f : %f, %f : %f\n",i, j,k, coeff[i][j].we, coeff[i+1][k].we, coeff[i][j+1].we, coeff[i+1][k+1].we); - - /* Compute the intersection of the two line segments */ - y1 = coeff[i][j].we; - y2 = coeff[i][j+1].we; - y3 = coeff[i+1][k].we; - y4 = coeff[i+1][k+1].we; - den = -y4 + y3 + y2 - y1; - yn = (y2 * y3 - y1 * y4)/den; - xn = (y3 - y1)/den; -// printf("~1 den = %f, yn = %f, xn = %f\n",den,yn,xn); - xp[i+1].wav = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i + 0.5); - xp[i+1].raw = (1.0 - xn) * coeff[i][j].ix + xn * coeff[i][j+1].ix; - xp[i+1].wei = yn; -// printf("Intersection %d: wav %f, raw %f, wei %f\n",i+1,xp[i+1].wav,xp[i+1].raw,xp[i+1].wei); -// printf("\n"); +// a1logd(p->log,3,"\n"); } /* Add the two points for the end filters */ @@ -8177,7 +8596,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { } } if (j >= m->mtx_o.nocoef[0]) { /* Assert */ - a1loge(p->log,1,"i1pro: failed to end crossover\n"); + a1loge(p->log,1,"i1pro: failed to find end crossover\n"); return I1PRO_INT_ASSERT; } @@ -8216,7 +8635,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { } } if (j >= m->mtx_o.nocoef[m->nwav[0]-1]) { /* Assert */ - a1loge(p->log,1,"i1pro: failed to end crossover\n"); + a1loge(p->log,1,"i1pro: failed to find end crossover\n"); return I1PRO_INT_ASSERT; } den = -y4 + y3 + y2 - y1; @@ -8277,6 +8696,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { avgdev[0] = 0.0; m->raw2wav->fit_rspl(m->raw2wav, 0, sd, m->nwav[0]+1, glow, ghigh, gres, vlow, vhigh, 0.5, avgdev, NULL); + } #ifdef HIGH_RES_PLOT /* Plot raw to wav lookup */ @@ -8299,99 +8719,98 @@ i1pro_code i1pro_create_hr(i1pro *p) { } printf("CCD bin to wavelength mapping of original filters + rspl:\n"); - do_plot6(xx, yy, y2, NULL, NULL, NULL, NULL, m->nwav+1); + do_plot6(xx, yy, y2, NULL, NULL, NULL, NULL, m->nwav[0]+1); free_dvector(xx, 0, m->nwav[0]+1); free_dvector(yy, 0, m->nwav[0]+1); free_dvector(y2, 0, m->nwav[0]+1); } #endif /* HIGH_RES_PLOT */ - } - } #ifdef ANALIZE_EXISTING - /* Convert each weighting curves values into normalized values and */ - /* accumulate into a single curve. */ - if (!m->hr_inited) { - for (i = 0; i < m->nwav[0]; i++) { - double cwl; /* center wavelegth */ - double weight = 0.0; + /* Convert each weighting curves values into normalized values and */ + /* accumulate into a single curve. */ + if (!m->hr_inited) { + for (i = 0; i < m->nwav[0]; i++) { + double cwl; /* center wavelegth */ + double weight = 0.0; - for (j = 0; j < (m->mtx_o.nocoef[i]); j++) { - double w1, w2, cellw; + for (j = 0; j < (m->mtx_o.nocoef[i]); j++) { + double w1, w2, cellw; - /* Translate CCD cell boundaries index to wavelength */ - w1 = i1pro_raw2wav_uncal(p, (double)coeff[i][j].ix - 0.5); + /* Translate CCD cell boundaries index to wavelength */ + w1 = i1pro_raw2wav_uncal(p, (double)coeff[i][j].ix - 0.5); - w2 = i1pro_raw2wav_uncal(p, (double)coeff[i][j].ix + 0.5); + w2 = i1pro_raw2wav_uncal(p, (double)coeff[i][j].ix + 0.5); - cellw = fabs(w2 - w1); + cellw = fabs(w2 - w1); - cwl = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i); + cwl = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i); - /* Translate CCD index to wavelength */ - fshape[ncp].wl = i1pro_raw2wav_uncal(p, (double)coeff[i][j].ix) - cwl; - fshape[ncp].we = coeff[i][j].we / (0.09 * cellw); - ncp++; + /* Translate CCD index to wavelength */ + fshape[ncp].wl = i1pro_raw2wav_uncal(p, (double)coeff[i][j].ix) - cwl; + fshape[ncp].we = coeff[i][j].we / (0.09 * cellw); + ncp++; + } } - } - /* Now sort by wavelength */ + /* Now sort by wavelength */ #define HEAP_COMPARE(A,B) (A.wl < B.wl) - HEAPSORT(i1pro_fs, fshape, ncp) + HEAPSORT(i1pro_fs, fshape, ncp) #undef HEAP_COMPARE - /* Strip out leading zero's */ - for (i = 0; i < ncp; i++) { - if (fshape[i].we != 0.0) - break; - } - if (i > 1 && i < ncp) { - memmove(&fshape[0], &fshape[i-1], sizeof(i1pro_fs) * (ncp - i + 1)); - ncp = ncp - i + 1; + /* Strip out leading zero's */ for (i = 0; i < ncp; i++) { if (fshape[i].we != 0.0) break; } - } + if (i > 1 && i < ncp) { + memmove(&fshape[0], &fshape[i-1], sizeof(i1pro_fs) * (ncp - i + 1)); + ncp = ncp - i + 1; + for (i = 0; i < ncp; i++) { + if (fshape[i].we != 0.0) + break; + } + } #ifdef HIGH_RES_PLOT - /* Plot the shape of the accumulated curve */ - { - double *x1 = dvectorz(0, ncp-1); - double *y1 = dvectorz(0, ncp-1); + /* Plot the shape of the accumulated curve */ + { + double *x1 = dvectorz(0, ncp-1); + double *y1 = dvectorz(0, ncp-1); - for (i = 0; i < ncp; i++) { - double x; - x1[i] = fshape[i].wl; - y1[i] = fshape[i].we; - } - printf("Accumulated curve:\n"); - do_plot(x1, y1, NULL, NULL, ncp); + for (i = 0; i < ncp; i++) { + double x; + x1[i] = fshape[i].wl; + y1[i] = fshape[i].we; + } + printf("Original accumulated curve:\n"); + do_plot(x1, y1, NULL, NULL, ncp); - free_dvector(x1, 0, ncp-1); - free_dvector(y1, 0, ncp-1); - } + free_dvector(x1, 0, ncp-1); + free_dvector(y1, 0, ncp-1); + } #endif /* HIGH_RES_PLOT */ #ifdef HIGH_RES_DEBUG - /* Check that the orginal filter sums to a constant */ - { - double x, sum; - - for (x = 0.0; x < 10.0; x += 0.2) { - sum = 0; - sum += lin_fshape(fshape, ncp, x - 30.0); - sum += lin_fshape(fshape, ncp, x - 20.0); - sum += lin_fshape(fshape, ncp, x - 10.0); - sum += lin_fshape(fshape, ncp, x - 0.0); - sum += lin_fshape(fshape, ncp, x + 10.0); - sum += lin_fshape(fshape, ncp, x + 20.0); - printf("Offset %f, sum %f\n",x, sum); + /* Check that the orginal filter sums to a constant */ + { + double x, sum; + + for (x = 0.0; x < 10.0; x += 0.2) { + sum = 0; + sum += lin_fshape(fshape, ncp, x - 30.0); + sum += lin_fshape(fshape, ncp, x - 20.0); + sum += lin_fshape(fshape, ncp, x - 10.0); + sum += lin_fshape(fshape, ncp, x - 0.0); + sum += lin_fshape(fshape, ncp, x + 10.0); + sum += lin_fshape(fshape, ncp, x + 20.0); + printf("Offset %f, sum %f\n",x, sum); + } } - } #endif /* HIGH_RES_DEBUG */ - } + } #endif /* ANALIZE_EXISTING */ + } /* End of compute wavelength cal from existing filters */ #ifdef COMPUTE_DISPERSION if (!m->hr_inited) { @@ -8498,7 +8917,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { datao vlow, vhigh; int gres[2]; double avgdev[2]; - int ii; + int ix, ii; co pp; if ((trspl = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) { @@ -8506,6 +8925,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { return I1PRO_INT_NEW_RSPL_FAILED; } + /* For ref, emis, ambient */ for (ii = 0; ii < 3; ii++) { double **ref2, *ref1; double smooth = 1.0; @@ -8513,17 +8933,26 @@ i1pro_code i1pro_create_hr(i1pro *p) { if (ii == 0) { ref1 = m->white_ref[0]; ref2 = &m->white_ref[1]; - smooth = 0.5; +// smooth = 0.5; + smooth = 1.5; } else if (ii == 1) { + /* Don't create high res. from low res., if there is */ + /* a better, calibrated cal read from .cal file */ + if (m->emis_coef[1] != NULL) { + if (m->emis_hr_cal) + continue; + free(m->emis_coef[1]); /* Regenerate it anyway */ + } ref1 = m->emis_coef[0]; ref2 = &m->emis_coef[1]; - smooth = 500.0; /* Hmm. Lagrange may work better ?? */ + m->emis_hr_cal = 0; + smooth = 10.0; } else { if (m->amb_coef[0] == NULL) break; ref1 = m->amb_coef[0]; ref2 = &m->amb_coef[1]; - smooth = 0.2; + smooth = 1.0; } if (ref1 == NULL) @@ -8531,46 +8960,58 @@ i1pro_code i1pro_create_hr(i1pro *p) { vlow[0] = 1e6; vhigh[0] = -1e6; - for (i = 0; i < m->nwav[0]; i++) { + for (ix = i = 0; i < m->nwav[0]; i++) { - sd[i].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i); - sd[i].v[0] = ref1[i]; - sd[i].w = 1.0; + sd[ix].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i); + sd[ix].v[0] = ref1[i]; + sd[ix].w = 1.0; - if (sd[i].v[0] < vlow[0]) - vlow[0] = sd[i].v[0]; - if (sd[i].v[0] > vhigh[0]) - vhigh[0] = sd[i].v[0]; + if (sd[ix].v[0] < vlow[0]) + vlow[0] = sd[ix].v[0]; + if (sd[ix].v[0] > vhigh[0]) + vhigh[0] = sd[ix].v[0]; + ix++; } - if (ii == 1) { /* fudge factors */ + /* 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 */ + /* when we do a reflective calibration, by using the */ + /* smoothness of the lamp as a reference. */ -#ifdef NEVER /* Doesn't help */ - /* Increase boost at short wavelegths */ - if (m->wl_short[1] < 380.0) { - sd[i].p[0] = m->wl_short[1]; - sd[i].v[0] = sd[0].v[0] * (1.0 + (380.0 - m->wl_short[1])/20.0); - sd[i++].w = 0.6; - } -#endif + /* Add inbetween points to restrain dips and peaks in interp. */ + for (i = 0; i < (m->nwav[0]-1); i++) { -#ifdef NEVER /* Doesn't help */ - /* Reduces boost at long wavelegths */ - if (m->wl_long[1] > 730.0) { - sd[i].p[0] = m->wl_long[1]; - sd[i].v[0] = sd[m->nwav[0]-1].v[0] * (1.0 + (m->wl_long[1] - 730.0)/100.0); - sd[i++].w = 0.3; - } -#endif + /* Use linear interp extra points */ + double wt = 0.05; + + sd[ix].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i + 0.3333); + sd[ix].v[0] = (2.0 * ref1[i] + ref1[i+1])/3.0; + sd[ix].w = wt; + + if (sd[ix].v[0] < vlow[0]) + vlow[0] = sd[ix].v[0]; + if (sd[ix].v[0] > vhigh[0]) + vhigh[0] = sd[ix].v[0]; + ix++; + + sd[ix].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i + 0.66667); + sd[ix].v[0] = (ref1[i] + 2.0 * ref1[i+1])/3.0; + sd[ix].w = wt; + + if (sd[ix].v[0] < vlow[0]) + vlow[0] = sd[ix].v[0]; + if (sd[ix].v[0] > vhigh[0]) + vhigh[0] = sd[ix].v[0]; + ix++; } - + glow[0] = m->wl_short[1]; ghigh[0] = m->wl_long[1]; - gres[0] = m->nwav[1]; + gres[0] = 3 * m->nwav[1]; avgdev[0] = 0.0; - trspl->fit_rspl_w(trspl, 0, sd, i, glow, ghigh, gres, vlow, vhigh, smooth, avgdev, NULL); - + trspl->fit_rspl_w(trspl, 0, sd, ix, glow, ghigh, gres, vlow, vhigh, smooth, avgdev, NULL); if ((*ref2 = (double *)calloc(m->nwav[1], sizeof(double))) == NULL) { trspl->del(trspl); a1logw(p->log, "i1pro: malloc ref2 failed!\n"); @@ -8583,6 +9024,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { trspl->interp(trspl, &pp); (*ref2)[i] = pp.v[0]; } + #ifdef HIGH_RES_PLOT /* Plot original and upsampled reference */ { @@ -8593,27 +9035,13 @@ i1pro_code i1pro_create_hr(i1pro *p) { for (i = 0; i < m->nwav[1]; i++) { double wl = m->wl_short[1] + (double)i * (m->wl_long[1] - m->wl_short[1])/(m->nwav[1]-1.0); x1[i] = wl; - y1[i] = (*ref2)[i]; - if (wl < m->wl_short[0] || wl > m->wl_long[0]) { - y2[i] = 0.0; - } else { - double x, wl1, wl2; - for (j = 0; j < (m->nwav[0]-1); j++) { - wl1 = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], j); - wl2 = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], j+1); - if (wl >= wl1 && wl <= wl2) - break; - } - x = (wl - wl1)/(wl2 - wl1); - y2[i] = ref1[j] + (ref1[j+1] - ref1[j]) * x; - } + y1[i] = wav_lerp_cv(m, 0, ref1, wl, 0.0); + y2[i] = (*ref2)[i]; } printf("Original and up-sampled "); if (ii == 0) { printf("Reflective cal. curve:\n"); } else if (ii == 1) { - ref1 = m->emis_coef[0]; - ref2 = &m->emis_coef[1]; printf("Emission cal. curve:\n"); } else { printf("Ambient cal. curve:\n"); @@ -8630,7 +9058,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { /* Upsample stray light */ if (p->itype == instI1Pro2) { -#ifdef SALONEINSTLIB +#ifdef ONEDSTRAYLIGHTUS double **slp; /* 2D Array of stray light values */ /* Then the 2D stray light using linear interpolation */ @@ -8676,7 +9104,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { } } } -#else /* !SALONEINSTLIB */ +#else /* !ONEDSTRAYLIGHTUS */ /* Then setup 2D stray light using rspl */ if ((trspl = new_rspl(RSPL_NOFLAGS, 2, 1)) == NULL) { a1logd(p->log,1,"i1pro: creating rspl for high res conversion failed\n"); @@ -8707,7 +9135,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { avgdev[1] = 0.0; trspl->fit_rspl_w(trspl, 0, sd, m->nwav[0] * m->nwav[0], glow, ghigh, gres, NULL, NULL, 0.5, avgdev, NULL); -#endif /* !SALONEINSTLIB */ +#endif /* !ONEDSTRAYLIGHTUS */ m->straylight[1] = dmatrixz(0, m->nwav[1]-1, 0, m->nwav[1]-1); @@ -8717,7 +9145,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { double p0, p1; p0 = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], i); p1 = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j); -#ifdef SALONEINSTLIB +#ifdef ONEDSTRAYLIGHTUS /* Do linear interp with clipping at ends */ { int x0, x1, y0, y1; @@ -8748,11 +9176,11 @@ i1pro_code i1pro_create_hr(i1pro *p) { + w1 * v0 * slp[x1][y0] + w1 * v1 * slp[x1][y1]; } -#else /* !SALONEINSTLIB */ +#else /* !ONEDSTRAYLIGHTUS */ pp.p[0] = p0; pp.p[1] = p1; trspl->interp(trspl, &pp); -#endif /* !SALONEINSTLIB */ +#endif /* !ONEDSTRAYLIGHTUS */ m->straylight[1][i][j] = pp.v[0] * HIGHRES_WIDTH/10.0; if (m->straylight[1][i][j] > 0.0) m->straylight[1][i][j] = 0.0; @@ -8823,39 +9251,48 @@ i1pro_code i1pro_create_hr(i1pro *p) { } #endif /* HIGH_RES_PLOT */ -#ifdef SALONEINSTLIB +#ifdef ONEDSTRAYLIGHTUS free_dmatrix(slp, 0, m->nwav[0]-1, 0, m->nwav[0]-1); -#else /* !SALONEINSTLIB */ +#else /* !ONEDSTRAYLIGHTUS */ trspl->del(trspl); -#endif /* !SALONEINSTLIB */ +#endif /* !ONEDSTRAYLIGHTUS */ } } /* Create or re-create the high resolution filters */ for (refl = 0; refl < 2; refl++) { /* for emis/trans and reflective */ +#define MXNOWL 500 /* Max hires bands */ +#define MXNOFC 64 /* Max hires coeffs */ + +#ifndef USE_TRI_LAGRANGE /* Use decimation filter */ + int super = 0; /* nz if we're super sampling */ double fshmax; /* filter shape max wavelength from center */ -#define MXNOFC 64 - i1pro_fc coeff2[500][MXNOFC]; /* New filter cooefficients */ - double twidth; + i1pro_fc coeff2[MXNOWL][MXNOFC]; /* New filter cooefficients */ /* Construct a set of filters that uses more CCD values */ - twidth = HIGHRES_WIDTH; - if (m->nwav[1] > 500) { /* Assert */ + if (twidth < 3.0) + super = 1; + + if (m->nwav[1] > MXNOWL) { /* Assert */ a1loge(p->log,1,"High res filter has too many bands\n"); return I1PRO_INT_ASSERT; } - /* Use a simple means of determining width */ - for (fshmax = 50.0; fshmax >= 0.0; fshmax -= 0.1) { - if (fabs(lanczos2(twidth, fshmax)) > 0.0001) { - fshmax += 0.1; - break; + if (super) { + fshmax = 5.0; + } else { + /* Use a simple means of determining width */ + for (fshmax = 50.0; fshmax >= 0.0; fshmax -= 0.1) { + if (fabs(lanczos2(twidth, fshmax)) > 0.0001) { + fshmax += 0.1; + break; + } + } + if (fshmax <= 0.0) { + a1logw(p->log, "i1pro: fshmax search failed\n"); + return I1PRO_INT_ASSERT; } - } - if (fshmax <= 0.0) { - a1logw(p->log, "i1pro: fshmax search failed\n"); - return I1PRO_INT_ASSERT; } // printf("~1 fshmax = %f\n",fshmax); @@ -8886,164 +9323,108 @@ i1pro_code i1pro_create_hr(i1pro *p) { return I1PRO_INT_MALLOC; } - /* For all the useful CCD bands */ - for (i = 1; i < 127; i++) { - double w1, wl, w2; + if (super) { /* Use linear interpolation */ - /* Translate CCD center and boundaries to calibrated wavelength */ - wl = i1pro_raw2wav(p, refl, (double)i); - w1 = i1pro_raw2wav(p, refl, (double)i - 0.5); - w2 = i1pro_raw2wav(p, refl, (double)i + 0.5); + /* For all the useful CCD bands */ + for (i = 1; i < (127-1); i++) { + double wl, wh; -// printf("~1 CCD %d, w1 %f, wl %f, w2 %f\n",i,w1,wl,w2); - - /* For each filter */ - for (j = 0; j < m->nwav[1]; j++) { - double cwl, rwl; /* center, relative wavelegth */ - double we; - - cwl = m->wl_short[1] + (double)j * (m->wl_long[1] - m->wl_short[1])/(m->nwav[1]-1.0); - rwl = wl - cwl; /* relative wavelength to filter */ - - if (fabs(w1 - cwl) > fshmax && fabs(w2 - cwl) > fshmax) - continue; /* Doesn't fall into this filter */ + /* Translate CCD centers to calibrated wavelength */ + wh = i1pro_raw2wav(p, refl, (double)(i+0)); + wl = i1pro_raw2wav(p, refl, (double)(i+1)); - /* Integrate in 0.05 nm increments from filter shape */ - /* using triangular integration. */ - { - int nn; - double lw, ll; - - nn = (int)(fabs(w2 - w1)/0.05 + 0.5); /* Number to integrate over */ - - lw = w1; /* start at lower boundary of CCD cell */ - ll = lanczos2(twidth, w1- cwl); - we = 0.0; - for (k = 0; k < nn; k++) { - double cw, cl; - -#if defined(__APPLE__) && defined(__POWERPC__) - gcc_bug_fix(k); -#endif - cw = w1 + (k+1.0)/(nn +1.0) * (w2 - w1); /* wl to sample */ - cl = lanczos2(twidth, cw- cwl); - we += 0.5 * (cl + ll) * (lw - cw); /* Area under triangle */ - ll = cl; - lw = cw; + /* For each filter */ + for (j = 0; j < m->nwav[1]; j++) { + double cwl; /* Center wavelength */ + double we; /* Weighting */ + double wwwe = 1.0; + + cwl = m->wl_short[1] + (double)j * (m->wl_long[1] - m->wl_short[1]) + /(m->nwav[1]-1.0); +//printf("~1 wl %f, wh %f, cwl %f\n",wl,wh,cwl); + if (cwl < (wl - 1e-6) || cwl > (wh + 1e-6)) + continue; /* Doesn't fall into this filter */ + + if ((m->mtx_c[1][refl].nocoef[j]+1) >= MXNOFC) { + a1logw(p->log, "i1pro: run out of high res filter space\n"); + return I1PRO_INT_ASSERT; } - } - - if (m->mtx_c[1][refl].nocoef[j] >= MXNOFC) { - a1logw(p->log, "i1pro: run out of high res filter space\n"); - return I1PRO_INT_ASSERT; - } - - coeff2[j][m->mtx_c[1][refl].nocoef[j]].ix = i; - coeff2[j][m->mtx_c[1][refl].nocoef[j]++].we = we; -// printf("~1 filter %d, cwl %f, rwl %f, ix %d, we %f, nocoefs %d\n",j,cwl,rwl,i,we,m->mtx_c[1][refl].nocoef[j]-1); - } - } - -#ifdef HIGH_RES_PLOT - /* Plot resampled curves */ - { - double *xx, *ss; - double **yy; - - xx = dvectorz(-1, m->nraw-1); /* X index */ - yy = dmatrixz(0, 5, -1, m->nraw-1); /* Curves distributed amongst 5 graphs */ - - for (i = 0; i < m->nraw; i++) - xx[i] = i; - /* For each output wavelength */ - for (j = 0; j < m->nwav[1]; j++) { - i = j % 5; + we = (cwl - wl)/(wh - wl); +// wwwe = 3.3/(wh - wl); - /* For each matrix value */ - for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) { - yy[5][coeff2[j][k].ix] += 0.5 * coeff2[j][k].we; - yy[i][coeff2[j][k].ix] = coeff2[j][k].we; + coeff2[j][m->mtx_c[1][refl].nocoef[j]].ix = i; + coeff2[j][m->mtx_c[1][refl].nocoef[j]++].we = wwwe * we; +//printf("~1 filter %d, cwl %f, ix %d, we %f, nocoefs %d\n",j,cwl,i,we,m->mtx_c[1][refl].nocoef[j]); + coeff2[j][m->mtx_c[1][refl].nocoef[j]].ix = i+1; + coeff2[j][m->mtx_c[1][refl].nocoef[j]++].we = wwwe * (1.0 - we); +//printf("~1 filter %d, cwl %f, ix %d, we %f, nocoefs %d\n",j,cwl,i+1,1.0-we,m->mtx_c[1][refl].nocoef[j]); } } - printf("Hi-Res wavelength sampling curves:\n"); - do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw); - free_dvector(xx, -1, m->nraw-1); - free_dmatrix(yy, 0, 2, -1, m->nraw-1); - } -#endif /* HIGH_RES_PLOT */ - - /* Normalise the filters area in CCD space, while maintaining the */ - /* total contribution of each CCD at the target too. */ - { - int ii; - double tot = 0.0; - double ccdweight[128], avgw; /* Weighting determined by cell widths */ - double ccdsum[128]; - - /* Normalize the overall filter weightings */ - for (j = 0; j < m->nwav[1]; j++) - for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) - tot += coeff2[j][k].we; - tot /= (double)m->nwav[1]; - for (j = 0; j < m->nwav[1]; j++) - for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) - coeff2[j][k].we /= tot; - - /* Determine the relative weights for each CCD */ - avgw = 0.0; + } else { + /* For all the useful CCD bands */ for (i = 1; i < 127; i++) { + double w1, wl, w2; - ccdweight[i] = i1pro_raw2wav(p, refl, (double)i - 0.5) - - i1pro_raw2wav(p, refl, (double)i + 0.5); - ccdweight[i] = fabs(ccdweight[i]); - avgw += ccdweight[i]; - } - avgw /= 126.0; - // ~~this isn't right because not all CCD's get used!! + /* Translate CCD center and boundaries to calibrated wavelength */ + wl = i1pro_raw2wav(p, refl, (double)i); + w1 = i1pro_raw2wav(p, refl, (double)i - 0.5); + w2 = i1pro_raw2wav(p, refl, (double)i + 0.5); -#ifdef NEVER - /* Itterate */ - for (ii = 0; ; ii++) { +// printf("~1 CCD %d, w1 %f, wl %f, w2 %f\n",i,w1,wl,w2); - /* Normalize the individual filter weightings */ + /* For each filter */ for (j = 0; j < m->nwav[1]; j++) { - double err; - tot = 0.0; - for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) - tot += coeff2[j][k].we; - err = 1.0 - tot; - - for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) - coeff2[j][k].we += err/m->mtx_c[1][refl].nocoef[j]; -// for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) -// coeff2[j][k].we *= 1.0/tot; - } + double cwl, rwl; /* center, relative wavelegth */ + double we; - /* Check CCD sums */ - for (i = 1; i < 127; i++) - ccdsum[i] = 0.0; + cwl = m->wl_short[1] + (double)j * (m->wl_long[1] - m->wl_short[1]) + /(m->nwav[1]-1.0); + rwl = wl - cwl; /* relative wavelength to filter */ - for (j = 0; j < m->nwav[1]; j++) { - for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) - ccdsum[coeff2[j][k].ix] += coeff2[j][k].we; - } + if (fabs(w1 - cwl) > fshmax && fabs(w2 - cwl) > fshmax) + continue; /* Doesn't fall into this filter */ - if (ii >= 6) - break; + /* Integrate in 0.05 nm increments from filter shape */ + /* using triangular integration. */ + { + int nn; + double lw, ll; +#ifdef FAST_HIGH_RES_SETUP +# define FINC 0.2 +#else +# define FINC 0.05 +#endif + nn = (int)(fabs(w2 - w1)/0.2 + 0.5); /* Number to integrate over */ + + lw = w1; /* start at lower boundary of CCD cell */ + ll = lanczos2(twidth, w1- cwl); + we = 0.0; + for (k = 0; k < nn; k++) { + double cw, cl; - /* Readjust CCD sum */ - for (i = 1; i < 127; i++) { - ccdsum[i] = ccdtsum[i]/ccdsum[i]; /* Amount to adjust */ - } +#if defined(__APPLE__) && defined(__POWERPC__) + gcc_bug_fix(k); +#endif + cw = w1 + (k+1.0)/(nn +1.0) * (w2 - w1); /* wl to sample */ + cl = lanczos2(twidth, cw- cwl); + we += 0.5 * (cl + ll) * (lw - cw); /* Area under triangle */ + ll = cl; + lw = cw; + } + } - for (j = 0; j < m->nwav[1]; j++) { - for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) - coeff2[j][k].we *= ccdsum[coeff2[j][k].ix]; + if (m->mtx_c[1][refl].nocoef[j] >= MXNOFC) { + a1logw(p->log, "i1pro: run out of high res filter space\n"); + return I1PRO_INT_ASSERT; + } + + coeff2[j][m->mtx_c[1][refl].nocoef[j]].ix = i; + coeff2[j][m->mtx_c[1][refl].nocoef[j]++].we = we; +// printf("~1 filter %d, cwl %f, rwl %f, ix %d, we %f, nocoefs %d\n",j,cwl,rwl,i,we,m->mtx_c[1][refl].nocoef[j]); } } -#endif /* NEVER */ } #ifdef HIGH_RES_PLOT @@ -9069,7 +9450,7 @@ i1pro_code i1pro_create_hr(i1pro *p) { } } - printf("Normalized Hi-Res wavelength sampling curves:\n"); + printf("Hi-Res wavelength sampling curves %s:\n",refl ? "refl" : "emis"); do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw); free_dvector(xx, -1, m->nraw-1); free_dmatrix(yy, 0, 2, -1, m->nraw-1); @@ -9109,93 +9490,229 @@ i1pro_code i1pro_create_hr(i1pro *p) { /* Set high res tables to new allocations */ m->mtx[1][refl] = m->mtx_c[1][refl]; } - } - /* Generate high res. per mode calibration factors. */ - if (!m->hr_inited) { +#else /* USE_TRI_LAGRANGE, use triangle over lagrange interp */ - m->hr_inited = 1; /* Set so that i1pro_compute_white_cal() etc. does right thing */ + /* Compute high res. reflective wavelength corrected filters */ + if ((ev = i1pro_compute_wav_filters(p, 1, refl)) != I1PRO_OK) { + a1logd(p->log,2,"i1pro_compute_wav_filters() failed\n"); + return ev; + } +#endif /* USE_TRI_LAGRANGE */ - for (i = 0; i < i1p_no_modes; i++) { - i1pro_state *s = &m->ms[i]; + /* Normalise the filters area in CCD space, while maintaining the */ + /* total contribution of each CCD at the target too. */ + /* Hmm. This will wreck super-sample. We should fix it */ +#ifdef DO_CCDNORM /* Normalise CCD values to original */ + { + double x[4], y[4]; + double avg[2], max[2]; + double ccdsum[2][128]; /* Target weight/actual for each CCD */ + double dth[2]; - if (s->cal_factor[1] == NULL) - s->cal_factor[1] = dvectorz(0, m->nwav[1]-1); + avg[0] = avg[1] = 0.0; + max[0] = max[1] = 0.0; + for (j = 0; j < 128; j++) { + ccdsum[0][j] = 0.0; + ccdsum[1][j] = 0.0; + } - switch(i) { - case i1p_refl_spot: - case i1p_refl_scan: - if (s->cal_valid) { - /* (Using cal_factor[] as temp. for i1pro_absraw_to_abswav()) */ -#ifdef NEVER - printf("~1 regenerating calibration for reflection\n"); - printf("~1 raw white data:\n"); - plot_raw(s->white_data); -#endif /* NEVER */ - i1pro_absraw_to_abswav(p, 0, s->reflective, 1, &s->cal_factor[0], &s->white_data); -#ifdef NEVER - printf("~1 Std res intmd. cal_factor:\n"); - plot_wav(m, 0, s->cal_factor[0]); -#endif /* NEVER */ - i1pro_absraw_to_abswav(p, 1, s->reflective, 1, &s->cal_factor[1], &s->white_data); -#ifdef NEVER - printf("~1 High intmd. cal_factor:\n"); - plot_wav(m, 1, s->cal_factor[1]); - printf("~1 Std res white ref:\n"); - plot_wav(m, 0, m->white_ref[0]); - printf("~1 High res white ref:\n"); - plot_wav(m, 1, m->white_ref[1]); -#endif /* NEVER */ - i1pro_compute_white_cal(p, s->cal_factor[0], m->white_ref[0], s->cal_factor[0], - s->cal_factor[1], m->white_ref[1], s->cal_factor[1]); -#ifdef NEVER - printf("~1 Std res final cal_factor:\n"); - plot_wav(m, 0, s->cal_factor[0]); - printf("~1 High final cal_factor:\n"); - plot_wav(m, 1, s->cal_factor[1]); -#endif /* NEVER */ + /* Compute the weighting of each CCD value in the normal output */ + for (cx = j = 0; j < m->nwav[0]; j++) { /* For each wavelength */ + + /* For each matrix value */ + sx = m->mtx_o.index[j]; /* Starting index */ + for (k = 0; k < m->mtx_o.nocoef[j]; k++, cx++, sx++) { + ccdsum[0][sx] += m->mtx_o.coef[cx]; +//printf("~1 Norm CCD [%d] %f += [%d] %f\n",sx,ccdsum[0][sx],cx, m->mtx_o.coef[cx]); + } + } + + /* Compute the weighting of each CCD value in the hires output */ + for (cx = j = 0; j < m->nwav[1]; j++) { /* For each wavelength */ + + /* For each matrix value */ + sx = m->mtx_c[1][refl].index[j]; /* Starting index */ + for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++, cx++, sx++) { + ccdsum[1][sx] += m->mtx_c[1][refl].coef[cx]; +//printf("~1 HiRes CCD [%d] %f += [%d] %f\n",sx,ccdsum[1][sx],cx, m->mtx_c[1][refl].coef[cx]); + } + } + + /* Figure valid range and extrapolate to edges */ + dth[0] = 0.0; /* ref */ +// dth[1] = 0.007; /* hires */ + dth[1] = 0.004; /* hires */ + + for (k = 0; k < 2; k++) { + + for (i = 0; i < 128; i++) { + if (ccdsum[k][i] > max[k]) + max[k] = ccdsum[k][i]; + } + +//printf("~1 max[%d] = %f\n",k, max[k]); + /* Figure out the valid range */ + for (i = 64; i >= 0; i--) { + if (ccdsum[k][i] > (0.8 * max[k])) { + x[0] = (double)i; + } else { + break; } - break; + } + for (i = 64; i < 128; i++) { + if (ccdsum[k][i] > (0.8 * max[k])) { + x[3] = (double)i; + } else { + break; + } + } + /* Space off the last couple of entries */ + x[0] += (3.0 + 3.0); + x[3] -= (3.0 + 3.0); + x[1] = floor((2 * x[0] + x[3])/3.0); + x[2] = floor((x[0] + 2 * x[3])/3.0); + + for (i = 0; i < 4; i++) { + y[i] = 0.0; + for (j = -3; j < 4; j++) { + y[i] += ccdsum[k][(int)x[i]+j]; + } + y[i] /= 7.0; + } - case i1p_emiss_spot_na: - case i1p_emiss_spot: - case i1p_emiss_scan: - for (j = 0; j < m->nwav[1]; j++) - s->cal_factor[1][j] = EMIS_SCALE_FACTOR * m->emis_coef[1][j]; - break; +//printf("~1 extrap nodes %f, %f, %f, %f\n",x[0],x[1],x[2],x[3]); +//printf("~1 extrap value %f, %f, %f, %f\n",y[0],y[1],y[2],y[3]); - case i1p_amb_spot: - case i1p_amb_flash: -#ifdef FAKE_AMBIENT - for (j = 0; j < m->nwav[1]; j++) - s->cal_factor[1][j] = EMIS_SCALE_FACTOR * m->emis_coef[1][j]; - s->cal_valid = 1; -#else + for (i = 0; i < 128; i++) { + double xw, yw; - if (m->amb_coef[0] != NULL) { - for (j = 0; j < m->nwav[1]; j++) - s->cal_factor[1][j] = AMB_SCALE_FACTOR * m->emis_coef[1][j] * m->amb_coef[1][j]; - s->cal_valid = 1; + xw = (double)i; + + /* Compute interpolated value using Lagrange: */ + yw = y[0] * (xw-x[1]) * (xw-x[2]) * (xw-x[3]) + /((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3])) + + y[1] * (xw-x[0]) * (xw-x[2]) * (xw-x[3]) + /((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3])) + + y[2] * (xw-x[0]) * (xw-x[1]) * (xw-x[3]) + /((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3])) + + y[3] * (xw-x[0]) * (xw-x[1]) * (xw-x[2]) + /((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2])); + + if ((xw < x[0] || xw > x[3]) + && fabs(ccdsum[k][i] - yw)/yw > dth[k]) { + ccdsum[k][i] = yw; } + avg[k] += ccdsum[k][i]; + } + avg[k] /= 128.0; + } + +#ifdef HIGH_RES_PLOT + /* Plot target CCD values */ + { + double xx[128], y1[128], y2[128]; + + for (i = 0; i < 128; i++) { + xx[i] = i; + y1[i] = ccdsum[0][i]/avg[0]; + y2[i] = ccdsum[1][i]/avg[1]; + } + + printf("Target and actual CCD weight sums:\n"); + do_plot(xx, y1, y2, NULL, 128); + } #endif - break; - case i1p_trans_spot: - case i1p_trans_scan: - if (s->cal_valid) { - /* (Using cal_factor[] as temp. for i1pro_absraw_to_abswav()) */ - i1pro_absraw_to_abswav(p, 0, s->reflective, 1, &s->cal_factor[0], &s->white_data); - i1pro_absraw_to_abswav(p, 1, s->reflective, 1, &s->cal_factor[1], &s->white_data); - i1pro_compute_white_cal(p, s->cal_factor[0], NULL, s->cal_factor[0], - s->cal_factor[1], NULL, s->cal_factor[1]); + +#ifdef DO_CCDNORMAVG /* Just correct by average */ + for (cx = j = 0; j < m->nwav[1]; j++) { /* For each wavelength */ + + /* For each matrix value */ + sx = m->mtx_c[1][refl].index[j]; /* Starting index */ + for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++, cx++, sx++) { + m->mtx_c[1][refl].coef[cx] *= 10.0/twidth * avg[0]/avg[1]; + } + } + +#else /* Correct by CCD bin */ + + /* Correct the weighting of each CCD value in the hires output */ + for (i = 0; i < 128; i++) { + ccdsum[1][i] = 10.0/twidth * ccdsum[0][i]/ccdsum[1][i]; /* Correction factor */ + } + for (cx = j = 0; j < m->nwav[1]; j++) { /* For each wavelength */ + + /* For each matrix value */ + sx = m->mtx_c[1][refl].index[j]; /* Starting index */ + for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++, cx++, sx++) { + m->mtx_c[1][refl].coef[cx] *= ccdsum[1][sx]; + } + } +#endif + } +#endif /* DO_CCDNORM */ + +#ifdef HIGH_RES_PLOT + { + static i1pro_fc coeff2[MXNOWL][MXNOFC]; + double *xx, *ss; + double **yy; + + /* Convert the native filter cooeficient representation to */ + /* a 2D array we can randomly index. */ + for (cx = j = 0; j < m->nwav[1]; j++) { /* For each output wavelength */ + if (j >= MXNOWL) { /* Assert */ + a1loge(p->log,1,"i1pro: number of hires output wavelenths is > %d\n",MXNOWL); + return I1PRO_INT_ASSERT; + } + + /* For each matrix value */ + sx = m->mtx[1][refl].index[j]; /* Starting index */ + for (k = 0; k < m->mtx[1][refl].nocoef[j]; k++, cx++, sx++) { + if (k >= MXNOFC) { /* Assert */ + a1loge(p->log,1,"i1pro: number of hires filter coeefs is > %d\n",MXNOFC); + return I1PRO_INT_ASSERT; } - break; + coeff2[j][k].ix = sx; + coeff2[j][k].we = m->mtx[1][refl].coef[cx]; +// printf("Output %d, filter %d weight = %e\n",j,k,coeff2[j][k].we); + } + } + + xx = dvectorz(-1, m->nraw-1); /* X index */ + yy = dmatrixz(0, 5, -1, m->nraw-1); /* Curves distributed amongst 5 graphs */ + + for (i = 0; i < m->nraw; i++) + xx[i] = i; + + /* For each output wavelength */ + for (j = 0; j < m->nwav[1]; j++) { + i = j % 5; + + /* For each matrix value */ + for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) { + yy[5][coeff2[j][k].ix] += 0.5 * coeff2[j][k].we; + yy[i][coeff2[j][k].ix] = coeff2[j][k].we; + } } + + printf("Normalized Hi-Res wavelength sampling curves: %s\n",refl ? "refl" : "emis"); + do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw); + free_dvector(xx, -1, m->nraw-1); + free_dmatrix(yy, 0, 2, -1, m->nraw-1); } - } +#endif /* HIGH_RES_PLOT */ +#undef MXNOWL +#undef MXNOFC + } /* Do next filter */ /* Hires has been initialised */ m->hr_inited = 1; + /* Generate high res. per mode calibration factors. */ + if ((ev = i1pro_create_hr_calfactors(p, 0)) != I1PRO_OK) + return ev; + return ev; } @@ -9373,6 +9890,7 @@ i1pro_code i1pro_conv2XYZ( if (!m->spec_en) { vals[i].sp.spec_n = 0; } + } conv->del(conv); @@ -9447,20 +9965,280 @@ i1pro_code i1pro_check_white_reference1( } /* Compute a mode calibration factor given the reading of the white reference. */ -/* Return 1 if any of the transmission wavelengths are low. */ -int i1pro_compute_white_cal( +/* We will also calibrate & smooth hi-res emis_coef[1] if they are present. */ +/* Return I1PRO_RD_TRANSWHITEWARN if any of the transmission wavelengths are low. */ +/* May return some other error (malloc) */ +i1pro_code i1pro_compute_white_cal( i1pro *p, double *cal_factor0, /* [nwav[0]] Calibration factor to compute */ double *white_ref0, /* [nwav[0]] White reference to aim for, NULL for 1.0 */ double *white_read0, /* [nwav[0]] The white that was read */ double *cal_factor1, /* [nwav[1]] Calibration factor to compute */ double *white_ref1, /* [nwav[1]] White reference to aim for, NULL for 1.0 */ - double *white_read1 /* [nwav[1]] The white that was read */ + double *white_read1, /* [nwav[1]] The white that was read */ + int do_emis_ft /* Do emission hires fine tune with this info. */ ) { i1proimp *m = (i1proimp *)p->m; i1pro_state *s = &m->ms[m->mmode]; - int j, warn = 0;; + int j, warn = I1PRO_OK;; + +#ifdef HIGH_RES + /* If we need to, fine calibrate the emission */ + /* calibration coefficients, using the reflectance cal. */ + /* illuminant as an (assumed) smooth light source reference. */ + /* (Do this first, befor white_read0/cal_factor0 is overwritten by white cal.) */ + if (do_emis_ft && m->hr_inited != 0 && white_ref1 != NULL) { + i1pro_code ev = I1PRO_OK; + int i; + double *lincal; + double *fudge; + xspect illA; +#ifdef HIGH_RES_PLOT + double *targ_smth; /* Hires target in smooth space */ + double *old_emis; + double avgl; + + if ((targ_smth = (double *)calloc(m->nwav[1], sizeof(double))) == NULL) { + return I1PRO_INT_MALLOC; + } + if ((old_emis = (double *)calloc(m->nwav[1], sizeof(double))) == NULL) { + return I1PRO_INT_MALLOC; + } + for (i = 0; i < m->nwav[1]; i++) + old_emis[i] = m->emis_coef[1][i]; +#endif + + if ((lincal = (double *)calloc(m->nwav[0], sizeof(double))) == NULL) { + return I1PRO_INT_MALLOC; + } + if ((fudge = (double *)calloc(m->nwav[0], sizeof(double))) == NULL) { + return I1PRO_INT_MALLOC; + } + + /* Fill in an xpsect with a standard illuminant spectrum */ + if (standardIlluminant(&illA, icxIT_Ptemp, 2990.0)) { + a1loge(p->log,1,"i1pro_compute_white_cal: standardIlluminant() failed"); + return I1PRO_INT_ASSERT; + } + + for (j = 0; j < m->nwav[0]; j++) { + double wl = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], j); + + lincal[j] = white_read0[j] * m->emis_coef[0][j] + / (white_ref0[j] * value_xspect(&illA, wl)); + } + +#ifndef NEVER + /* Generate the hires emis_coef by interpolating lincal */ + /* using rspl, and reversing computation through hi-res readings */ + { + rspl *trspl; /* Upsample rspl */ + cow sd[40]; /* Scattered data points of existing references */ + datai glow, ghigh; + datao vlow, vhigh; + int gres[1]; + double avgdev[1]; + int ix; + co pp; + + if ((trspl = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) { + a1logd(p->log,1,"i1pro: creating rspl for high res conversion failed\n"); + return I1PRO_INT_NEW_RSPL_FAILED; + } + + vlow[0] = 1e6; + vhigh[0] = -1e6; + for (ix = i = 0; i < m->nwav[0]; i++) { + + sd[ix].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i); + sd[ix].v[0] = lincal[i]; + sd[ix].w = 1.0; + + if (sd[ix].v[0] < vlow[0]) + vlow[0] = sd[ix].v[0]; + if (sd[ix].v[0] > vhigh[0]) + vhigh[0] = sd[ix].v[0]; + ix++; + } + + glow[0] = m->wl_short[1]; + ghigh[0] = m->wl_long[1]; + gres[0] = 6 * m->nwav[1]; + avgdev[0] = 0.0; + + /* The smoothness factor of 0.02 seems critical in tuning the RevE */ + /* hires accuracy, as measured on an LCD display */ + trspl->fit_rspl_w(trspl, 0, sd, ix, glow, ghigh, gres, vlow, vhigh, 0.05, avgdev, NULL); + + /* Create a linear interp fudge factor to place interp at low */ + /* res. exactly on lowres linear curve. This compensates */ + /* if the rspl moves the target at the lowres points */ + for (j = 0; j < m->nwav[0]; j++) { + double wl = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], j); + pp.p[0] = wl; + trspl->interp(trspl, &pp); + + fudge[j] = lincal[j] / pp.v[0]; + } + + /* Compute hires emis_coef */ + for (i = 0; i < m->nwav[1]; i++) { + double wl = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], i); + double ff; + + pp.p[0] = wl; + trspl->interp(trspl, &pp); + +#ifdef HIGH_RES_PLOT + targ_smth[i] = pp.v[0]; +#endif + /* Invert lincal at high res */ + ff = wav_lerp(m, 0, fudge, wl); + m->emis_coef[1][i] = (ff * pp.v[0] * white_ref1[i] * value_xspect(&illA, wl)) + /white_read1[i]; + } + trspl->del(trspl); + } + +#else + /* Generate the hires emis_coef by interpolating lincal */ + /* using lagrange, and reversing computation through hi-res readings */ + for (i = 0; i < m->nwav[1]; i++) { + int k; + double x[4], xw; + double y[4], yw; + + xw = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], i); + + /* Locate lowres index below it */ + j = (int)floor(XSPECT_DIX(m->wl_short[0], m->wl_long[0], m->nwav[0], xw)) -1; + if (j < 0) + j = 0; + if (j > (m->nwav[0]-4)) + j = (m->nwav[0]-4); + + /* Setup the surrounding point values */ + for (k = 0; k < 4; k++) { + x[k] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], j+k); + y[k] = lincal[j+k]; + } + + /* Compute interpolated value using Lagrange: */ + yw = y[0] * (xw-x[1]) * (xw-x[2]) * (xw-x[3]) + /((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3])) + + y[1] * (xw-x[0]) * (xw-x[2]) * (xw-x[3]) + /((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3])) + + y[2] * (xw-x[0]) * (xw-x[1]) * (xw-x[3]) + /((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3])) + + y[3] * (xw-x[0]) * (xw-x[1]) * (xw-x[2]) + /((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2])); + + targ_smth[i] = yw; + + /* Invert lincal at high res */ + m->emis_coef[1][i] = (yw * white_ref1[i] * value_xspect(&illA, xw))/white_read1[i]; + } +#endif /* NEVER */ + +#ifdef HIGH_RES_PLOT + /* Plot linear target curve and measured curve */ + { + double *xx, *y1, *y2, *y3, *y4; + + xx = dvector(0, m->nwav[1]); /* X wl */ + y1 = dvector(0, m->nwav[1]); + y2 = dvector(0, m->nwav[1]); + y3 = dvector(0, m->nwav[1]); + y4 = dvector(0, m->nwav[1]); + + for (j = 0; j < (m->nwav[1]); j++) { + xx[j] = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j); + y1[j] = wav_lerp_cv(m, 0, lincal, xx[j], 0.0); + y2[j] = targ_smth[j]; + y3[j] = white_read1[j] * wav_lerp(m, 0, m->emis_coef[0], xx[j]) + /(white_ref1[j] * value_xspect(&illA, xx[j])); + y4[j] = white_read1[j] * old_emis[j] + /(white_ref1[j] * value_xspect(&illA, xx[j])); + } + + printf("stdres interp targ (bk), smoothed targ (rd), measured hires resp. (gn), previous cal resp.(bu):\n"); + do_plot6(xx, y1, y2, y3, y4, NULL, NULL, m->nwav[1]); + free_dvector(xx, 0, m->nwav[1]); + free_dvector(y1, 0, m->nwav[1]); + free_dvector(y2, 0, m->nwav[1]); + free_dvector(y4, 0, m->nwav[1]); + } + /* Plot target and achieved smooth space responses */ + { + double *xx, *y2; + + xx = dvector(0, m->nwav[1]); + y2 = dvector(0, m->nwav[1]); + + for (j = 0; j < (m->nwav[1]); j++) { + xx[j] = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j); + y2[j] = white_read1[j] * m->emis_coef[1][j] + /(white_ref1[j] * value_xspect(&illA, xx[j])); + } + + printf("target smooth curve, achived smooth curve:\n"); + do_plot6(xx, targ_smth, y2, NULL, NULL, NULL, NULL, m->nwav[1]); + + free_dvector(xx, 0, m->nwav[1]); + free_dvector(y2, 0, m->nwav[1]); + } + /* Plot lowres and hires lamp response */ + { + double *xx, *y1, *y2; + + xx = dvector(0, m->nwav[1]); + y1 = dvector(0, m->nwav[1]); + y2 = dvector(0, m->nwav[1]); + + for (j = 0; j < (m->nwav[1]); j++) { + xx[j] = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j); + y1[j] = wav_lerp(m, 0, white_read0, xx[j]) * wav_lerp(m, 0, m->emis_coef[0], xx[j]) + /wav_lerp(m, 0, white_ref0, xx[j]); + y2[j] = white_read1[j] * m->emis_coef[1][j] + /white_ref1[j]; + } + + printf("lowres, high res lamp response:\n"); + do_plot6(xx, y1, y2, NULL, NULL, NULL, NULL, m->nwav[1]); + + free_dvector(xx, 0, m->nwav[1]); + free_dvector(y1, 0, m->nwav[1]); + free_dvector(y2, 0, m->nwav[1]); + } + /* Plot hires emis calibration */ + { + double *xx; + + xx = dvector(0, m->nwav[1]); /* X wl */ + + for (j = 0; j < (m->nwav[1]); j++) { + xx[j] = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j); + } + + printf("orig upsampled + smoothed hires emis_coef:\n"); + do_plot6(xx, old_emis, m->emis_coef[1], NULL, NULL, NULL, NULL, m->nwav[1]); + free_dvector(xx, 0, m->nwav[1]); + } + free(old_emis); + free(targ_smth); +#endif /* HIGH_RES_PLOT */ + free(fudge); + free(lincal); + + m->emis_hr_cal = 1; /* We don't have to do a reflective calibration */ + + /* Make sure these are updated */ + if ((ev = i1pro_create_hr_calfactors(p, 1)) != I1PRO_OK) + return ev; + } +#endif /* HIGH_RES */ + if (white_ref0 == NULL) { /* transmission white reference */ double avgwh = 0.0; @@ -9474,7 +10252,7 @@ int i1pro_compute_white_cal( /* If reference is < 0.4% of average */ if (white_read0[j]/avgwh < 0.004) { cal_factor0[j] = 1.0/(0.004 * avgwh); - warn = 1; + warn = I1PRO_RD_TRANSWHITEWARN; } else { cal_factor0[j] = 1.0/white_read0[j]; } @@ -9508,7 +10286,7 @@ int i1pro_compute_white_cal( /* If reference is < 0.4% of average */ if (white_read1[j]/avgwh < 0.004) { cal_factor1[j] = 1.0/(0.004 * avgwh); - warn = 1; + warn = I1PRO_RD_TRANSWHITEWARN; } else { cal_factor1[j] = 1.0/white_read1[j]; } @@ -9523,8 +10301,8 @@ int i1pro_compute_white_cal( else cal_factor1[j] = white_ref1[j]/white_read1[j]; } - } #endif /* HIGH_RES */ + } return warn; } @@ -10136,8 +10914,12 @@ i1pro_delayed_trigger(void *pp) { a1logd(p->log,2,"i1pro_delayed_trigger: start sleep @ %d msec\n", msec_time() - m->msec); +#ifdef USE_RD_SYNC + p->icom->usb_wait_io(p->icom, &m->rd_sync); /* Wait for read to start */ +#else /* Delay the trigger */ msec_sleep(m->trig_delay); +#endif m->tr_t1 = msec_time(); /* Diagnostic */ @@ -10197,7 +10979,7 @@ i1pro_triggermeasure(i1pro *p, int delay) { /* A buffer full of bytes is returned. */ /* (This will fail on a Rev. A if there is more than about a 40 msec delay */ /* between triggering the measurement and starting this read. */ -/* It appears though that the read can be pending before triggering though. */ +/* 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 i1pro_readmeasurement( @@ -10268,7 +11050,7 @@ i1pro_readmeasurement( m->tr_t6 = msec_time(); /* Diagnostic, start of subsequent reads */ if (m->tr_t3 == 0) m->tr_t3 = m->tr_t6; /* Diagnostic, start of first read */ - se = p->icom->usb_read(p->icom, NULL, 0x82, buf, size, &rwbytes, top); + se = p->icom->usb_read(p->icom, &m->rd_sync, 0x82, buf, size, &rwbytes, top); m->tr_t5 = m->tr_t7; m->tr_t7 = msec_time(); /* Diagnostic, end of subsequent reads */ @@ -10538,7 +11320,7 @@ i1pro_code i1pro_waitfor_switch_th(i1pro *p, double top) { (stime = msec_time()) - m->msec); /* Now read 1 byte */ - se = p->icom->usb_read(p->icom, &m->cancelt, 0x84, buf, 1, &rwbytes, top); + se = p->icom->usb_read(p->icom, &m->sw_cancel, 0x84, buf, 1, &rwbytes, top); if (se & ICOM_TO) { a1logd(p->log,2,"i1pro_waitfor_switch_th: read 0x%x bytes, timed out (%d msec)\n", @@ -10596,7 +11378,7 @@ i1pro_terminate_switch( msec_sleep(50); if (m->th_termed == 0) { a1logd(p->log,3,"i1pro terminate switch thread failed, canceling I/O\n"); - p->icom->usb_cancel_io(p->icom, &m->cancelt); + p->icom->usb_cancel_io(p->icom, &m->sw_cancel); } return rv; @@ -10731,9 +11513,12 @@ i1pro2_delayed_trigger(void *pp) { a1logd(p->log,2,"i1pro2_delayed_trigger: Rev E start sleep @ %d msec\n", msec_time() - m->msec); - +#ifdef USE_RD_SYNC + p->icom->usb_wait_io(p->icom, &m->rd_sync); /* Wait for read to start */ +#else /* Delay the trigger */ msec_sleep(m->trig_delay); +#endif m->tr_t1 = msec_time(); /* Diagnostic */ diff --git a/spectro/i1pro_imp.h b/spectro/i1pro_imp.h index 64296b2..c0d0708 100644 --- a/spectro/i1pro_imp.h +++ b/spectro/i1pro_imp.h @@ -114,7 +114,6 @@ struct _i1pro_state { double *cal_factor[2]; /* [low res, high res][nwav] calibration scale factor for this mode */ double *white_data; /* [-1 nraw] linear absolute dark subtracted white data */ /* used to compute cal_factor */ -// double *cal_factor1, *cal_factor2; /* (Underlying tables for two resolutions) */ /* Adaptive emission/transparency black data */ int idark_valid; /* idark calibration factors valid */ @@ -163,9 +162,10 @@ struct _i1proimp { athread *th; /* Switch monitoring thread (NULL if not used) */ volatile int switch_count; /* Incremented in thread */ volatile int hide_switch; /* Set to supress switch event during read */ - usb_cancelt cancelt; /* Token to allow cancelling an outstanding I/O */ + usb_cancelt sw_cancel; /* Token to allow cancelling switch I/O */ volatile int th_term; /* Terminate thread on next return */ volatile int th_termed; /* Thread has terminated */ + usb_cancelt rd_sync; /* Token to allow meas. read to be synchronized */ inst_opt_type trig; /* Reading trigger mode */ int noinitcalib; /* Disable initial calibration if not essential */ int highres; /* High resolution mode */ @@ -268,6 +268,7 @@ struct _i1proimp { double *emis_coef[2]; /* [low res, high res][nwav] Emission cal coefficients */ double *amb_coef[2]; /* [low res, high res][nwav] Ambient light cal values */ /* (compound with Emission), NULL if ambient not supported */ + int emis_hr_cal; /* NZ if emis_coef[1] has been fine calibrated using reflective cal. */ double **straylight[2]; /* [nwav][nwav] Stray light convolution matrix (Rev E) */ @@ -392,6 +393,11 @@ void del_i1proimp(i1pro *p); #define I1PRO_RD_NOFLASHES 0x3E /* No flashes recognized */ #define I1PRO_RD_NOAMBB4FLASHES 0x3F /* No ambient before flashes found */ #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 @@ -471,14 +477,20 @@ i1pro_code i1pro_imp_meas_refrate( double *ref_rate ); +/* Measure the display update delay */ +i1pro_code i1pro_imp_meas_delay( + i1pro *p, + int *msecdelay +); + /* Given a raw measurement of the wavelength LED, */ /* Compute the base offset that best fits it to the reference */ i1pro_code i1pro2_match_wl_meas(i1pro *p, double *pled_off, double *wlraw); -/* Compute standard res downsampling filters */ +/* Compute downsampling filters using the default filters. */ /* mtx_index1, mtx_nocoef1, mtx_coef1 given the */ /* current wl_led_off */ -i1pro_code i1pro2_compute_wav_filters(i1pro *p, int reflective); +i1pro_code i1pro_compute_wav_filters(i1pro *p, int hires, int reflective); /* return nz if high res is supported */ int i1pro_imp_highres(i1pro *p); @@ -603,6 +615,18 @@ i1pro_code i1pro2_wl_measure( double targoscale /* Optimal reading scale factor */ ); +/* Take a measurement reading using the current mode (combined parts 1 & 2a) */ +/* Converts to completely processed output readings, without averaging or extracting */ +/* sample patches. */ +/* (NOTE:- this can't be used for calibration, as it implements uv mode) */ +i1pro_code i1pro_read_patches_all( + i1pro *p, + double **specrd, /* Return array [numpatches][nwav] of spectral reading values */ + int numpatches, /* Number of sample to measure */ + double *inttime, /* Integration time to use/used */ + int gainmode /* Gain mode to use, 0 = normal, 1 = high */ +); + /* Take a measurement reading using the current mode, part 1 */ /* Converts to completely processed output readings. */ i1pro_code i1pro_read_patches_1( @@ -811,15 +835,16 @@ i1pro_code i1pro_check_white_reference1( ); /* Compute a calibration factor given the reading of the white reference. */ -/* Return nz if any of the transmission wavelengths are low */ -int i1pro_compute_white_cal( +/* Return I1PRO_RD_TRANSWHITEWARN if any of the transmission wavelengths are low */ +i1pro_code i1pro_compute_white_cal( i1pro *p, double *cal_factor0, /* [nwav0] Calibration factor to compute */ double *white_ref0, /* [nwav0] White reference to aim for, NULL for 1.0 */ double *white_read0, /* [nwav0] The white that was read */ double *cal_factor1, /* [nwav1] Calibration factor to compute */ double *white_ref1, /* [nwav1] White reference to aim for, NULL for 1.0 */ - double *white_read1 /* [nwav1] The white that was read */ + double *white_read1, /* [nwav1] The white that was read */ + int do_emis_ft /* Do emission hires fine tune with this info. */ ); /* For adaptive mode, compute a new integration time and gain mode */ diff --git a/spectro/icoms.c b/spectro/icoms.c index 0dc815c..0166e21 100644 --- a/spectro/icoms.c +++ b/spectro/icoms.c @@ -43,17 +43,64 @@ /* ----------------------------------------------------- */ -/* Fake device */ -icompath icomFakeDevice = { "Fake Display Device" }; +/* Fake display & instrument device */ +icompath icomFakeDevice = { instFakeDisp, "Fake Display Device" }; + + +/* Free an icompath */ +static +void icompath_del_contents(icompath *p) { + + if (p->name != NULL) + free(p->name); +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + if (p->spath != NULL) + free(p->spath); +#endif /* ENABLE_SERIAL */ +#ifdef ENABLE_USB + usb_del_usb_idevice(p->usbd); + hid_del_hid_idevice(p->hidd); +#endif /* ENABLE_USB */ +} + +/* Free an icompath */ +static void icompath_del(icompath *p) { + + icompath_del_contents(p); + free(p); +} + +/* Delete the last path */ +static void icompaths_del_last_path( + icompaths *p +) { + if (p->npaths == 0) + return; + + icompath_del(p->paths[p->npaths-1]); + p->paths[p->npaths-1] = NULL; + p->npaths--; +} + +/* Return the last path */ +static icompath *icompaths_get_last_path( + icompaths *p +) { + if (p->npaths == 0) + return NULL; + + return p->paths[p->npaths-1]; +} /* Return the path corresponding to the port number, or NULL if out of range */ static icompath *icompaths_get_path( icompaths *p, int port /* Enumerated port number, 1..n */ ) { - if (port == -99) + if (port == FAKE_DEVICE_PORT) return &icomFakeDevice; + if (port <= 0 || port > p->npaths) return NULL; @@ -63,20 +110,10 @@ static icompath *icompaths_get_path( static void icompaths_clear(icompaths *p) { /* Free any old list */ - if (p->paths != NULL) { + if (p != NULL && p->paths != NULL) { int i; for (i = 0; i < p->npaths; i++) { - if (p->paths[i]->name != NULL) - free(p->paths[i]->name); -#ifdef ENABLE_SERIAL - if (p->paths[i]->spath != NULL) - free(p->paths[i]->spath); -#endif /* ENABLE_SERIAL */ -#ifdef ENABLE_USB - usb_del_usb_idevice(p->paths[i]->usbd); - hid_del_hid_idevice(p->paths[i]->hidd); -#endif /* ENABLE_USB */ - free(p->paths[i]); + icompath_del(p->paths[i]); } free(p->paths); p->npaths = 0; @@ -111,11 +148,11 @@ static int icompaths_add_path(icompaths *p) { return ICOM_OK; } -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) /* Add a serial path */ /* return icom error */ -static int icompaths_add_serial(icompaths *p, char *name, char *spath) { +static int icompaths_add_serial(icompaths *p, char *name, char *spath, int fast) { int rv; if ((rv = icompaths_add_path(p)) != ICOM_OK) @@ -129,6 +166,28 @@ static int icompaths_add_serial(icompaths *p, char *name, char *spath) { a1loge(p->log, ICOM_SYS, "icompaths: strdup failed!\n"); return ICOM_SYS; } + p->paths[p->npaths-1]->fast = fast; + + return ICOM_OK; +} + +/* Modify a serial path to add the instrument type */ +int icompaths_set_serial_itype(icompath *p, instType itype) { + char pname[400], *cp; + + p->itype = itype; + + /* Strip any existing description in brackets */ + if ((cp = strchr(p->name, '(')) != NULL && cp > p->name) + cp[-1] = '\000'; + sprintf(pname,"%s (%s)", p->name, inst_name(itype)); + cp = p->name; + if ((p->name = strdup(pname)) == NULL) { + p->name = cp; + a1loge(g_log, ICOM_SYS, "icompaths_set_serial_itype: strdup path failed!\n"); + return ICOM_SYS; + } + free(cp); return ICOM_OK; } @@ -138,6 +197,7 @@ static int icompaths_add_serial(icompaths *p, char *name, char *spath) { /* Set an icompath details */ /* return icom error */ +static int icompath_set_usb(a1log *log, icompath *p, char *name, unsigned int vid, unsigned int pid, int nep, struct usb_idevice *usbd, instType itype) { int rv; @@ -195,10 +255,12 @@ static int icompaths_add_hid(icompaths *p, char *name, unsigned int vid, unsigne #endif /* ENABLE_USB */ static void icompaths_del(icompaths *p) { - icompaths_clear(p); - - p->log = del_a1log(p->log); /* Unreference it */ - free(p); + if (p != NULL) { + icompaths_clear(p); + + p->log = del_a1log(p->log); /* Unreference it and set to NULL */ + free(p); + } } int icompaths_refresh_paths(icompaths *p); /* Defined in platform specific */ @@ -216,8 +278,10 @@ icompaths *new_icompaths(a1log *log) { p->clear = icompaths_clear; p->refresh = icompaths_refresh_paths; + p->del_last_path = icompaths_del_last_path; + p->get_last_path = icompaths_get_last_path; p->get_path = icompaths_get_path; -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) p->add_serial = icompaths_add_serial; #endif /* ENABLE_SERIAL */ #ifdef ENABLE_USB @@ -228,6 +292,7 @@ icompaths *new_icompaths(a1log *log) { if (icompaths_refresh_paths(p)) { a1loge(log, ICOM_SYS, "new_icompaths: icompaths_refresh_paths failed!\n"); + free(p); return NULL; } @@ -242,16 +307,19 @@ icompaths *new_icompaths(a1log *log) { static int icom_copy_path_to_icom(icoms *p, icompath *pp) { int rv; + if (p->name != NULL) + free(p->name); if ((p->name = strdup(pp->name)) == NULL) { a1loge(p->log, ICOM_SYS, "copy_path_to_icom: malloc name failed\n"); return ICOM_SYS; } -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) if (pp->spath != NULL) { if ((p->spath = strdup(pp->spath)) == NULL) { a1loge(p->log, ICOM_SYS, "copy_path_to_icom: malloc spath failed\n"); return ICOM_SYS; } + p->fast = pp->fast; } else { p->spath = NULL; } @@ -274,12 +342,14 @@ static int icom_copy_path_to_icom(icoms *p, icompath *pp) { static icom_type icoms_port_type( icoms *p ) { + #ifdef ENABLE_USB if (p->hidd != NULL) return icomt_hid; if (p->usbd != NULL) return icomt_usb; #endif + return icomt_serial; } @@ -301,8 +371,8 @@ icoms *p, char *wbuf, /* Write puffer */ char *rbuf, /* Read buffer */ int bsize, /* Buffer size */ -char tc, /* Terminating characer */ -int ntc, /* Number of terminating characters */ +char *tc, /* Terminating characers, NULL for none */ +int ntc, /* Number of terminating characters needed to terminate */ double tout ) { int rv = ICOM_OK; @@ -314,10 +384,11 @@ double tout return ICOM_NOTS; } -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) /* Flush any stray chars if serial */ if (p->usbd == NULL && p->hidd == NULL) { int debug = p->log->debug; + if (debug < 8) p->log->debug = 0; for (; rv == ICOM_OK;) /* Until we get a timeout */ @@ -364,11 +435,12 @@ icoms *new_icoms( /* Copy ipath info to this icoms */ if (icom_copy_path_to_icom(p, ipath)) { + free(p->name); free(p); return NULL; } -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) #ifdef NT p->phandle = NULL; #endif @@ -383,7 +455,7 @@ icoms *new_icoms( #endif /* ENABLE_SERIAL */ p->lserr = 0; - p->tc = -1; +// p->tc = -1; p->log = new_a1log_d(log); p->debug = p->log->debug; /* Legacy code */ @@ -391,7 +463,7 @@ icoms *new_icoms( p->close_port = icoms_close_port; p->port_type = icoms_port_type; -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) p->set_ser_port = icoms_set_ser_port; #endif /* ENABLE_SERIAL */ @@ -442,6 +514,10 @@ icoms_fix(char *ss) { unsigned char *s = (unsigned char *)ss; if (++ix >= 5) ix = 0; + if (ss == NULL) { + strcpy((char *)buf[ix],"(null)"); + return (char *)buf[ix]; + } for(d = buf[ix]; (d - buf[ix]) < 1000;) { if (*s < ' ' && *s > '\000') { *d++ = '^'; diff --git a/spectro/icoms.h b/spectro/icoms.h index aac351a..f219312 100644 --- a/spectro/icoms.h +++ b/spectro/icoms.h @@ -18,26 +18,6 @@ * Derived from serio.h */ -/* - - Notes: The user keyboard/interrupt handling is really broken. - It's not noticed most of the time because mostly keys are only - hit at the expected times. Serial instruments (X-Rite) are more - forgiving in interrupting coms to/from the instrument, as well - as being long winded and needing abort. For USB insruments it's - not necessarily robust to interrupt or terminate after a give - USB transaction. The handling of user commands and aborts - is not consistent either, possibly leaving some instruments - suseptable to bodgy results due to an unrecognised aborted - command. Really, the instrument driver should deterimine - at what points an operation can be aborted, and how to recover. - - Because the instrument itself can be a source of commands, - some way of waiting for instrument or user input is needed. - Could a threaded approach with instrument abort work ? - -*/ - /* Some MSWin specific stuff is in icoms, and used by usbio & hidio */ #if defined (NT) #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0501 @@ -76,7 +56,12 @@ typedef struct { int packetsize; /* The max packet size */ int type; /* 2 = bulk, 3 = interrupt */ int interface; /* interface number */ +#if defined(__APPLE__) int pipe; /* pipe number (1..N, OS X only) */ +#endif +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + int fd; /* ep file descriptor */ +#endif } usb_ep; #define ICOM_EP_TYPE_BULK 2 @@ -92,9 +77,11 @@ typedef struct { /* Store information about a possible instrument communication path */ /* (Note a path doesn't have a reference to icompaths or its' log) */ struct _icompath{ + instType itype; /* Type of instrument if known */ char *name; /* instance description */ -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) char *spath; /* Serial device path */ + int fast; /* Virtual serial port that can be identified quickly */ #endif #ifdef ENABLE_USB int nep; /* Number of end points */ @@ -102,7 +89,6 @@ struct _icompath{ struct usb_idevice *usbd; /* USB port, NULL if not USB */ struct hid_idevice *hidd; /* HID port, NULL if not HID */ #endif /* ENABLE_USB */ - instType itype; /* Type of instrument if known */ }; extern icompath icomFakeDevice; /* Declare fake device */ @@ -117,9 +103,9 @@ struct _icompaths { /* return icom error */ int (*refresh)(struct _icompaths *p); -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) /* Add a serial path. path is copied. Return icom error */ - int (*add_serial)(struct _icompaths *p, char *name, char *spath); + int (*add_serial)(struct _icompaths *p, char *name, char *spath, int fast); #endif /* ENABLE_SERIAL */ #ifdef ENABLE_USB @@ -132,10 +118,18 @@ struct _icompaths { int nep, struct hid_idevice *hidd, instType itype); #endif /* ENABLE_USB */ + /* Delete the last path */ + void (*del_last_path)(struct _icompaths *p); + + /* Return the last path */ + icompath *(*get_last_path)(struct _icompaths *p); + /* Return the path corresponding to the port number, or NULL if out of range */ icompath *(*get_path)( struct _icompaths *p, int port); /* Enumerated port number, 1..n */ +#define FAKE_DEVICE_PORT -98 /* Fake display & instrument */ +#define DEMO_DEVICE_PORT -99 /* Fake Demo instrument */ /* Clear all the paths */ void (*clear)(struct _icompaths *p); @@ -175,7 +169,8 @@ typedef enum { baud_19200 = 9, baud_38400 = 10, baud_57600 = 11, - baud_115200 = 12 + baud_115200 = 12, + baud_921600 = 13 } baud_rate; /* Possible parity */ @@ -214,6 +209,7 @@ typedef enum { /* Type of port */ typedef enum { icomt_serial, /* Serial port */ + icomt_usbserial, /* USB Serial port */ icomt_usb, /* USB port */ icomt_hid /* HID (USB) port */ } icom_type; @@ -223,8 +219,8 @@ typedef enum { #define ICOM_NOTS 0x001000 /* Not supported */ #define ICOM_SIZE 0x002000 /* Request/response size exceeded limits */ -#define ICOM_TO 0x004000 /* Timed out */ -#define ICOM_SHORT 0x008000 /* Number of bytes wasn't read/written */ +#define ICOM_TO 0x004000 /* Timed out, but there may be bytes read/written */ +#define ICOM_SHORT 0x008000 /* No timeout but number of bytes wasn't read/written */ #define ICOM_CANC 0x010000 /* Was cancelled */ #define ICOM_SYS 0x020000 /* System error (ie. malloc, system call fail) */ #define ICOM_VER 0x040000 /* Version error - need up to date kernel driver */ @@ -250,7 +246,7 @@ typedef struct _usb_cancelt usb_cancelt; #ifdef ENABLE_USB void usb_init_cancel(usb_cancelt *p); void usb_uninit_cancel(usb_cancelt *p); - +void usb_reinit_cancel(usb_cancelt *p); #endif struct _icoms { @@ -262,7 +258,7 @@ struct _icoms { int is_open; /* Flag, NZ if this port is open */ -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) /* Serial port parameters */ char *spath; /* Serial port path */ @@ -275,6 +271,8 @@ struct _icoms { #if defined (UNIX) || defined(__APPLE__) int fd; /* Unix file descriptor */ #endif + int fast; /* Virtual serial port that can be identified quickly */ + flow_control fc; baud_rate br; parity py; @@ -287,13 +285,6 @@ struct _icoms { /* USB port parameters */ struct usb_idevice *usbd; /* USB port - copy of ppath->usbd */ -#ifndef NATIVE_USB /* Hmm. could put all this within usb_idevice if not #defined ? */ -# ifdef USE_LIBUSB1 - libusb_device_handle *usbh; -# else - struct usb_dev_handle *usbh; -# endif -#endif icomuflags uflags; /* Bug workaround flags */ unsigned int vid, pid; /* USB vendor and product id's, used to distiguish instruments */ @@ -304,6 +295,8 @@ 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 latmsec; /* Latency timeout in msec for modem status bytes */ usb_ep ep[32]; /* Information about each end point for general usb i/o */ @@ -317,7 +310,6 @@ struct _icoms { /* General parameters */ int lserr; /* Last serial communication error code */ - int tc; /* Current serial parser termination character (-1 if not set) */ a1log *log; /* Debug & Error log */ int debug; /* legacy - Flag, nz to print instrument io info to stderr */ @@ -329,7 +321,7 @@ struct _icoms { /* Return the port type */ icom_type (*port_type)(struct _icoms *p); -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) /* Select the serial communications port and characteristics */ /* return icom error */ int (*set_ser_port)( @@ -382,8 +374,8 @@ struct _icoms { struct _icoms *p, char *buf, /* Buffer to store characters read */ int bsize, /* Buffer size */ - char tc, /* Terminating character */ - int ntc, /* Number of terminating characters */ + char *tc, /* Terminating characters */ + int ntc, /* Number of terminating characters seen */ double tout); /* Timeout in seconds */ /* "Serial" write and read */ @@ -393,8 +385,8 @@ struct _icoms { char *wbuf, /* Write puffer */ char *rbuf, /* Read buffer */ int bsize, /* Buffer size */ - char tc, /* Terminating characer */ - int ntc, /* Number of terminating characters */ + char *tc, /* Terminating characers */ + int ntc, /* Number of terminating characters seen */ double tout); /* Timeout in seconds */ /* For a USB device, do a control message */ @@ -428,6 +420,11 @@ struct _icoms { int *bwritten, /* Bytes written */ double tout); /* Timeout in seconds */ + /* Wait until a read/write in another thread has started. */ + /* return icom error */ + int (*usb_wait_io)(struct _icoms *p, + usb_cancelt *cantelt); /* Cancel handle */ + /* Cancel a read/write in another thread */ /* return icom error */ int (*usb_cancel_io)(struct _icoms *p, diff --git a/spectro/icoms_nt.c b/spectro/icoms_nt.c index ef364c4..3caa8e0 100644 --- a/spectro/icoms_nt.c +++ b/spectro/icoms_nt.c @@ -16,6 +16,16 @@ #include <conio.h> +/* Link list element to hold fast_serial port names */ +typedef struct fast_com_name { + char name[100]; + struct fast_com_name *next; +} fast_com_name; + +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) +instType fast_ser_inst_type(icoms *p, int tryhard, void *, void *); +#endif /* ENABLE_SERIAL */ + /* Create and return a list of available serial ports or USB instruments for this system. */ /* We look at the registry key "HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM" */ /* to determine serial ports, and use libusb to discover USB instruments. */ @@ -23,9 +33,10 @@ /* return icom error */ int icompaths_refresh_paths(icompaths *p) { int rv, usbend = 0; - int i,j; + int i, j; LONG stat; HKEY sch; /* Serial coms handle */ + fast_com_name *fastlist = NULL, *fn, *fn2; a1logd(p->log, 8, "icoms_get_paths: called\n"); @@ -42,7 +53,131 @@ int icompaths_refresh_paths(icompaths *p) { a1logd(p->log, 6, "icoms_get_paths: got %d paths, looking up the registry for serial ports\n",p->npaths); -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + // (Beware KEY_WOW64_64KEY ?) + + /* See if there are and FTDI fast_serial ports, and make a list of them */ + if ((stat = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS", + 0, KEY_READ, &sch)) != ERROR_SUCCESS) { + a1logd(p->log, 1, "icoms_get_paths: There don't appear to be any FTDI serial ports\n"); + } else { + +#define MXKSIZE 500 +#define MXVSIZE 300 + + a1logd(p->log, 6, "icoms_get_paths: looking through FTDI ports\n"); + for (i = 0; ; i++) { + char ftdiname[MXKSIZE]; + DWORD ftdisize = MXKSIZE; + HKEY devkey; + + stat = RegEnumKeyEx( + sch, /* handle to key to enumerate */ + i, /* index of subkey to enumerate */ + ftdiname, /* address of buffer for value name */ + &ftdisize, /* address for size of value name buffer */ + NULL, /* reserved */ + NULL, /* Address of value type */ + NULL, /* Address of value buffer */ + NULL /* Address of value buffer size */ + ); + if (stat == ERROR_NO_MORE_ITEMS) { + a1logd(p->log, 9, "icoms_get_paths: got ERROR_NO_MORE_ITEMS\n"); + break; + } + if (stat == ERROR_MORE_DATA /* Hmm. Should expand buffer size */ + || stat != ERROR_SUCCESS) { + a1logw(p->log, "icoms_get_paths: RegEnumValue failed with %d\n",stat); + break; + } + ftdiname[MXKSIZE-1] = '\000'; + + /* Enumerate subkeys, looking for Device Parameters/PortName */ + if ((stat = RegOpenKeyEx(sch, ftdiname, 0, KEY_READ, &devkey)) != ERROR_SUCCESS) { + a1logw(p->log, "icoms_get_paths: OpenKey '%s' failed with %d\n",ftdiname,stat); + continue; + } + + a1logd(p->log, 6, "icoms_get_paths: looking through '%s'\n",ftdiname); + + for (j = 0; ; j++) { + char skname[MXKSIZE + 50]; /* Allow for cat of "\Device Parameters" */ + DWORD sksize = MXKSIZE; + HKEY skkey; + DWORD vtype; + char value[MXVSIZE]; + DWORD vsize = MXVSIZE; + + stat = RegEnumKeyEx( + devkey, /* handle to key to enumerate */ + j, /* index of subkey to enumerate */ + skname, /* address of buffer for value name */ + &sksize, /* address for size of value name buffer */ + NULL, /* reserved */ + NULL, /* Address of value type */ + NULL, /* Address of value buffer */ + NULL /* Address of value buffer size */ + ); + if (stat == ERROR_NO_MORE_ITEMS) { + a1logd(p->log, 9, "icoms_get_paths: got ERROR_NO_MORE_ITEMS\n"); + break; + } + if (stat == ERROR_MORE_DATA /* Hmm. Should expand buffer size */ + || stat != ERROR_SUCCESS) { + a1logw(p->log, "icoms_get_paths: RegEnumValue failed with %d\n",stat); + break; + } + skname[MXKSIZE-1] = '\000'; + + /* See if there is a Device Parameters\PortName */ + strcat(skname, "\\Device Parameters"); + + if ((stat = RegOpenKeyEx(devkey, skname, 0, KEY_READ, &skkey)) != ERROR_SUCCESS) { + a1logw(p->log, "icoms_get_paths: OpenKey '%s' failed with %d\n",skname,stat); + continue; + } + stat = RegQueryValueEx( + skkey, /* handle to key to enumerate */ + "PortName", /* address of buffer for value name */ + NULL, /* reserved */ + &vtype, /* Address of value type */ + value, /* Address of value buffer */ + &vsize /* Address of value buffer size */ + ); + RegCloseKey(skkey); + + if (stat == ERROR_MORE_DATA /* Hmm. Should expand buffer size */ + || stat != ERROR_SUCCESS) { + a1logw(p->log, "icoms_get_paths: RegQueryValueEx '%s' failed with %d\n",skname,stat); + break; + } + if (vtype != REG_SZ) { + a1logw(p->log, "icoms_get_paths: RegEnumValue '%s' didn't return stringz type\n",skname); + continue; + } + value[MXVSIZE-1] = '\000'; + + if ((fn = malloc(sizeof(fast_com_name))) == NULL) { + a1loge(p->log, 1, "icoms_get_paths: malloc failed\n"); + continue; + } + strcpy(fn->name, value); + fn->next = fastlist; + fastlist = fn; + a1logd(p->log, 2, "icoms_get_paths: got FTDI port '%s'\n",value); + + } + if ((stat = RegCloseKey(devkey)) != ERROR_SUCCESS) { + a1logw(p->log, "icoms_get_paths: RegCloseKey failed with %d\n",stat); + } + + } + if ((stat = RegCloseKey(sch)) != ERROR_SUCCESS) { + a1logw(p->log, "icoms_get_paths: RegCloseKey failed with %d\n",stat); + } + } + + /* Look in the registry for serial ports */ if ((stat = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, &sch)) != ERROR_SUCCESS) { @@ -52,13 +187,14 @@ int icompaths_refresh_paths(icompaths *p) { /* Look at all the values in this key */ a1logd(p->log, 8, "icoms_get_paths: looking through all the values in the SERIALCOMM key\n"); - + for (i = 0; ; i++) { - char valname[500]; - DWORD vnsize = 500; + char valname[MXKSIZE]; + DWORD vnsize = MXKSIZE; DWORD vtype; - char value[500]; - DWORD vsize = 500; + char value[MXVSIZE]; + DWORD vsize = MXVSIZE; + int fast = 0; stat = RegEnumValue( sch, /* handle to key to enumerate */ @@ -79,17 +215,36 @@ int icompaths_refresh_paths(icompaths *p) { a1logw(p->log, "icoms_get_paths: RegEnumValue failed with %d\n",stat); break; } - valname[500-1] = '\000'; - value[500-1] = '\000'; + valname[MXKSIZE-1] = '\000'; + value[MXVSIZE-1] = '\000'; if (vtype != REG_SZ) { a1logw(p->log, "icoms_get_paths: RegEnumValue didn't return stringz type\n"); continue; } + /* Check if it's a fast serial port */ + for (fn = fastlist; fn != NULL; fn = fn->next) { + if (strcmp(fn->name, value) == 0) + fast = 1; + } + /* Add the port to the list */ - p->add_serial(p, value, value); - a1logd(p->log, 8, "icoms_get_paths: Added path '%s'\n",value); + p->add_serial(p, value, value, fast); + a1logd(p->log, 8, "icoms_get_paths: Added path '%s' fast %d\n",value,fast); + + /* If fast, try and identify it */ + if (fast) { + icompath *path; + icoms *icom; + if ((path = p->get_last_path(p)) != NULL + && (icom = new_icoms(path, p->log)) != NULL) { + instType itype = fast_ser_inst_type(icom, 0, NULL, NULL); + if (itype != instUnknown) + icompaths_set_serial_itype(path, itype); + icom->del(icom); + } + } } if ((stat = RegCloseKey(sch)) != ERROR_SUCCESS) { a1logw(p->log, "icoms_get_paths: RegCloseKey failed with %d\n",stat); @@ -97,10 +252,14 @@ int icompaths_refresh_paths(icompaths *p) { #endif /* ENABLE_SERIAL */ /* Sort the COM keys so people don't get confused... */ + /* Sort identified instruments ahead of unknown serial ports */ a1logd(p->log, 6, "icoms_get_paths: we now have %d entries and are about to sort them\n",p->npaths); for (i = usbend; i < (p->npaths-1); i++) { for (j = i+1; j < p->npaths; j++) { - if (strcmp(p->paths[i]->name, p->paths[j]->name) > 0) { + if ((p->paths[i]->itype == instUnknown && p->paths[j]->itype != instUnknown) + || (((p->paths[i]->itype == instUnknown && p->paths[j]->itype == instUnknown) + || (p->paths[i]->itype != instUnknown && p->paths[j]->itype != instUnknown)) + && strcmp(p->paths[i]->name, p->paths[j]->name) > 0)) { icompath *tt = p->paths[i]; p->paths[i] = p->paths[j]; p->paths[j] = tt; @@ -110,6 +269,12 @@ int icompaths_refresh_paths(icompaths *p) { a1logd(p->log, 8, "icoms_get_paths: returning %d paths and ICOM_OK\n",p->npaths); + /* Free fast list */ + for (fn = fastlist; fn != NULL; fn = fn2) { + fn2 = fn->next; + free(fn); + } + return ICOM_OK; } @@ -124,7 +289,7 @@ static void icoms_close_port(icoms *p) { hid_close_port(p); } #endif -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) if (p->phandle != NULL) { CloseHandle(p->phandle); } @@ -133,10 +298,14 @@ static void icoms_close_port(icoms *p) { } } -#ifdef ENABLE_SERIAL +#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_read(icoms *p, char *rbuf, int bsize, char *tc, int ntc, double tout); + +#ifndef CBR_921600 +# define CBR_921600 921600 +#endif /* Set the serial port characteristics */ /* This always re-opens the port */ @@ -150,6 +319,7 @@ parity parity, stop_bits stop, word_length word) { + a1logd(p->log, 8, "icoms_set_ser_port: About to set port characteristics:\n" " Port name = %s\n" " Flow control = %d\n" @@ -180,10 +350,15 @@ word_length word) /* Make sure the port is open */ if (!p->is_open) { + char buf[50]; /* Temporary for COM device path */ + a1logd(p->log, 8, "icoms_set_ser_port: about to open serial port '%s'\n",p->spath); + /* Workaround bug of ports > COM9 */ + sprintf(buf, "\\\\.\\%s", p->spath); + if ((p->phandle = CreateFile( - p->spath, + buf, GENERIC_READ|GENERIC_WRITE, 0, /* Exclusive access */ NULL, /* No security */ @@ -191,7 +366,7 @@ word_length word) 0, /* No overlapped I/O */ NULL) /* NULL template */ ) == INVALID_HANDLE_VALUE) { - a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: open port '%s' failed with LastError %d\n",p->spath,GetLastError()); + a1logd(p->log, 1, "icoms_set_ser_port: open port '%s' failed with LastError %d\n",buf,GetLastError()); return ICOM_SYS; } p->is_open = 1; @@ -215,7 +390,8 @@ word_length word) dcb.fErrorChar = FALSE; dcb.fNull = FALSE; dcb.fRtsControl = RTS_CONTROL_ENABLE; /* Turn RTS on during connection */ - dcb.fAbortOnError = TRUE; +// dcb.fAbortOnError = TRUE; // Hmm. Stuffs up FTDI. Is it needed ? + dcb.fAbortOnError = FALSE; switch (p->fc) { case fc_nc: @@ -329,6 +505,9 @@ word_length word) case baud_115200: dcb.BaudRate = CBR_115200; break; + case baud_921600: + dcb.BaudRate = CBR_921600; + break; default: CloseHandle(p->phandle); a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal baud rate! (0x%x)\n",p->br); @@ -370,7 +549,7 @@ double tout) { COMMTIMEOUTS tmo; DWORD wbytes; - int c, len; + int len; long toc, i, top; /* Timout count, counter, timeout period */ int rv = ICOM_OK; @@ -384,7 +563,7 @@ double tout) len = strlen(wbuf); tout *= 1000.0; /* Timout in msec */ - top = 100; /* Timeout period in msecs */ + top = 20; /* Timeout period in msecs */ toc = (int)(tout/top + 0.5); /* Number of timout periods in timeout */ if (toc < 1) toc = 1; @@ -393,8 +572,8 @@ double tout) tmo.ReadIntervalTimeout = top; tmo.ReadTotalTimeoutMultiplier = 0; tmo.ReadTotalTimeoutConstant = top; - tmo.WriteTotalTimeoutMultiplier = top; - tmo.WriteTotalTimeoutConstant = 0; + tmo.WriteTotalTimeoutMultiplier = 0; + tmo.WriteTotalTimeoutConstant = top; if (!SetCommTimeouts(p->phandle, &tmo)) { a1loge(p->log, ICOM_SYS, "icoms_ser_write: SetCommTimeouts failed with %d\n",GetLastError()); p->lserr = rv = ICOM_SYS; @@ -441,13 +620,13 @@ icoms_ser_read( icoms *p, char *rbuf, /* Buffer to store characters read */ int bsize, /* Buffer size */ -char tc, /* Terminating characer */ -int ntc, /* Number of terminating characters */ +char *tc, /* Terminating characers, NULL for none */ +int ntc, /* Number of terminating characters seen */ double tout /* Time out in seconds */ ) { COMMTIMEOUTS tmo; DWORD rbytes; - int c, j; + int j; long toc, i, top; /* Timout count, counter, timeout period */ char *rrbuf = rbuf; /* Start of return buffer */ DCB dcb; @@ -460,35 +639,15 @@ double tout /* Time out in seconds */ } if (bsize < 3) { - a1loge(p->log, ICOM_SYS, "icoms_read: given too small a buffer\n"); + a1loge(p->log, ICOM_SYS, "icoms_read: given too small a bufferi (%d)\n",bsize); p->lserr = rv = ICOM_SYS; return rv; } -#ifdef NEVER - /* The Prolific 2303 USB<->serial seems to choke on this, */ - /* so we just put up with a 100msec delay at the end of each */ - /* reply. */ - if (GetCommState(p->phandle, &dcb) == FALSE) { - a1loge(p->log, ICOM_SYS, "icoms_ser_read: GetCommState failed with %d\n",GetLastError()); - p->lserr = rv = ICOM_SYS; - return rv; - } - - dcb.EofChar = tc; - - if (!SetCommState(p->phandle, &dcb)) { - a1loge(p->log, ICOM_SYS, "icoms_ser_read: SetCommState failed %d\n",GetLastError()); - p->lserr = rv = ICOM_SYS; - return rv; - } -#endif - p->tc = tc; - tout *= 1000.0; /* Timout in msec */ bsize--; /* Allow space for null */ - top = 100; /* Timeout period in msecs */ + top = 20; /* Timeout period in msecs */ toc = (int)(tout/top + 0.5); /* Number of timout periods in timeout */ if (toc < 1) toc = 1; @@ -497,8 +656,8 @@ double tout /* Time out in seconds */ tmo.ReadIntervalTimeout = top; tmo.ReadTotalTimeoutMultiplier = 0; tmo.ReadTotalTimeoutConstant = top; - tmo.WriteTotalTimeoutMultiplier = top; - tmo.WriteTotalTimeoutConstant = 0; + tmo.WriteTotalTimeoutMultiplier = 0; + tmo.WriteTotalTimeoutConstant = top; if (!SetCommTimeouts(p->phandle, &tmo)) { a1loge(p->log, ICOM_SYS, "icoms_ser_read: SetCommTimeouts failed with %d\n",GetLastError()); p->lserr = rv = ICOM_SYS; @@ -525,10 +684,18 @@ double tout /* Time out in seconds */ } else if (rbytes > 0) { /* Account for bytes done */ i = toc; bsize -= rbytes; - while(rbytes--) { /* Count termination characters */ - if (*rbuf++ == tc) - j++; - } + if (tc != NULL) { + while(rbytes--) { /* Count termination characters */ + char ch = *rbuf++, *tcp = tc; + + while(*tcp != '\000') { + if (ch == *tcp) + j++; + tcp++; + } + } + } else + rbuf += rbytes; } } if (i <= 0) { /* timed out */ @@ -557,6 +724,13 @@ icoms_del(icoms *p) { usb_del_usb(p); hid_del_hid(p); #endif +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + if (p->spath != NULL) + free(p->spath); +#endif + p->log = del_a1log(p->log); + if (p->name != NULL) + free(p->name); p->log = del_a1log(p->log); /* unref */ free (p); } diff --git a/spectro/icoms_ux.c b/spectro/icoms_ux.c index 722492e..3e49f3e 100644 --- a/spectro/icoms_ux.c +++ b/spectro/icoms_ux.c @@ -46,6 +46,10 @@ #include <mach/task_policy.h> #endif /* __APPLE__ */ +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) +instType fast_ser_inst_type(icoms *p, int tryhard, void *, void *); +#endif /* ENABLE_SERIAL */ + /* Create and return a list of available serial ports or USB instruments for this system */ /* return icom error */ int icompaths_refresh_paths(icompaths *p) { @@ -65,7 +69,7 @@ int icompaths_refresh_paths(icompaths *p) { #endif /* ENABLE_USB */ usbend = p->npaths; -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) #ifdef __APPLE__ /* Search the OSX registry for serial ports */ { @@ -93,7 +97,8 @@ int icompaths_refresh_paths(icompaths *p) { /* Find all the matching serial ports */ for (;;) { - char pname[100]; + char pname[200]; + int fast = 0; CFTypeRef dfp; /* Device file path */ @@ -115,10 +120,26 @@ int icompaths_refresh_paths(icompaths *p) { || strstr(pname, "Bluetooth") != NULL) goto continue2; - /* Add the port to the list */ - p->add_serial(p, pname, pname); - a1logd(p->log, 8, "icoms_get_paths: Added path '%s'\n",pname); + /* Would be nice to identify FTDI serial ports more specifically ? */ + if (strstr(pname, "usbserial") != NULL) + fast = 1; + /* 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); + + /* If fast, try and identify it */ + if (fast) { + icompath *path; + icoms *icom; + if ((path = p->get_last_path(p)) != NULL + && (icom = new_icoms(path, p->log)) != NULL) { + instType itype = fast_ser_inst_type(icom, 0, NULL, NULL); + if (itype != instUnknown) + icompaths_set_serial_itype(path, itype); + icom->del(icom); + } + } continue2: CFRelease(dfp); continue1: @@ -177,6 +198,7 @@ int icompaths_refresh_paths(icompaths *p) { */ /* Search for devices that match the pattern /dev/ttyS[0-9]* and /dev/ttyUSB* */ + /* We will assume that ttyUSB* ports includes FTDI ports */ { DIR *dd; struct dirent *de; @@ -190,6 +212,7 @@ int icompaths_refresh_paths(icompaths *p) { for (;;) { int fd; char *dpath; + int fast = 0; if ((de = readdir(dd)) == NULL) break; @@ -242,9 +265,26 @@ int icompaths_refresh_paths(icompaths *p) { } a1logd(p->log, 8, "icoms_get_paths: open'd serial \"%s\" - assume real\n",dpath); + if (strncmp(de->d_name, "ttyUSB", 5) == 0) + fast = 1; + /* Add the path to the list */ - p->add_serial(p, dpath, dpath); - a1logd(p->log, 8, "icoms_get_paths: Added path '%s'\n",dpath); + p->add_serial(p, dpath, dpath, 0); + a1logd(p->log, 8, "icoms_get_paths: Added path '%s' fast %d\n",dpath,fast); + free(dpath); + + /* If fast, try and identify it */ + if (fast) { + icompath *path; + icoms *icom; + if ((path = p->get_last_path(p)) != NULL + && (icom = new_icoms(path, p->log)) != NULL) { + instType itype = fast_ser_inst_type(icom, 0, NULL, NULL); + if (itype != instUnknown) + icompaths_set_serial_itype(path, itype); + icom->del(icom); + } + } } closedir(dd); } @@ -252,12 +292,13 @@ int icompaths_refresh_paths(icompaths *p) { #endif /* ENABLE_SERIAL */ /* Sort the serial /dev keys so people don't get confused... */ - /* Sort USB serial ports ahead of normal serial ports. */ + /* Sort identified instruments ahead of unknown serial ports */ for (i = usbend; i < (p->npaths-1); i++) { for (j = i+1; j < p->npaths; j++) { - if ((strncmp(p->paths[i]->name, "/dev/ttyUSB", 11) || - strncmp(p->paths[j]->name, "/dev/ttyS", 9)) && - strcmp(p->paths[i]->name, p->paths[j]->name) > 0) { + if ((p->paths[i]->itype == instUnknown && p->paths[j]->itype != instUnknown) + || (((p->paths[i]->itype == instUnknown && p->paths[j]->itype == instUnknown) + || (p->paths[i]->itype != instUnknown && p->paths[j]->itype != instUnknown)) + && strcmp(p->paths[i]->name, p->paths[j]->name) > 0)) { icompath *tt = p->paths[i]; p->paths[i] = p->paths[j]; p->paths[j] = tt; @@ -279,7 +320,7 @@ static void icoms_close_port(icoms *p) { hid_close_port(p); } #endif -#ifdef ENABLE_SERIAL +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) if (p->fd != -1) { close(p->fd); p->fd = -1; @@ -289,10 +330,20 @@ static void icoms_close_port(icoms *p) { } } -#ifdef ENABLE_SERIAL +#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_read(icoms *p, char *rbuf, int bsize, char *tc, int ntc, double tout); + + +#ifdef __APPLE__ +# ifndef IOSSIOSPEED +# define IOSSIOSPEED _IOW('T', 2, speed_t) +# endif +# ifndef B921600 +# define B921600 921600 +# endif +#endif /* Set the serial port number and characteristics */ /* This always re-opens the port */ @@ -344,7 +395,7 @@ word_length word a1logd(p->log, 8, "icoms_set_ser_port: about to open serial port '%s'\n",p->spath); if ((p->fd = open(p->spath, O_RDWR | O_NOCTTY )) < 0) { - a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: open port '%s' failed with %d (%s)\n",p->spath,p->fd,strerror(errno)); + a1logd(p->log, 1, "icoms_set_ser_port: open port '%s' failed with %d (%s)\n",p->spath,p->fd,strerror(errno)); return ICOM_SYS; } /* O_NONBLOCK O_SYNC */ @@ -490,6 +541,9 @@ word_length word case baud_115200: speed = B115200; break; + case baud_921600: + speed = B921600; + break; default: close(p->fd); a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal baud rate! (0x%x)\n",p->br); @@ -498,6 +552,15 @@ word_length word tcflush(p->fd, TCIOFLUSH); /* Discard any current in/out data */ +#ifdef NEVER + // for osx >= 10.4 ? or >= 10.3 ?? + // Doesn't actually seem to be needed... ?? + if (speed > B115200) { + if (ioctl(p->fd, IOSSIOSPEED, &speed ) == -1) + printf( "Error %d calling ioctl( ..., IOSSIOSPEED, ... )\n", errno ); + + } +#endif if ((rv = cfsetispeed(&tio, speed)) < 0) { close(p->fd); a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: cfsetispeed failed with '%s'\n", strerror(errno)); @@ -611,7 +674,7 @@ icoms_ser_read( icoms *p, char *rbuf, /* Buffer to store characters read */ int bsize, /* Buffer size */ -char tc, /* Terminating characer */ +char *tc, /* Terminating characers, NULL for none */ int ntc, /* Number of terminating characters */ double tout /* Time out in seconds */ ) { @@ -635,34 +698,6 @@ double tout /* Time out in seconds */ return rv; } -#ifdef NEVER - /* The Prolific 2303 USB<->serial seems to choke on this, */ - /* so we just put up with the 100msec delay at the end of each reply. */ - if (tc != p->tc) { /* Set the termination char */ - struct termios tio; - - if (tcgetattr(p->fd, &tio) < 0) { - a1loge(p->log, ICOM_SYS, "icoms_ser_read: tcgetattr failed with '%s' on '%s'\n", - strerror(errno),p->spath); - p->lserr = rv = ICOM_SYS; - return rv; - } - - tio.c_cc[VEOL] = tc; - - /* Make change immediately */ - tcflush(p->fd, TCIFLUSH); - if (tcsetattr(p->fd, TCSANOW, &tio) < 0) { - a1loge(p->log, ICOM_SYS, "icoms_ser_read: tcsetattr failed with '%s' on '%s'\n", - strerror(errno),p->spath); - p->lserr = rv = ICOM_SYS; - return rv; - } - - p->tc = tc; - } -#endif - /* Wait for serial input to have data */ pa[0].fd = p->fd; pa[0].events = POLLIN | POLLPRI; @@ -695,10 +730,18 @@ double tout /* Time out in seconds */ } else if (rbytes > 0) { i = toc; /* Reset time */ bsize -= rbytes; - while(rbytes--) { /* Count termination characters */ - if (*rbuf++ == tc) - j++; - } + if (tc != NULL) { + while(rbytes--) { /* Count termination characters */ + char ch = *rbuf++, *tcp = tc; + + while(*tcp != '\000') { + if (ch == *tcp) + j++; + tcp++; + } + } + } else + rbuf += rbytes; } } } else { @@ -733,6 +776,13 @@ icoms_del(icoms *p) { usb_del_usb(p); hid_del_hid(p); #endif +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + if (p->spath != NULL) + free(p->spath); +#endif + p->log = del_a1log(p->log); + if (p->name != NULL) + free(p->name); p->log = del_a1log(p->log); free (p); } diff --git a/spectro/ifiles b/spectro/ifiles index 6e73997..717fcca 100644 --- a/spectro/ifiles +++ b/spectro/ifiles @@ -8,7 +8,11 @@ huey.c i1d3.c spyd2.c ss.c +ss_imp.c i1pro.c +i1pro_imp.c munki.c +munki_imp.c hcfr.c colorhug.c +specbos.c diff --git a/spectro/illumread.c b/spectro/illumread.c index 282ec2f..7cc7eae 100644 --- a/spectro/illumread.c +++ b/spectro/illumread.c @@ -30,7 +30,7 @@ */ -#undef DEBUG /* Save measurements and restore them to outname_X.sp */ +#define DEBUG /* Save measurements and restore them to outname_X.sp */ /* outname_i.sp Illuminant spectrum @@ -41,7 +41,7 @@ */ -#define SHOWDXX /* Plot the best matched daylight as well */ +#undef SHOWDXX /* Plot the best matched daylight as well */ #include <stdio.h> #include <stdlib.h> @@ -109,7 +109,7 @@ typedef struct { xsp2cie *pap; /* Estimated illuminant + UV on paper lookup inc FWA */ xsp2cie *ref; /* Measured illuminant on paper lookup */ - xspect ill; /* Illuminant with UV added */ + xspect ill; /* Illuminant with UV added - set by bfindfunc() */ xspect cpsp; /* FWA corrected calculated paper reflectance */ xspect srop; /* Scaled measured paper reflectance */ @@ -124,7 +124,7 @@ static double bfindfunc(void *adata, double pv[]) { /* Add UV level to illuminant */ b->ill = *b->i_sp; - xsp_setUV(&b->ill, b->i_sp, pv[0]); + xsp_setUV(&b->ill, b->i_sp, pv[0]); /* Extends ill range into UV */ /* Update the conversion */ if (b->pap->update_fwa_custillum(b->pap, NULL, &b->ill) != 0) @@ -212,12 +212,20 @@ static double cfindfunc(void *adata, double pv[]) { /* ============================================================== */ void -usage() { +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: "); + va_start(args, diag); + vfprintf(stderr, diag, args); + va_end(args); + fprintf(stderr,"\n"); + } fprintf(stderr,"usage: illumread [-options] output.sp\n"); fprintf(stderr," -v Verbose mode\n"); fprintf(stderr," -S Plot spectrum for each reading\n"); @@ -239,6 +247,8 @@ usage() { } fprintf(stderr," -N Disable initial calibration of instrument if possible\n"); fprintf(stderr," -H Use high resolution spectrum mode (if available)\n"); + 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," -D [level] Print debug diagnostics to stderr\n"); fprintf(stderr," illuminant.sp File to save measurement to\n"); @@ -257,6 +267,8 @@ int main(int argc, char *argv[]) int nocal = 0; /* Disable initial calibration */ int pspec = 0; /* 2 = Plot out the spectrum for each reading */ int highres = 0; /* Use high res mode if available */ + 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; @@ -316,7 +328,7 @@ int main(int argc, char *argv[]) } if (argv[fa][1] == '?') { - usage(); + usage(NULL); } else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { verb = 1; @@ -328,9 +340,9 @@ int main(int argc, char *argv[]) /* COM port */ } else if (argv[fa][1] == 'c') { fa = nfa; - if (na == NULL) usage(); + if (na == NULL) usage(NULL); comno = atoi(na); - if (comno < 1 || comno > 99) usage(); + if (comno < 1 || comno > 99) usage(NULL); /* No initial calibration */ } else if (argv[fa][1] == 'N') { @@ -340,18 +352,36 @@ int main(int argc, char *argv[]) } else if (argv[fa][1] == 'H') { highres = 1; + /* Extra flags */ + } else if (argv[fa][1] == 'Y') { + if (na == NULL) + usage(NULL); + + if (na[0] == 'R') { + if (na[1] != ':') + usage("-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); + } else if (na[0] == 'r') + refrmode = 1; + else + usage(NULL); + fa = nfa; + /* Serial port flow control */ } else if (argv[fa][1] == 'W') { fa = nfa; - if (na == NULL) usage(); - if (na[0] == 'n' || na[0] == 'N') - fc = fc_none; - else if (na[0] == 'h' || na[0] == 'H') - fc = fc_Hardware; - else if (na[0] == 'x' || na[0] == 'X') - fc = fc_XonXOff; - else - usage(); + if (na == NULL) usage(NULL); + + if (na[0] == 'n' || na[0] == 'N') + fc = fc_none; + else if (na[0] == 'h' || na[0] == 'H') + fc = fc_Hardware; + else if (na[0] == 'x' || na[0] == 'X') + fc = fc_XonXOff; + else + usage(NULL); } else if (argv[fa][1] == 'D') { debug = 1; @@ -361,7 +391,7 @@ int main(int argc, char *argv[]) } g_log->debug = debug; } else - usage(); + usage(NULL); } else break; @@ -369,7 +399,7 @@ int main(int argc, char *argv[]) /* Get the output spectrum file name argument */ if (fa >= argc) - usage(); + usage(NULL); strncpy(outname,argv[fa++],MAXNAMEL-1); outname[MAXNAMEL-1] = '\000'; @@ -495,25 +525,22 @@ int main(int argc, char *argv[]) printf("Instrument lacks spectral measurement capability"); } + if (refrmode >= 0 && !IMODETST(cap, inst_mode_emis_refresh_ovd) + && !IMODETST(cap, inst_mode_emis_norefresh_ovd)) { + if (verb) { + printf("Requested refresh mode override and instrument doesn't support it (ignored)\n"); + refrmode = -1; + } + } + /* Disable iniial calibration of machine if selected */ - if (nocal != 0){ + if (nocal != 0) { if ((rv = it->get_set_opt(it,inst_opt_noinitcalib, 0)) != inst_ok) { printf("Setting no-initial calibrate failed failed with '%s' (%s) !!!\n", it->inst_interp_error(it, rv), it->interp_error(it, rv)); printf("Disable initial-calibrate not supported\n"); } } - if (highres) { - if (IMODETST(cap, inst_mode_highres)) { - inst_code ev; - if ((ev = it->get_set_opt(it, inst_opt_highres)) != inst_ok) { - printf("!!! Setting high res mode failed with error :'%s' (%s) !!!\n", - it->inst_interp_error(it, ev), it->interp_error(it, ev)); - } - } else if (verb) { - printf("!!! High resolution ignored - instrument doesn't support it !!!\n"); - } - } } /* Check the instrument has the necessary capabilities for this measurement */ @@ -558,6 +585,19 @@ int main(int argc, char *argv[]) } mode |= inst_mode_spectral; + if (refrmode == 0) + mode |= inst_mode_emis_norefresh_ovd; + else if (refrmode == 1) + mode |= inst_mode_emis_refresh_ovd; + + if (highres) { + if (IMODETST(cap, inst_mode_highres)) { + mode |= inst_mode_highres; + } else if (verb) { + printf("!!! High resolution ignored - instrument doesn't support it !!!\n"); + } + } + if ((rv = it->set_mode(it, mode)) != inst_ok) { printf("!!! Setting instrument mode failed with error :'%s' (%s) !!!\n", it->inst_interp_error(it, rv), it->interp_error(it, rv)); @@ -565,6 +605,19 @@ int main(int argc, char *argv[]) } it->capabilities(it, &cap, &cap2, &cap3); + if (refrate > 0.0) { + if (!(cap2 & inst2_set_refresh_rate)) { + if (verb) + printf("Attempted to set refresh rate and instrument doesn't support setting it (ignored)\n"); + refrate = 0.0; + } else { + if ((rv = it->set_refr_rate(it, refrate)) != inst_ok) { + error("Setting instrument refresh rate to %f Hz failed with error :'%s' (%s)", + refrate, it->inst_interp_error(it, rv), it->interp_error(it, rv)); + } + } + } + /* If it batter powered, show the status of the battery */ if ((cap2 & inst2_has_battery)) { double batstat = 0.0; diff --git a/spectro/inst.c b/spectro/inst.c index d57a84a..2d3adc3 100644 --- a/spectro/inst.c +++ b/spectro/inst.c @@ -49,18 +49,21 @@ #include "ccmx.h" #include "ccss.h" #include "conv.h" -#include "insttypes.h" +#include "insttypes.h" #include "icoms.h" #include "inst.h" #include "insttypeinst.h" - #include "sort.h" -#ifdef ENABLE_SERIAL +#if defined(ENABLE_FAST_SERIAL) +instType fast_ser_inst_type(icoms *p, int tryhard, + inst_code (*uicallback)(void *cntx, inst_ui_purp purp), void *cntx); +# if defined(ENABLE_SERIAL) static instType ser_inst_type(icoms *p, -inst_code (*uicallback)(void *cntx, inst_ui_purp purp), void *cntx); -#endif /* ENABLE_SERIAL */ + inst_code (*uicallback)(void *cntx, inst_ui_purp purp), void *cntx); +# endif /* ENABLE_SERIAL */ +#endif /* ENABLE_FAST_SERIAL */ /* ------------------------------------ */ /* Default methods for instrument class */ @@ -336,10 +339,10 @@ inst_cal_type *n_cals, inst_cal_type *a_cals) { if (n_cals != NULL) - *n_cals = inst_calc_none; + *n_cals = inst_calt_none; if (a_cals != NULL) - *a_cals = inst_mode_none; + *a_cals = inst_calt_none; return inst_ok; } @@ -521,13 +524,23 @@ void *cntx /* Context for callback */ icoms *icom; inst *p = NULL; + a1logd(log, 2, "new_inst: called with path '%s'\n",path->name); + if ((icom = new_icoms(path, log)) == NULL) { + a1logd(log, 2, "new_inst: new_icoms failed to open it\n"); return NULL; } + /* Set instrument type from USB port, if not specified */ itype = icom->itype; /* Instrument type if its known from usb/hid */ -#ifdef ENABLE_SERIAL + +#if defined(ENABLE_FAST_SERIAL) + 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) if (itype == instUnknown && !nocoms) itype = ser_inst_type(icom, uicallback, cntx); /* Else type from serial */ #endif /* ENABLE_SERIAL */ @@ -552,6 +565,12 @@ void *cntx /* Context for callback */ */ #endif /* ENABLE_SERIAL */ +#ifdef ENABLE_FAST_SERIAL + if (itype == instSpecbos1201 + || itype == instSpecbos) + p = (inst *)new_specbos(icom, itype); +#endif /* ENABLE_SERIAL */ + #ifdef ENABLE_USB if (itype == instDTP94) p = (inst *)new_dtp92(icom, itype); @@ -585,9 +604,14 @@ void *cntx /* Context for callback */ p = (inst *)new_colorhug(icom, itype); #endif /* ENABLE_USB */ + + /* Nothing matched */ - if (p == NULL) + if (p == NULL) { + a1logd(log, 2, "new_inst: instrument type not recognised\n"); + icom->del(icom); return NULL; + } /* Add default methods if constructor did not supply them */ if (p->init_coms == NULL) @@ -696,6 +720,35 @@ void *cntx /* Context for callback */ 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) { @@ -751,17 +804,31 @@ static inst_disptypesel *expand_dlist(inst_disptypesel *list, int nlist, int *na 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 */ -static int set_sel(char *sel, char *usels, int *k, char *asels) { - char *d, *s; +/* 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 (d = s = sel; *s != '\000'; s++) { - if (usels[*s] == 0) + 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'; @@ -805,9 +872,9 @@ int doccmx /* Add matching installed ccmx files */ for (i = 0; i < 256; i++) usels[i] = 0; - k = 0; + k = 0; /* Next selector index */ - /* Add entries from the static list */ + /* Add entries from the static list and their primary selectors */ /* Count the number in the static list */ for (i = 0; !(sdtlist[i].flags & inst_dtflags_end); i++) { @@ -816,14 +883,12 @@ int doccmx /* Add matching installed ccmx files */ list[nlist-1] = sdtlist[i]; /* Struct copy */ - if (set_sel(list[nlist-1].sel, usels, &k, asels)) { + if (set_sel(1, list[nlist-1].sel, usels, &k, asels)) { a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n"); break; } } - k = 0; /* Next selector index */ - /* Add any ccss's */ if (doccss) { iccss *ss_list; @@ -843,7 +908,7 @@ int doccmx /* Add matching installed ccmx files */ 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(list[nlist-1].sel, usels, &k, asels)) { + if (set_sel(1, list[nlist-1].sel, usels, &k, asels)) { a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n"); break; } @@ -889,7 +954,7 @@ int doccmx /* Add matching installed ccmx files */ 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(list[nlist-1].sel, usels, &k, asels)) { + if (set_sel(1, list[nlist-1].sel, usels, &k, asels)) { a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n"); break; } @@ -902,6 +967,11 @@ int doccmx /* Add matching installed ccmx files */ } } + /* Verify or delete any secondary selectors from the list */ + for (i = 0; i < nlist; i++) { + set_sel(2, list[i].sel, usels, &k, asels); + } + if (pndtlist != NULL) *pndtlist = nlist; if (pdtlist != NULL) @@ -963,6 +1033,7 @@ iccmx *list_iccmx(char *inst, int *no) { if (inst != NULL && cs->inst != NULL && strcmp(inst, cs->inst) != 0) continue; + a1logd(g_log, 5, "Reading '%s'\n",paths[i]); if ((tech = cs->tech) == NULL) tech = ""; if ((disp = cs->disp) == NULL) @@ -1089,6 +1160,7 @@ iccss *list_iccss(int *no) { continue; /* Skip any unreadable ccss's */ } + a1logd(g_log, 5, "Reading '%s'\n",paths[i]); if ((tech = cs->tech) == NULL) tech = ""; if ((disp = cs->disp) == NULL) @@ -1170,6 +1242,102 @@ void free_iccss(iccss *list) { } /* ============================================================= */ +/* Detect fast serial instruments */ + +#ifdef ENABLE_FAST_SERIAL +static void hex2bin(char *buf, int len); + +/* Heuristicly determine the instrument type for */ +/* a fast serial connection, and instUnknown if not serial. */ +/* Set it in icoms and also return it. */ +instType fast_ser_inst_type( + icoms *p, + int tryhard, + inst_code (*uicallback)(void *cntx, inst_ui_purp purp), /* optional uicallback */ + void *cntx /* Context for callback */ +) { + instType rv = instUnknown; + char buf[100]; + baud_rate brt[] = { baud_921600, baud_115200, baud_38400, baud_nc }; + unsigned int etime; + unsigned int i; + int se, len; + + if (p->port_type(p) != icomt_serial + && p->port_type(p) != icomt_usbserial) + return p->itype; + + /* The tick to give up on */ + etime = msec_time() + (long)(2000.0 + 0.5); + + a1logd(p->log, 1, "fser_inst_type: 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; + if (!tryhard) + break; /* try only once */ + } + if ((se = p->set_ser_port(p, fc_none, brt[i], parity_none, + stop_1, length_8)) != ICOM_OK) { + a1logd(p->log, 5, "fser_inst_type: set_ser_port failed with 0x%x\n",se); + return instUnknown; /* Give up */ + } + +// 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; + } + } + 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; + } + } + + if (rv == instUnknown + && msec_time() >= etime) { /* We haven't established comms */ + a1logd(p->log, 5, "fser_inst_type: Failed to establish coms\n"); + p->itype = rv; + return instUnknown; + } + + a1logd(p->log, 5, "fser_inst_type: Instrument type is '%s'\n", inst_name(rv)); + + p->itype = rv; + + return rv; +} + +#endif /* ENABLE_FAST_SERIAL */ + +/* ============================================================= */ +/* Detect serial instruments */ #ifdef ENABLE_SERIAL static void hex2bin(char *buf, int len); @@ -1217,7 +1385,7 @@ 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) { + if ((se = p->write_read(p, ";D024\r\n", buf, 100, "\r", 1, 0.5)) != inst_ok) { /* Check for user abort */ if (uicallback != NULL) { inst_code ev; @@ -1226,6 +1394,7 @@ static instType ser_inst_type( return instUnknown; } } + continue; } len = strlen(buf); @@ -1254,7 +1423,8 @@ static instType ser_inst_type( } } - if (msec_time() >= etime) { /* We haven't established comms */ + if (rv == instUnknown + && msec_time() >= etime) { /* We haven't established comms */ a1logd(p->log, 5, "ser_inst_type: Failed to establish coms\n"); return instUnknown; } @@ -1269,7 +1439,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", buf, 100, "\n", 1, 1.5)) == 0) { if (strlen(buf) >= 41) { hex2bin(&buf[5], 12); // a1logd(p->log, 5, "spectroscan type = '%s'\n",buf); @@ -1281,7 +1451,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", buf, 100, ">", 1, 2.5)) != 0) return instUnknown; if (strlen(buf) >= 12) { @@ -1311,6 +1481,10 @@ static instType ser_inst_type( return rv; } +#endif /* ENABLE_SERIAL */ + +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + /* Convert an ASCII Hex character to an integer. */ static int h2b(char c) { if (c >= '0' && c <= '9') diff --git a/spectro/inst.h b/spectro/inst.h index 5b5d710..d69a37f 100644 --- a/spectro/inst.h +++ b/spectro/inst.h @@ -66,14 +66,15 @@ #define ICOM_MAX_LOC_LEN 10 /* Type of measurement result */ -typedef enum { /* XYZ units, spectral units */ +typedef enum { /* XYZ units, Spectral units */ inst_mrt_none = 0, /* Not set */ - inst_mrt_emission = 1, /* cd/m^2, mW/m^2/nm */ - inst_mrt_ambient = 2, /* Lux/3.1415926 ?? */ - inst_mrt_emission_flash = 3, /* cd/m^2.s, mW/m^2/nm.s*/ - inst_mrt_ambient_flash = 4, /* Lux/3.1415926/s ?? */ + 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_reflective = 5, /* %, %/nm */ - inst_mrt_transmissive = 6 /* %, %/nm */ + inst_mrt_transmissive = 6, /* %, %/nm */ + inst_mrt_frequency = 7 /* Hz */ } inst_meas_type; struct _ipatch { @@ -101,31 +102,31 @@ struct _ipatch { /* Note :- update inst_interp_error() in inst.c if anything here is changed. */ /* and also check all the instrument specific XXX_interp_code() routines too. */ typedef enum { - inst_ok = 0x0000, - inst_notify = 0x0100, /* A Notification */ - inst_warning = 0x0200, /* A Warning */ - inst_no_coms = 0x0300, /* init_coms() hasn't been called yet */ - inst_no_init = 0x0400, /* init_inst() hasn't been called yet */ - inst_unsupported = 0x0500, /* Unsupported function */ - inst_internal_error = 0x0600, /* Internal software error */ - inst_coms_fail = 0x0700, /* Communication failure */ - inst_unknown_model = 0x0800, /* Not the expected instrument */ - inst_protocol_error = 0x0900, /* Read or Write protocol error */ - inst_user_abort = 0x0A00, /* User hit escape */ - inst_user_trig = 0x0C00, /* User hit trigger key */ - inst_misread = 0x0E00, /* Bad reading, or strip misread */ - inst_nonesaved = 0x0F00, /* No saved data to read */ - inst_nochmatch = 0x1000, /* Chart doesn't match */ - inst_needs_cal = 0x1100, /* Instrument needs calibration, and read retried */ - inst_cal_setup = 0x1200, /* Calibration retry with correct setup is needed */ - inst_wrong_config = 0x1300, /* Retry with correct inst. config./sensor posn. needed */ - inst_unexpected_reply = 0x1400, /* Unexpected Reply */ - inst_wrong_setup = 0x1500, /* Setup is wrong or conflicting */ - inst_hardware_fail = 0x1600, /* Hardware failure */ - inst_bad_parameter = 0x1700, /* Bad parameter value */ - inst_other_error = 0x1800, /* Some other error */ - inst_mask = 0xff00, /* inst_code mask value */ - inst_imask = 0x00ff /* instrument specific mask value */ + inst_ok = 0x000000, + inst_notify = 0x010000, /* A Notification */ + inst_warning = 0x020000, /* A Warning */ + inst_no_coms = 0x030000, /* init_coms() hasn't been called yet */ + inst_no_init = 0x040000, /* init_inst() hasn't been called yet */ + inst_unsupported = 0x050000, /* Unsupported function */ + inst_internal_error = 0x060000, /* Internal software error */ + inst_coms_fail = 0x070000, /* Communication failure */ + inst_unknown_model = 0x080000, /* Not the expected instrument */ + inst_protocol_error = 0x090000, /* Read or Write protocol error */ + inst_user_abort = 0x0A0000, /* User hit escape */ + inst_user_trig = 0x0C0000, /* User hit trigger key */ + inst_misread = 0x0E0000, /* Bad reading, or strip misread */ + inst_nonesaved = 0x0F0000, /* No saved data to read */ + inst_nochmatch = 0x100000, /* Chart doesn't match */ + inst_needs_cal = 0x110000, /* Instrument needs calibration, and read retried */ + inst_cal_setup = 0x120000, /* Calibration retry with correct setup is needed */ + inst_wrong_config = 0x130000, /* Retry with correct inst. config./sensor posn. needed */ + 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_mask = 0xff0000, /* inst_code mask value */ + inst_imask = 0x00ffff /* instrument specific mask value */ } inst_code; /* Instrument capabilities & modes */ @@ -257,7 +258,11 @@ typedef enum { inst2_xy_position = 0x00000004, /* Can be positioned at a given location */ inst2_meas_disp_update = 0x00000010, /* Is able to measure display update delay */ - inst2_refresh_rate = 0x00000020, /* Is able to retrieve the calibrated refresh rate */ + + inst2_get_refresh_rate = 0x00000020, /* Is able to get the calibrated refresh rate */ + inst2_set_refresh_rate = 0x00000040, /* Is able to set the calibrated refresh rate */ + + inst2_emis_refr_meas = 0x00000080, /* Has an emissive refresh rate measurement func. */ inst2_prog_trig = 0x00000100, /* Progromatic trigger measure capability */ inst2_user_trig = 0x00000200, /* User trigger measure capability */ @@ -270,17 +275,21 @@ typedef enum { inst2_no_feedback = 0x00008000, /* Instrument doesn't give any user feedback */ inst2_has_leds = 0x00200000, /* Instrument has some user viewable indicator LEDs */ - inst2_has_sensmode = 0x00400000, /* Instrument can report it's sensors mode */ + inst2_has_target = 0x00400000, /* Instrument has aiming target */ + inst2_has_sensmode = 0x00800000, /* Instrument can report it's sensors mode */ + + inst2_has_battery = 0x01000000, /* Instrument is battery powered */ - inst2_has_battery = 0x00800000, /* Instrument is battery powered */ + inst2_disptype = 0x02000000, /* Has a display type selector */ + inst2_ccmx = 0x04000000, /* Colorimeter Correction Matrix capability */ + inst2_ccss = 0x08000000, /* Colorimeter Cal. Spectral Set capability */ - inst2_disptype = 0x01000000, /* Has a display type selector */ - inst2_ccmx = 0x02000000, /* Colorimeter Correction Matrix capability */ - inst2_ccss = 0x04000000, /* Colorimeter Cal. Spectral Set capability */ + inst2_ambient_mono = 0x10000000, /* The ambient measurement is monochrome */ - inst2_ambient_mono = 0x08000000, /* The ambient measurement is monochrome */ + inst2_ambient_cardioid = 0x20000000, /* The ambient measurement has a cardioid response */ - inst2_emis_refr_meas = 0x10000000, /* Has an emissive refresh rate measurement func. */ + inst2_get_min_int_time = 0x40000000, /* Is able to get the minimum integration time */ + inst2_set_min_int_time = 0x80000000 /* Is able to set the minimum integration time */ } inst2_capability; @@ -297,7 +306,6 @@ typedef enum { inst_dtflags_ccss = 0x0002, /* ccss */ inst_dtflags_ccmx = 0x0004, /* ccmx */ inst_dtflags_end = 0x8000 /* end marker */ - } inst_dtflags; #define INST_DTYPE_SEL_LEN 10 @@ -310,7 +318,7 @@ typedef struct _inst_disptypesel { inst_dtflags flags; /* Attribute flags */ int cbid; /* Calibration base ID. NZ if valid ccmx calibration base. */ /* Should remain constant between releases */ - char sel[INST_DTYPE_SEL_LEN]; /* String of selector characters */ + char sel[INST_DTYPE_SEL_LEN]; /* String of selector character aliases */ char desc[INST_DTYPE_DESC_LEN]; /* Textural description */ int refr; /* Refresh mode flag */ @@ -353,9 +361,8 @@ 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 */ - /* Only takes effect after inst_opt_set_disp_type */ - /* or col_cal_spec_set() */ + inst_opt_set_ccss_obs = 0x000A, /* Set the observer used with ccss device types - */ + /* Not applicable to any other type of instrument. */ /* [args: icxObserverType obType,*/ /* xspect custObserver[3] */ @@ -371,7 +378,7 @@ typedef enum { inst_opt_trig_switch = 0x0010, /* Trigger using instrument switch [No args] */ inst_opt_trig_user_switch = 0x0011, /* Trigger from user via uicallback or switch (def) [No args] */ - inst_opt_highres = 0x0012, /* Enable high resolution spectral mode */ + inst_opt_highres = 0x0012, /* Enable high res spectral mode indep. of set_mode() */ inst_opt_stdres = 0x0013, /* Revert to standard resolution spectral mode */ inst_opt_scan_toll = 0x0014, /* Modify the patch scan recognition tollnce [double] */ @@ -383,7 +390,11 @@ typedef enum { inst_opt_get_pulse_ledmask = 0x0018, /* Get the bitmask for pulseable ind. LEDs [*int] */ 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_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_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_type; @@ -480,9 +491,11 @@ typedef enum { inst_calc_man_man_mask = 0x000000F0, /* user configured calibration mask */ inst_calc_emis_white = 0x00000100, /* Provide a white test patch */ - inst_calc_emis_grey = 0x00000200, /* Provide a grey test patch */ - inst_calc_emis_grey_darker = 0x00000300, /* Provide a darker grey test patch */ - inst_calc_emis_grey_ligher = 0x00000400, /* Provide a darker grey test patch */ + inst_calc_emis_80pc = 0x00000200, /* Provide an 80% white test patch */ + inst_calc_emis_grey = 0x00000300, /* Provide a grey test patch */ + inst_calc_emis_grey_darker = 0x00000400, /* Provide a darker grey test patch */ + inst_calc_emis_grey_ligher = 0x00000500, /* Provide a darker grey test patch */ + inst_calc_emis_mask = 0x00000F00, /* Emmissive/display provided reference patch */ inst_calc_change_filter = 0x00010000, /* Filter needs changing on device - see id[] */ @@ -803,6 +816,7 @@ typedef enum { int *msecdelay); /* Return the number of msec */ \ \ /* Return the last calibrated refresh rate in Hz. Returns: */ \ + /* (Available if cap2 & inst2_get_refresh_rate) */ \ /* inst_unsupported - if this instrument doesn't suport a refresh mode */ \ /* or is unable to retrieve the refresh rate */ \ /* inst_needs_cal - if the refresh rate value is not valid */ \ @@ -813,6 +827,7 @@ typedef enum { double *ref_rate); /* Return the Hz */ \ \ /* Set the calibrated refresh rate in Hz. */ \ + /* (Available if cap2 & inst2_set_refresh_rate) */ \ /* 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 */ \ /* Note that not all instruments that can return a refresh rate, */ \ @@ -824,6 +839,7 @@ typedef enum { /* Insert a compensation filter in the instrument readings */ \ /* This is typically needed if an adapter is being used, that alters */ \ /* the spectrum of the light reaching the instrument */ \ + /* Not fully implemented across all instrument types (spectrolino only ?) */ \ /* To remove the filter, pass NULL for the filter filename */ \ inst_code (*comp_filter)( \ struct _inst *p, \ diff --git a/spectro/instappsup.c b/spectro/instappsup.c index 67849c7..c83c953 100644 --- a/spectro/instappsup.c +++ b/spectro/instappsup.c @@ -317,6 +317,19 @@ inst_code inst_handle_calibrate( } break; + case inst_calc_emis_80pc: + if (disp_setup == NULL || dwi == NULL) { /* No way of creating a test window */ + printf("Place the instrument on a 80%% white test patch,\n"); + printf(" and then hit any key to continue,\n"); + printf(" or hit Esc or Q to abort: "); + } else { + /* We need to display a 80% white patch to proceed with this */ + /* type of calibration */ + if ((rv = disp_setup(p, calc, dwi)) != inst_ok) + return rv; + } + break; + case inst_calc_emis_grey: case inst_calc_emis_grey_darker: case inst_calc_emis_grey_ligher: diff --git a/spectro/instlib.ksh b/spectro/instlib.ksh index 88fcba5..e112b3c 100644 --- a/spectro/instlib.ksh +++ b/spectro/instlib.ksh @@ -97,6 +97,8 @@ SPECTRO_FILES=" spyd2.h spyd2setup.h spyd2PLD.h + specbos.h + specbos.c oemarch.c oemarch.h oeminst.c @@ -111,10 +113,10 @@ SPECTRO_FILES=" iusb.h usbio.h usbio.c - usbio_lusb.c usbio_nt.c usbio_ox.c usbio_lx.c + usbio_bsd.c xdg_bds.c xdg_bds.h spotread.c diff --git a/spectro/insttypeinst.h b/spectro/insttypeinst.h index c61a5c4..8ed93d8 100644 --- a/spectro/insttypeinst.h +++ b/spectro/insttypeinst.h @@ -22,4 +22,5 @@ #include "hcfr.h" #include "spyd2.h" #include "huey.h" +#include "specbos.h" #include "colorhug.h" diff --git a/spectro/insttypes.c b/spectro/insttypes.c index e0863d0..d70bc71 100644 --- a/spectro/insttypes.c +++ b/spectro/insttypes.c @@ -86,6 +86,10 @@ char *inst_sname(instType itype) { return "Huey"; case instSmile: return "Smile"; + case instSpecbos1201: + return "specbos 1201"; + case instSpecbos: + return "specbos"; case instColorHug: return "ColorHug"; default: @@ -143,6 +147,10 @@ char *inst_name(instType itype) { return "GretagMacbeth Huey"; case instSmile: return "ColorMunki Smile"; + case instSpecbos1201: + return "JETI specbos 1201"; + case instSpecbos: + return "JETI specbos"; case instColorHug: return "Hughski ColorHug"; default: @@ -205,9 +213,14 @@ instType inst_enum(char *name) { return instHuey; else if (strcmp(name, "ColorMunki Smile") == 0) return instSmile; + else if (strcmp(name, "JETI specbos 1201") == 0) + return instSpecbos1201; + else if (strcmp(name, "JETI specbos") == 0) + return instSpecbos; else if (strcmp(name, "Hughski ColorHug") == 0) return instColorHug; + return instUnknown; } @@ -286,6 +299,8 @@ int nep) { /* Number of end points */ } /* Add other instruments here */ + + return instUnknown; } @@ -371,6 +386,10 @@ int inst_illuminant(xspect *sp, instType itype) { case instSmile: return 1; /* Not applicable */ + case instSpecbos1201: + case instSpecbos: + return 1; /* Not applicable */ + case instColorHug: return 1; /* Not applicable */ diff --git a/spectro/insttypes.h b/spectro/insttypes.h index 88e800b..ff86cc5 100644 --- a/spectro/insttypes.h +++ b/spectro/insttypes.h @@ -21,6 +21,7 @@ extern "C" { #endif + /* ----------------------------- */ /* Possible types of instruments */ typedef enum { @@ -48,8 +49,13 @@ typedef enum { instSpyder4, /* Datacolor Spyder4 */ instHuey, /* GretagMacbeth Huey */ instSmile, /* X-rite Colormunki Smile */ + instSpecbos1201, /* JETI specbos 1201 */ + instSpecbos, /* JETI specbos XXXX */ instColorHug, /* Hughski ColorHug */ + + instFakeDisp = 9998, /* Fake display & instrument device id */ + } instType; struct _icoms; /* Forward declaration */ diff --git a/spectro/linear.cal b/spectro/linear.cal index 30da145..5b494f9 100644 --- a/spectro/linear.cal +++ b/spectro/linear.cal @@ -2,7 +2,7 @@ CAL DESCRIPTOR "Argyll Device Calibration Curves" ORIGINATOR "Argyll synthcal" -CREATED "Sat Mar 09 18:33:22 2013" +CREATED "Fri Jan 31 13:50:43 2014" KEYWORD "DEVICE_CLASS" DEVICE_CLASS "DISPLAY" KEYWORD "COLOR_REP" @@ -16,260 +16,260 @@ END_DATA_FORMAT NUMBER_OF_SETS 256 BEGIN_DATA -0.0000 0.0000 0.0000 0.0000 -3.9216e-003 3.9216e-003 3.9216e-003 3.9216e-003 -7.8431e-003 7.8431e-003 7.8431e-003 7.8431e-003 -0.011765 0.011765 0.011765 0.011765 -0.015686 0.015686 0.015686 0.015686 -0.019608 0.019608 0.019608 0.019608 -0.023529 0.023529 0.023529 0.023529 -0.027451 0.027451 0.027451 0.027451 -0.031373 0.031373 0.031373 0.031373 -0.035294 0.035294 0.035294 0.035294 -0.039216 0.039216 0.039216 0.039216 -0.043137 0.043137 0.043137 0.043137 -0.047059 0.047059 0.047059 0.047059 -0.050980 0.050980 0.050980 0.050980 -0.054902 0.054902 0.054902 0.054902 -0.058824 0.058824 0.058824 0.058824 -0.062745 0.062745 0.062745 0.062745 -0.066667 0.066667 0.066667 0.066667 -0.070588 0.070588 0.070588 0.070588 -0.074510 0.074510 0.074510 0.074510 -0.078431 0.078431 0.078431 0.078431 -0.082353 0.082353 0.082353 0.082353 -0.086275 0.086275 0.086275 0.086275 -0.090196 0.090196 0.090196 0.090196 -0.094118 0.094118 0.094118 0.094118 -0.098039 0.098039 0.098039 0.098039 -0.10196 0.10196 0.10196 0.10196 -0.10588 0.10588 0.10588 0.10588 -0.10980 0.10980 0.10980 0.10980 -0.11373 0.11373 0.11373 0.11373 -0.11765 0.11765 0.11765 0.11765 -0.12157 0.12157 0.12157 0.12157 -0.12549 0.12549 0.12549 0.12549 -0.12941 0.12941 0.12941 0.12941 -0.13333 0.13333 0.13333 0.13333 -0.13725 0.13725 0.13725 0.13725 -0.14118 0.14118 0.14118 0.14118 -0.14510 0.14510 0.14510 0.14510 -0.14902 0.14902 0.14902 0.14902 -0.15294 0.15294 0.15294 0.15294 -0.15686 0.15686 0.15686 0.15686 -0.16078 0.16078 0.16078 0.16078 -0.16471 0.16471 0.16471 0.16471 -0.16863 0.16863 0.16863 0.16863 -0.17255 0.17255 0.17255 0.17255 -0.17647 0.17647 0.17647 0.17647 -0.18039 0.18039 0.18039 0.18039 -0.18431 0.18431 0.18431 0.18431 -0.18824 0.18824 0.18824 0.18824 -0.19216 0.19216 0.19216 0.19216 -0.19608 0.19608 0.19608 0.19608 -0.20000 0.20000 0.20000 0.20000 -0.20392 0.20392 0.20392 0.20392 -0.20784 0.20784 0.20784 0.20784 -0.21176 0.21176 0.21176 0.21176 -0.21569 0.21569 0.21569 0.21569 -0.21961 0.21961 0.21961 0.21961 -0.22353 0.22353 0.22353 0.22353 -0.22745 0.22745 0.22745 0.22745 -0.23137 0.23137 0.23137 0.23137 -0.23529 0.23529 0.23529 0.23529 -0.23922 0.23922 0.23922 0.23922 -0.24314 0.24314 0.24314 0.24314 -0.24706 0.24706 0.24706 0.24706 -0.25098 0.25098 0.25098 0.25098 -0.25490 0.25490 0.25490 0.25490 -0.25882 0.25882 0.25882 0.25882 -0.26275 0.26275 0.26275 0.26275 -0.26667 0.26667 0.26667 0.26667 -0.27059 0.27059 0.27059 0.27059 -0.27451 0.27451 0.27451 0.27451 -0.27843 0.27843 0.27843 0.27843 -0.28235 0.28235 0.28235 0.28235 -0.28627 0.28627 0.28627 0.28627 -0.29020 0.29020 0.29020 0.29020 -0.29412 0.29412 0.29412 0.29412 -0.29804 0.29804 0.29804 0.29804 -0.30196 0.30196 0.30196 0.30196 -0.30588 0.30588 0.30588 0.30588 -0.30980 0.30980 0.30980 0.30980 -0.31373 0.31373 0.31373 0.31373 -0.31765 0.31765 0.31765 0.31765 -0.32157 0.32157 0.32157 0.32157 -0.32549 0.32549 0.32549 0.32549 -0.32941 0.32941 0.32941 0.32941 -0.33333 0.33333 0.33333 0.33333 -0.33725 0.33725 0.33725 0.33725 -0.34118 0.34118 0.34118 0.34118 -0.34510 0.34510 0.34510 0.34510 -0.34902 0.34902 0.34902 0.34902 -0.35294 0.35294 0.35294 0.35294 -0.35686 0.35686 0.35686 0.35686 -0.36078 0.36078 0.36078 0.36078 -0.36471 0.36471 0.36471 0.36471 -0.36863 0.36863 0.36863 0.36863 -0.37255 0.37255 0.37255 0.37255 -0.37647 0.37647 0.37647 0.37647 -0.38039 0.38039 0.38039 0.38039 -0.38431 0.38431 0.38431 0.38431 -0.38824 0.38824 0.38824 0.38824 -0.39216 0.39216 0.39216 0.39216 -0.39608 0.39608 0.39608 0.39608 -0.40000 0.40000 0.40000 0.40000 -0.40392 0.40392 0.40392 0.40392 -0.40784 0.40784 0.40784 0.40784 -0.41176 0.41176 0.41176 0.41176 -0.41569 0.41569 0.41569 0.41569 -0.41961 0.41961 0.41961 0.41961 -0.42353 0.42353 0.42353 0.42353 -0.42745 0.42745 0.42745 0.42745 -0.43137 0.43137 0.43137 0.43137 -0.43529 0.43529 0.43529 0.43529 -0.43922 0.43922 0.43922 0.43922 -0.44314 0.44314 0.44314 0.44314 -0.44706 0.44706 0.44706 0.44706 -0.45098 0.45098 0.45098 0.45098 -0.45490 0.45490 0.45490 0.45490 -0.45882 0.45882 0.45882 0.45882 -0.46275 0.46275 0.46275 0.46275 -0.46667 0.46667 0.46667 0.46667 -0.47059 0.47059 0.47059 0.47059 -0.47451 0.47451 0.47451 0.47451 -0.47843 0.47843 0.47843 0.47843 -0.48235 0.48235 0.48235 0.48235 -0.48627 0.48627 0.48627 0.48627 -0.49020 0.49020 0.49020 0.49020 -0.49412 0.49412 0.49412 0.49412 -0.49804 0.49804 0.49804 0.49804 -0.50196 0.50196 0.50196 0.50196 -0.50588 0.50588 0.50588 0.50588 -0.50980 0.50980 0.50980 0.50980 -0.51373 0.51373 0.51373 0.51373 -0.51765 0.51765 0.51765 0.51765 -0.52157 0.52157 0.52157 0.52157 -0.52549 0.52549 0.52549 0.52549 -0.52941 0.52941 0.52941 0.52941 -0.53333 0.53333 0.53333 0.53333 -0.53725 0.53725 0.53725 0.53725 -0.54118 0.54118 0.54118 0.54118 -0.54510 0.54510 0.54510 0.54510 -0.54902 0.54902 0.54902 0.54902 -0.55294 0.55294 0.55294 0.55294 -0.55686 0.55686 0.55686 0.55686 -0.56078 0.56078 0.56078 0.56078 -0.56471 0.56471 0.56471 0.56471 -0.56863 0.56863 0.56863 0.56863 -0.57255 0.57255 0.57255 0.57255 -0.57647 0.57647 0.57647 0.57647 -0.58039 0.58039 0.58039 0.58039 -0.58431 0.58431 0.58431 0.58431 -0.58824 0.58824 0.58824 0.58824 -0.59216 0.59216 0.59216 0.59216 -0.59608 0.59608 0.59608 0.59608 -0.60000 0.60000 0.60000 0.60000 -0.60392 0.60392 0.60392 0.60392 -0.60784 0.60784 0.60784 0.60784 -0.61176 0.61176 0.61176 0.61176 -0.61569 0.61569 0.61569 0.61569 -0.61961 0.61961 0.61961 0.61961 -0.62353 0.62353 0.62353 0.62353 -0.62745 0.62745 0.62745 0.62745 -0.63137 0.63137 0.63137 0.63137 -0.63529 0.63529 0.63529 0.63529 -0.63922 0.63922 0.63922 0.63922 -0.64314 0.64314 0.64314 0.64314 -0.64706 0.64706 0.64706 0.64706 -0.65098 0.65098 0.65098 0.65098 -0.65490 0.65490 0.65490 0.65490 -0.65882 0.65882 0.65882 0.65882 -0.66275 0.66275 0.66275 0.66275 -0.66667 0.66667 0.66667 0.66667 -0.67059 0.67059 0.67059 0.67059 -0.67451 0.67451 0.67451 0.67451 -0.67843 0.67843 0.67843 0.67843 -0.68235 0.68235 0.68235 0.68235 -0.68627 0.68627 0.68627 0.68627 -0.69020 0.69020 0.69020 0.69020 -0.69412 0.69412 0.69412 0.69412 -0.69804 0.69804 0.69804 0.69804 -0.70196 0.70196 0.70196 0.70196 -0.70588 0.70588 0.70588 0.70588 -0.70980 0.70980 0.70980 0.70980 -0.71373 0.71373 0.71373 0.71373 -0.71765 0.71765 0.71765 0.71765 -0.72157 0.72157 0.72157 0.72157 -0.72549 0.72549 0.72549 0.72549 -0.72941 0.72941 0.72941 0.72941 -0.73333 0.73333 0.73333 0.73333 -0.73725 0.73725 0.73725 0.73725 -0.74118 0.74118 0.74118 0.74118 -0.74510 0.74510 0.74510 0.74510 -0.74902 0.74902 0.74902 0.74902 -0.75294 0.75294 0.75294 0.75294 -0.75686 0.75686 0.75686 0.75686 -0.76078 0.76078 0.76078 0.76078 -0.76471 0.76471 0.76471 0.76471 -0.76863 0.76863 0.76863 0.76863 -0.77255 0.77255 0.77255 0.77255 -0.77647 0.77647 0.77647 0.77647 -0.78039 0.78039 0.78039 0.78039 -0.78431 0.78431 0.78431 0.78431 -0.78824 0.78824 0.78824 0.78824 -0.79216 0.79216 0.79216 0.79216 -0.79608 0.79608 0.79608 0.79608 -0.80000 0.80000 0.80000 0.80000 -0.80392 0.80392 0.80392 0.80392 -0.80784 0.80784 0.80784 0.80784 -0.81176 0.81176 0.81176 0.81176 -0.81569 0.81569 0.81569 0.81569 -0.81961 0.81961 0.81961 0.81961 -0.82353 0.82353 0.82353 0.82353 -0.82745 0.82745 0.82745 0.82745 -0.83137 0.83137 0.83137 0.83137 -0.83529 0.83529 0.83529 0.83529 -0.83922 0.83922 0.83922 0.83922 -0.84314 0.84314 0.84314 0.84314 -0.84706 0.84706 0.84706 0.84706 -0.85098 0.85098 0.85098 0.85098 -0.85490 0.85490 0.85490 0.85490 -0.85882 0.85882 0.85882 0.85882 -0.86275 0.86275 0.86275 0.86275 -0.86667 0.86667 0.86667 0.86667 -0.87059 0.87059 0.87059 0.87059 -0.87451 0.87451 0.87451 0.87451 -0.87843 0.87843 0.87843 0.87843 -0.88235 0.88235 0.88235 0.88235 -0.88627 0.88627 0.88627 0.88627 -0.89020 0.89020 0.89020 0.89020 -0.89412 0.89412 0.89412 0.89412 -0.89804 0.89804 0.89804 0.89804 -0.90196 0.90196 0.90196 0.90196 -0.90588 0.90588 0.90588 0.90588 -0.90980 0.90980 0.90980 0.90980 -0.91373 0.91373 0.91373 0.91373 -0.91765 0.91765 0.91765 0.91765 -0.92157 0.92157 0.92157 0.92157 -0.92549 0.92549 0.92549 0.92549 -0.92941 0.92941 0.92941 0.92941 -0.93333 0.93333 0.93333 0.93333 -0.93725 0.93725 0.93725 0.93725 -0.94118 0.94118 0.94118 0.94118 -0.94510 0.94510 0.94510 0.94510 -0.94902 0.94902 0.94902 0.94902 -0.95294 0.95294 0.95294 0.95294 -0.95686 0.95686 0.95686 0.95686 -0.96078 0.96078 0.96078 0.96078 -0.96471 0.96471 0.96471 0.96471 -0.96863 0.96863 0.96863 0.96863 -0.97255 0.97255 0.97255 0.97255 -0.97647 0.97647 0.97647 0.97647 -0.98039 0.98039 0.98039 0.98039 -0.98431 0.98431 0.98431 0.98431 -0.98824 0.98824 0.98824 0.98824 -0.99216 0.99216 0.99216 0.99216 -0.99608 0.99608 0.99608 0.99608 -1.0000 1.0000 1.0000 1.0000 +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 new file mode 100644 index 0000000..9769b71 --- /dev/null +++ b/spectro/madvrwin.c @@ -0,0 +1,589 @@ + + +/* + * Argyll Color Correction System + * Web Display target patch window + * + * Author: Graeme W. Gill + * Date: 3/4/12 + * + * Copyright 2013 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License.txt file for licencing details. + */ + +#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 "madvrwin.h" +#include "conv.h" +#include "mongoose.h" + +#define ENABLE_RAMDAC + +#undef DEBUG +//#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 + +/* ----------------------------------------------- */ +/* MadVR functions */ + +#ifndef KEY_WOW64_32KEY +# define KEY_WOW64_32KEY (0x0200) +#endif + +HMODULE HcNetDll = NULL; +BOOL (WINAPI *madVR_BlindConnect)(BOOL searchLan, DWORD timeOut) = NULL; +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_ShowRGB)(double r, double g, double b) = NULL; +BOOL (WINAPI *madVR_SetProgressBarPos)(int currentPos, int maxPos); +BOOL (WINAPI *madVR_Disconnect)() = NULL; + +/* Locate and populate the MadVR functions */ +/* Return NZ on failure */ +static int initMadVR(dispwin *p) { + wchar_t *dllname; + + if (sizeof(dllname) > 4) /* Compiled as 64 bit */ + dllname = L"madHcNet64.dll"; + else + dllname = L"madHcNet32.dll"; + + if ((HcNetDll = LoadLibraryW(dllname)) == NULL) { + HKEY hk1; + if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"CLSID\\{E1A8B82A-32CE-4B0D-BE0D-AA68C772E423}\\InprocServer32", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk1) == ERROR_SUCCESS) { + DWORD size; + LPWSTR us1; + int i1; + size = MAX_PATH * 2 + 2; + us1 = (LPWSTR) LocalAlloc(LPTR, size + 20); + i1 = RegQueryValueExW(hk1, NULL, NULL, NULL, (LPBYTE) us1, &size); + if (i1 == ERROR_MORE_DATA) { + LocalFree(us1); + us1 = (LPWSTR) LocalAlloc(LPTR, size + 20); + i1 = RegQueryValueExW(hk1, NULL, NULL, NULL, (LPBYTE) us1, &size); + } + if (i1 == ERROR_SUCCESS) { + for (i1 = lstrlenW(us1) - 2; i1 > 0; i1--) + if (us1[i1] == L'\\') { + us1[i1 + 1] = 0; + break; + } + wcscat(us1, dllname); + HcNetDll = LoadLibraryW(us1); + } + LocalFree(us1); + RegCloseKey(hk1); + } + } + if (HcNetDll != NULL) { + *(FARPROC*)&madVR_BlindConnect = GetProcAddress(HcNetDll, "madVR_BlindConnect"); + *(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_ShowRGB = GetProcAddress(HcNetDll, "madVR_ShowRGB"); + *(FARPROC*)&madVR_SetProgressBarPos = GetProcAddress(HcNetDll, "madVR_SetProgressBarPos"); + *(FARPROC*)&madVR_Disconnect = GetProcAddress(HcNetDll, "madVR_Disconnect"); + + if (madVR_BlindConnect + && madVR_SetOsdText + && madVR_Disable3dlut + && madVR_GetDeviceGammaRamp + && madVR_SetDeviceGammaRamp + && madVR_SetBackground + && madVR_ShowRGB + && madVR_SetProgressBarPos + && madVR_Disconnect) { + return 0; + } + debugr2((errout,"Failed to locate function in %ls\n",dllname)); + FreeLibrary(HcNetDll); + } else { + debugr2((errout,"Failed to load %ls\n",dllname)); + } + return 1; +} + +static void deinitMadVR(dispwin *p) { + if (HcNetDll != NULL) { + FreeLibrary(HcNetDll); + HcNetDll = NULL; + } +} + +/* ----------------------------------------------- */ + +/* Get RAMDAC values. ->del() when finished. */ +/* Return NULL if not possible */ +static ramdac *madvrwin_get_ramdac(dispwin *p) { + ramdac *r = NULL; + +#ifdef ENABLE_RAMDAC + WORD vals[3][256]; /* 256 x 16 bit elements (Quantize) */ + int i, j; + + debugr("madvrwin_get_ramdac called\n"); + + /* Allocate a ramdac */ + if ((r = (ramdac *)calloc(sizeof(ramdac), 1)) == NULL) { + debugr("madvrwin_get_ramdac failed on malloc()\n"); + return NULL; + } + r->pdepth = p->pdepth; + r->nent = (1 << p->pdepth); + r->clone = dispwin_clone_ramdac; + r->setlin = dispwin_setlin_ramdac; + r->del = dispwin_del_ramdac; + + for (j = 0; j < 3; j++) { + + if ((r->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) { + for (j--; j >= 0; j--) + free(r->v[j]); + free(r); + debugr("madvrwin_get_ramdac failed on malloc()\n"); + return NULL; + } + } + + /* GetDeviceGammaRamp() is hard coded for 3 x 256 entries (Quantize) */ + if (r->nent != 256) { + free(r); + debugr2((errout,"GetDeviceGammaRamp() is hard coded for nent == 256, and we've got nent = %d!\n",r->nent)); + return NULL; + } + + if (madVR_GetDeviceGammaRamp(vals) == 0) { + free(r); + debugr2((errout,"madvrwin_get_ramdac failed on madVR_GetDeviceGammaRamp()\n")); + return NULL; + } + + for (j = 0; j < 3; j++) { + for (i = 0; i < r->nent; i++) { + r->v[j][i] = vals[j][i]/65535.0; + } + } +#endif // ENABLE_RAMDAC + debugr2((errout,"madvrwin_get_ramdac returning 0x%x\n",r)); + + return r; +} + +/* Set the RAMDAC values. */ +/* Return nz if not possible */ +static int madvrwin_set_ramdac(dispwin *p, ramdac *r, int persist) { + int rv = 1; + +#ifdef ENABLE_RAMDAC + int i, j; + WORD vals[3][256]; /* 16 bit elements */ + + debugr("madvrwin_set_ramdac called\n"); + + for (j = 0; j < 3; j++) { + for (i = 0; i < r->nent; i++) { + double vv = r->v[j][i]; + if (vv < 0.0) + vv = 0.0; + else if (vv > 1.0) + vv = 1.0; + vals[j][i] = (int)(65535.0 * vv + 0.5); + } + } + + if (madVR_SetDeviceGammaRamp(vals) == 0) { + debugr2((errout,"madvrwin_set_ramdac failed on madVR_SetDeviceGammaRamp()\n")); + rv = 1; + } else { + debugr("madvrwin_set_ramdac set\n"); + rv = 0; + } +#endif // ENABLE_RAMDAC + return rv; +} + +/* ----------------------------------------------- */ +/* Install a display profile and make */ +/* it the default for this display. */ +/* Return nz if failed */ +int madvrwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { + debugr("madVRdisp doesn't support installing profiles\n"); + return 1; +} + +/* Un-Install a display profile */ +/* Return nz if failed, */ +int madvrwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { + debugr("madVRdisp doesn't support uninstalling profiles\n"); + return 1; +} + +/* Get the currently installed display profile. */ +/* Return NULL if failed. */ +icmFile *madvrwin_get_profile(dispwin *p, char *name, int mxlen) { + debugr("madVRdisp 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 madvrwin_set_color( +dispwin *p, +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 */ + + debugr("madvrwin_set_color called\n"); + + if (p->nowin) + 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; + + if (!madVR_ShowRGB(p->rgb[0], p->rgb[1], p->rgb[2])) { + debugr2((errout,"madVR_ShowRGB failed\n")); + 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 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); + + return 0; +} + +/* ----------------------------------------------- */ +/* set patch info */ +/* Return nz on error */ +static int madvrwin_set_pinfo(dispwin *p, int pno, int tno) { + + if (!madVR_SetProgressBarPos(pno, tno)) + return 1; + + 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 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, +char *callout +) { + debugr2((errout,"madvrwin_set_callout called with '%s'\n",callout)); + + p->callout = strdup(callout); +} + +/* ----------------------------------------------- */ +/* Destroy ourselves */ +static void madvrwin_del( +dispwin *p +) { + + debugr("madvrwin_del called\n"); + + if (p == NULL) + return; + + deinitMadVR(p); + + if (p->name != NULL) + free(p->name); + if (p->description != NULL) + free(p->description); + if (p->callout != NULL) + free(p->callout); + + /* Since we don't restore the display, delete these here */ + if (p->oor != NULL) { + p->oor->del(p->oor); + p->oor = NULL; + } + if (p->or != NULL) { + p->or->del(p->or); + p->or = NULL; + } + if (p->r != NULL) { + p->r->del(p->r); + p->r = NULL; + } + + free(p); +} + +/* ----------------------------------------------- */ + +/* Create a web display test window, default grey */ +dispwin *new_madvrwin( +double width, double height, /* Width and height in mm - turned into % of screen */ +double hoff, double voff, /* Offset from center in fraction of screen - ignored */ +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; + struct mg_context *mg; + const char *options[3]; + char port[50]; + + debug("new_madvrwin called with native = %d\n"); + + if (out_tvenc) + return NULL; + + if ((p = (dispwin *)calloc(sizeof(dispwin), 1)) == NULL) { + if (ddebug) fprintf(stderr,"new_madvrwin failed because malloc failed\n"); + return NULL; + } + + /* !!!! Make changes in dispwin.c & webwin.c as well !!!! */ + p->name = strdup("Web Window"); + 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; + + debugr2((errout, "new_madvrwin got native = %d\n",native)); + +#ifndef ENABLE_RAMDAC + if (noramdac != NULL) + *noramdac = 1; + p->native &= ~1; +#endif + + 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; + + p->pdepth = 8; /* Assume this */ + p->edepth = 16; + + if (initMadVR(p)) { + free(p); + return NULL; + } + + if (!madVR_BlindConnect(0, 1000)) { + debugr2((errout,"Failed to locate MadVR\n")); + free(p); + return NULL; + } + + if (p->native & 2) { + debugr2((errout,"new_madvrwin: disbling 3dLuts\n")); + madVR_Disable3dlut(); + } + + if (blackbg) { + int perc; + + perc = (int)(width * height * 100 + 0.5); + madVR_SetBackground(perc, 0x0000); + } + + /* Create a suitable description */ + { + char buf[1000]; + + sprintf(buf,"ArgyllCMS Patches"); + p->description = strdup(buf); + + if (verb) + printf("Created MadVR window\n"); + + madVR_SetOsdText(L"ArgyllCMS Patches"); + } + +#ifdef ENABLE_RAMDAC + + /* Save the original ramdac, which gets restored on exit */ + if ((p->or = p->get_ramdac(p)) != NULL) { + + debugr("Saved original VideoLUT\n"); + + if (noramdac != NULL) + *noramdac = 0; + + /* Copy original ramdac that never gets altered */ + if ((p->oor = p->or->clone(p->or)) == NULL) { + madvrwin_del(p); + debugr("ramdac clone failed - memory ?\n"); + return NULL; + } + + /* Create a working ramdac for native or other use */ + if ((p->r = p->or->clone(p->or)) == NULL) { + madvrwin_del(p); + debugr("ramdac clone failed - memory ?\n"); + return NULL; + } + + /* Setup for native mode == linear RAMDAC */ + if ((p->native & 1) == 1) { +int ii = 0; + debug("About to setup native mode\n"); + + /* Since we MadVR does dithering, we don't need to use the VideoLUTs */ + /* to emulate higher precision, so simply set it to linear here. */ + if ((ii = madVR_SetDeviceGammaRamp(NULL)) == 0) { + madvrwin_del(p); + debugr("Clear gamma ramp failed\n"); + return NULL; + } + } + + } else { + debugr2((errout,"Unable to access VideoLUT\n")); + if (noramdac != NULL) + *noramdac = 1; + p->oor = p->or = p->r = NULL; + } + + if (!p->nowin) { + + /* Make sure initial test color is displayed */ + madvrwin_set_color(p, p->rgb[0], p->rgb[1], p->rgb[2]); + } +#endif + + debugr("new_madvrwin: return sucessfully\n"); + + return p; +} + diff --git a/spectro/madvrwin.h b/spectro/madvrwin.h new file mode 100644 index 0000000..74c8f4f --- /dev/null +++ b/spectro/madvrwin.h @@ -0,0 +1,37 @@ + +#ifndef MADVRWIN_H + +/* + * Argyll Color Correction System + * MadVR target patch window + * + * Author: Graeme W. Gill + * Date: 26/6/13 + * + * Copyright 2013 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License.txt file for licencing details. + */ + + +/* Create a MadVR display test window, default grey */ +dispwin *new_madvrwin( +double width, double height, /* Width and height in mm - turned into % of screen */ +double hoff, double voff, /* Offset from center in fraction of screen - ignored */ +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 - will error */ +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 MADVRWIN_H +#endif /* MADVRWIN_H */ diff --git a/spectro/munki.c b/spectro/munki.c index 6fa898e..5d4de2d 100644 --- a/spectro/munki.c +++ b/spectro/munki.c @@ -7,7 +7,7 @@ * Author: Graeme W. Gill * Date: 12/1/2009 * - * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2006 - 2014, Graeme W. Gill * All rights reserved. * * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- @@ -90,8 +90,8 @@ munki_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { a1logd(p->log, 2, "munki_init_coms: called\n"); if (p->icom->port_type(p->icom) != icomt_usb) { - a1logd(p->log, 1, "munki_init_coms: wrong sort of coms!\n"); - return munki_interp_code(p, MUNKI_UNKNOWN_MODEL); + a1logd(p->log, 1, "munki_init_coms: wrong communications type for device!\n"); + return inst_coms_fail; } a1logd(p->log, 2, "munki_init_coms: about to init USB\n"); @@ -145,8 +145,10 @@ munki_determine_capabilities(munki *p) { if (p->m != NULL) { munkiimp *m = (munkiimp *)p->m; munki_state *s = &m->ms[m->mmode]; - if (s->emiss) + if (s->emiss) { p->cap2 |= inst2_emis_refr_meas; + p->cap2 |= inst2_meas_disp_update; + } } p->cap3 = inst3_none; @@ -314,6 +316,25 @@ double *ref_rate) { rv = munki_imp_meas_refrate(p, ref_rate); + + return munki_interp_code(p, rv); +} + +/* Read the display update delay */ +static inst_code +munki_meas_delay( +inst *pp, +int *msecdelay) { + munki *p = (munki *)pp; + munki_code rv; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + rv = munki_imp_meas_delay(p, msecdelay); + return munki_interp_code(p, rv); } @@ -425,6 +446,8 @@ munki_interp_error(inst *pp, munki_code ec) { return "No ambient found before first flash"; case MUNKI_RD_NOREFR_FOUND: return "No refresh rate detected or failed to measure it"; + case MUNKI_RD_NOTRANS_FOUND: + return "No delay calibration transition found"; case MUNKI_SPOS_PROJ: return "Sensor should be in projector position"; @@ -557,6 +580,7 @@ munki_interp_code(munki *p, munki_code ec) { case MUNKI_RD_NOFLASHES: case MUNKI_RD_NOAMBB4FLASHES: case MUNKI_RD_NOREFR_FOUND: + case MUNKI_RD_NOTRANS_FOUND: return inst_misread | ec; case MUNKI_INTERNAL_ERROR: @@ -700,8 +724,7 @@ inst_code munki_set_mode(inst *pp, inst_mode m) { if ((mmode = munki_convert_mode(p, m)) == mk_no_modes) return inst_unsupported; - if ((rv = munki_interp_code(p, munki_imp_set_mode(p, mmode, m & inst_mode_spectral))) - != inst_ok) + if ((rv = munki_interp_code(p, munki_imp_set_mode(p, mmode, m))) != inst_ok) return rv; munki_determine_capabilities(p); @@ -920,6 +943,7 @@ extern munki *new_munki(icoms *icom, instType itype) { p->read_refrate = munki_read_refrate; p->get_n_a_cals = munki_get_n_a_cals; p->calibrate = munki_calibrate; + p->meas_delay = munki_meas_delay; 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 c76e9bc..9f431cc 100644 --- a/spectro/munki_imp.c +++ b/spectro/munki_imp.c @@ -5,7 +5,7 @@ * Author: Graeme W. Gill * Date: 12/1/2009 * - * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2006 - 2014, Graeme W. Gill * All rights reserved. * * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- @@ -67,14 +67,22 @@ #include "sort.h" /* Configuration */ -#undef USE_HIGH_GAIN_MODE /* [Und] Make use of high gain mode */ +#undef USE_HIGH_GAIN_MODE /* [Und] Make use of high gain mode in emissive measurements */ #define USE_THREAD /* [Def] Need to use thread, or there are 1.5 second internal */ /* instrument delays ! */ -#define ENABLE_NONVCAL /* [Def] Enable saving calibration state between program runs in a file */ +#define ENABLE_NONVCAL /* [Def] Enable saving calibration state between program runs to a file */ #define ENABLE_NONLINCOR /* [Def] Enable non-linear correction */ /* NOTE :- high gain scaling will be stuffed if disabled! */ #define ENABLE_LEDTEMPC /* [Def] Enable LED temperature compensation */ -#define ENABLE_SPOS_CHECK /* [Def] Chech the sensor position is reasonable for measurement */ +#define ENABLE_BKDRIFTC /* [Def] Enable Emis. Black drift compensation using sheilded cell values */ +#define HEURISTIC_BKDRIFTC /* [Def] Use heusristic black drift correction version */ +#undef ENABLE_REFSTRAYC /* [Und] Enable Reflective stray light compensation */ +#define REFSTRAYC_FACTOR 0.000086 /* [0.00043] Compensation factor */ +#undef ENABLE_REFLEDINTER /* [Und] Enable Reflective LED interference correction */ + +#define ENABLE_SPOS_CHECK /* [Def] Check the sensor position is reasonable for measurement */ +#define FILTER_SPOS_EVENTS /* [Def] Use a thread to filter SPOS event changes */ +#define FILTER_TIME 500 /* [500] Filter time in msec */ #define DCALTOUT (1 * 60 * 60) /* [1 Hrs] Dark Calibration timeout in seconds */ #define WCALTOUT (24 * 60 * 60) /* [24 Hrs] White Calibration timeout in seconds */ #define MAXSCANTIME 20.0 /* [20 Sec] Maximum scan time in seconds */ @@ -83,24 +91,27 @@ #define SINGLE_READ /* [Def] Use a single USB read for scan to eliminate latency issues. */ #define HIGH_RES /* [Def] Enable high resolution spectral mode code. Disable */ /* to break dependency on rspl library. */ +# undef FAST_HIGH_RES_SETUP /* Slightly better accuracy ? */ /* Debug [Und] */ #undef DEBUG /* Turn on extra messages & plots */ #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 RAWR_DEBUG /* Print out raw reading processing values */ #undef DUMP_SCANV /* Dump scan readings to a file "mkdump.txt" */ #undef DUMP_DARKM /* Append raw dark readings to file "mkddump.txt" */ +#undef DUMP_BKLED /* Save REFSTRAYC & REFLEDNOISE comp plot to "refbk1.txt" & "refbk2.txt" */ #undef APPEND_MEAN_EMMIS_VAL /* Append averaged uncalibrated reading to file "mkdump.txt" */ #undef IGNORE_WHITE_INCONS /* Ignore define reference reading inconsistency */ #undef TEST_DARK_INTERP /* Test out the dark interpolation (need DEBUG for plot) */ #undef PLOT_RCALCURVE /* Plot the reflection reference curve */ #undef PLOT_ECALCURVES /* Plot the emission reference curves */ -#undef PLOT_TEMPCOMP /* Plot before and after temp. compensation */ -#undef PATREC_DEBUG /* Print & Plot patch/flash recognition information */ +#undef PLOT_TEMPCOMP /* Plot before and after LED temp. compensation */ +#undef PLOT_PATREC /* Print & Plot patch/flash recognition information */ #undef HIGH_RES_DEBUG #undef HIGH_RES_PLOT -#undef HIGH_RES_PLOT_STRAYL /* Plot strat light upsample */ +#undef HIGH_RES_PLOT_STRAYL /* Plot stray light upsample */ #define DISP_INTT 0.7 /* Seconds per reading in display spot mode */ @@ -116,24 +127,22 @@ #define ADARKINT_MAX 2.0 /* Max cal time for adaptive dark cal with high gain mode */ #define ADARKINT_MAX2 4.0 /* Max cal time for adaptive dark for no high gain */ -#define SCAN_OP_LEV 0.10 /* Degree of optimimum sensor value to aim for */ - /* Make it scan as fast as possible */ - #define RDEAD_TIME 0.004432 /* Fudge figure to make reflecting intn. time scale linearly */ #define EMIS_SCALE_FACTOR 1.0 /* Emission mode scale factor */ -#define AMB_SCALE_FACTOR (1.0/3.141592654) /* Ambient mode scale factor - convert */ - /* from Lux to Lux/PI */ - /* These factors get the same behaviour as the GMB drivers. */ +#define AMB_SCALE_FACTOR 1.0 /* Ambient mode scale factor for Lux */ #define NSEN_MAX 140 /* Maximum nsen/raw value we can cope with */ +/* Wavelength to start duplicating values below, because it is too noisy */ +#define WL_REF_MIN 420.0 +#define WL_EMIS_MIN 400.0 + /* High res mode settings */ -#define HIGHRES_SHORT 360 /* Wavelength to calculate */ -#define HIGHRES_LONG 740 +#define HIGHRES_SHORT 380 /* Wavelength to calculate. Too noisy to try expanding range. */ +#define HIGHRES_LONG 730 #define HIGHRES_WIDTH (10.0/3.0) /* (The 3.3333 spacing and lanczos2 seems a good combination) */ -#define HIGHRES_REF_MIN 410.0 /* Too much stray light below this in reflective mode */ -#define HIGHRES_TRANS_MIN 380.0 /* Too much stray light below this in reflective mode */ + #include "munki.h" #include "munki_imp.h" @@ -177,7 +186,7 @@ static void dump_bytes(a1log *log, char *pfx, unsigned char *buf, int base, int bp += sprintf(bp,"."); } bp += sprintf(bp,"\n"); - a1logd(log,0,oline); + a1logd(log,0,"%s",oline); bp = oline; } } @@ -186,7 +195,7 @@ static void dump_bytes(a1log *log, char *pfx, unsigned char *buf, int base, int /* ============================================================ */ /* Debugging plot support */ -#if defined(DEBUG) || defined(PLOT_DEBUG) || defined(PATREC_DEBUG) || defined(HIGH_RES_PLOT) || defined(HIGH_RES_PLOT_STRAYL) +#if defined(DEBUG) || defined(PLOT_DEBUG) || defined(PLOT_PATREC) || defined(HIGH_RES_PLOT) || defined(HIGH_RES_PLOT_STRAYL) # include <plot.h> @@ -322,6 +331,11 @@ void del_munkiimp(munki *p) { munkiimp *m = (munkiimp *)p->m; munki_state *s; +#ifdef FILTER_SPOS_EVENTS + if (m->spos_th != NULL) + m->spos_th_term = 1; +#endif + if (m->th != NULL) { /* Terminate switch monitor thread by simulating an event */ m->th_term = 1; /* Tell thread to exit on error */ munki_simulate_event(p, mk_eve_spos_change, 0); @@ -331,9 +345,20 @@ void del_munkiimp(munki *p) { a1logd(p->log,3,"Munki switch thread termination failed\n"); } m->th->del(m->th); - usb_uninit_cancel(&m->cancelt); /* Don't need cancel token now */ + usb_uninit_cancel(&m->sw_cancel); /* Don't need cancel token now */ } +#ifdef FILTER_SPOS_EVENTS + if (m->spos_th != NULL) { + for (i = 0; m->spos_th_termed == 0 && i < 5; i++) + msec_sleep(50); /* Wait for thread to terminate */ + if (i >= 5) { + a1logd(p->log,3,"Munki spos thread termination failed\n"); + } + m->spos_th->del(m->spos_th); + } +#endif + /* Free any per mode data */ for (i = 0; i < mk_no_modes; i++) { s = &m->ms[i]; @@ -568,11 +593,17 @@ munki_code munki_imp_init(munki *p) { #ifdef USE_THREAD /* Setup the switch monitoring thread */ - usb_init_cancel(&m->cancelt); /* Get cancel token ready */ + usb_init_cancel(&m->sw_cancel); /* Get cancel token ready */ if ((m->th = new_athread(munki_switch_thread, (void *)p)) == NULL) return MUNKI_INT_THREADFAILED; #endif +#ifdef FILTER_SPOS_EVENTS + /* Setup the sensor position filter thread */ + if ((m->spos_th = new_athread(munki_spos_thread, (void *)p)) == NULL) + return MUNKI_INT_THREADFAILED; +#endif + /* Set up the current state of each mode */ { int i, j; @@ -589,6 +620,11 @@ munki_code munki_imp_init(munki *p) { s->targmaxitime = 2.0; /* Maximum integration time to aim for */ s->targoscale2 = 0.15; /* Proportion of targoscale to meed targmaxitime */ +#ifdef USE_HIGH_GAIN_MODE + s->auto_gain = 1; /* No high gain by default */ +#else + s->auto_gain = 0; /* No high gain by default */ +#endif s->gainmode = 0; /* Normal gain mode */ s->inttime = 0.5; /* Initial integration time */ @@ -608,18 +644,14 @@ munki_code munki_imp_init(munki *p) { s->idark_valid = 0; /* Interpolatable Dark cal invalid */ s->idark_data = dmatrixz(0, 3, -1, m->nraw-1); - s->min_wl = 0.0; /* Default minimum to report */ - s->dark_int_time = DISP_INTT; /* 0.7 */ s->dark_int_time2 = DISP_INTT2; /* 0.3 */ s->dark_int_time3 = DISP_INTT3; /* 0.1 */ s->idark_int_time[0] = s->idark_int_time[2] = m->min_int_time; -#ifdef USE_HIGH_GAIN_MODE - s->idark_int_time[1] = s->idark_int_time[3] = ADARKINT_MAX; /* 2.0 */ -#else - s->idark_int_time[1] = s->idark_int_time[3] = ADARKINT_MAX2; /* 4.0 */ -#endif + s->idark_int_time[1] = ADARKINT_MAX; /* 2.0 */ + s->idark_int_time[3] = ADARKINT_MAX2; /* 4.0 */ + s->want_calib = 1; /* By default want an initial calibration */ s->want_dcalib = 1; } @@ -629,7 +661,8 @@ munki_code munki_imp_init(munki *p) { s = &m->ms[i]; switch(i) { case mk_refl_spot: - s->targoscale = 1.0; /* Optimised sensor scaling to full */ + s->auto_gain = 0; /* Don't automatically set gain */ + s->targoscale = 1.0; /* Optimised sensor scaling to full */ s->reflective = 1; s->adaptive = 0; s->inttime = s->targoscale * m->cal_int_time; @@ -642,17 +675,19 @@ munki_code munki_imp_init(munki *p) { s->dreadtime = 0.5; /* same as reading */ s->wreadtime = 0.5; s->maxscantime = 0.0; - s->min_wl = HIGHRES_REF_MIN; /* Not enogh illumination to go below this */ break; case mk_refl_scan: - s->targoscale = SCAN_OP_LEV; /* Maximize scan rate */ + s->auto_gain = 0; /* Don't automatically set gain */ + s->targoscale = 1.0; /* Maximize level */ s->reflective = 1; s->scan = 1; s->adaptive = 0; - s->inttime = (s->targoscale * m->cal_int_time - RDEAD_TIME) + RDEAD_TIME; - if (s->inttime < m->min_int_time) - s->inttime = m->min_int_time; +// s->inttime = (s->targoscale * m->cal_int_time - RDEAD_TIME) + RDEAD_TIME; +// if (s->inttime < m->min_int_time) +// s->inttime = m->min_int_time; +// s->dark_int_time = s->inttime; + s->inttime = s->targoscale * m->cal_int_time; s->dark_int_time = s->inttime; s->dpretime = 0.20; /* Pre-measure time */ @@ -662,7 +697,6 @@ munki_code munki_imp_init(munki *p) { s->dreadtime = 0.10; s->wreadtime = 0.10; s->maxscantime = MAXSCANTIME; - s->min_wl = HIGHRES_REF_MIN; /* Not enogh illumination to go below this */ break; case mk_emiss_spot_na: /* Emissive spot not adaptive */ @@ -767,14 +801,13 @@ munki_code munki_imp_init(munki *p) { s->dreadtime = 0.0; s->wreadtime = 1.0; s->maxscantime = 0.0; - s->min_wl = HIGHRES_TRANS_MIN; /* Too much stray light below this ? */ break; case mk_trans_scan: - s->targoscale = 0.90; /* Allow extra 10% margine */ + // ~~99 should we use high gain mode ?? + s->targoscale = 0.10; /* Scan as fast as possible */ s->trans = 1; s->scan = 1; - s->targoscale = SCAN_OP_LEV; s->inttime = s->targoscale * m->cal_int_time; if (s->inttime < m->min_int_time) s->inttime = m->min_int_time; @@ -788,7 +821,6 @@ munki_code munki_imp_init(munki *p) { s->dreadtime = 0.00; s->wreadtime = 0.10; s->maxscantime = MAXSCANTIME; - s->min_wl = HIGHRES_TRANS_MIN; /* Too much stray light below this ? */ break; } } @@ -837,12 +869,12 @@ char *munki_imp_get_serial_no(munki *p) { /* Set the measurement mode. It may need calibrating */ munki_code munki_imp_set_mode( munki *p, - mk_mode mmode, - int spec_en /* nz to enable reporting spectral */ + mk_mode mmode, /* Operating mode */ + inst_mode mode /* Full mode mask for options */ ) { munkiimp *m = (munkiimp *)p->m; - a1logd(p->log,3,"munki_imp_set_mode called with %d\n",mmode); + a1logd(p->log,2,"munki_imp_set_mode called with mode no. %d and mask 0x%x\n",mmode,m); switch(mmode) { case mk_refl_spot: case mk_refl_scan: @@ -856,12 +888,21 @@ munki_code munki_imp_set_mode( case mk_trans_spot: case mk_trans_scan: m->mmode = mmode; - m->spec_en = spec_en ? 1 : 0; - return MUNKI_OK; - default: break; + default: + return MUNKI_INT_ILLEGALMODE; } - return MUNKI_INT_ILLEGALMODE; + m->spec_en = (mode & inst_mode_spectral) != 0; + + if ((mode & inst_mode_highres) != 0) { + munki_code rv; + if ((rv = munki_set_highres(p)) != MUNKI_OK) + return rv; + } else { + munki_set_stdres(p); /* Ignore any error */ + } + + return MUNKI_OK; } /* Return needed and available inst_cal_type's */ @@ -875,7 +916,7 @@ munki_code munki_imp_get_n_a_cals(munki *p, inst_cal_type *pn_cals, inst_cal_typ a1logd(p->log,3,"munki_imp_get_n_a_cals: checking mode %d\n",m->mmode); /* Timout calibrations that are too old */ - a1logd(p->log,4,"curtime = %u, iddate = %u\n",curtime,cs->iddate); + 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; @@ -972,7 +1013,7 @@ munki_code munki_imp_calibrate( else if (*calt == inst_calt_available) *calt = available & inst_calt_n_dfrble_mask; - a1logd(p->log,4,"munki_imp_calibrate: doing calt 0x%x\n",calt); + a1logd(p->log,4,"munki_imp_calibrate: doing calt 0x%x\n",*calt); if ((*calt & inst_calt_n_dfrble_mask) == 0) /* Nothing todo */ return MUNKI_OK; @@ -989,6 +1030,16 @@ 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) { + *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 */ if (*calc == inst_calc_man_cal_smode) { @@ -1001,13 +1052,6 @@ munki_code munki_imp_calibrate( } } - /* If the instrument is in the calibration position, */ - /* we know what the conditions are. */ - if (!m->nosposcheck && spos == mk_spos_calib) { - *calc = inst_calc_man_cal_smode; - a1logd(p->log,4,"munki set calc to cal conditions\n",spos); - } - a1logd(p->log,4,"munki_imp_calibrate has right conditions\n"); if (*calt & inst_calt_ap_flag) { @@ -1025,9 +1069,9 @@ munki_code munki_imp_calibrate( /* Sanity check scan mode settings, in case something strange */ /* has been restored from the persistence file. */ - if (s->scan && s->inttime > (2.1 * m->min_int_time)) { - s->inttime = m->min_int_time; /* Maximize scan rate */ - } +// if (s->scan && s->inttime > (2.1 * m->min_int_time)) { +// s->inttime = m->min_int_time; /* Maximize scan rate */ +// } /* We are now either in inst_calc_man_cal_smode, */ /* inst_calc_man_trans_white, inst_calc_disp_white or inst_calc_proj_white */ @@ -1053,7 +1097,7 @@ munki_code munki_imp_calibrate( nummeas = munki_comp_nummeas(p, s->dcaltime, s->inttime); - a1logd(p->log,3,"\nDoing initial display black calibration with dcaltime %f, int_time %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode); + a1logd(p->log,3,"\nDoing initial black calibration with dcaltime %f, int_time %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode); stm = msec_time(); if ((ev = munki_dark_measure(p, s->dark_data, nummeas, &s->inttime, s->gainmode)) != MUNKI_OK) { @@ -1207,25 +1251,25 @@ if (ss->dark_int_time2 != s->dark_int_time2 return ev; } -#ifdef USE_HIGH_GAIN_MODE - s->idark_int_time[2] = m->min_int_time; /* 0.01 */ - nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]); - a1logd(p->log,3,"Doing adaptive interpolated black calibration, dcaltime %f, idark_int_time[2] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[2], nummeas, 1); - if ((ev = munki_dark_measure(p, s->idark_data[2], nummeas, &s->idark_int_time[2], 1)) - != MUNKI_OK) { - m->mmode = mmode; /* Restore actual mode */ - return ev; - } - - s->idark_int_time[3] = ADARKINT_MAX; /* 2.0 */ - a1logd(p->log,3,"Doing adaptive interpolated black calibration, dcaltime %f, idark_int_time[3] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[3], nummeas, 1); - nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[3]); - if ((ev = munki_dark_measure(p, s->idark_data[3], nummeas, &s->idark_int_time[3], 1)) - != MUNKI_OK) { - m->mmode = mmode; /* Restore actual mode */ - return ev; + if (s->auto_gain) { /* If high gain is permitted */ + s->idark_int_time[2] = m->min_int_time; /* 0.01 */ + nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]); + a1logd(p->log,3,"Doing adaptive interpolated black calibration, dcaltime %f, idark_int_time[2] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[2], nummeas, 1); + if ((ev = munki_dark_measure(p, s->idark_data[2], nummeas, &s->idark_int_time[2], 1)) + != MUNKI_OK) { + m->mmode = mmode; /* Restore actual mode */ + return ev; + } + + s->idark_int_time[3] = ADARKINT_MAX; /* 2.0 */ + a1logd(p->log,3,"Doing adaptive interpolated black calibration, dcaltime %f, idark_int_time[3] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[3], nummeas, 1); + nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[3]); + if ((ev = munki_dark_measure(p, s->idark_data[3], nummeas, &s->idark_int_time[3], 1)) + != MUNKI_OK) { + m->mmode = mmode; /* Restore actual mode */ + return ev; + } } -#endif /* USE_HIGH_GAIN_MODE */ munki_prepare_idark(p); @@ -1263,12 +1307,7 @@ if (ss->dark_int_time != s->dark_int_time || ss->dark_gain_mode != s->dark_gain_mode) a1logd(p->log,1,"copying cal to mode with different cal/gain mode: %d -> %d\n",s->mode, ss->mode); #endif -#ifdef USE_HIGH_GAIN_MODE - for (j = 0; j < 4; j++) -#else - for (j = 0; j < 2; j++) -#endif - { + for (j = 0; j < (s->auto_gain ? 4 : 2); j++) { ss->idark_int_time[j] = s->idark_int_time[j]; #ifndef NEVER // ~~99 if (ss->idark_int_time[j] != s->idark_int_time[j]) @@ -1318,30 +1357,30 @@ if (ss->idark_int_time[j] != s->idark_int_time[j]) ddumpdarkm = 0; #endif -#ifdef USE_HIGH_GAIN_MODE - // fprintf(stderr,"High gain offsets, base:\n"); - // plot_raw(s->idark_data[2]); - // fprintf(stderr,"High gain offsets, multiplier:\n"); - // plot_raw(s->idark_data[3]); + if (s->auto_gain) { +// fprintf(stderr,"High gain offsets, base:\n"); +// plot_raw(s->idark_data[2]); +// fprintf(stderr,"High gain offsets, multiplier:\n"); +// plot_raw(s->idark_data[3]); - for (tinttime = m->min_int_time; ; tinttime *= 2.0) { - if (tinttime >= m->max_int_time) - tinttime = m->max_int_time; - - nummeas = munki_comp_nummeas(p, s->dcaltime, tinttime); - if ((ev = munki_dark_measure(p, ref, nummeas, &tinttime, 1)) != MUNKI_OK) { - m->mmode = mmode; /* Restore actual mode */ - return ev; - } - munki_interp_dark(p, interp, tinttime, 1); + for (tinttime = m->min_int_time; ; tinttime *= 2.0) { + if (tinttime >= m->max_int_time) + tinttime = m->max_int_time; + + nummeas = munki_comp_nummeas(p, s->dcaltime, tinttime); + if ((ev = munki_dark_measure(p, ref, nummeas, &tinttime, 1)) != MUNKI_OK) { + m->mmode = mmode; /* Restore actual mode */ + return ev; + } + munki_interp_dark(p, interp, tinttime, 1); #ifdef DEBUG - printf("High gain, int time %f:\n",tinttime); - plot_raw2(ref, interp); + printf("High gain, int time %f:\n",tinttime); + plot_raw2(ref, interp); #endif - if ((tinttime * 1.1) > m->max_int_time) - break; - } -#endif /* USE_HIGH_GAIN_MODE */ + if ((tinttime * 1.1) > m->max_int_time) + break; + } + } } #endif /* TEST_DARK_INTERP */ @@ -1370,27 +1409,24 @@ if (ss->idark_int_time[j] != s->idark_int_time[j]) return ev; } -#ifdef USE_HIGH_GAIN_MODE - s->idark_int_time[2] = s->inttime; - nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]); - a1logd(p->log,3,"Doing adaptive scan black calibration, dcaltime %f, idark_int_time[2] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[2], nummeas, s->gainmode); - if ((ev = munki_dark_measure(p, s->idark_data[2], nummeas, &s->idark_int_time[2], 1)) - != MUNKI_OK) { - m->mmode = mmode; /* Restore actual mode */ - return ev; + if (s->auto_gain) { + s->idark_int_time[2] = s->inttime; + nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]); + a1logd(p->log,3,"Doing adaptive scan black calibration, dcaltime %f, idark_int_time[2] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[2], nummeas, s->gainmode); + if ((ev = munki_dark_measure(p, s->idark_data[2], nummeas, &s->idark_int_time[2], 1)) + != MUNKI_OK) { + m->mmode = mmode; /* Restore actual mode */ + return ev; + } } -#endif s->idark_valid = 1; s->iddate = cdate; -#ifdef USE_HIGH_GAIN_MODE - if (s->gainmode) { + if (s->auto_gain && s->gainmode) { for (j = -1; j < m->nraw; j++) s->dark_data[j] = s->idark_data[2][j]; - } else -#endif - { + } else { for (j = -1; j < m->nraw; j++) s->dark_data[j] = s->idark_data[0][j]; } @@ -1418,12 +1454,8 @@ if (ss->idark_int_time[j] != s->idark_int_time[j]) ss->iddate = s->iddate; ss->dark_int_time = s->dark_int_time; ss->dark_gain_mode = s->dark_gain_mode; -#ifdef USE_HIGH_GAIN_MODE - for (j = 0; j < 4; j += 2) -#else - for (j = 0; j < 2; j += 2) -#endif - { + + for (j = 0; j < (s->auto_gain ? 4 : 2); j += 2) { ss->idark_int_time[j] = s->idark_int_time[j]; for (k = -1; k < m->nraw; k++) ss->idark_data[j][k] = s->idark_data[j][k]; @@ -1724,6 +1756,154 @@ int icoms2munki_err(int se) { return MUNKI_OK; } +/* - - - - - - - - - - - - - - - - */ +/* 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 0.6 seconds. */ +/* inst_misread will be returned on failure to find a transition to black. */ +#define NDMXTIME 0.7 /* Maximum time to take */ +#define NDSAMPS 500 /* Debug samples */ + +typedef struct { + double sec; + double rgb[3]; + double tot; +} i1rgbdsamp; + +munki_code munki_imp_meas_delay( +munki *p, +int *msecdelay) { /* Return the number of msec */ + munki_code ev = MUNKI_OK; + munkiimp *m = (munkiimp *)p->m; + munki_state *s = &m->ms[m->mmode]; + int i, j, k, mm; + 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; + i1rgbdsamp *samp; + double stot, etot, del, thr; + double etime; + int isdeb; + + /* Read the samples */ + inttime = m->min_int_time; + nummeas = (int)(NDMXTIME/inttime + 0.5); + multimeas = dmatrix(0, nummeas-1, -1, m->nwav-1); + if ((samp = (i1rgbdsamp *)calloc(sizeof(i1rgbdsamp), nummeas)) == NULL) { + a1logd(p->log, 1, "munki_meas_delay: malloc failed\n"); + 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; + } + + /* Convert the samples to RGB */ + for (i = 0; i < nummeas; i++) { + samp[i].sec = i * inttime; + 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); + +//printf("~1 multimeas %d %d = %f\n",i, j, multimeas[i][j]); + for (k = 0; k < 3; k++) { + double tt = (double)(wl - rgbw[k]); + tt = (50.0 - fabs(tt))/50.0; + if (tt < 0.0) + tt = 0.0; + samp[i].rgb[k] += sqrt(tt) * multimeas[i][j]; + } + } + samp[i].tot = samp[i].rgb[0] + samp[i].rgb[1] + samp[i].rgb[2]; + } + free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav-1); + + 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; + stot = -1e9; + for (i = 0; i < nummeas; i++) { + if (samp[i].tot > stot) + stot = samp[i].tot; + if (samp[i].sec > 0.1) + break; + } + + /* Over the last 100msec, locate the maximum value */ + etime = samp[nummeas-1].sec; + etot = -1e9; + for (i = nummeas-1; i >= 0; i--) { + if (samp[i].tot > etot) + etot = samp[i].tot; + if ((etime - samp[i].sec) > 0.1) + break; + } + + del = stot - etot; + thr = etot + 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 + + /* 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"); + return MUNKI_RD_NOTRANS_FOUND; + } + + /* Locate the time at which the values are above the end values */ + for (i = nummeas-1; i >= 0; 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); + +#ifdef PLOT_UPDELAY + a1logd(p->log, 0, "munki_meas_delay: returning %d msec\n",*msecdelay); +#endif + free(samp); + + return MUNKI_OK; +} +#undef NDSAMPS +#undef NDMXTIME + /* - - - - - - - - - - - - - - - - */ /* Measure a patch or strip or flash in the current mode. */ @@ -2276,7 +2456,7 @@ munki_code munki_imp_meas_refrate( tt = (40.0 - fabs(tt))/40.0; if (tt < 0.0) tt = 0.0; - samp[i].rgb[k] += tt * multimeas[i][j]; + samp[i].rgb[k] += sqrt(tt) * multimeas[i][j]; } } } @@ -3461,7 +3641,8 @@ munki_code munki_dark_measure_2( free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); #ifdef PLOT_DEBUG - printf("Average absolute sensor readings, average = %f, darkthresh %f\n",sensavg,darkthresh); + a1logd(p->log,4,"dark_measure_2: Avg abs. sensor readings = %f, sh/darkthresh %f\n",sensavg,darkthresh); + printf("sens data:\n"); plot_raw(sens); #endif @@ -3681,6 +3862,7 @@ munki_code munki_whitemeasure( a1logd(p->log,3,"Average absolute sensor readings, avg %f, max %f, darkth %f satth %f\n", sensavg,maxval,darkthresh,trackmax[2]); #ifdef PLOT_DEBUG + printf("absraw whitemeas:\n"); plot_raw(absraw); #endif } @@ -3720,7 +3902,7 @@ munki_code munki_compute_wav_whitemeas( munki_absraw_to_abswav1(p, 1, &abswav1, &absraw); #ifdef PLOT_DEBUG - printf("Converted to wavelengths std res:\n"); + printf("White meas converted to wavelengths std res:\n"); plot_wav1(m, abswav1); #endif } @@ -3810,7 +3992,7 @@ munki_code munki_ledtemp_whitemeasure( *reftemp = 0.5 * (ledtemp[0] + ledtemp[nummeas-1]); #ifdef PLOT_DEBUG - printf("Dark data:\n"); + printf("Ledtemp Dark data:\n"); plot_raw(s->dark_data); #endif @@ -3961,8 +4143,7 @@ munki_code munki_ledtemp_comp( return MUNKI_OK; } -/* Take a measurement reading using the current mode, part 1 */ -/* Converts to completely processed output readings. */ +/* Trigger measure and gather raw readings using the current mode. */ munki_code munki_read_patches_1( munki *p, int ninvmeas, /* Number of extra invalid measurements at start */ @@ -3972,7 +4153,7 @@ munki_code munki_read_patches_1( int gainmode, /* Gain mode to use, 0 = normal, 1 = high */ int *nmeasuered, /* Number actually measured (excluding ninvmeas) */ unsigned char *buf, /* Raw USB reading buffer */ - unsigned int bsize + unsigned int bsize /* Raw USB readings buffer size in bytes */ ) { munki_code ev = MUNKI_OK; munkiimp *m = (munkiimp *)p->m; @@ -4002,8 +4183,8 @@ munki_code munki_read_patches_1( return ev; } -/* Take a measurement reading using the current mode, part 2 */ -/* Converts to completely processed output readings. */ +/* Given a buffer full of raw USB values, process them into */ +/* completely processed spectral output patch readings. */ munki_code munki_read_patches_2( munki *p, double *duration, /* Return flash duration in seconds */ @@ -4014,7 +4195,7 @@ munki_code munki_read_patches_2( int ninvmeas, /* Number of extra invalid measurements at start */ int nummeas, /* Number of actual measurements */ unsigned char *buf, /* Raw USB reading buffer */ - unsigned int bsize + unsigned int bsize /* Raw USB reading buffer size */ ) { munki_code ev = MUNKI_OK; munkiimp *m = (munkiimp *)p->m; @@ -4049,32 +4230,6 @@ munki_code munki_read_patches_2( munki_sub_raw_to_absraw(p, nummeas, inttime, gainmode, multimes, s->dark_data, &darkthresh, 1, NULL); -#ifdef PLOT_TEMPCOMP - /* Plot the raw spectra, 6 at a time */ - if (s->reflective) { - int i, j, k; - double *indx; - double **mod; - indx = dvectorz(0, nummeas-1); - mod = dmatrix(0, 5, 0, nummeas-1); - for (i = 0; i < nummeas; i++) - indx[i] = (double)i; - -// for (j = 0; (j+5) < m->nraw; j += 6) { - for (j = 50; (j+5) < 56; j += 6) { - for (i = 0; i < nummeas; i++) { - for (k = j; k < (j + 6); k++) { - mod[k-j][i] = multimes[i][k]; - } - } - - printf("Before temp comp, bands %d - %d\n",j, j+5); - do_plot6(indx, mod[0], mod[1], mod[2], mod[3], mod[4], mod[5], nummeas); - } - free_dvector(indx, 0, nummeas-1); - free_dmatrix(mod, 0, 5, 0, nummeas-1); - } -#endif #ifdef DUMP_SCANV /* Dump raw scan readings to a file "mkdump.txt" */ { @@ -4097,8 +4252,38 @@ munki_code munki_read_patches_2( #endif #ifdef ENABLE_LEDTEMPC + + /* Do LED temperature compensation of absraw values */ if (s->reflective) { + +#ifdef PLOT_TEMPCOMP + /* Plot the raw spectra, 6 at a time */ + { + int i, j, k; + double *indx; + double **mod; + indx = dvectorz(0, nummeas-1); + mod = dmatrix(0, 5, 0, nummeas-1); + for (i = 0; i < nummeas; i++) + indx[i] = (double)i; + +// for (j = 0; (j+5) < m->nraw; j += 6) { + for (j = 50; (j+5) < 56; j += 6) { + for (i = 0; i < nummeas; i++) { + for (k = j; k < (j + 6); k++) { + mod[k-j][i] = multimes[i][k]; + } + } + + printf("Before temp comp, bands %d - %d\n",j, j+5); + do_plot6(indx, mod[0], mod[1], mod[2], mod[3], mod[4], mod[5], nummeas); + } + free_dvector(indx, 0, nummeas-1); + free_dmatrix(mod, 0, 5, 0, nummeas-1); + } +#endif + /* Do the LED temperature compensation */ if ((ev = munki_ledtemp_comp(p, multimes, ledtemp, nummeas, s->reftemp, s->iwhite_data)) != MUNKI_OK) { free_dvector(ledtemp, 0, nummeas-1); @@ -4177,7 +4362,7 @@ munki_code munki_read_patches_2( /* Recognise the required number of ref/trans patch locations, */ /* and average the measurements within each patch. */ if ((ev = munki_extract_patches_multimeas(p, &rv, absraw, numpatches, multimes, - nummeas, inttime)) != MUNKI_OK) { + nummeas, inttime)) != MUNKI_OK) { free_dvector(ledtemp, 0, nummeas-1); free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1); @@ -4195,6 +4380,52 @@ munki_code munki_read_patches_2( return MUNKI_RD_READINCONS; } +#ifdef ENABLE_REFSTRAYC /* Enable Reflective stray light compensation */ + if (s->reflective) { + int i, j; +# if defined(PLOT_DEBUG) || defined(DUMP_BKLED) + double xx[140]; + double yy[3][140]; +# endif + + double fact = REFSTRAYC_FACTOR; /* Slightly conservative */ + + for (i = 0; i < numpatches; i++) { + + for (j = 0; j < m->nraw; j++) { +# if defined(PLOT_DEBUG) || defined(DUMP_BKLED) + yy[0][j] = absraw[i][j]; +# endif + absraw[i][j] -= fact * s->white_data[j]; +# if defined(PLOT_DEBUG) || defined(DUMP_BKLED) + xx[j] = j; + yy[1][j] = fact * s->white_data[j]; + yy[2][j] = absraw[i][j]; +# endif + } +# ifdef PLOT_DEBUG + printf("Before/After subtracting stray ref. light %d:\n",i); + do_plot6(xx, yy[0], yy[1], yy[2], NULL, NULL, NULL, m->nraw); +# endif +# ifdef DUMP_BKLED /* Save REFSTRAYC & REFLEDNOISE comp plot to "refbk1.txt" & "refbk2.txt" */ + { + xspect sp[3]; + for (i = 0; i < 3; i++) { + sp[i].spec_n = 128; + sp[i].spec_wl_short = 0.0; + sp[i].spec_wl_long = 127.0; + sp[i].norm = 1.0; + for (j = 0; j < 128; j++) + sp[i].spec[j] = yy[i][j]; + } + write_nxspect("refbk2.txt", sp, 3, 0); +# pragma message("######### munki DUMP_BKLED enabled! ########") + } +# endif /* DUMP_BKLED */ + } + } +#endif /* ENABLE_REFSTRAYC */ + /* Convert an absraw array from raw wavelengths to output wavelenths */ munki_absraw_to_abswav(p, numpatches, specrd, absraw); free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1); @@ -4243,9 +4474,11 @@ munki_code munki_read_patches_2( return ev; } -/* Take a measurement reading using the current mode, part 2a */ -/* Converts to completely processed output readings, */ +/* Given a buffer full of raw USB values, process them into */ +/* completely processed spectral output readings, */ /* but don't average together or extract patches or flash. */ +/* This is used for delay & refresh rate measurement. */ +/* !! Doesn't do LED temperature compensation for reflective !! */ /* (! Note that we aren't currently detecting saturation here!) */ munki_code munki_read_patches_2a( munki *p, @@ -4309,7 +4542,8 @@ munki_code munki_read_patches_2a( /* Take a measurement reading using the current mode (combined parts 1 & 2a) */ /* Converts to completely processed output readings, without averaging or extracting */ -/* sample patches. */ +/* sample patches, for emissive measurement mode. */ +/* This is used for delay & refresh rate measurement. */ munki_code munki_read_patches_all( munki *p, double **specrd, /* Return array [numpatches][nwav] of spectral reading values */ @@ -4535,7 +4769,7 @@ munki_code munki_sens_to_raw( int ninvalid, /* Number of initial invalid readings to skip */ int nummeas, /* Number of readings measured */ double satthresh, /* Saturation threshold to trigger error in raw units (if > 0.0) */ - double *pdarkthresh /* Return a dark threshold value */ + double *pdarkthresh /* Return a dark threshold value = sheilded cell values */ ) { munkiimp *m = (munkiimp *)p->m; int i, j, k; @@ -4551,11 +4785,6 @@ munki_code munki_sens_to_raw( return MUNKI_INT_ASSERT; } - if (ninvalid >= nummeas) { - a1logw(p->log,"ninvalid %d is >= nummeas %d!\n",ninvalid,nummeas); - return MUNKI_INT_ASSERT; - } - if (ninvalid > 0) a1logd(p->log, 4, "munki_sens_to_raw: Skipping %d invalid readings\n",ninvalid); @@ -4624,12 +4853,13 @@ void munki_sub_raw_to_absraw( double inttime, /* Integration time used */ int gainmode, /* Gain mode, 0 = normal, 1 = high */ double **absraw, /* Source/Desination array [-1 nraw] */ - double *sub, /* Value to subtract [-1 nraw] */ + double *sub, /* Value to subtract [-1 nraw] (ie. cal dark data) */ double *trackmax, /* absraw values that should be offset the same as max */ int ntrackmax, /* Number of trackmax values */ double *maxv /* If not NULL, return the maximum value */ ) { munkiimp *m = (munkiimp *)p->m; + munki_state *s = &m->ms[m->mmode]; int npoly; /* Number of linearisation coefficients */ double *polys; /* the coeficients */ double scale; /* Absolute scale value */ @@ -4639,6 +4869,11 @@ void munki_sub_raw_to_absraw( double rawmax, maxval = -1e38; double maxzero = 0.0; int i, j, k; + + /* Heusristic correction for LED interference bump for 0.018 secs int_time */ + int pos[] = { 0, 20, 56, 62, 75, 127 }; +// double off[] = { 0.7, 0.0, 0.6, -0.9, -1.2, -0.7 }; + double off[] = { 0.7, 0.0, 0.6, -0.9, -0.8, -0.5 }; if (gainmode) { /* High gain */ npoly = m->nlin1; /* Encodes gain too */ @@ -4681,18 +4916,129 @@ void munki_sub_raw_to_absraw( a1logd(p->log,4,"Black shielded value = %f, Reading shielded value = %f\n",sub[-1], avgscell); - /* Compute the adjusted black */ - for (j = 0; j < m->nraw; j++) { -#ifdef NEVER - /* simple additive correction */ -# pragma message("######### munki Simple shielded cell temperature correction! ########") - asub[j] = sub[j] + avgscell - sub[-1]; + /* Compute the adjusted black for each band */ + if (s->reflective) { + + /* It seems that having the LED on shifts the shielded cell values */ + /* by about 2.5, and this stuffs up the reflective measurement. */ + /* This seems to be from the LED PWM driver, which perhaps */ + /* is synchronous to the sensor clock, and so switches */ + /* at a certain point in the transfer of data from the sensor. */ + /* The result is a step up from 0-60, and then down from 61-128. */ + /* Perhaps altering the LED PWM setting and seeing if this point */ + /* shifts would be a way of confirming this ? */ + /* There is also some stray light reflected into the sensor */ + /* from the LED, but due to the LED step, the sensor reading is */ + /* less than the dark data at some wavelengths. */ + /* The details of the LED step seem to be integration time dependent, */ + /* but decresing the scanning rate therebye increasing integration */ + /* time and light level reduces the impact of this error. */ + + /* Since we do an on the fly black measurement before each */ + /* reflective measurement, ignoring the shielded cell values */ + /* shouldn't affect accuracy so much. */ + +#ifdef ENABLE_REFLEDINTER + /* A heuristic to correct for the LED noise. */ + /* This is only valid for int_time of 0.0182 secs, */ + /* and it's not clear how well it works across different */ + /* temperatures or examples of the ColorMunki. */ + /* in another revision ?? */ + for (j = 0; j < m->nraw; j++) { + int ix; + double bl, val; + + for (ix = 0; ; ix++) { + if (j >= pos[ix] && j <= pos[ix+1]) + break; + } + bl = (j - pos[ix])/((double)pos[ix+1] - pos[ix]); + val = (1.0 - bl) * off[ix] + bl * off[ix+1]; + asub[j] = sub[j] + val; + } +#else + for (j = 0; j < m->nraw; j++) + asub[j] = sub[j]; /* Just use the calibration dark data */ +#endif + + } else { + /* No LED on operation - use sheilded cell values */ + for (j = 0; j < m->nraw; j++) { +#ifdef ENABLE_BKDRIFTC + +# ifdef HEURISTIC_BKDRIFTC + /* heuristic scaled correction */ + asub[j] = zero - (zero - sub[j]) * (zero - avgscell)/(zero - sub[-1]); +# else + /* simple additive correction */ +# pragma message("######### munki Simple shielded cell temperature correction! ########") + asub[j] = sub[j] + (avgscell - sub[-1]); +# endif #else - /* heuristic scaled correction */ - asub[j] = zero - (zero - sub[j]) * (zero - avgscell)/(zero - sub[-1]); +# pragma message("######### munki No shielded cell temperature correction! ########") + asub[j] = sub[j]; /* Just use the calibration dark data */ #endif + } } - + +#if defined(PLOT_DEBUG) || defined(DUMP_BKLED) + { + double xx[130]; + double yy[3][130]; + + for (j = -1; j < m->nraw+1; j++) + yy[0][j+1] = 0.0; + + for (i = 0; i < nummeas; i++) { + for (j = -1; j < m->nraw; j++) + yy[0][j+1] += absraw[i][j]; + } + for (j = -1; j < m->nraw; j++) + yy[0][j+1] /= (double)nummeas; + + for (j = -1; j < m->nraw; j++) + yy[1][j+1]= sub[j]; + + /* Show what ENABLE_REFLEDINTER would do */ + for (j = 0; j < m->nraw; j++) { + int ix; + double bl, val; + + for (ix = 0; ; ix++) { + if (j >= pos[ix] && j <= pos[ix+1]) + break; + } + bl = (j - pos[ix])/((double)pos[ix+1] - pos[ix]); + val = (1.0 - bl) * off[ix] + bl * off[ix+1]; + yy[2][j+1] = yy[0][j+1] - val; + } + yy[2][0] = yy[0][0]; + + for (j = -1; j < m->nraw; j++) + xx[j+1] = (double)j; + + xx[0]= -10.0; + +# ifdef PLOT_DEBUG + printf("sub_raw_to_absraw %d samp avg - dark ref:\n",nummeas); + do_plot(xx, yy[0], yy[1], yy[2], 129); +# endif +# ifdef DUMP_BKLED + { + xspect sp[3]; + for (i = 0; i < 3; i++) { + sp[i].spec_n = 128; + sp[i].spec_wl_short = 0.0; + sp[i].spec_wl_long = 127.0; + sp[i].norm = 1.0; + for (j = 0; j < 128; j++) + sp[i].spec[j] = yy[i][j+1]; + } + write_nxspect("refbk1.txt", sp, 3, 0); + } +# endif /* DUMP_BKLED */ + } +#endif /* PLOT_DEBUG || DUMP_BKLED */ /* For each measurement */ for (i = 0; i < nummeas; i++) { @@ -4701,6 +5047,7 @@ void munki_sub_raw_to_absraw( for (j = 0; j < m->nraw; j++) { rval = absraw[i][j]; + sval = rval - asub[j]; /* Make zero based */ #ifdef ENABLE_NONLINCOR @@ -4825,7 +5172,7 @@ int munki_average_multimeas( #define BH 105 /* End */ #define BW 5 /* Width */ -/* Record of possible patch */ +/* Record of possible patch within a reading buffer */ typedef struct { int ss; /* Start sample index */ int no; /* Number of samples */ @@ -4866,7 +5213,7 @@ munki_code munki_extract_patches_multimeas( double white_avg; /* Average of (aproximate) white data */ int rv = 0; double patch_cons_thr = PATCH_CONS_THR * m->scan_toll_ratio; -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC double **plot; #endif @@ -4888,7 +5235,7 @@ munki_code munki_extract_patches_multimeas( maxval[j] = 1.0; } -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC /* Plot out 6 lots of 6 values each */ plot = dmatrixz(0, 6, 0, nummeas-1); // for (j = 1; j < (m->nraw-6); j += 6) /* Plot all the bands */ @@ -4906,6 +5253,16 @@ munki_code munki_extract_patches_multimeas( } #endif +#ifdef NEVER + /* Plot the shielded cell value */ + for (i = 0; i < nummeas; i++) { + plot[0][i] = multimeas[i][-1]; + plot[6][i] = (double)i; + } + printf("Sheilded values\n"); + do_plot6(plot[6], plot[0], NULL, NULL, NULL, NULL, NULL, nummeas); +#endif + sslope = dmatrixz(0, nummeas-1, -1, m->nraw-1); slope = dvectorz(0, nummeas-1); fslope = dvectorz(0, nummeas-1); @@ -5144,7 +5501,7 @@ munki_code munki_extract_patches_multimeas( } #endif -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC printf("Slope filter output\n"); for (i = 0; i < nummeas; i++) { int jj; @@ -5196,7 +5553,8 @@ munki_code munki_extract_patches_multimeas( } a1logd(p->log,7,"Number of patches = %d\n",npat); - /* We don't count the first and last patches, as we assume they are white leader */ + /* We don't count the first and last patches, as we assume they are white leader. */ + /* (They are marked !use in list anyway) */ if (npat < (tnpatch + 2)) { free_ivector(sizepop, 0, nummeas-1); free_dvector(slope, 0, nummeas-1); @@ -5214,7 +5572,7 @@ munki_code munki_extract_patches_multimeas( } avglegth /= (double)npat; -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC for (i = 0; i < npat; i++) { printf("Raw patch %d, start %d, length %d\n",i, pat[i].ss, pat[i].no); } @@ -5227,7 +5585,7 @@ munki_code munki_extract_patches_multimeas( /* Locate the median potential patch width */ for (j = 0, i = 0; i < nummeas; i++) { j += sizepop[i]; - if (j >= ((npat-2)/2)) + if (j > ((npat-2)/2)) break; } median = (double)i; @@ -5308,7 +5666,7 @@ munki_code munki_extract_patches_multimeas( return MUNKI_RD_NOTENOUGHPATCHES; } -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC printf("Got %d patches out of potentional %d:\n",tnpatch, npat); printf("Average patch legth %f\n",avglegth); for (i = 1; i < (npat-1); i++) { @@ -5325,15 +5683,15 @@ munki_code munki_extract_patches_multimeas( if (pat[k].use == 0) continue; - nno = (pat[k].no * 3)/4; +// nno = (pat[k].no * 2)/3; // experimental tighter trim trim = (pat[k].no - nno)/2; pat[k].ss += trim; pat[k].no = nno; } -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC printf("After trimming got:\n"); for (i = 1; i < (npat-1); i++) { if (pat[i].use == 0) @@ -5351,7 +5709,8 @@ munki_code munki_extract_patches_multimeas( slope[i] = 0.0; } - printf("Trimmed output:\n"); + printf("Trimmed output - averaged bands:\n"); + /* Plot box averaged bands */ for (i = 0; i < nummeas; i++) { int jj; for (jj = 0, j = BL; jj < 6 && j < BH; jj++, j += ((BH-BL)/6)) { @@ -5364,8 +5723,27 @@ munki_code munki_extract_patches_multimeas( for (i = 0; i < nummeas; i++) plot[6][i] = (double)i; do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas); + +#ifdef NEVER + /* Plot all the bands */ + printf("Trimmed output - all bands:\n"); + for (j = 0; j < (m->nraw-5); j += 5) { + for (k = 0; k < 5; k ++) { + for (i = 0; i < nummeas; i++) { + plot[k][i] = multimeas[i][j+k]/maxval[j+k]; + } + } + for (i = 0; i < nummeas; i++) + plot[6][i] = (double)i; + printf("Bands %d - %d\n",j,j+5); + do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas); + } +#endif + #endif + /* Now compute averaged patch values */ + /* Compute average of (aproximate) white */ white_avg = 0.0; for (j = 1; j < (m->nraw-1); j++) @@ -5429,14 +5807,14 @@ munki_code munki_extract_patches_multimeas( if (flags != NULL) *flags = rv; -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC free_dmatrix(plot, 0, 6, 0, nummeas-1); #endif free_dvector(slope, 0, nummeas-1); free_ivector(sizepop, 0, nummeas-1); free_dvector(maxval, -1, m->nraw-1); - free(pat); + free(pat); /* Otherwise caller will have to do it */ a1logd(p->log,3,"munki_extract_patches_multimeas done, sat = %s, inconsist = %s\n", rv & 2 ? "true" : "false", rv & 1 ? "true" : "false"); @@ -5475,7 +5853,7 @@ munki_code munki_extract_patches_flash( double *aavg; /* ambient average [-1 nraw] */ double finttime; /* Flash integration time */ int rv = 0; -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC double **plot; #endif @@ -5511,7 +5889,7 @@ munki_code munki_extract_patches_flash( thresh = (3.0 * mean + maxval)/4.0; a1logd(p->log,7,"munki_extract_patches_flash band %d minval %f maxval %f, mean = %f, thresh = %f\n",maxband,minval,maxval,mean, thresh); -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC /* Plot out 6 lots of 6 values each */ plot = dmatrixz(0, 6, 0, nummeas-1); for (j = maxband -3; j>= 0 && j < (m->nraw-6); j += 100) /* Do one set around max */ @@ -5529,7 +5907,7 @@ munki_code munki_extract_patches_flash( free_dmatrix(plot,0,6,0,nummeas-1); #endif -#ifdef PATREC_DEBUG +#ifdef PLOT_PATREC /* Plot just the pulses */ { int start, end; @@ -5841,8 +6219,11 @@ void munki_scale_specrd( #ifdef HIGH_RES /* High res congiguration */ -#undef EXISTING_SHAPE /* Else generate filter shape */ -#undef USE_GAUSSIAN /* Use gaussian filter shape, else lanczos2 */ +#undef EXISTING_SHAPE /* [und] Else generate filter shape */ +#define USE_GAUSSIAN /* [def] Use gaussian filter shape, else lanczos2 */ + +#define DO_CCDNORM /* [def] Normalise CCD values to original */ +#define DO_CCDNORMAVG /* [unde] Normalise averages rather than per CCD bin */ #ifdef NEVER /* Plot the matrix coefficients */ @@ -5940,7 +6321,8 @@ static double lanczos2(double wi, double x) { /* gausian */ wi = wi/(2.0 * sqrt(2.0 * log(2.0))); /* Convert width at half max to std. dev. */ x = x/(sqrt(2.0) * wi); - y = 1.0/(wi * sqrt(2.0 * DBL_PI)) * exp(-(x * x)); +// y = 1.0/(wi * sqrt(2.0 * DBL_PI)) * exp(-(x * x)); /* Unity area */ + y = exp(-(x * x)); /* Center at 1.0 */ #else /* lanczos2 */ @@ -5964,6 +6346,10 @@ static int gcc_bug_fix(int i) { } #endif /* APPLE */ +#ifdef SALONEINSTLIB +# define ONEDSTRAYLIGHTUS +#endif + /* Create high resolution mode references, */ /* Create Reflective if ref nz, else create Emissive */ /* We expect this to be called twice, once for each. */ @@ -5982,6 +6368,8 @@ munki_code munki_create_hr(munki *p, int ref) { int *mtx_index1, **pmtx_index2, *mtx_index2; int *mtx_nocoef1, **pmtx_nocoef2, *mtx_nocoef2; double *mtx_coef1, **pmtx_coef2, *mtx_coef2; + + double min_wl = ref ? WL_REF_MIN : WL_EMIS_MIN; /* Start with nominal values. May alter these if filters are not unique */ nwav1 = m->nwav1; @@ -5993,7 +6381,8 @@ munki_code munki_create_hr(munki *p, int ref) { mtx_index1 = m->rmtx_index1; mtx_nocoef1 = m->rmtx_nocoef1; mtx_coef1 = m->rmtx_coef1; - mtx_index2 = mtx_nocoef2 = NULL; + mtx_index2 = NULL; + mtx_nocoef2 = NULL; mtx_coef2 = NULL; pmtx_index2 = &m->rmtx_index2; pmtx_nocoef2 = &m->rmtx_nocoef2; @@ -6002,7 +6391,8 @@ munki_code munki_create_hr(munki *p, int ref) { mtx_index1 = m->emtx_index1; mtx_nocoef1 = m->emtx_nocoef1; mtx_coef1 = m->emtx_coef1; - mtx_index2 = mtx_nocoef2 = NULL; + mtx_index2 = NULL; + mtx_nocoef2 = NULL; mtx_coef2 = NULL; pmtx_index2 = &m->emtx_index2; pmtx_nocoef2 = &m->emtx_nocoef2; @@ -6020,7 +6410,7 @@ munki_code munki_create_hr(munki *p, int ref) { /* For each matrix value */ sx = mtx_index1[j]; /* Starting index */ - if (jj == 0 && (j+1) < m->nwav1 && sx == mtx_index1[j+1]) { /* Same index */ + if (j < (m->nwav1-1) && sx == mtx_index1[j+1]) { /* Skip duplicates + last */ // printf("~1 skipping %d\n",j); wl_short1 += wl_step1; nwav1--; @@ -6076,6 +6466,7 @@ munki_code munki_create_hr(munki *p, int ref) { /* Compute the crossover points between each filter */ for (i = 0; i < (nwav1-1); i++) { double den, y1, y2, y3, y4, yn, xn; /* Location of intersection */ + double eps = 1e-6; /* Numerical tollerance */ double besty = -1e6; /* between filter i and i+1, we want to find the two */ @@ -6085,13 +6476,8 @@ munki_code munki_create_hr(munki *p, int ref) { for (j = 0; j < (mtx_nocoef1[i]-1); j++) { for (k = 0; k < (mtx_nocoef1[i+1]-1); k++) { if (coeff[i][j].ix == coeff[i+1][k].ix - && coeff[i][j+1].ix == coeff[i+1][k+1].ix - && coeff[i][j].we > 0.0 && coeff[i][j+1].we > 0.0 - && coeff[i+1][k].we > 0.0 && coeff[i+1][k+1].we > 0.0 - && (( coeff[i][j].we >= coeff[i+1][k].we - && coeff[i][j+1].we <= coeff[i+1][k+1].we) - || ( coeff[i][j].we <= coeff[i+1][k].we - && coeff[i][j+1].we >= coeff[i+1][k+1].we))) { + && coeff[i][j+1].ix == coeff[i+1][k+1].ix) { + // a1logd(p->log,3,"got it at %d, %d: %d = %d, %d = %d\n",j,k, coeff[i][j].ix, coeff[i+1][k].ix, coeff[i][j+1].ix, coeff[i+1][k+1].ix); /* Compute the intersection of the two line segments */ @@ -6099,17 +6485,22 @@ munki_code munki_create_hr(munki *p, int ref) { y2 = coeff[i][j+1].we; y3 = coeff[i+1][k].we; y4 = coeff[i+1][k+1].we; +// a1logd(p->log,3,"y1 %f, y2 %f, y3 %f, y4 %f\n",y1, y2, y3, y4); den = -y4 + y3 + y2 - y1; + if (fabs(den) < eps) + continue; yn = (y2 * y3 - y1 * y4)/den; xn = (y3 - y1)/den; + if (xn < -eps || xn > (1.0 + eps)) + continue; // a1logd(p->log,3,"den = %f, yn = %f, xn = %f\n",den,yn,xn); -// a1logd(p->log,3,"Intersection %d: wav %f, raw %f, wei %f\n",i+1,xp[i+1].wav,xp[i+1].raw,xp[i+1].wei); if (yn > besty) { xp[i+1].wav = XSPECT_WL(wl_short1, wl_long1, nwav1, i + 0.5); xp[i+1].raw = (1.0 - xn) * coeff[i][j].ix + xn * coeff[i][j+1].ix; xp[i+1].wei = yn; besty = yn; -// a1logd(p->log,3,"Found new best y\n"); +// a1logd(p->log,3,"Intersection %d: wav %f, raw %f, wei %f\n",i+1,xp[i+1].wav,xp[i+1].raw,xp[i+1].wei); +// a1logd(p->log,3,"Found new best y %f\n",yn); } // a1logd(p->log,3,"\n"); } @@ -6151,7 +6542,7 @@ munki_code munki_create_hr(munki *p, int ref) { } } if (j >= mtx_nocoef1[0]) { /* Assert */ - a1logw(p->log,"munki: failed to end crossover\n"); + a1logw(p->log,"munki: failed to find end crossover\n"); return MUNKI_INT_ASSERT; } den = -y4 + y3 + y2 - y1; @@ -6189,7 +6580,7 @@ munki_code munki_create_hr(munki *p, int ref) { } } if (j >= mtx_nocoef1[nwav1-1]) { /* Assert */ - a1logw(p->log, "munki: failed to end crossover\n"); + a1logw(p->log, "munki: failed to find end crossover\n"); return MUNKI_INT_ASSERT; } den = -y4 + y3 + y2 - y1; @@ -6397,14 +6788,15 @@ munki_code munki_create_hr(munki *p, int ref) { { double fshmax; /* filter shape max wavelength from center */ +#define MXNOWL 500 /* Max hires bands */ #define MXNOFC 64 - munki_fc coeff2[500][MXNOFC]; /* New filter cooefficients */ + munki_fc coeff2[MXNOWL][MXNOFC]; /* New filter cooefficients */ double twidth; /* Construct a set of filters that uses more CCD values */ twidth = HIGHRES_WIDTH; - if (m->nwav2 > 500) { /* Assert */ + if (m->nwav2 > MXNOWL) { /* Assert */ a1logw(p->log,"High res filter has too many bands\n"); return MUNKI_INT_ASSERT; } @@ -6474,7 +6866,7 @@ munki_code munki_create_hr(munki *p, int ref) { raw2wav->interp(raw2wav, &pp); w2 = pp.v[0]; -// a1logd(p->log,1,"CCD %d, wl %f\n",i,wl); + a1logd(p->log,1,"CCD %d, wl %f - %f\n",i,w1,w2); /* For each filter */ for (j = 0; j < m->nwav2; j++) { @@ -6482,6 +6874,10 @@ munki_code munki_create_hr(munki *p, int ref) { double we; cwl = m->wl_short2 + (double)j * (m->wl_long2 - m->wl_short2)/(m->nwav2-1.0); + + if (cwl < min_wl) /* Duplicate below this wl */ + cwl = min_wl; + rwl = wl - cwl; /* relative wavelgth to filter */ if (fabs(w1 - cwl) > fshmax && fabs(w2 - cwl) > fshmax) @@ -6492,8 +6888,12 @@ munki_code munki_create_hr(munki *p, int ref) { { int nn; double lw, ll; - - nn = (int)(fabs(w2 - w1)/0.05 + 0.5); +#ifdef FAST_HIGH_RES_SETUP +# define FINC 0.2 +#else +# define FINC 0.05 +#endif + nn = (int)(fabs(w2 - w1)/FINC + 0.5); lw = w1; #ifdef EXISTING_SHAPE @@ -6538,7 +6938,21 @@ munki_code munki_create_hr(munki *p, int ref) { coeff2[j][mtx_nocoef2[j]].ix = i; coeff2[j][mtx_nocoef2[j]++].we = we; -// a1logd(p->log,1,"filter %d, cwl %f, rwl %f, ix %d, we %f\n",j,cwl,rwl,i,we); + a1logd(p->log,1,"filter %d, cwl %f, rwl %f, ix %d, we %f\n",j,cwl,rwl,i,we); + } + } + + /* Dump the filter coefficients */ + if (p->log->debug >= 1) { + + /* For each output wavelength */ + for (j = 0; j < m->nwav2; j++) { + + a1logd(p->log,1,"filter %d, cwl %f\n",j,XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, j)); + /* For each matrix value */ + for (k = 0; k < mtx_nocoef2[j]; k++) { + a1logd(p->log,1," CCD %d, we %f\n",coeff2[j][k].ix,coeff2[j][k].we); + } } } @@ -6575,89 +6989,230 @@ munki_code munki_create_hr(munki *p, int ref) { } #endif /* HIGH_RES_PLOT */ + /* Convert into runtime format */ + { + int xcount; + + if ((*pmtx_index2 = mtx_index2 = (int *)calloc(m->nwav2, sizeof(int))) == NULL) { + a1logd(p->log,1,"munki: malloc mtx_index2 failed!\n"); + return MUNKI_INT_MALLOC; + } + + xcount = 0; + for (j = 0; j < m->nwav2; j++) { + mtx_index2[j] = coeff2[j][0].ix; + xcount += mtx_nocoef2[j]; + } + + if ((*pmtx_coef2 = mtx_coef2 = (double *)calloc(xcount, sizeof(double))) == NULL) { + a1logd(p->log,1,"munki: malloc mtx_coef2 failed!\n"); + return MUNKI_INT_MALLOC; + } + + for (i = j = 0; j < m->nwav2; j++) + for (k = 0; k < mtx_nocoef2[j]; k++, i++) + mtx_coef2[i] = coeff2[j][k].we; + } + /* Normalise the filters area in CCD space, while maintaining the */ /* total contribution of each CCD at the target too. */ + /* Hmm. This will wreck super-sample. We should fix it */ +#ifdef DO_CCDNORM /* Normalise CCD values to original */ { - int ii; - double tot = 0.0; - double ccdweight[NSEN_MAX], avgw; /* Weighting determined by cell widths */ - double ccdsum[NSEN_MAX]; + double x[4], y[4]; + double avg[2], max[2]; + double ccdsum[2][128]; /* Target weight/actual for each CCD */ + double dth[2]; + + avg[0] = avg[1] = 0.0; + max[0] = max[1] = 0.0; + for (j = 0; j < 128; j++) { + ccdsum[0][j] = 0.0; + ccdsum[1][j] = 0.0; + } - /* Normalize the overall filter weightings */ - for (j = 0; j < m->nwav2; j++) - for (k = 0; k < mtx_nocoef2[j]; k++) - tot += coeff2[j][k].we; - tot /= (double)m->nwav2; - for (j = 0; j < m->nwav2; j++) - for (k = 0; k < mtx_nocoef2[j]; k++) - coeff2[j][k].we /= tot; + /* Compute the weighting of each CCD value in the normal output */ + for (cx = j = 0; j < m->nwav1; j++) { /* For each wavelength */ - /* Determine the relative weights for each CCD */ - avgw = 0.0; - for (i = 0; i < m->nraw; i++) { - co pp; + /* For each matrix value */ + sx = mtx_index1[j]; /* Starting index */ + if (j < (m->nwav1-2) && sx == mtx_index1[j+1]) { + cx += mtx_nocoef1[j]; + continue; /* Skip all duplicate filters */ + } + for (k = 0; k < mtx_nocoef1[j]; k++, cx++, sx++) { + ccdsum[0][sx] += mtx_coef1[cx]; +//printf("~1 Norm CCD [%d] %f += [%d] %f\n",sx,ccdsum[0][sx],cx, mtx_coef1[cx]); + } + } - pp.p[0] = i - 0.5; - raw2wav->interp(raw2wav, &pp); - ccdweight[i] = pp.v[0]; + /* Compute the weighting of each CCD value in the hires output */ + for (cx = j = 0; j < m->nwav2; j++) { /* For each wavelength */ - pp.p[0] = i + 0.5; - raw2wav->interp(raw2wav, &pp); - ccdweight[i] = fabs(ccdweight[i] - pp.v[0]); - avgw += ccdweight[i]; + /* For each matrix value */ + sx = mtx_index2[j]; /* Starting index */ + if (j < (m->nwav2-2) && sx == mtx_index2[j+1]) { + cx += mtx_nocoef2[j]; + continue; /* Skip all duplicate filters */ + } + for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) { + ccdsum[1][sx] += mtx_coef2[cx]; +//printf("~1 HiRes CCD [%d] %f += [%d] %f\n",sx,ccdsum[1][sx],cx, mtx_coef2[cx]); + } } - avgw /= 126.0; - // ~~this isn't right because not all CCD's get used!! -#ifdef NEVER - /* Itterate */ - for (ii = 0; ; ii++) { - - /* Normalize the individual filter weightings */ - for (j = 0; j < m->nwav2; j++) { - double err; - tot = 0.0; - for (k = 0; k < mtx_nocoef2[j]; k++) - tot += coeff2[j][k].we; - err = 1.0 - tot; +#ifdef HIGH_RES_PLOT + /* Plot target CCD values */ + { + double xx[128], y1[128], y2[128]; + + for (i = 0; i < 128; i++) { + xx[i] = i; + y1[i] = ccdsum[0][i]; + y2[i] = ccdsum[1][i]; + } - for (k = 0; k < mtx_nocoef2[j]; k++) - coeff2[j][k].we += err/mtx_nocoef2[j]; -// for (k = 0; k < mtx_nocoef2[j]; k++) -// coeff2[j][k].we *= 1.0/tot; + printf("Raw target and actual CCD weight sums:\n"); + do_plot(xx, y1, y2, NULL, 128); + } +#endif + + /* Figure valid range and extrapolate to edges */ + dth[0] = 0.0; /* ref */ + dth[1] = 0.007; /* hires */ + + for (k = 0; k < 2; k++) { + + for (i = 0; i < 128; i++) { + if (ccdsum[k][i] > max[k]) + max[k] = ccdsum[k][i]; } - /* Check CCD sums */ - for (i = 0; i < nraw; i++) - ccdsum[i] = 0.0; +//printf("~1 max[%d] = %f\n",k, max[k]); + /* Figure out the valid range */ + for (i = 64; i >= 0; i--) { + if (ccdsum[k][i] > (0.8 * max[k])) { + x[0] = (double)i; + } else { + break; + } + } + for (i = 64; i < 128; i++) { + if (ccdsum[k][i] > (0.8 * max[k])) { + x[3] = (double)i; + } else { + break; + } + } + /* Space off the last couple of entries */ + x[0] += 2.0; + x[3] -= 6.0; + x[1] = floor((2 * x[0] + x[3])/3.0); + x[2] = floor((x[0] + 2 * x[3])/3.0); + + for (i = 0; i < 4; i++) + y[i] = ccdsum[k][(int)x[i]]; + +//printf("~1 extrap nodes %f, %f, %f, %f\n",x[0],x[1],x[2],x[3]); +//printf("~1 extrap value %f, %f, %f, %f\n",y[0],y[1],y[2],y[3]); - for (j = 0; j < m->nwav2; j++) { - for (k = 0; k < mtx_nocoef2[j]; k++) - ccdsum[coeff2[j][k].ix] += coeff2[j][k].we; + for (i = 0; i < 128; i++) { + double xw, yw; + + xw = (double)i; + + /* Compute interpolated value using Lagrange: */ + yw = y[0] * (xw-x[1]) * (xw-x[2]) * (xw-x[3]) + /((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3])) + + y[1] * (xw-x[0]) * (xw-x[2]) * (xw-x[3]) + /((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3])) + + y[2] * (xw-x[0]) * (xw-x[1]) * (xw-x[3]) + /((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3])) + + y[3] * (xw-x[0]) * (xw-x[1]) * (xw-x[2]) + /((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2])); + + if ((xw < x[0] || xw > x[3]) + && fabs(ccdsum[k][i] - yw)/yw > dth[k]) { + ccdsum[k][i] = yw; + } + avg[k] += ccdsum[k][i]; } + avg[k] /= 128.0; + } - if (ii >= 6) - break; +#ifdef HIGH_RES_PLOT + /* Plot target CCD values */ + { + double xx[129], y1[129], y2[129]; + + for (i = 0; i < 128; i++) { + xx[i] = i; + y1[i] = ccdsum[0][i]/avg[0]; + y2[i] = ccdsum[1][i]/avg[1]; + } + xx[i] = i; + y1[i] = 0.0; + y2[i] = 0.0; + + printf("Extrap. target and actual CCD weight sums:\n"); + do_plot(xx, y1, y2, NULL, 129); + } +#endif + +#ifdef DO_CCDNORMAVG /* Just correct by average */ + for (cx = j = 0; j < m->nwav2; j++) { /* For each wavelength */ - /* Readjust CCD sum */ - for (i = 0; i < nraw; i++) { - ccdsum[i] = ccdtsum[i]/ccdsum[i]; /* Amount to adjust */ + /* For each matrix value */ + sx = mtx_index2[j]; /* Starting index */ + for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) { + mtx_coef2[cx] *= 10.0/twidth * avg[0]/avg[1]; } + } + +#else /* Correct by CCD bin */ + + /* Correct the weighting of each CCD value in the hires output */ + for (i = 0; i < 128; i++) { + ccdsum[1][i] = 10.0/twidth * ccdsum[0][i]/ccdsum[1][i]; /* Correction factor */ + } + for (cx = j = 0; j < m->nwav2; j++) { /* For each wavelength */ - for (j = 0; j < m->nwav2; j++) { - for (k = 0; k < mtx_nocoef2[j]; k++) - coeff2[j][k].we *= ccdsum[coeff2[j][k].ix]; + /* For each matrix value */ + sx = mtx_index2[j]; /* Starting index */ + for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) { + mtx_coef2[cx] *= ccdsum[1][sx]; } } -#endif /* NEVER */ +#endif } +#endif /* DO_CCDNORM */ #ifdef HIGH_RES_PLOT - /* Plot resampled curves */ { + static munki_fc coeff2[MXNOWL][MXNOFC]; double *xx, *ss; double **yy; + /* Convert the native filter cooeficient representation to */ + /* a 2D array we can randomly index. */ + for (cx = j = 0; j < m->nwav2; j++) { /* For each output wavelength */ + if (j >= MXNOWL) { /* Assert */ + a1loge(p->log,1,"munki: number of hires output wavelenths is > %d\n",MXNOWL); + return MUNKI_INT_ASSERT; + } + + /* For each matrix value */ + sx = mtx_index2[j]; /* Starting index */ + for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) { + if (k >= MXNOFC) { /* Assert */ + a1loge(p->log,1,"munki: number of hires filter coeefs is > %d\n",MXNOFC); + return MUNKI_INT_ASSERT; + } + coeff2[j][k].ix = sx; + coeff2[j][k].we = mtx_coef2[cx]; + } + } + xx = dvectorz(-1, m->nraw-1); /* X index */ yy = dmatrixz(0, 5, -1, m->nraw-1); /* Curves distributed amongst 5 graphs */ @@ -6675,36 +7230,14 @@ munki_code munki_create_hr(munki *p, int ref) { } } - printf("Normalized Hi-Res wavelength sampling curves:\n"); + printf("Normalized Hi-Res wavelength sampling curves: %s\n",ref ? "refl" : "emis"); do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw); free_dvector(xx, -1, m->nraw-1); free_dmatrix(yy, 0, 2, -1, m->nraw-1); } #endif /* HIGH_RES_PLOT */ - /* Convert into runtime format */ - { - int xcount; - - if ((*pmtx_index2 = mtx_index2 = (int *)calloc(m->nwav2, sizeof(int))) == NULL) { - a1logd(p->log,1,"munki: malloc mtx_index2 failed!\n"); - return MUNKI_INT_MALLOC; - } - - xcount = 0; - for (j = 0; j < m->nwav2; j++) { - mtx_index2[j] = coeff2[j][0].ix; - xcount += mtx_nocoef2[j]; - } - - if ((*pmtx_coef2 = mtx_coef2 = (double *)calloc(xcount, sizeof(double))) == NULL) { - a1logd(p->log,1,"munki: malloc mtx_coef2 failed!\n"); - return MUNKI_INT_MALLOC; - } - - for (i = j = 0; j < m->nwav2; j++) - for (k = 0; k < mtx_nocoef2[j]; k++, i++) - mtx_coef2[i] = coeff2[j][k].we; - } +#undef MXNOWL +#undef MXNOFC /* Basic capability is initialised */ m->hr_inited++; @@ -6713,9 +7246,9 @@ munki_code munki_create_hr(munki *p, int ref) { /* If both reflective and emissive samplings have been created, */ /* deal with upsampling the references and calibrations */ if (m->hr_inited == 2) { -#ifdef SALONEINSTLIB +#ifdef ONEDSTRAYLIGHTUS double **slp; /* 2D Array of stray light values */ -#endif /* !SALONEINSTLIB */ +#endif /* !ONEDSTRAYLIGHTUS */ rspl *trspl; /* Upsample rspl */ cow sd[40 * 40]; /* Scattered data points of existing references */ datai glow, ghigh; @@ -6765,6 +7298,7 @@ munki_code munki_create_hr(munki *p, int ref) { vhigh[0] = sd[i].v[0]; } +#ifdef NEVER /* Add some corrections at short wavelengths */ /* (The combination of the diffraction grating and */ /* LED light source doesn't give us much to work with here.) */ @@ -6788,13 +7322,14 @@ munki_code munki_create_hr(munki *p, int ref) { sd[i].v[0] = 0.2 * sd[0].v[0]; sd[i++].w = 1.0; } +#endif glow[0] = m->wl_short2; ghigh[0] = m->wl_long2; gres[0] = m->nwav2; avgdev[0] = 0.0; - trspl->fit_rspl_w(trspl, 0, sd, i, glow, ghigh, gres, vlow, vhigh, 0.5, avgdev, NULL); + trspl->fit_rspl_w(trspl, 0, sd, i, glow, ghigh, gres, vlow, vhigh, 5.0, avgdev, NULL); if ((*ref2 = (double *)calloc(m->nwav2, sizeof(double))) == NULL) { raw2wav->del(raw2wav); @@ -6806,6 +7341,8 @@ munki_code munki_create_hr(munki *p, int ref) { /* Create upsampled version */ for (i = 0; i < m->nwav2; i++) { pp.p[0] = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i); + if (pp.p[0] < min_wl) /* Duplicate below this wl */ + pp.p[0] = min_wl; trspl->interp(trspl, &pp); if (pp.v[0] < 0.0) pp.v[0] = 0.0; @@ -6813,6 +7350,7 @@ munki_code munki_create_hr(munki *p, int ref) { } +#ifdef NEVER /* Add some corrections at short wavelengths */ if (ii == 0) { /* 376.67 - 470 */ @@ -6839,6 +7377,7 @@ munki_code munki_create_hr(munki *p, int ref) { } } +#endif #ifdef HIGH_RES_PLOT /* Plot original and upsampled reference */ @@ -6885,7 +7424,7 @@ munki_code munki_create_hr(munki *p, int ref) { } trspl->del(trspl); -#ifdef SALONEINSTLIB +#ifdef ONEDSTRAYLIGHTUS /* Then the 2D stray light using linear interpolation */ slp = dmatrix(0, m->nwav1-1, 0, m->nwav1-1); @@ -6929,7 +7468,7 @@ munki_code munki_create_hr(munki *p, int ref) { } } } -#else /* !SALONEINSTLIB */ +#else /* !ONEDSTRAYLIGHTUS */ /* Then setup 2D stray light using rspl */ if ((trspl = new_rspl(RSPL_NOFLAGS, 2, 1)) == NULL) { a1logd(p->log,3,"munki: creating rspl for high res conversion failed\n"); @@ -6961,7 +7500,7 @@ munki_code munki_create_hr(munki *p, int ref) { avgdev[1] = 0.0; trspl->fit_rspl_w(trspl, 0, sd, m->nwav1 * m->nwav1, glow, ghigh, gres, NULL, NULL, 0.5, avgdev, NULL); -#endif /* !SALONEINSTLIB */ +#endif /* !ONEDSTRAYLIGHTUS */ m->straylight2 = dmatrixz(0, m->nwav2-1, 0, m->nwav2-1); @@ -6970,8 +7509,12 @@ munki_code munki_create_hr(munki *p, int ref) { for (j = 0; j < m->nwav2; j++) { /* Input wavelength */ double p0, p1; p0 = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i); + if (p0 < min_wl) /* Duplicate below this wl */ + p0 = min_wl; p1 = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, j); -#ifdef SALONEINSTLIB + if (p1 < min_wl) /* Duplicate below this wl */ + p1 = min_wl; +#ifdef ONEDSTRAYLIGHTUS /* Do linear interp with clipping at ends */ { int x0, x1, y0, y1; @@ -7002,11 +7545,11 @@ munki_code munki_create_hr(munki *p, int ref) { + w1 * v0 * slp[x1][y0] + w1 * v1 * slp[x1][y1]; } -#else /* !SALONEINSTLIB */ +#else /* !ONEDSTRAYLIGHTUS */ pp.p[0] = p0; pp.p[1] = p1; trspl->interp(trspl, &pp); -#endif /* !SALONEINSTLIB */ +#endif /* !ONEDSTRAYLIGHTUS */ m->straylight2[i][j] = pp.v[0] * HIGHRES_WIDTH/10.0; if (m->straylight2[i][j] > 0.0) m->straylight2[i][j] = 0.0; @@ -7077,11 +7620,11 @@ munki_code munki_create_hr(munki *p, int ref) { } #endif /* HIGH_RES_PLOT */ -#ifdef SALONEINSTLIB +#ifdef ONEDSTRAYLIGHTUS free_dmatrix(slp, 0, m->nwav1-1, 0, m->nwav1-1); -#else /* !SALONEINSTLIB */ +#else /* !ONEDSTRAYLIGHTUS */ trspl->del(trspl); -#endif /* !SALONEINSTLIB */ +#endif /* !ONEDSTRAYLIGHTUS */ /* - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Allocate space for per mode calibration reference */ @@ -7278,22 +7821,9 @@ munki_code munki_conv2XYZ( if (conv == NULL) return MUNKI_INT_CIECONVFAIL; - /* Don't report any wavelengths below the minimum for this mode */ - if ((s->min_wl-1e-3) > wl_short) { - double wl; - for (j = 0; j < m->nwav; j++) { - wl = XSPECT_WL(m->wl_short, m->wl_long, m->nwav, j); - if (wl >= (s->min_wl-1e-3)) - break; - } - six = j; - wl_short = wl; - nwl -= six; - } - - a1logd(p->log,3,"munki_conv2XYZ got wl_short %f, wl_long %f, nwav %d, min_wl %f\n" + a1logd(p->log,3,"munki_conv2XYZ got wl_short %f, wl_long %f, nwav %d\n" " after skip got wl_short %f, nwl = %d\n", - m->wl_short, m->wl_long, m->nwav, s->min_wl, wl_short, nwl); + m->wl_short, m->wl_long, m->nwav, wl_short, nwl); for (sms = 0.0, i = 1; i < 21; i++) sms += opt_adj_weights[i]; @@ -7493,15 +8023,15 @@ munki_code munki_optimise_sensor( new_int_time *= s->targoscale2; a1logd(p->log,3,"Using compromse sensor target\n"); } -#ifdef USE_HIGH_GAIN_MODE - /* Hmm. It may not be a good idea to use high gain mode if it compromises */ + /* Hmm. It seems not be a good idea to use high gain mode if it compromises */ /* the longer integration time which reduces noise. */ - if (new_int_time > m->max_int_time && permithg) { - new_int_time /= m->highgain; - new_gain_mode = 1; - a1logd(p->log,3,"Switching to high gain mode\n"); + if (s->auto_gain) { + if (new_int_time > m->max_int_time && permithg) { + new_int_time /= m->highgain; + new_gain_mode = 1; + a1logd(p->log,3,"Switching to high gain mode\n"); + } } -#endif } a1logd(p->log,3,"after low light adjust, inttime %f, gain mode %d\n",new_int_time,new_gain_mode); @@ -7616,10 +8146,8 @@ munki_interp_dark( return MUNKI_INT_NOTCALIBRATED; i = 0; -#ifdef USE_HIGH_GAIN_MODE - if (gainmode) + if (s->auto_gain && gainmode) i = 2; -#endif for (j = -1; j < m->nraw; j++) { double tt; @@ -7654,7 +8182,7 @@ inst_opt_type munki_get_trig(munki *p) { } /* Switch thread handler */ -int munki_switch_thread(void *pp) { +static int munki_switch_thread(void *pp) { int nfailed = 0; munki *p = (munki *)pp; munkiimp *m = (munkiimp *)p->m; @@ -7683,15 +8211,51 @@ int munki_switch_thread(void *pp) { p->eventcallback(p->event_cntx, inst_event_switch); } } else if (ecode == mk_eve_spos_change) { - if (p->eventcallback != NULL) { - p->eventcallback(p->event_cntx, inst_event_mconf); +#ifdef FILTER_SPOS_EVENTS + /* Signal change to filer thread */ + m->spos_msec = msec_time(); + m->spos_change++; +#else + if (m->eventcallback != NULL) { + m->eventcallback(p->event_cntx, inst_event_mconf); } +#endif } } a1logd(p->log,3,"Switch thread returning\n"); return rv; } +#ifdef FILTER_SPOS_EVENTS +static int munki_spos_thread(void *pp) { + munki *p = (munki *)pp; + munkiimp *m = (munkiimp *)p->m; + int change = m->spos_change; /* Current count */ + + a1logd(p->log,3,"spos thread started\n"); + + for (;;) { + + if (m->spos_th_term) { + m->spos_th_termed = 1; + break; + } + + /* Do callback if change has persisted for 1 second */ + if (change != m->spos_change + && (msec_time() - m->spos_msec) >= FILTER_TIME) { + change = m->spos_change; + if (p->eventcallback != NULL) { + p->eventcallback(p->event_cntx, inst_event_mconf); + } + } + msec_sleep(100); + } + return 0; +} +#endif + + /* ============================================================ */ /* Low level commands */ @@ -8246,7 +8810,7 @@ munki_code munki_simulate_event(munki *p, mk_eve ecode, int timestamp) { msec_sleep(50); if (m->th_termed == 0) { a1logd(p->log,1,"munki_simulate_event: terminate switch thread failed, canceling I/O\n"); - p->icom->usb_cancel_io(p->icom, &m->cancelt); + p->icom->usb_cancel_io(p->icom, &m->sw_cancel); } return rv; @@ -8323,7 +8887,7 @@ munki_code munki_waitfor_switch_th(munki *p, mk_eve *ecode, int *timest, double a1logd(p->log,2,"munki_waitfor_switch_th: Read 8 bytes from switch hit port\n"); /* Now read 8 bytes */ - se = p->icom->usb_read(p->icom, &m->cancelt, 0x83, buf, 8, &rwbytes, top); + se = p->icom->usb_read(p->icom, &m->sw_cancel, 0x83, buf, 8, &rwbytes, top); if (se & ICOM_TO) { a1logd(p->log,1,"munki_waitfor_switch_th: read 0x%x bytes, timed out\n",rwbytes); @@ -8561,21 +9125,6 @@ munki_code munki_parse_eeprom(munki *p, unsigned char *buf, unsigned int len) { if ((m->rmtx_coef1 = d->get_32_doubles(d, NULL, 184, 36 * 16)) == NULL) return MUNKI_DATA_RANGE; -#ifdef NEVER -// ~~~ hack !!! -m->rmtx_index1[4] = m->rmtx_index1[5] + 3; -m->rmtx_index1[3] = m->rmtx_index1[4] + 3; -m->rmtx_index1[2] = m->rmtx_index1[3] + 3; -m->rmtx_index1[1] = m->rmtx_index1[2] + 3; -m->rmtx_index1[0] = m->rmtx_index1[1] + 3; - -for(i = 0; i < 5; i++) { - for (j = 0; j < 16; j++) { - m->rmtx_coef1[i * 16 + j] = m->rmtx_coef1[5 * 16 + j]; - } -} -#endif - if (p->log->debug >= 7) { a1logd(p->log,7,"Reflectance matrix:\n"); for(i = 0; i < 36; i++) { diff --git a/spectro/munki_imp.h b/spectro/munki_imp.h index a9af3a9..6af366e 100644 --- a/spectro/munki_imp.h +++ b/spectro/munki_imp.h @@ -89,6 +89,7 @@ struct _munki_state { double targoscale; /* Optimal reading scale factor <= 1.0 */ double targmaxitime;/* maximum integration time to aim for (ie. 2.0 sec) */ double targoscale2; /* Proportion of targoscale allowed to meed targmaxitime */ + int auto_gain; /* Whether high gain mode should be used */ int gainmode; /* Gain mode, 0 = normal, 1 = high */ double inttime; /* Integration time */ double invsampt; /* Invalid sample time */ @@ -104,8 +105,6 @@ struct _munki_state { double maxscantime; /* Maximum scan time sets buffer size allocated */ - double min_wl; /* Minimum wavelegth to report for this mode */ - /* calibration information for this mode */ int dark_valid; /* dark calibration factor valid */ time_t ddate; /* Date/time of last dark calibration */ @@ -145,6 +144,8 @@ struct _munki_state { double dark_int_time3; /* Integration time used for dark data 3 */ double *dark_data3; /* [-1 nraw] of dark level to subtract for dark_int_time3. */ + char padding[1]; /* to change structure size to invalidate cal file */ + }; typedef struct _munki_state munki_state; @@ -158,7 +159,7 @@ struct _munkiimp { athread *th; /* Switch monitoring thread (NULL if not used) */ volatile int switch_count; /* Incremented in thread */ volatile int hide_switch; /* Set to supress switch event during read */ - usb_cancelt cancelt; /* Token to allow cancelling an outstanding I/O */ + usb_cancelt sw_cancel; /* Token to allow cancelling switch I/O */ volatile int th_term; /* Thread terminate on error rather than retry */ volatile int th_termed; /* Thread has terminated */ inst_opt_type trig; /* Reading trigger mode */ @@ -284,6 +285,12 @@ struct _munkiimp { int trig_se; /* Delayed trigger icoms error */ munki_code trig_rv; /* Delayed trigger result */ + athread *spos_th; /* Position change filter thread */ + volatile int spos_th_term; /* nz to terminate thread */ + volatile int spos_th_termed; /* nz when terminated */ + volatile int spos_change; /* counter that increments on an spos event change */ + unsigned int spos_msec; /* Time when spos last changes */ + }; typedef struct _munkiimp munkiimp; /* Add an implementation structure */ @@ -342,6 +349,7 @@ void del_munkiimp(munki *p); #define MUNKI_RD_NOFLASHES 0x3E /* No flashes recognized */ #define MUNKI_RD_NOAMBB4FLASHES 0x3F /* No ambient before flashes found */ #define MUNKI_RD_NOREFR_FOUND 0x40 /* Unable to measure refresh rate */ +#define MUNKI_RD_NOTRANS_FOUND 0x41 /* Unable to measure delay transition */ #define MUNKI_SPOS_PROJ 0x48 /* Sensor needs to be in projector position */ #define MUNKI_SPOS_SURF 0x49 /* Sensor needs to be in surface position */ @@ -392,8 +400,9 @@ char *munki_imp_get_serial_no(munki *p); /* Set the measurement mode. It may need calibrating */ munki_code munki_imp_set_mode( munki *p, - mk_mode mmode, /* munki mode to use */ - int spec_en); /* nz to enable reporting spectral */ + mk_mode mmode, /* Operating mode */ + inst_mode mode /* Full mode mask for options */ +); /* Implement get_n_a_cals */ munki_code munki_imp_get_n_a_cals(munki *p, inst_cal_type *pn_cals, inst_cal_type *pa_cals); @@ -421,6 +430,12 @@ munki_code munki_imp_meas_refrate( double *ref_rate ); +/* Measure the display update delay */ +munki_code munki_imp_meas_delay( + munki *p, + int *msecdelay +); + /* return nz if high res is supported */ int munki_imp_highres(munki *p); @@ -549,6 +564,18 @@ munki_code munki_whitemeasure_buf( unsigned char *buf /* Raw buffer */ ); +/* Take a measurement reading using the current mode (combined parts 1 & 2a) */ +/* Converts to completely processed output readings, without averaging or extracting */ +/* sample patches, for emissive measurement mode. */ +/* This is used for delay & refresh rate measurement. */ +munki_code munki_read_patches_all( + munki *p, + double **specrd, /* Return array [numpatches][nwav] of spectral reading values */ + int numpatches, /* Number of sample to measure */ + double *inttime, /* Integration time to use/used */ + int gainmode /* Gain mode to use, 0 = normal, 1 = high */ +); + /* Take a measurement reading using the current mode, part 1 */ /* Converts to completely processed output readings. */ munki_code munki_read_patches_1( @@ -795,8 +822,14 @@ inst_opt_type munki_get_trig(munki *p); /* Set the trigger return */ void munki_set_trigret(munki *p, int val); +#ifdef USE_THREAD /* Switch thread handler */ -int munki_switch_thread(void *pp); +static int munki_switch_thread(void *pp); +#endif + +#ifdef FILTER_SPOS_EVENTS +static int munki_spos_thread(void *pp); +#endif /* ============================================================ */ /* Low level commands */ diff --git a/spectro/oemarch.c b/spectro/oemarch.c index 733156e..e27d25d 100644 --- a/spectro/oemarch.c +++ b/spectro/oemarch.c @@ -87,6 +87,8 @@ oem_target oemtargs = { { "/Applications/Spyder2pro 2.2/Spyder2pro.app/Contents/MacOSClassic/Spyder.lib", targ_spyd_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 }, + { NULL } }, { /* Volume names */ @@ -168,7 +170,9 @@ int is_s4cal(xfile *xf); int is_inno(xfile *xf); int is_cab(xfile *xf); static xfile *inno_extract(xfile *xi, char *tfilename, int verb); -static xfile *msi_extract(xfile **pxf, xfile *xi, char *tname, int verb); +static xfile *ai_extract_cab(xfile **pxf, xfile *xi, char *tname, int verb); +//static xfile *aifile_extract(xfile **pxf, xfile *xi, char *tname, int verb); +static xfile *msi_extract_cab(xfile **pxf, xfile *xi, char *tname, int verb); static xfile *cab_extract(xfile **pxf, xfile *xi, char *text, int verb); /* edr to ccss functions */ @@ -180,6 +184,9 @@ int is_ccss(xfile *xf); int is_ccmx(xfile *xf); #ifdef DEBUG + +# pragma message("######### oemarch DEBUG is defined! ########") + static void list_files(char *s, xfile *xf) { int i; @@ -281,28 +288,48 @@ xfile *oemarch_get_ifiles(xfile *files, int verb) { /* If this could be i1d3 .edr files: */ if (arch->ttype & targ_i1d3_edr) { - xfile *msi = NULL; /* .msi */ + xfile *exe = NULL; /* .exe extracted */ + xfile *msi = NULL; /* .msi extracted */ -#ifdef NEVER /* Don't have to do it this way */ +#ifdef NEVER /* Don't have to do it this way for most installs */ /* Extract .msi from it */ if ((msi = inno_extract(arch, "{tmp}\\XRD i1d3.msi", verb)) != NULL) { /* Extract the .cab from it */ - if (msi_extract(&nfiles, msi, "XRD_i1d3.cab", verb) != NULL) { + if (msi_extract_cab(&nfiles, msi, "XRD_i1d3.cab", verb) != NULL) { + del_xf(msi); + continue; + } + if (msi_extract_cab(&nfiles, msi, "XRD_Manager.cab", verb) != NULL) { del_xf(msi); continue; } del_xf(msi); } + + /* Try and extract XRD Manager.exe from it */ + if ((exe = inno_extract(arch, "{tmp}\\XRD Manager.exe", verb)) != NULL) { + + /* Extract the "disk1.cab" from the AI installer exectutable */ + if ((ai_extract_cab(&nfiles, exe, "disk1.cab", verb)) != NULL) { + del_xf(exe); + continue; + } + del_xf(exe); + } #else /* Extract the .cab directly from Setup.exe */ - if (msi_extract(&nfiles, arch, "XRD_i1d3.cab", verb) != NULL) + if (msi_extract_cab(&nfiles, arch, "XRD_i1d3.cab", verb) != NULL) + continue; + if (msi_extract_cab(&nfiles, arch, "XRD_Manager.cab", verb) != NULL) continue; - if (msi_extract(&nfiles, arch, "XRD_Manager.cab", verb) != NULL) + + /* Extract the "disk1.cab" from the AI installer exectutable */ + if ((ai_extract_cab(&nfiles, arch, "disk1.cab", verb)) != NULL) continue; #endif } - if (verb) printf("Warning: unhandled '%s' discarded\n",arch->name); + if (verb) printf("Warning: unhandled archive '%s' discarded\n",arch->name); } ofiles = files; /* Swap to new list */ files = nfiles; @@ -350,7 +377,7 @@ xfile *oemarch_get_ifiles(xfile *files, int verb) { if (cab_extract(&nfiles, dllcab, ".edr", verb) != NULL) continue; } - if (verb) printf("Warning: unhandled '%s' discarded\n",dllcab->name); + if (verb) printf("Warning: unhandled dll/cab '%s' discarded\n",dllcab->name); } ofiles = files; /* Swap to new list */ files = nfiles; @@ -381,7 +408,7 @@ xfile *oemarch_get_ifiles(xfile *files, int verb) { if (edr_convert(&nfiles, files + i, verb) != NULL) continue; - if (verb) printf("Warning: unhandled '%s' discarded\n",files[i].name); + if (verb) printf("Warning: unhandled edr '%s' discarded\n",files[i].name); } ofiles = files; /* Swap to new list */ files = nfiles; @@ -1295,7 +1322,7 @@ static xfile *edr_convert(xfile **pxf, xfile *xi, int verb) { xfile *xf = NULL; ccss *c; - if (verb) printf("Translating '%s' (%d bytes)\n",xi->name, xi->len); + if (verb) printf("Translating '%s' (%d bytes)\n",xi->name, (int)xi->len); if ((c = parse_EDR(xi->buf, xi->len, xi->name, verb)) == NULL) { if (verb) printf("Failed to parse EDR '%s'\n",xi->name); @@ -1306,7 +1333,7 @@ static xfile *edr_convert(xfile **pxf, xfile *xi, int verb) { unsigned char *buf; int len; if (c->buf_write_ccss(c, &buf, &len)) { - error("Failed to create ccss for '%s'",xi->name); + error("Failed to create ccss for '%s' error '%s'",xi->name,c->err); } /* Convert .edr file name to .ccss */ @@ -1356,9 +1383,10 @@ static void dump_bytes(FILE *fp, char *pfx, unsigned char *buf, int len) { fprintf(fp,"\n"); } } + fflush(fp); } -/* Take a 64 sized return buffer, and convert it to an ORD64 */ +/* Take a 64 sized return buffer, and convert it to an ORD64, little endian */ static ORD64 buf2ord64(unsigned char *buf) { ORD64 val; val = buf[7]; @@ -1372,7 +1400,7 @@ static ORD64 buf2ord64(unsigned char *buf) { return val; } -/* Take a word sized return buffer, and convert it to an unsigned int */ +/* Take a word sized return buffer, and convert it to an unsigned int, little endian */ static unsigned int buf2uint(unsigned char *buf) { unsigned int val; val = buf[3]; @@ -1382,7 +1410,17 @@ static unsigned int buf2uint(unsigned char *buf) { return val; } -/* Take a word sized return buffer, and convert it to an int */ +/* Inverted bytes version of above */ +static unsigned int ibuf2uint(unsigned char *buf) { + unsigned int val; + val = (0xff & ~buf[3]); + val = ((val << 8) + (0xff & ~buf[2])); + val = ((val << 8) + (0xff & ~buf[1])); + val = ((val << 8) + (0xff & ~buf[0])); + return val; +} + +/* Take a word sized return buffer, and convert it to an int, little endian */ static int buf2int(unsigned char *buf) { int val; val = buf[3]; @@ -1392,7 +1430,7 @@ static int buf2int(unsigned char *buf) { return val; } -/* Take a short sized return buffer, and convert it to an int */ +/* Take a short sized return buffer, and convert it to an int, little endian */ static int buf2short(unsigned char *buf) { int val; val = buf[1]; @@ -1431,7 +1469,7 @@ static ccss *parse_EDR( 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 **tsels; /* Corresponding UI selection chars */ char **ttstrings; /* Corresponding technology strings */ int ttype; /* Technology type idex */ int nsets, set; @@ -1480,7 +1518,7 @@ static ccss *parse_EDR( 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"; // is this LCD ?? + 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"; @@ -1629,6 +1667,7 @@ static ccss *parse_EDR( samples[set].spec[j] *= 1000.0; } #ifdef PLOT_SAMPLES +# pragma message("######### oemarch PLOT_SAMPLES defined! ########") /* Plot the spectra */ { double xx[500]; @@ -1911,7 +1950,26 @@ ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc) { return res; } -/* extract the given file from Setup.exe */ +/* extract the given file from Inno Setup.exe */ +/* Known versions needed: + 5.3.5 ColorMunkiDisplaySetup.exe + 5.3.10 i1d3Setup.exe + 5.4.2 V1.5 i1ProfilerSetup.exe +*/ + +/* Unicode to 8 bit strncmp */ +static int ustrncmp(char *w, char *c, int n) { + int i; + for (i = 0; i < n; i++, w += 2, c++) { + if ((w[0] == '\000' && w[1] == '\000') + || c[0] == '\000') + return 0; + + if (w[0] != c[0] || w[1] != '\000') + return 1; + } + return 0; +} /* Return a list of xfiles, with one entry if successful */ /* Return NULL if not found or not inno file */ @@ -1919,8 +1977,12 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { int i, j, k; unsigned char *ibuf; unsigned long ilen; - char *headerid = "Inno Setup Setup Data (5.3.10)"; + char *headerid = "Inno Setup Setup Data "; int headerlen = strlen(headerid); + int maj, min, bfix; /* Inno version */ + int vers = 0; /* Version as int max * 1000 + min * 100 + bfix */ + int unicode = 0; /* Is it unicode filenames ? */ + int chsize = 1; /* Bytes per character */ unsigned int ldrbase = 0; unsigned long haddr, srclen; unsigned char *d1buf, *d2buf; /* Decompress buffer */ @@ -1939,14 +2001,15 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { ibuf = xi->buf; ilen = xi->len; - /* Search for the start of the loader. All the file offsets */ - /* are relative to this */ + if (verb > 1) printf("inno_extract: ilen = %lu\n",ilen); + + /* Search for the start of the loader. We need this because */ + /* All the file offsets are relative to this. */ for (i = 0; i < (ilen - 4); i++) { if (ibuf[i + 0] == 0x4d && ibuf[i + 1] == 0x5a && ibuf[i + 2] == 0x90 && ibuf[i + 3] == 0x00) { - if (verb > 1) printf("Found archive base 0x%x\n",i); ldrbase = i; break; } @@ -1955,26 +2018,51 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { if (verb) printf("Failed to locate loader base\n"); return NULL; } + if (verb > 1) printf("Found archive base 0x%x\n",ldrbase); /* Search for inno header pattern. */ - for (i = 0; i < (ilen - 64 - 4 - 5 - 4); i++) { + haddr = 0; + for (i = 1; i < (ilen - 64 - 4 - 5 - 4); i++) { if (ibuf[i] == headerid[0] && strncmp((char *)ibuf + i, headerid, headerlen) == 0 && ibuf[i + 64] != 'I' && ibuf[i + 65] != 'n' && ibuf[i + 66] != 'n' && ibuf[i + 67] != 'o') { + + /* Grab the version */ + for (j = 0; j < 64; j++) { + if (ibuf[i + j] == '\000') + break; + } + if (j >= 64) { + if (verb) printf("Failed to find null terminator after header\n"); + continue; + } + + if (sscanf((char *)ibuf + i + headerlen, " (%d.%d.%d) ",&maj,&min,&bfix) != 3) { + if (verb) printf("Failed to find Inno version number\n"); + continue; + } + vers = maj * 1000 + min * 100 + bfix; + + j = strlen((char *)ibuf + i); + if (strncmp((char *)ibuf + i + j -3, "(u)", 3) == 0) { + unicode = 1; + chsize = 2; + } haddr = i; } } - if (i >= (ilen - 64 - 4 - 5 - 4)) { + + if (haddr == 0) { if (verb) printf("Failed to locate header pattern\n"); return NULL; } - if (verb > 1) printf("Found header at 0x%x\n",i); + if (verb > 1) printf("Found header at 0x%x, Inno version %d.%d.%d%s\n",i,maj,min,bfix, unicode ? " Unicode" : ""); - /* Use the last header found (or cound search all found ?) */ + /* Use the last header found (or could search all found ?) */ haddr += 64; /* Skip Inno header */ /* Next 9 bytes are the compression header */ @@ -1994,7 +2082,7 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { } haddr += 5; /* Skip compression header */ - /* We'r now at the start of the compressed data */ + /* We're now at the start of the compressed data which holds the filenames */ d1sz = cblocklen * 30; if ((d1buf = (unsigned char *)malloc(d1sz)) == NULL) { @@ -2016,6 +2104,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); /* - - - - - - - - - - - - - - - - -*/ @@ -2049,7 +2138,7 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { } haddr += 5; /* Skip compression header */ - /* We're now at the start of the compressed data */ + /* We're now at the start of the compressed data that holds the file data locations. */ d2sz = cblocklen * 10; if ((d2buf = (unsigned char *)malloc(d2sz)) == NULL) { @@ -2073,34 +2162,65 @@ 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); if (verb > 1) printf("Searching for file '%s' in Header\n",tfilename); - for (i = 0; i < (d1sz - 101); i++) { - if (d1buf[i+4] == tfilename[0] - && strncmp((char *)d1buf + 4 + i, tfilename, filelen) == 0 - && d1buf[i+0] == filelen - && d1buf[i+1] == 0 - && d1buf[i+2] == 0 - && d1buf[i+3] == 0) { - if (verb > 1) printf("Found it at 0x%x\n",i); - break; + if (unicode) { + for (i = 0; i < (d1sz - 101); i++) { + if (d1buf[i+4] == tfilename[0] + && ustrncmp((char *)d1buf + 4 + i, tfilename, filelen) == 0 + && d1buf[i+0] == 2 * filelen + && d1buf[i+1] == 0 + && d1buf[i+2] == 0 + && d1buf[i+3] == 0) { + if (verb > 1) printf("Found it at 0x%x\n",i); + break; + } + } + if (i >= (d1sz - 101)) { + if (verb) printf("Failed to find file '%s'\n",tfilename); + free(d1buf); + free(d2buf); + return NULL; + } + + } else { + for (i = 0; i < (d1sz - 101); i++) { + if (d1buf[i+4] == tfilename[0] + && strncmp((char *)d1buf + 4 + i, tfilename, filelen) == 0 + && d1buf[i+0] == filelen + && d1buf[i+1] == 0 + && d1buf[i+2] == 0 + && d1buf[i+3] == 0) { + if (verb > 1) printf("Found it at 0x%x\n",i); + break; + } + } + if (i >= (d1sz - 101)) { + if (verb) printf("Failed to find file '%s'\n",tfilename); + free(d1buf); + free(d2buf); + return NULL; } } - if (i >= (d1sz - 101)) { - if (verb) printf("Failed to find file '%s'\n",tfilename); - free(d1buf); - free(d2buf); - return NULL; - } + fflush(stdout); - /* Need to skip 8 more strings */ - i += 4 + filelen; + /* Need to skip 8 more strings containing other info about the file */ + i += 4 + chsize * filelen; for (j = 0; j < 8; j++) { unsigned long len; len = buf2uint(d1buf + i); i += 4 + len; + if (i >= d1sz) { + if (verb) printf("Failed to skip 8 strings\n"); + free(d1buf); + free(d2buf); + return NULL; + } } + if (verb > 1) printf("At 0x%x after skippin another 8 strings\n",i); + /* Skip another 40 bytes to location entry index */ i += 20; @@ -2108,13 +2228,12 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { if (verb > 1) printf("Got file location index %ld at 0x%x\n",ix,i); - /* Now get the ix file entry information. */ /* They are in 74 byte structures */ i = ix * 74; if ((i + 74) > d2sz) { - if (verb) printf("File location structure is out of range\n"); + if (verb) printf("File location structure is out of range (%d > %lu)\n",i + 74, d2sz); free(d1buf); free(d2buf); return NULL; @@ -2141,7 +2260,8 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { free(d2buf); return NULL; } - /* Sanity check */ +#ifdef NEVER + /* Sanity check it's an .msi file */ if (ibuf[ldrbase + fileso + 0] != 0xd0 || ibuf[ldrbase + fileso + 1] != 0xcf || ibuf[ldrbase + fileso + 2] != 0x11 @@ -2151,6 +2271,7 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { free(d2buf); return NULL; } +#endif /* Copy to new buffer and free everything */ msisz = filesz; @@ -2181,6 +2302,9 @@ static xfile *inno_extract(xfile *xi, char *tfilename, int verb) { xf->buf = msibuf; xf->len = filesz; + xf->ftype = file_dllcab; + xf->ttype = xi->ttype; + if (verb) printf("Returning '%s' length %ld from '%s'\n",xf->name,xf->len, xi->name); return xf; @@ -2274,22 +2398,25 @@ int is_dll(xfile *xf) { return 0; } -/* Extract the .cab file from another file. */ +// ~~99 something strange about this code - it doesn't look for *tname. +// ?? What's going on ?? + +/* Extract a .cab file from another file. */ /* It's stored in the .msi uncompressed and contiguous, so we */ /* just need to identify where it is and its length. */ /* (This will work on any file that has the .cab file uncompressed and contiguous) */ /* Return NULL if not found */ -static xfile *msi_extract(xfile **pxf, xfile *xi, char *tname, int verb) { +static xfile *msi_extract_cab(xfile **pxf, xfile *xi, char *tname, int verb) { int i, j, k; xfile *xf = NULL; - char *fid = "i1d3.xrdevice"; /* File in .cab to look for */ + char *fid = "i1d3.xrdevice"; /* Known file in .cab to look for ???? */ unsigned long fle = strlen(fid); unsigned long cabo, cabsz; size_t len; if (verb) printf("Attempting to extract '%s' from '%s'\n",tname,xi->name); - /* Search for a filename in the .cab */ + /* Search for a filename that is expected to be in the right .cab */ for (i = 0; i < (xi->len - fle - 2); i++) { if (xi->buf[i + 0] == 0x00 && xi->buf[i + 1] == fid[0] @@ -2299,7 +2426,7 @@ static xfile *msi_extract(xfile **pxf, xfile *xi, char *tname, int verb) { } } if (i >= (xi->len - fle - 2)) { - if (verb) printf(".cab not found\n"); + if (verb) printf(".cab identifier file not found\n"); return NULL; } @@ -2313,12 +2440,12 @@ static xfile *msi_extract(xfile **pxf, xfile *xi, char *tname, int verb) { && xi->buf[i + 5] == 0x00 && xi->buf[i + 6] == 0x00 && xi->buf[i + 7] == 0x00) { - if (verb > 1) printf("Found '%s' at 0x%x\n",tname,i); + if (verb > 1) printf("Found .cab sig at 0x%x\n",i); break; } } if (i < 0) { - if (verb) printf(".cab not found\n"); + if (verb) printf(".cab sig not found\n"); return NULL; } @@ -2354,6 +2481,299 @@ static xfile *msi_extract(xfile **pxf, xfile *xi, char *tname, int verb) { return xf; } +/* Extract a .cab file from an "Advanced Installer" file. */ +/* It's stored in the file uncompressed and contiguous, but */ +/* with the first 0x200 bytes inverted, so we */ +/* just need to identify where it is and its length. */ +/* Return NULL if not found */ +static xfile *ai_extract_cab(xfile **pxf, xfile *xi, char *tname, int verb) { + int i, j, k; + xfile *xf = NULL; + unsigned long cabo, cabsz; + size_t len; + + if (verb) printf("Attempting to extract '%s' from '%s'\n",tname,xi->name); + + /* Search for inverted .cab signature */ + for (i = 0; i < (xi->len - 8 - 4); i++) { + if (xi->buf[i + 0] == 0xb2 + && xi->buf[i + 1] == 0xac + && xi->buf[i + 2] == 0xbc + && xi->buf[i + 3] == 0xb9 + && xi->buf[i + 4] == 0xff + && xi->buf[i + 5] == 0xff + && xi->buf[i + 6] == 0xff + && xi->buf[i + 7] == 0xff) { + if (verb > 1) printf("Found inverted .cab sig at 0x%x\n",i); + break; + } + } + if (i > (xi->len - 8 - 4)) { + if (verb) printf(".cab sig not found\n"); + return NULL; + } + + /* Lookup the .cab size (really 64 bit, but we don't care) */ + len = ibuf2uint(xi->buf + i + 8); + + if (verb > 1) printf("'%s' is length %ld\n",tname,len); + + if ((xi->len - i) < len) { + if (verb) printf("Not enough room for .cab file in source\n"); + return NULL; + } + + xf = add_xf(pxf); + xf->len = len; + + if ((xf->buf = malloc(xf->len)) == NULL) { + fprintf(stderr,"maloc of .cab buffer failed\n"); + exit(-1); + } + memmove(xf->buf, xi->buf + i ,xf->len); + + /* Restor first 0x200 bytes */ + for (i = 0; i < 0x200 && i < xf->len; i++) { + xf->buf[i] = ~xf->buf[i]; + } + + if ((xf->name = strdup(tname)) == NULL) { + fprintf(stderr,"maloc of .cab name failed\n"); + exit(-1); + } + + xf->ftype = file_dllcab; + xf->ttype = xi->ttype; + + if (verb) printf("Extacted '%s' length %ld\n",xf->name,xf->len); + +save_xfile(xf, "temp.cab", NULL, verb); + + return xf; +} + + +/* ================================================================ */ + +/* Not used: */ + +/* Given an offset into xfile, return the offset to any extra section in a PE file. */ +/* return 0 if none found or not a PE file */ +static int extraPEsection(xfile *xi, int boff, int verb) { + int i, j; + unsigned char *fbuf = xi->buf + boff; + int flen = xi->len - boff; + int off; + int nsect; /* Number of sections */ + int ophsz; /* Optional header size */ + int chars; /* File characteristics */ + int plus = 0; /* PE32+ format */ + int nddirs; /* Number of data directories */ + int ddirroff; /* Offset of data directories */ + unsigned int asects = 0; /* Offset after all the sections */ + + if (flen < 0x40 + || fbuf[0] != 0x4d + || fbuf[1] != 0x5a) { + if (verb) printf("'%s' is not an executable file\n",xi->name); + return 0; + } + + off = fbuf[0x3c] /* Offset to PE header */ + + (fbuf[0x3d] << 8) + + (fbuf[0x3e] << 16) + + (fbuf[0x3f] << 24); + + if (verb > 2) printf("PE header at 0x%x\n",off); + + if (flen < (off + 0x18) || off > 0x1000 || (off & 7) != 0) { + if (verb) printf("'%s' is not a PE file (1)\n",xi->name); + return 0; + } + + if (fbuf[off + 0] != 0x50 /* "PE" */ + || fbuf[off + 1] != 0x45 + || fbuf[off + 2] != 0x00 + || fbuf[off + 3] != 0x00) { + if (verb) printf("'%s' is not a PE file (2)\n",xi->name); + return 0; + } + + nsect = fbuf[off + 0x06] /* Number of sections */ + + (fbuf[off + 0x07] << 8); + + ophsz = fbuf[off + 0x14] /* Optional header size */ + + (fbuf[off + 0x15] << 8); + + chars = fbuf[off + 0x16] /* PE Characteristics */ + + (fbuf[off + 0x17] << 8); + /* + 0x0002 = executable (no unresolved refs) + 0x1000 = .sys driver + 0x2000 = DLL + */ + + if (verb > 1) printf("Number of sections = %d, chars = 0x%x\n",nsect,chars); + if (verb > 1) printf("Opt header size = %d = 0x%x\n",ophsz,ophsz); + + /* Skip to optional header */ + off = off + 0x18; + if (flen < off + ophsz) { + if (verb) printf("'%s' not big enough for optional header\n",xi->name); + return 0; + } + if (verb > 2) printf("opt header at 0x%x\n",off); + if ( fbuf[off + 0] != 0x0b + || (fbuf[off + 1] != 0x01 && fbuf[off + 1] != 0x02)) { + if (verb) printf("'%s' is not a PE file (3)\n",xi->name); + return 0; + } + if (fbuf[off + 1] == 0x02) + plus = 1; /* PE32+ format */ + + if (plus) { + nddirs = fbuf[off+0x6c+0] + + (fbuf[off+0x6c+1] << 8) + + (fbuf[off+0x6c+2] << 16) + + (fbuf[off+0x6c+3] << 24); + ddirroff = 0x70; + } else { + nddirs = fbuf[off+0x5c+0] + + (fbuf[off+0x5c+1] << 8) + + (fbuf[off+0x5c+2] << 16) + + (fbuf[off+0x5c+3] << 24); + ddirroff = 0x60; + } + + /* Go through each data directory */ + for (i = 0; i < nddirs; i++) { + unsigned int addr, size; + + j = off + ddirroff + 8 * i; + + addr = fbuf[j+0] + + (fbuf[j+1] << 8) + + (fbuf[j+2] << 16) + + (fbuf[j+3] << 24); + size = fbuf[j+4] + + (fbuf[j+5] << 8) + + (fbuf[j+6] << 16) + + (fbuf[j+7] << 24); + + if (verb > 2) printf("Data block %d addr 0x%x size %u\n",i,addr,size); + } + + /* Skip to start of section headers */ + off = off + ophsz; + + /* Go through each section header and locat size used by them */ + asects = 0; + for (i = 0; i < nsect; i++) { + char sname[9] = { '\000' }; + unsigned int vaddr, vsize; + unsigned int paddr, psize; + unsigned int flags; + + if (flen < (off + 0x28)) { + if (verb) printf("'%s' not big enough for section headers\n",xi->name); + return 0; + } + + strncpy(sname, (char *)fbuf + off, 8); + + vsize = fbuf[off+0x08+0] + + (fbuf[off+0x08+1] << 8) + + (fbuf[off+0x08+2] << 16) + + (fbuf[off+0x08+3] << 24); + + vaddr = fbuf[off+0x0c+0] + + (fbuf[off+0x0c+1] << 8) + + (fbuf[off+0x0c+2] << 16) + + (fbuf[off+0x0c+3] << 24); + + psize = fbuf[off+0x10+0] + + (fbuf[off+0x10+1] << 8) + + (fbuf[off+0x10+2] << 16) + + (fbuf[off+0x10+3] << 24); + + paddr = fbuf[off+0x14+0] + + (fbuf[off+0x14+1] << 8) + + (fbuf[off+0x14+2] << 16) + + (fbuf[off+0x14+3] << 24); + + flags = fbuf[off+0x24+0] + + (fbuf[off+0x24+1] << 8) + + (fbuf[off+0x24+2] << 16) + + (fbuf[off+0x24+3] << 24); + + if (verb > 1) printf("Section '%s' phys off 0x%x size %u\n",sname,paddr,psize); + + if ((paddr + psize) > asects) + asects = paddr + psize; + off += 0x28; + } + if (verb > 2) printf("After section hedares at 0x%x\n",off); + + if (flen <= asects) { + if (verb) printf("No extra section at end\n"); + return 0; + } + + if (verb > 1) printf("Extra data at 0x%x, %d bytes\n",asects, flen - asects); + + return boff + asects; +} + +/* Not used */ + +/* Extract a module from a PE "Advance Installer" .exe file */ +/* Return NULL if not found */ +static xfile *aifile_extract(xfile **pxf, xfile *xi, char *tname, int verb) { + xfile *xf = NULL; + int i, j; + int asects; /* Offset after all the sections */ + int off; + + /* First we parse up the PE to figure out how much room is */ + /* used for known sections */ + if ((asects = extraPEsection(xi, 0, verb)) == 0) { + return NULL; + } + + /* Now we can search the "Advanced Installer" archive */ + /* It seems to be a set of concatenated PE files ? */ + + for (i= 0, off = asects; off != 0; i++) { + printf("\nPE %d at 0x%x\n",i,off); + off = extraPEsection(xi, off, verb > 2 ? 2 : verb); + } + + if (verb) printf(" pefile_extract_mod not implemented yet\n"); + return NULL; + +#ifdef NEVER + xf = add_xf(pxf); + xf->len = len; + + if ((xf->buf = malloc(xf->len)) == NULL) { + fprintf(stderr,"maloc of .cab buffer failed\n"); + exit(-1); + } + memmove(xf->buf, xi->buf + i ,xf->len); + + if ((xf->name = strdup(tname)) == NULL) { + fprintf(stderr,"maloc of .cab name failed\n"); + exit(-1); + } + + xf->ftype = file_dllcab; + xf->ttype = xi->ttype; + + if (verb) printf("Extacted '%s' length %ld\n",xf->name,xf->len); + + return xf; +#endif +} /* ================================================================ */ /* Extract files of a given type from a .cab file */ @@ -2407,6 +2827,8 @@ static xfile *cab_extract(xfile **pxf, xfile *xi, char *text, int verb) { unsigned long len = xi->len; unsigned long off; unsigned long filesize, headeroffset, datastart; + int headerres = 0, folderres = 0, datares = 0; + int hextra = 0; int nofolders, nofiles, flags, comptype; unsigned int totubytes; int ufiles = 0; @@ -2443,18 +2865,28 @@ static xfile *cab_extract(xfile **pxf, xfile *xi, char *text, int verb) { fprintf(stderr,"'%s' has more than one folder\n",xi->name); exit(-1); } - if (flags != 0) { - fprintf(stderr,"'%s' has non-zero flags\n",xi->name); + if (flags != 0 && flags != 4) { + fprintf(stderr,"'%s' has unhandled flags 0x%x\n",xi->name,flags); exit(-1); } + if (flags & 4) { /* If researved fields */ + // cbCFHeader, cbCFFolder, and cbCFData are present. + headerres = buf2short(buf + 0x24); + folderres = buf[0x26]; + datares = buf[0x27]; + + hextra = 4 + headerres; + } + /* Read the first folders info (assumed flags == 0) */ - datastart = buf2uint(buf + 0x24); - comptype = buf[0x2a]; + datastart = buf2uint(buf + hextra + 0x24); + comptype = buf[hextra + 0x2a]; if (comptype!= 1) { - fprintf(stderr,"'%s' doesn't use MSZip compression\n",xi->name); + fprintf(stderr,"'%s' doesn't use MSZip compression, uses %d\n",xi->name,comptype); exit(-1); } + /* Should skip folderres bytes here */ if (verb > 1) printf(".cab headeroffset = 0x%lx, datastart = 0x%lx, nofiles = %d\n",headeroffset,datastart,nofiles); @@ -2502,7 +2934,7 @@ static xfile *cab_extract(xfile **pxf, xfile *xi, char *text, int verb) { totubytes += ubytes; - off += 8 + cbytes; + off += 8 + datares + cbytes; } @@ -2530,7 +2962,7 @@ static xfile *cab_extract(xfile **pxf, xfile *xi, char *text, int verb) { cbytes = buf2short(buf + off + 0x04); ubytes = buf2short(buf + off + 0x06); - i_buf = buf + off + 8; + i_buf = buf + off + 8 + datares; i_len = cbytes; i_ix = 0; @@ -2548,7 +2980,7 @@ static xfile *cab_extract(xfile **pxf, xfile *xi, char *text, int verb) { /* The history buffer is meant to survive from one block to the next. */ /* Not sure what that means, as it seems to work as is... */ - off += 8 + cbytes; + off += 8 + datares + cbytes; } xf = add_xf(pxf); @@ -2569,8 +3001,8 @@ static xfile *cab_extract(xfile **pxf, xfile *xi, char *text, int verb) { fname[94] = '\000'; namelen = strlen(fname); - /* Lop of the junk in the filename */ - if ((cp = strrchr(fname, '.')) != NULL) + /* Lop of the junk in the filename, if it's present */ + if ((cp = strrchr(fname, '.')) != NULL && cp != strchr(fname, '.')) *cp = '\000'; /* See if it's the type of file we want */ diff --git a/spectro/oemarch.h b/spectro/oemarch.h index 72a3bb6..1b8c4a4 100644 --- a/spectro/oemarch.h +++ b/spectro/oemarch.h @@ -1,7 +1,7 @@ #ifndef OEMARCH_H - /* OEM archive access library. + /* OEM archive access library. */ /* This supports installing OEM data files */ /* @@ -29,7 +29,7 @@ typedef enum { file_none = 0x0000, /* None */ file_vol = 0x0001, /* Volume name */ file_arch = 0x0002, /* Archive file */ - file_dllcab = 0x0004, /* .dll or .cab */ + file_dllcab = 0x0004, /* .dll or .cab - msi ?? */ file_data = 0x0008 /* final data file */ } file_type; diff --git a/spectro/oeminst.c b/spectro/oeminst.c index 88efae0..2aa96e5 100644 --- a/spectro/oeminst.c +++ b/spectro/oeminst.c @@ -37,7 +37,7 @@ void usage(void) { fprintf(stderr,"Install OEM data files, Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill, licensed under the GPL Version 2 or later\n"); fprintf(stderr,"usage: oeminst [-options] [infile(s)]\n"); - fprintf(stderr," -v Verbose\n"); + fprintf(stderr," -v [level] Verbose\n"); fprintf(stderr," -n Don't install, show where files would be installed\n"); fprintf(stderr," -c Don't install, save files to current directory\n"); fprintf(stderr," -S d Specify the install scope u = user (def.), l = local system]\n"); @@ -88,6 +88,10 @@ main(int argc, char *argv[]) { /* Verbosity */ else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { verb = 1; + if (na != NULL && na[0] >= '0' && na[0] <= '9') { + verb = atoi(na); + fa = nfa; + } } /* No install */ diff --git a/spectro/spec2cie.c b/spectro/spec2cie.c index 99fe898..3f2e9d6 100644 --- a/spectro/spec2cie.c +++ b/spectro/spec2cie.c @@ -530,6 +530,7 @@ main(int argc, char *argv[]) illum = icxIT_none; /* Displays are assumed to be self luminous */ } + /* copy fields to output file (except spectral if nospec) */ for (i = 0; i < icg->t[0].nfields; i++) { /* See if this is a input spectral field */ diff --git a/spectro/specbos.c b/spectro/specbos.c new file mode 100644 index 0000000..f81f2d7 --- /dev/null +++ b/spectro/specbos.c @@ -0,0 +1,1674 @@ + +/* + * Argyll Color Correction System + * + * JETI specbos 1211/1201 related functions + * + * Author: Graeme W. Gill + * Date: 13/3/2013 + * + * 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 + */ + +/* + 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: + + Should add a reflective and transmissive modes, + by doing a white calibration and dividing the measurement. + +*/ + +#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 "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 */ +#define MAX_RD_SIZE 8000 /* Maximum reading message reply size */ + +/* Interpret an icoms error into a SPECBOS error */ +static int icoms2specbos_err(int se) { + if (se != ICOM_OK) { + if (se & ICOM_TO) + return SPECBOS_TIMEOUT; + return SPECBOS_COMS_FAIL; + } + return SPECBOS_OK; +} + +/* Do a full command/response echange with the specbos */ +/* (This level is not multi-thread safe) */ +/* Return the specbos error code. */ +static int +specbos_fcommand( +struct _specbos *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; + char *cp, *tc = "", *dp; + + 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 */ + + if ((se = p->icom->write_read(p->icom, in, out, bsize, tc, ntc, to)) != 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 */ + char buf[100]; + + if ((se = p->icom->write_read(p->icom, "*stat:err?\r", buf, 100, "\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); + } + } + break; + } + if (*cp == '\005') /* Got an Enquire */ + continue; /* remove it */ + *dp = *cp; + dp++; + } + 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; +} + +/* Do a normal command/response echange with the specbos. */ +/* (This level is not multi-thread safe) */ +/* Return the inst code */ +static inst_code +specbos_command( +struct _specbos *p, +char *in, /* In string */ +char *out, /* Out string buffer */ +int bsize, /* Out buffer size */ +double to) { /* Timout in seconds */ + int rv = specbos_fcommand(p, in, out, bsize, to, 1, 0, 0); + return specbos_interp_code((inst *)p, rv); +} + +/* Read another line of response */ +/* (This level is not multi-thread safe) */ +/* Return the inst code */ +static int +specbos_readresp( +struct _specbos *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 */ + + if ((se = p->icom->read(p->icom, out, bsize, tc, 1, to)) != 0) { + a1logd(p->log, 1, "specbos_readresp: serial i/o failure\n"); + return icoms2specbos_err(se); + } + return inst_ok; +} + +/* 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) { + specbos *p = (specbos *) pp; + char buf[MAX_MES_SIZE]; + baud_rate brt[] = { baud_921600, baud_115200, baud_38400, baud_nc }; + unsigned int etime; + unsigned int i; + instType itype = pp->itype; + int se; + + inst_code ev = inst_ok; + + a1logd(p->log, 2, "specbos_init_coms: About to init Serial I/O\n"); + + amutex_lock(p->lock); + + if (p->icom->port_type(p->icom) != icomt_serial + && p->icom->port_type(p->icom) != icomt_usbserial) { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_init_coms: wrong communications type for device!\n"); + return inst_coms_fail; + } + + /* The tick to give up on */ + etime = msec_time() + (long)(1500.0 + 0.5); + + a1logd(p->log, 1, "specbos_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, "specbos_init_coms: trying baud ix %d\n",brt[i]); + if ((se = p->icom->set_ser_port(p->icom, fc_none, brt[i], parity_none, + stop_1, length_8)) != ICOM_OK) { + amutex_unlock(p->lock); + a1logd(p->log, 5, "specbos_init_coms: set_ser_port failed with 0x%x\n",se); + return specbos_interp_code((inst *)p, icoms2specbos_err(se));; /* Give up */ + } + + + /* Check instrument is responding */ + if (((ev = specbos_command(p, "*idn?\r", buf, MAX_MES_SIZE, 0.2)) & 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, "specbos_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, "specbos_init_coms: failed to establish coms\n"); + return inst_coms_fail; + } + + /* Check the response */ + p->model = 0; + + if (strncmp (buf, "SB05", 4) == 0) /* Special case */ + p->model = 1201; + else { + if (strlen(buf) < 11 + || sscanf(buf, "JETI_SB%d\r", &p->model) != 1) { + amutex_unlock(p->lock); + a1logd(p->log, 2, "specbos_init_coms: unrecognised ident string '%s'\n",icoms_fix(buf)); + return inst_protocol_error; + } + } + + if (p->model != 1201 && p->model != 1211) { + amutex_unlock(p->lock); + a1logd(p->log, 2, "specbos_init_coms: unrecognised model %04d\n",p->model); + return inst_unknown_model; + } + a1logd(p->log, 2, "specbos_init_coms: init coms has suceeded\n"); + + p->gotcoms = 1; + +#ifdef NEVER /* Test a change in baud rate on next plug in */ + a1logd(p->log, 2, "specbos_init_coms: changing baudrate\n"); +// if ((ev = specbos_command(p, "*para:baud 384\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) +// if ((ev = specbos_command(p, "*para:baud 115\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) + if ((ev = specbos_command(p, "*para:baud 921\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) + { + amutex_unlock(p->lock); + return ev; + } + if ((ev = specbos_command(p, "*para:save\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } +#endif + + amutex_unlock(p->lock); + + return inst_ok; +} + +static inst_code set_default_disp_type(specbos *p); + +/* + Notes on commands + + *conf is temporary, *para can be saved to instrument + Since we set the instrument up every time, use *conf ? + *PARAmeter:SAVE + + Set calibration to auto on presense of ambient cap + *para:calibn 0 + + To check what mode it's in: + *contr:mhead? + 0 for emissive, 1 for ambient. + + Set auto exposure + *para:expo 1 // Adapt + *para:adap 2 // Adapt if under or over + *conf:maxtin 4000 // Limit to 4 secs for display measurement rather than 60000 + + Set output format + *conf:form 10 // interpolated ASCII with wavelength + *conf:form 12 // H/L 32 bit float with length & checksum - doesn't seem to work :-( + + Measurement function + *conf:func 6 // Radiometric spectrum + + Set to wavelength range and step + *conf:wran 350 850 1 + + Set to average just 1 reading + *conf:aver 1 + + Do a configured measurement + *init + + Fetch the results + *fetch:XYZ // Fetches XYZ whatever the function ? + *fetch 10 // Fetch configured function in format 10 + + Measure refresh + *conf:cycmod 1 + *contr:cyctim 200 4000 // Returns msec + + Set refresh sync mode + *conf:cycmod 1 + *conf:cyctim cyctim // in usec + + message terminations: + + *xxx? \r after data \r\r after spectral ?? + \025 Nak on error ?? + + *para + *conf + *contr \006 Ack on success, + \025 Nak on error + + All enquiries and parameter setting: '\r' + + *init \006 Ack on command accepted + \025 Nak on error + \007 Bell on completion + + *fetch + *calc + *calib + *stat + *help + *fetch \r after data, \r\r after spectral ?? + +*/ + +static inst_code specbos_get_diffpos(specbos *p, int *pos, int nd); + +/* Diffuser position 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; + a1logd(p->log,3,"Diffuser thread started\n"); + for (nfailed = 0; nfailed < 5;) { + int pos; + + amutex_lock(p->lock); + rv = specbos_get_diffpos(p, &pos, 1); + amutex_unlock(p->lock); + + if (p->th_term) { + p->th_termed = 1; + break; + } + if (rv != inst_ok) { + nfailed++; + a1logd(p->log,3,"Diffuser thread failed with 0x%x\n",rv); + continue; + } + if (pos != p->dpos) { + p->dpos = pos; + if (p->eventcallback != NULL) { + p->eventcallback(p->event_cntx, inst_event_mconf); + } + } + msec_sleep(500); + } + a1logd(p->log,3,"Diffuser thread returning\n"); + return rv; +} + +/* Initialise the SPECBOS */ +/* return non-zero on an error, with dtp error code */ +static inst_code +specbos_init_inst(inst *pp) { + specbos *p = (specbos *)pp; + char mes[100]; + char buf[MAX_MES_SIZE]; + inst_code ev = inst_ok; + + a1logd(p->log, 2, "specbos_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 = specbos_command(p, "*conf:default\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) + return ev; + + /* Set calibration type to auto on ambient cap */ + if ((ev = specbos_command(p, "*para:calibn 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + /* Set auto exposure/integration time */ + /* Set calibration type to auto on ambient cap */ + if ((ev = specbos_command(p, "*para:expo 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok + || (ev = specbos_command(p, "*para:adapt 2\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + 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 = specbos_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 = specbos_command(p, "*conf:func 6\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + /* Set the measurement format to ASCII */ + if ((ev = specbos_command(p, "*conf:form 4\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + if ((ev = specbos_command(p, "*para:wavbeg?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + if (sscanf(buf, "Predefined start wave: %lf ",&p->wl_short) != 1) { + amutex_unlock(p->lock); + a1loge(p->log, 1, "specbos_init_inst: failed to parse start wave\n"); + return ev; + } + a1logd(p->log, 1, " Short wl range %f\n",p->wl_short); + + if ((ev = specbos_command(p, "*para:wavend?\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + if (sscanf(buf, "Predefined end wave: %lf ",&p->wl_long) != 1) { + amutex_unlock(p->lock); + a1loge(p->log, 1, "specbos_init_inst: failed to parse end wave\n"); + return ev; + } + if (p->wl_long > 830.0) /* Could go to 1000 with 1211 */ + p->wl_long = 830.0; + + a1logd(p->log, 1, " Long wl range %f\n",p->wl_long); + + p->nbands = (int)((p->wl_long - p->wl_short + 1.0)/1.0 + 0.5); + + /* Set the wavelength range and resolution */ + sprintf(mes, "*conf:wran %d %d 1\r", (int)(p->wl_short+0.5), (int)(p->wl_long+0.5)); + if ((ev = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + /* Set to average just 1 reading */ + if ((ev = specbos_command(p, "*conf:aver 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + + if (p->log->verb) { + int val; + char *sp; + + if ((ev = specbos_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 = specbos_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 = specbos_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 = specbos_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); + } + } + + /* Start the diffuser monitoring thread */ + if ((p->th = new_athread(specbos_diff_thread, (void *)p)) == NULL) { + amutex_unlock(p->lock); + return SPECBOS_INT_THREADFAILED; + } + + p->inited = 1; + a1logd(p->log, 2, "specbos_init_inst: instrument inited OK\n"); + amutex_unlock(p->lock); + + return inst_ok; +} + +static inst_code specbos_measure_set_refresh(specbos *p); +static inst_code specbos_imp_set_refresh(specbos *p); + +/* Get the ambient diffuser position */ +/* (This is not multithread safe) */ +static inst_code +specbos_get_diffpos( + specbos *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 = specbos_fcommand(p, "*contr:mhead?\r", buf, MAX_MES_SIZE, 1.0, 1, 0, nd)) != inst_ok) { + return specbos_interp_code((inst *)p, ec); + } + if (sscanf(buf, "mhead: %d ",pos) != 1) { + a1logd(p->log, 2, "specbos_init_coms: unrecognised measuring head string '%s'\n",icoms_fix(buf)); + return inst_protocol_error; + } + return inst_ok; +} + +/* Read a single sample */ +/* Return the dtp error code */ +static inst_code +specbos_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 */ + specbos *p = (specbos *)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, "specbos: 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 = specbos_get_diffpos(p, &pos, 0) ) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + + if (p->mode & inst_mode_ambient) { + if (pos != 1) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, SPECBOS_SPOS_AMB); + } + } else { + if (pos != 0) { + amutex_unlock(p->lock); + return specbos_interp_code((inst *)p, SPECBOS_SPOS_EMIS); + } + } + + /* Make sure the target laser is off */ + if ((rv = specbos_command(p, "*contr:laser 0\r", buf, MAX_MES_SIZE, 1.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) { + if ((rv = specbos_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) { + 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 (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 { + amutex_unlock(p->lock); + a1logd(p->log, 1, "specbos_read_sample: failed to parse '%s'\n",buf); + return inst_protocol_error; + } + + /* spectrum data is returned only if requested */ + if (p->mode & inst_mode_spectral) { + 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 */ + + /* 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; + } + *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; + } + cp = ncp+1; + } + } + 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 +specbos_imp_set_refresh(specbos *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 = specbos_command(p, "*conf:cycmod 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + sprintf(mes,"*conf:cyctim %f\r",p->refperiod * 1e6); + if ((rv = specbos_command(p, mes, buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + a1logd(p->log,5,"specbos_imp_set_refresh set refresh rate to %f Hz\n",1.0/p->refperiod); + } else { + if ((rv = specbos_command(p, "*conf:cycmod 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + a1logd(p->log,5,"specbos_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 +specbos_imp_measure_refresh( +specbos *p, +double *ref_rate +) { + char buf[MAX_MES_SIZE], *cp; + double refperiod = 0.0; + int ec; + inst_code rv; + + if (p->model == 1201) + return inst_unsupported; + + /* Make sure the target laser is off */ + if ((rv = specbos_command(p, "*contr:laser 0\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + return rv; + } + + if ((ec = specbos_fcommand(p, "*contr:cyctim 200 4000\r", buf, MAX_MES_SIZE, 5.0, 1, 2, 0)) != SPECBOS_OK) { + return specbos_interp_code((inst *)p, ec); + } + + if ((cp = strchr(buf, 'c')) == NULL) + cp = buf; + if (sscanf(cp, "cyctim[ms]: %lf ", &refperiod) != 1) { + a1logd(p->log, 1, "specbos_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 +specbos_read_refrate( +inst *pp, +double *ref_rate +) { + specbos *p = (specbos *)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; + + amutex_lock(p->lock); + if ((rv = specbos_imp_measure_refresh(p, &refrate)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + amutex_unlock(p->lock); + + if (refrate == 0.0) + return inst_misread; + + *ref_rate = refrate; + + return inst_ok; +} + +/* Measure and then set refperiod, refrate if possible */ +static inst_code +specbos_measure_set_refresh( + specbos *p /* Object */ +) { + inst_code rv; + double refrate = 0.0; + int mul; + double pval; + + amutex_lock(p->lock); + if ((rv = specbos_imp_measure_refresh(p, &refrate)) != inst_ok) { + amutex_unlock(p->lock); + 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 = specbos_imp_set_refresh(p)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + amutex_unlock(p->lock); + + return inst_ok; +} + +/* 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; + 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 specbos_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) */ +) { + specbos *p = (specbos *)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 = specbos_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,"specbos_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 = specbos_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 specbos_get_refr_rate(inst *pp, +double *ref_rate +) { + specbos *p = (specbos *)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 specbos_set_refr_rate(inst *pp, +double ref_rate +) { + specbos *p = (specbos *)pp; + inst_code rv; + + a1logd(p->log,5,"specbos_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 = specbos_imp_set_refresh(p)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + amutex_unlock(p->lock); + + return inst_ok; +} + + +/* Error codes interpretation */ +static char * +specbos_interp_error(inst *pp, int ec) { +// specbos *p = (specbos *)pp; + ec &= inst_imask; + switch (ec) { + case SPECBOS_INTERNAL_ERROR: + return "Internal software error"; + case SPECBOS_TIMEOUT: + return "Communications timeout"; + case SPECBOS_COMS_FAIL: + return "Communications failure"; + case SPECBOS_UNKNOWN_MODEL: + return "Not a JETI specbos"; + case SPECBOS_DATA_PARSE_ERROR: + return "Data from specbos didn't parse as expected"; + case SPECBOS_SPOS_EMIS: + return "Ambient filter should be removed"; + case SPECBOS_SPOS_AMB: + return "Ambient filter should be used"; + + case SPECBOS_OK: + return "No device error"; + + case SPECBOS_COMMAND: + return "Command"; + case SPECBOS_PASSWORD: + return "Password"; + case SPECBOS_DIGIT: + return "Digit"; + case SPECBOS_ARG_1: + return "Argument 1"; + case SPECBOS_ARG_2: + return "Argument 2"; + case SPECBOS_ARG_3: + return "Argument 3"; + case SPECBOS_ARG_4: + return "Argument 4"; + case SPECBOS_PARAM: + return "Parameter argument"; + case SPECBOS_CONFIG_ARG: + return "Config argument"; + case SPECBOS_CONTROL_ARG: + return "Control argument"; + case SPECBOS_READ_ARG: + return "Read argument"; + case SPECBOS_FETCH_ARG: + return "Fetch argument"; + case SPECBOS_MEAS_ARG: + return "Measuring argument"; + case SPECBOS_CALC_ARG: + return "Calculation argument"; + case SPECBOS_CAL_ARG: + return "Calibration argument"; + case SPECBOS_PARAM_CHSUM: + return "Parameter checksum"; + case SPECBOS_USERFILE_CHSUM: + return "Userfile checksum"; + case SPECBOS_USERFILE2_CHSUM: + return "Userfile2 checksum"; + case SPECBOS_USERFILE2_ARG: + return "Userfile2 argument"; + case SPECBOS_OVEREXPOSED: + return "Overexposure"; + case SPECBOS_UNDEREXPOSED: + return "Underexposure"; + case SPECBOS_ADAPT_INT_TIME: + return "Adaption integration time"; + case SPECBOS_NO_SHUTTER: + return "Shutter doesn't exist"; + case SPECBOS_NO_DARK_MEAS: + return "No dark measurement"; + case SPECBOS_NO_REF_MEAS: + return "No reference measurement"; + case SPECBOS_NO_TRANS_MEAS: + return "No transmission measurement"; + case SPECBOS_NO_RADMTRC_CALC: + return "No radiometric calculation"; + case SPECBOS_NO_CCT_CALC: + return "No CCT calculation"; + case SPECBOS_NO_CRI_CALC: + return "No CRI calculation"; + case SPECBOS_NO_DARK_COMP: + return "No dark compensation"; + case SPECBOS_NO_LIGHT_MEAS: + return "No light measurement"; + case SPECBOS_NO_PEAK_CALC: + return "No peak calculation"; + case SPECBOS_CAL_DATA: + return "Calibration data"; + case SPECBOS_EXCEED_CAL_WL: + return "Exceeded calibration wavelength"; + case SPECBOS_SCAN_BREAK: + return "Scan break"; + case SPECBOS_TO_CYC_OPT_TRIG: + return "Timeout cycle on optical trigger"; + case SPECBOS_DIV_CYC_TIME: + return "Divider cycle time"; + case SPECBOS_WRITE_FLASH_PARM: + return "Write parameter to flash"; + case SPECBOS_READ_FLASH_PARM: + return "Read parameter from flash"; + case SPECBOS_FLASH_ERASE: + return "Erase flash"; + case SPECBOS_NO_CALIB_FILE: + return "No calibration file"; + case SPECBOS_CALIB_FILE_HEADER: + return "Calibration file header"; + case SPECBOS_WRITE_CALIB_FILE: + return "Write calibration file"; + case SPECBOS_CALIB_FILE_VALS: + return "Calibration file values"; + case SPECBOS_CALIB_FILE_NO: + return "Calibration file number"; + case SPECBOS_CLEAR_CALIB_FILE: + return "Clear calibration file"; + case SPECBOS_CLEAR_CALIB_ARG: + return "Clear calibration file argument"; + case SPECBOS_NO_LAMP_FILE: + return "No lamp file"; + case SPECBOS_LAMP_FILE_HEADER: + return "Lamp file header"; + case SPECBOS_WRITE_LAMP_FILE: + return "Write lamp file"; + case SPECBOS_LAMP_FILE_VALS: + return "Lamp file values"; + case SPECBOS_LAMP_FILE_NO: + return "Lamp file number"; + case SPECBOS_CLEAR_LAMP_FILE: + return "Clear lamp file"; + case SPECBOS_CLEAR_LAMP_FILE_ARG: + return "Clear lamp file argument"; + case SPECBOS_RAM_CHECK: + return "RAM check"; + case SPECBOS_DATA_OUTPUT: + return "Data output"; + case SPECBOS_RAM_LOW: + return "Insufcient RAM"; + case SPECBOS_FIRST_MEM_ALLOC: + return "First memory allocation"; + case SPECBOS_SECOND_MEM_ALLOC: + return "Second memory allocation"; + case SPECBOS_THIRD_MEM_ALLOC: + return "Third memory allocation"; + case SPECBOS_RADMTRC_WL_RANGE: + return "Wavelength range for radiometric calculation"; + case SPECBOS_BOOT_BAT_POWER: + return "Boot by battery power"; + case SPECBOS_TRIG_CONF_1: + return "Trigger configuration 1"; + case SPECBOS_TRIG_CONF_2: + return "Trigger configuration 2"; + + case SPECBOS_INT_THREADFAILED: + return "Starting diffuser position thread failed"; + + default: + return "Unknown error code"; + } +} + + +/* Convert a machine specific error code into an abstract dtp code */ +static inst_code +specbos_interp_code(inst *pp, int ec) { + + ec &= inst_imask; + switch (ec) { + + case SPECBOS_OK: + return inst_ok; + + case SPECBOS_INTERNAL_ERROR: + case SPECBOS_INT_THREADFAILED: + return inst_internal_error | ec; + + case SPECBOS_TIMEOUT: + case SPECBOS_COMS_FAIL: + return inst_coms_fail | ec; + + case SPECBOS_UNKNOWN_MODEL: + return inst_unknown_model | ec; + + case SPECBOS_DATA_PARSE_ERROR: + return inst_protocol_error | ec; + + case SPECBOS_SPOS_EMIS: + case SPECBOS_SPOS_AMB: + return inst_wrong_config | ec; + + case SPECBOS_COMMAND: + case SPECBOS_DIGIT: + case SPECBOS_ARG_1: + case SPECBOS_ARG_2: + case SPECBOS_ARG_3: + case SPECBOS_ARG_4: + case SPECBOS_PARAM: + case SPECBOS_CONFIG_ARG: + case SPECBOS_CONTROL_ARG: + case SPECBOS_READ_ARG: + case SPECBOS_FETCH_ARG: + case SPECBOS_MEAS_ARG: + case SPECBOS_CALC_ARG: + case SPECBOS_CAL_ARG: + return inst_bad_parameter | ec; + + case SPECBOS_OVEREXPOSED: + case SPECBOS_UNDEREXPOSED: + return inst_misread | ec; + + case SPECBOS_PASSWORD: + case SPECBOS_PARAM_CHSUM: + case SPECBOS_USERFILE_CHSUM: + case SPECBOS_USERFILE2_CHSUM: + case SPECBOS_USERFILE2_ARG: + case SPECBOS_ADAPT_INT_TIME: + case SPECBOS_NO_SHUTTER: + case SPECBOS_NO_DARK_MEAS: + case SPECBOS_NO_REF_MEAS: + case SPECBOS_NO_TRANS_MEAS: + case SPECBOS_NO_RADMTRC_CALC: + case SPECBOS_NO_CCT_CALC: + case SPECBOS_NO_CRI_CALC: + case SPECBOS_NO_DARK_COMP: + case SPECBOS_NO_LIGHT_MEAS: + case SPECBOS_NO_PEAK_CALC: + case SPECBOS_CAL_DATA: + case SPECBOS_EXCEED_CAL_WL: + case SPECBOS_SCAN_BREAK: + case SPECBOS_TO_CYC_OPT_TRIG: + case SPECBOS_DIV_CYC_TIME: + case SPECBOS_WRITE_FLASH_PARM: + case SPECBOS_READ_FLASH_PARM: + case SPECBOS_FLASH_ERASE: + case SPECBOS_NO_CALIB_FILE: + case SPECBOS_CALIB_FILE_HEADER: + case SPECBOS_WRITE_CALIB_FILE: + case SPECBOS_CALIB_FILE_VALS: + case SPECBOS_CALIB_FILE_NO: + case SPECBOS_CLEAR_CALIB_FILE: + case SPECBOS_CLEAR_CALIB_ARG: + case SPECBOS_NO_LAMP_FILE: + case SPECBOS_LAMP_FILE_HEADER: + case SPECBOS_WRITE_LAMP_FILE: + case SPECBOS_LAMP_FILE_VALS: + case SPECBOS_LAMP_FILE_NO: + case SPECBOS_CLEAR_LAMP_FILE: + case SPECBOS_CLEAR_LAMP_FILE_ARG: + case SPECBOS_RAM_CHECK: + case SPECBOS_DATA_OUTPUT: + case SPECBOS_RAM_LOW: + case SPECBOS_FIRST_MEM_ALLOC: + case SPECBOS_SECOND_MEM_ALLOC: + case SPECBOS_THIRD_MEM_ALLOC: + case SPECBOS_RADMTRC_WL_RANGE: + case SPECBOS_BOOT_BAT_POWER: + case SPECBOS_TRIG_CONF_1: + case SPECBOS_TRIG_CONF_2: + return inst_hardware_fail | ec; + } + return inst_other_error | ec; +} + +/* Destroy ourselves */ +static void +specbos_del(inst *pp) { + if (pp != NULL) { + specbos *p = (specbos *)pp; + if (p->th != NULL) { /* Terminate diffuser monitor thread */ + int i; + p->th_term = 1; /* Tell thread to exit on error */ + for (i = 0; p->th_termed == 0 && i < 5; i++) + msec_sleep(100); /* Wait for thread to terminate */ + if (i >= 5) { + a1logd(p->log,3,"specbos diffuser thread termination failed\n"); + } + p->th->del(p->th); + } + if (p->icom != NULL) + p->icom->del(p->icom); + amutex_del(p->lock); + free(p); + } +} + +/* Return the instrument mode capabilities */ +void specbos_capabilities(inst *pp, +inst_mode *pcap1, +inst2_capability *pcap2, +inst3_capability *pcap3) { + specbos *p = (specbos *)pp; + inst_mode cap1 = 0; + inst2_capability cap2 = 0; + + cap1 |= inst_mode_emis_tele + | inst_mode_ambient + | 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 */ + ; + + 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. */ +static inst_code specbos_meas_config( +inst *pp, +inst_mode *mmodes, +inst_cal_cond *cconds, +int *conf_ix +) { + specbos *p = (specbos *)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 = specbos_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 */ +inst_code specbos_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 */ +inst_code specbos_set_mode(inst *pp, inst_mode m) { + specbos *p = (specbos *)pp; + int refrmode; + inst_code ev; + + if ((ev = specbos_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; +} + +inst_disptypesel specbos_disptypesel[3] = { + { + inst_dtflags_default, + 1, + "nl", + "Non-Refresh display", + 0, + 0 + }, + { + inst_dtflags_none, /* flags */ + 2, /* cbid */ + "rc", /* sel */ + "Refresh display", /* desc */ + 1, /* refr */ + 1 /* ix */ + }, + { + inst_dtflags_end, + 0, + "", + "", + 0, + 0 + } +}; + +/* Get mode and option details */ +static inst_code specbos_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 */ +) { + specbos *p = (specbos *)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 = specbos_disptypesel; + + return inst_ok; +} + +/* Given a display type entry, setup for that type */ +static inst_code set_disp_type(specbos *p, inst_disptypesel *dentry) { + inst_code rv; + int refrmode; + + refrmode = dentry->refr; + + a1logd(p->log,5,"specbos 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 = specbos_imp_set_refresh(p)) != inst_ok) { + amutex_unlock(p->lock); + return rv; + } + amutex_unlock(p->lock); + + return inst_ok; +} + +/* Set the display type */ +static inst_code specbos_set_disptype(inst *pp, int ix) { + specbos *p = (specbos *)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,"specbos specbos_set_disptype ix %d\n",ix); + dentry = &specbos_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 +specbos_get_set_opt(inst *pp, inst_opt_type m, ...) +{ + specbos *p = (specbos *)pp; + char buf[MAX_MES_SIZE]; + inst_code ev = inst_ok; + + a1logd(p->log, 5, "specbos_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; + } + + /* Set laser target state */ + if (m == inst_opt_set_target_state) { + 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) { + 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) { + 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) { + if ((ev = specbos_command(p, "*contr:laser 1\r", buf, MAX_MES_SIZE, 1.0)) != inst_ok) { + amutex_unlock(p->lock); + return ev; + } + } 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; + } + } + 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 specbos *new_specbos(icoms *icom, instType itype) { + specbos *p; + if ((p = (specbos *)calloc(sizeof(specbos),1)) == NULL) { + a1loge(icom->log, 1, "new_specbos: malloc failed!\n"); + return NULL; + } + + p->log = new_a1log_d(icom->log); + + p->init_coms = specbos_init_coms; + p->init_inst = specbos_init_inst; + p->capabilities = specbos_capabilities; + p->meas_config = specbos_meas_config; + p->check_mode = specbos_check_mode; + p->set_mode = specbos_set_mode; + p->get_disptypesel = specbos_get_disptypesel; + p->set_disptype = specbos_set_disptype; + p->get_set_opt = specbos_get_set_opt; + p->read_sample = specbos_read_sample; + p->read_refrate = specbos_read_refrate; + p->get_n_a_cals = specbos_get_n_a_cals; + p->calibrate = specbos_calibrate; + p->get_refr_rate = specbos_get_refr_rate; + p->set_refr_rate = specbos_set_refr_rate; + p->interp_error = specbos_interp_error; + p->del = specbos_del; + + p->icom = icom; + p->itype = icom->itype; + if (p->itype == instSpecbos1201) + p->model = 1201; + + amutex_init(p->lock); + + return p; +} + diff --git a/spectro/specbos.h b/spectro/specbos.h new file mode 100644 index 0000000..2bc601e --- /dev/null +++ b/spectro/specbos.h @@ -0,0 +1,160 @@ +#ifndef SPECBOS_H + +/* + * Argyll Color Correction System + * + * JETI specbos related defines + * + * Author: Graeme W. Gill + * Date: 13/3/2013 + * + * Copyright 2001 - 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. + * + * Based on DTP02.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 SPECBOS_INTERNAL_ERROR 0xff01 /* Internal software error */ +#define SPECBOS_TIMEOUT 0xff02 /* Communication timeout */ +#define SPECBOS_COMS_FAIL 0xff03 /* Communication failure */ +#define SPECBOS_UNKNOWN_MODEL 0xff04 /* Not a JETI specbos */ +#define SPECBOS_DATA_PARSE_ERROR 0xff05 /* Read data parsing error */ + +#define SPECBOS_SPOS_EMIS 0xff06 /* Needs to be in emsissive configuration */ +#define SPECBOS_SPOS_AMB 0xff07 /* Needs to be in ambient configuration */ + +/* Real instrument error code */ +#define SPECBOS_OK 0 + +#define SPECBOS_COMMAND 4 +#define SPECBOS_PASSWORD 7 +#define SPECBOS_DIGIT 8 +#define SPECBOS_ARG_1 10 +#define SPECBOS_ARG_2 11 +#define SPECBOS_ARG_3 12 +#define SPECBOS_ARG_4 13 +#define SPECBOS_PARAM 20 +#define SPECBOS_CONFIG_ARG 21 +#define SPECBOS_CONTROL_ARG 22 +#define SPECBOS_READ_ARG 23 +#define SPECBOS_FETCH_ARG 24 +#define SPECBOS_MEAS_ARG 25 +#define SPECBOS_CALC_ARG 26 +#define SPECBOS_CAL_ARG 27 +#define SPECBOS_PARAM_CHSUM 101 +#define SPECBOS_USERFILE_CHSUM 102 +#define SPECBOS_USERFILE2_CHSUM 103 +#define SPECBOS_USERFILE2_ARG 104 +#define SPECBOS_OVEREXPOSED 120 +#define SPECBOS_UNDEREXPOSED 121 +#define SPECBOS_ADAPT_INT_TIME 123 +#define SPECBOS_NO_SHUTTER 130 +#define SPECBOS_NO_DARK_MEAS 131 +#define SPECBOS_NO_REF_MEAS 132 +#define SPECBOS_NO_TRANS_MEAS 133 +#define SPECBOS_NO_RADMTRC_CALC 134 +#define SPECBOS_NO_CCT_CALC 135 +#define SPECBOS_NO_CRI_CALC 136 +#define SPECBOS_NO_DARK_COMP 137 +#define SPECBOS_NO_LIGHT_MEAS 138 +#define SPECBOS_NO_PEAK_CALC 139 +#define SPECBOS_CAL_DATA 140 +#define SPECBOS_EXCEED_CAL_WL 141 +#define SPECBOS_SCAN_BREAK 147 +#define SPECBOS_TO_CYC_OPT_TRIG 160 +#define SPECBOS_DIV_CYC_TIME 161 +#define SPECBOS_WRITE_FLASH_PARM 170 +#define SPECBOS_READ_FLASH_PARM 171 +#define SPECBOS_FLASH_ERASE 172 +#define SPECBOS_NO_CALIB_FILE 180 +#define SPECBOS_CALIB_FILE_HEADER 181 +#define SPECBOS_WRITE_CALIB_FILE 182 +#define SPECBOS_CALIB_FILE_VALS 183 +#define SPECBOS_CALIB_FILE_NO 184 +#define SPECBOS_CLEAR_CALIB_FILE 186 +#define SPECBOS_CLEAR_CALIB_ARG 187 +#define SPECBOS_NO_LAMP_FILE 190 +#define SPECBOS_LAMP_FILE_HEADER 191 +#define SPECBOS_WRITE_LAMP_FILE 192 +#define SPECBOS_LAMP_FILE_VALS 193 +#define SPECBOS_LAMP_FILE_NO 194 +#define SPECBOS_CLEAR_LAMP_FILE 196 +#define SPECBOS_CLEAR_LAMP_FILE_ARG 197 +#define SPECBOS_RAM_CHECK 200 +#define SPECBOS_DATA_OUTPUT 220 +#define SPECBOS_RAM_LOW 225 +#define SPECBOS_FIRST_MEM_ALLOC 230 +#define SPECBOS_SECOND_MEM_ALLOC 231 +#define SPECBOS_THIRD_MEM_ALLOC 232 +#define SPECBOS_RADMTRC_WL_RANGE 251 +#define SPECBOS_BOOT_BAT_POWER 280 +#define SPECBOS_TRIG_CONF_1 500 +#define SPECBOS_TRIG_CONF_2 501 + +/* Internal software errors */ +#define SPECBOS_INT_THREADFAILED 1000 + +/* SPECBOS communication object */ +struct _specbos { + INST_OBJ_BASE + + amutex lock; /* Command lock */ + + int model; /* JETI specbos model number */ + /* 1201 */ + /* 1211 */ + + 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 */ + athread *th; /* Diffuser position monitoring thread */ + volatile int th_term; /* nz to terminate thread */ + volatile int th_termed; /* nz when thread terminated */ + int dpos; /* Diffuser position, 0 = emissive, 1 = ambient */ + + }; typedef struct _specbos specbos; + +/* Constructor */ +extern specbos *new_specbos(icoms *icom, instType itype); + + +#define SPECBOS_H +#endif /* SPECBOS_H */ diff --git a/spectro/spotread.c b/spectro/spotread.c index 3218243..4d65916 100644 --- a/spectro/spotread.c +++ b/spectro/spotread.c @@ -21,6 +21,9 @@ /* TTBD * + * Make -V average the spectrum too (if present), and allow it to + * be saved to a .sp file. + * * Should fix plot so that it is a separate object running its own thread, * so that it can be sent a graph without needing to be clicked in all the time. * @@ -287,10 +290,10 @@ usage(char *diag, ...) { fprintf(stderr," -t Use transmission measurement mode\n"); fprintf(stderr," -e Use emissive measurement mode (absolute results)\n"); fprintf(stderr," -eb Use display white brightness relative measurement mode\n"); - fprintf(stderr," -ew Use display white relative measurement mode\n"); + fprintf(stderr," -ew Use display white point relative chromatically adjusted mode\n"); fprintf(stderr," -p Use telephoto measurement mode (absolute results)\n"); fprintf(stderr," -pb Use projector white brightness relative measurement mode\n"); - fprintf(stderr," -pw Use projector white relative measurement mode\n"); + fprintf(stderr," -pw Use projector white point relative chromatically adjusted mode\n"); fprintf(stderr," -a Use ambient measurement mode (absolute results)\n"); fprintf(stderr," -f Use ambient flash measurement mode (absolute results)\n"); cap2 = inst_show_disptype_options(stderr, " -y ", icmps, 0); @@ -334,6 +337,7 @@ usage(char *diag, ...) { fprintf(stderr," -X file.ccss Use Colorimeter Calibration Spectral Samples for calibration\n"); } fprintf(stderr," -Y r|n Override refresh, non-refresh display mode\n"); + fprintf(stderr," -Y R:rate Override measured refresh rate with rate Hz\n"); fprintf(stderr," -Y A Use non-adaptive integration time mode (if available).\n"); // fprintf(stderr," -Y U Test i1pro2 UV measurement mode\n"); fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n"); @@ -361,7 +365,8 @@ int main(int argc, char *argv[]) { int ambient = 0; /* 1 = Use ambient emissive mode, 2 = ambient flash mode */ int highres = 0; /* Use high res mode if available */ int uvmode = 0; /* ~~~ i1pro2 test mode ~~~ */ - int refrmode = -1; /* -1 = default, 0 = non-refresh mode, 1 = non-refresh mode */ + int refrmode = -1; /* -1 = default, 0 = non-refresh mode, 1 = refresh mode */ + double refrate = 0.0; /* 0.0 = default, > 0.0 = override refresh rate */ int nadaptive = 0; /* Use non-apative mode if available */ int doYxy= 0; /* Display Yxy instead of Lab */ int doLCh= 0; /* Display LCh instead of Lab */ @@ -398,6 +403,7 @@ 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 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 */ @@ -467,8 +473,10 @@ int main(int argc, char *argv[]) { } else if (argv[fa][1] == 'c') { fa = nfa; if (na == NULL) usage("Paramater expected following -c"); - comport = atoi(na); - if (comport < 1 || comport > 40) usage("-c parameter %d out of range",comport); + { + comport = atoi(na); + if (comport < 1 || comport > 40) usage("-c parameter %d out of range",comport); + } /* Display type */ } else if (argv[fa][1] == 'y') { @@ -702,19 +710,6 @@ int main(int argc, char *argv[]) { if (na == NULL) usage("Parameter expected after -K"); strncpy(ccxxname,na,MAXNAMEL-1); ccxxname[MAXNAMEL-1] = '\000'; - /* Serial port flow control */ - } else if (argv[fa][1] == 'W') { - fa = nfa; - if (na == NULL) usage("Parameter expected after -W"); - if (na[0] == 'n' || na[0] == 'N') - fc = fc_none; - else if (na[0] == 'h' || na[0] == 'H') - fc = fc_Hardware; - else if (na[0] == 'x' || na[0] == 'X') - fc = fc_XonXOff; - else - usage("-W parameter '%c' not recognised",na[0]); - /* Extra flags */ } else if (argv[fa][1] == 'Y') { if (na == NULL) @@ -726,12 +721,32 @@ int main(int argc, char *argv[]) { refrmode = 1; } else if (na[0] == 'n') { refrmode = 0; + } else if (na[0] == 'R') { + if (na[1] != ':') + usage("-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); /* ~~~ i1pro2 test code ~~~ */ } else if (na[0] == 'U') { uvmode = 1; } else { usage("-Y parameter '%c' not recognised",na[0]); } + fa = nfa; + + /* Serial port flow control */ + } else if (argv[fa][1] == 'W') { + fa = nfa; + if (na == NULL) usage("Parameter expected after -W"); + if (na[0] == 'n' || na[0] == 'N') + fc = fc_none; + else if (na[0] == 'h' || na[0] == 'H') + fc = fc_Hardware; + else if (na[0] == 'x' || na[0] == 'X') + fc = fc_XonXOff; + else + usage("-W parameter '%c' not recognised",na[0]); } else usage("Flag -%c not recognised",argv[fa][1]); @@ -765,7 +780,6 @@ int main(int argc, char *argv[]) { if ((ipath = icmps->get_path(icmps, comport)) == NULL) error("No instrument at port %d",comport); - /* Setup the instrument ready to do reads */ if ((it = new_inst(ipath, 0, g_log, DUIH_FUNC_AND_CONTEXT)) == NULL) { usage("Unknown, inappropriate or no instrument detected"); @@ -852,14 +866,7 @@ int main(int argc, char *argv[]) { || it->check_mode(it, inst_mode_emis_ambient) != inst_ok) { printf("Requested ambient light capability,\n"); printf("and instrument doesn't support it.\n"); - if (!IMODETST(cap, inst_mode_emis_spot)) { - it->del(it); - return -1; - } else { - printf("Will use emissive mode instead,\n"); - printf("but note that light level readings may be wrong!\n"); - - } + return -1; } else { if (verb) { printf("Please make sure the instrument is fitted with\n"); @@ -884,6 +891,12 @@ int main(int argc, char *argv[]) { } else if (emiss || tele) { + /* If there is a tele mode but no emission, use tele */ + if (!IMODETST(cap, inst_mode_emis_spot) + && IMODETST(cap, inst_mode_emis_tele)) { + tele = 1; + } + if (tele) { if (!IMODETST(cap, inst_mode_emis_tele) || it->check_mode(it, inst_mode_emis_tele) != inst_ok) { @@ -915,10 +928,8 @@ int main(int argc, char *argv[]) { refrmode = -1; } } - /* Set display type */ if (dtype != 0) { - if (cap2 & inst2_disptype) { int ix; if ((ix = inst_get_disptype_index(it, dtype, 0)) < 0) { @@ -973,21 +984,6 @@ int main(int argc, char *argv[]) { printf("Disable initial-calibrate not supported\n"); } } - if (highres) { - if (IMODETST(cap, inst_mode_highres)) { - inst_code ev; - if ((ev = it->get_set_opt(it, inst_opt_highres)) != inst_ok) { - printf("\nSetting high res mode failed with error :'%s' (%s)\n", - it->inst_interp_error(it, ev), it->interp_error(it, ev)); - it->del(it); - return -1; - } - highres = 1; - } else if (verb) { - printf("high resolution ignored - instrument doesn't support high res. mode\n"); - } - } - /* Set it to the appropriate mode */ /* Should look at instrument type & user spec ??? */ @@ -1032,6 +1028,16 @@ int main(int argc, char *argv[]) { smode |= inst_mode_spectral; } + if (highres) { + if (IMODETST(cap, inst_mode_highres)) { + mode |= inst_mode_highres; + smode |= inst_mode_highres; + } else if (verb) { + printf("high resolution ignored - instrument doesn't support high res. mode\n"); + highres = 0; + } + } + // ~~~ i1pro2 test code ~~~ */ if (uvmode) { if (!IMODETST(cap, inst_mode_ref_uv)) { @@ -1128,6 +1134,21 @@ int main(int argc, char *argv[]) { } } + if (refrate > 0.0) { + if (!(cap2 & inst2_set_refresh_rate)) { + if (verb) + printf("Attempted to set refresh rate and instrument doesn't support setting it (ignored)\n"); + refrate = 0.0; + } else { + if ((rv = it->set_refr_rate(it, refrate)) != inst_ok) { + printf("\nSetting instrument refresh rate to %f Hz failed with error :'%s' (%s)\n", + refrate, it->inst_interp_error(it, rv), it->interp_error(it, rv)); + it->del(it); + return -1; + } + } + } + /* If non-standard observer wasn't set by a CCSS file above */ if (obType != icxOT_default && (cap2 & inst2_ccss) && ccssset == 0) { if ((rv = it->get_set_opt(it, inst_opt_set_ccss_obs, obType, 0)) != inst_ok) { @@ -1138,7 +1159,12 @@ int main(int argc, char *argv[]) { } } - /* If it batter powered, show the status of the battery */ + /* Warm the user that they should do a frequency calibration */ + if (it->needs_calibration(it) & inst_calt_ref_freq) { + printf("Please read an 80%% white patch first to calibrate refresh frequency\n"); + } + + /* If it battery powered, show the status of the battery */ if ((cap2 & inst2_has_battery)) { double batstat = 0.0; if ((rv = it->get_set_opt(it, inst_stat_battery, &batstat)) != inst_ok) { @@ -1186,6 +1212,8 @@ int main(int argc, char *argv[]) { inst_set_uih('K', 'K', DUIH_CMND); inst_set_uih('s', 's', DUIH_CMND); inst_set_uih('S', 'S', DUIH_CMND); + if (cap2 & inst2_has_target) + inst_set_uih('t', 't', DUIH_CMND); inst_set_uih('f', 'f', DUIH_CMND); inst_set_uih('F', 'F', DUIH_CMND); inst_set_uih('q', 'q', DUIH_ABORT); @@ -1239,7 +1267,7 @@ int main(int argc, char *argv[]) { /* Read spots until the user quits */ for (ix = 1;; ix++) { ipatch val; - double XYZ[3]; /* XYZ scaled 0..100 or absolute */ + double XYZ[3] = { 0.0, 0.0, 0.0 }; /* XYZ scaled 0..100 or absolute */ double tXYZ[3]; #ifndef SALONEINSTLIB double cct, vct, vdt; @@ -1250,6 +1278,43 @@ int main(int argc, char *argv[]) { int dofwa = 0; /* Do FWA compensation */ int fidx = -1; /* FWA compensated index, default = none */ +#ifdef NEVER // test i1d3 min_int_time code + { + double cval; + char *cp; + + if ((rv = it->get_set_opt(it, inst_opt_get_min_int_time, &cval)) != inst_ok) { + printf("\nGetting min_int)time failed with error :'%s' (%s)\n", + it->inst_interp_error(it, rv), it->interp_error(it, rv)); + it->del(it); + return -1; + } + printf("Current min int time = %f\n",cval); + + if ((cp = getenv("I1D3_MIN_INT_TIME")) != NULL) { + cval = atof(cp); + + printf("Setting int time %f\n",cval); + if ((rv = it->get_set_opt(it, inst_opt_set_min_int_time, cval)) != inst_ok) { + printf("\nSetting min_int_time failed with error :'%s' (%s)\n", + it->inst_interp_error(it, rv), it->interp_error(it, rv)); + it->del(it); + return -1; + } + + if ((rv = it->get_set_opt(it, inst_opt_get_min_int_time, &cval)) != inst_ok) { + printf("\nGetting min_int)time failed with error :'%s' (%s)\n", + it->inst_interp_error(it, rv), it->interp_error(it, rv)); + it->del(it); + return -1; + } + + printf("Check current min int time = %f\n",cval); + } + } + +#endif // NEVER + if (savdrd != -1 && IMODETST(cap, inst_mode_s_ref_spot) && it->check_mode(it, inst_mode_s_ref_spot) == inst_ok) { inst_stat_savdrd sv; @@ -1429,6 +1494,8 @@ int main(int argc, char *argv[]) { printf("Hit 'r' to set reference\n"); #endif /* SALONEINSTLIB */ printf("'h' to toggle high res., 'k' to do a calibration\n"); + if (cap2 & inst2_has_target) + printf("'t' to toggle laser target\n"); } if (uswitch) printf("Hit ESC or Q to exit, instrument switch or any other key to take a reading: "); @@ -1610,9 +1677,21 @@ int main(int argc, char *argv[]) { if (ch == 0x1b || ch == 0x03 || ch == 'q' || ch == 'Q') { /* Or ^C */ break; } + if ((cap2 & inst2_has_target) && ch == 't') { // Toggle target + inst_code ev; + if ((ev = it->get_set_opt(it, inst_opt_set_target_state, 2)) != inst_ok) { + printf("\nToggling target failed with error :'%s' (%s)\n", + it->inst_interp_error(it, ev), it->interp_error(it, ev)); + it->del(it); + return -1; + } + --ix; + continue; + } if (ch == 'H' || ch == 'h') { /* Toggle high res mode */ if (IMODETST(cap, inst_mode_highres)) { inst_code ev; + /* Hmm. Could simply re-do set_mode() here instead */ if (highres) { if ((ev = it->get_set_opt(it, inst_opt_stdres)) != inst_ok) { printf("\nSetting std res mode failed with error :'%s' (%s)\n", @@ -1778,7 +1857,7 @@ int main(int argc, char *argv[]) { double refr; inst_code ev; - if (!(cap2 & inst2_refresh_rate)) { + if (!(cap2 & inst2_get_refresh_rate)) { printf("\nInstrument isn't capable of refresh rate calibration\n"); --ix; continue; @@ -2063,7 +2142,7 @@ int main(int argc, char *argv[]) { #endif /* SALONEINSTLIB */ if (emiss > 1 || tele > 1) { - if (wXYZ[0] < 0.0) { + if (wXYZ[0] < 0.0) { /* If we haven't save a white ref. yet */ if (XYZ[1] < 10.0) error ("White of XYZ %f %f %f doesn't seem reasonable",XYZ[0], XYZ[1], XYZ[2]); printf("\n Making result XYZ: %f %f %f, D50 Lab: %f %f %f white reference.\n", @@ -2071,6 +2150,13 @@ int main(int argc, char *argv[]) { wXYZ[0] = XYZ[0]; wXYZ[1] = XYZ[1]; wXYZ[2] = XYZ[2]; +#ifndef SALONEINSTLIB + { /* Compute a Chromatic adapation matrix to D50 */ + icmXYZNumber s_wp; + icmAry2XYZ(s_wp, wXYZ); + icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, s_wp, chmat); + } +#endif /* !SALONEINSTLIB */ continue; } if (emiss == 2 || tele == 2) { @@ -2080,12 +2166,14 @@ int main(int argc, char *argv[]) { XYZ[2] = 100.0 * XYZ[2] / wXYZ[1]; } #ifndef SALONEINSTLIB - else { + 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]; +// 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); } /* recompute Lab */ @@ -2174,25 +2262,25 @@ int main(int argc, char *argv[]) { printf(" Apparent flash duration = %f seconds\n",val.duration); if (cap2 & inst2_ambient_mono) { printf(" Ambient = %.1f Lux%s\n", - 3.141592658 * XYZ[1], ambient == 2 ? "-Seconds" : ""); + XYZ[1], ambient == 2 ? "-Seconds" : ""); if (ambient != 2) printf(" Suggested EV @ ISO100 for %.1f Lux incident light = %.1f\n", - 3.141592658 * XYZ[1], - log(3.141592658 * XYZ[1]/2.5)/log(2.0)); + XYZ[1], + log(XYZ[1]/2.5)/log(2.0)); } else { #ifndef SALONEINSTLIB printf(" Ambient = %.1f Lux%s, CCT = %.0fK (Delta E %f)\n", - 3.1415926 * XYZ[1], ambient == 2 ? "-Seconds" : "", + XYZ[1], ambient == 2 ? "-Seconds" : "", cct, cct_de); if (ambient != 2) printf(" Suggested EV @ ISO100 for %.1f Lux incident light = %.1f\n", - 3.141592658 * XYZ[1], - log(3.141592658 * XYZ[1]/2.5)/log(2.0)); + XYZ[1], + log(XYZ[1]/2.5)/log(2.0)); printf(" Closest Planckian temperature = %.0fK (Delta E %f)\n",vct, vct_de); printf(" Closest Daylight temperature = %.0fK (Delta E %f)\n",vdt, vdt_de); #else /* SALONEINSTLIB */ printf(" Ambient = %.1f Lux%s\n", - 3.1415926 * XYZ[1], ambient == 2 ? "-Seconds" : ""); + XYZ[1], ambient == 2 ? "-Seconds" : ""); #endif /* SALONEINSTLIB */ } #ifndef SALONEINSTLIB diff --git a/spectro/spyd2.c b/spectro/spyd2.c index f8f9bac..dc09cea 100644 --- a/spectro/spyd2.c +++ b/spectro/spyd2.c @@ -7,7 +7,7 @@ * Author: Graeme W. Gill * Date: 17/9/2007 * - * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2006 - 2014, Graeme W. Gill * All rights reserved. * * (Based initially on i1disp.c) @@ -26,7 +26,7 @@ The purchaser of a Spyder 2 instrument should have received a copy of this firmware along with their instrument, and should therefore be able to - enable the Argyll driver for this instrument by using the spyd2en utility + 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. ] @@ -1869,7 +1869,8 @@ spyd2_GetAmbientReading( // a1logd(p->log, 4, "spyd2_GetAmbientReading: combined ambient = %f Lux\n",amb); /* Compute the Y value */ - XYZ[1] = amb; /* cd/m^2 ??? - not very accurate, due to */ +// 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. */ XYZ[0] = icmD50.X * XYZ[1]; /* Convert to D50 neutral */ XYZ[2] = icmD50.Z * XYZ[1]; @@ -1889,7 +1890,7 @@ xspect *spyd4_cals = NULL; /* [nocals] Device spectrum */ /* calibration data. */ static inst_code -spyd4_set_cal( +spyd4_set_cal_ix( spyd2 *p, /* Object */ int ix /* Selection, 0 .. spyd4_nocals-1 */ ) { @@ -1907,8 +1908,14 @@ spyd4_set_cal( /* default calibration selections, to be faithful to the Manufacturers */ /* intentions. */ - if (standardObserver(oc, icxOT_CIE_1931_2)) { - return spyd2_interp_code((inst *)p, SPYD2_DISP_SEL_RANGE) ; + if (p->obType == icxOT_custom) { + oc[0] = &p->custObserver[0]; + oc[1] = &p->custObserver[1]; + oc[2] = &p->custObserver[2]; + } else { + if (standardObserver(oc, p->obType)) { + return spyd2_interp_code((inst *)p, SPYD2_DISP_SEL_RANGE) ; + } } /* We compute X,Y & Z independently. */ @@ -2163,7 +2170,6 @@ spyd4_comp_calmat( /* Copy the matrix into place */ for (i = 0; i < 7; i++) { for (j = 0; j < 3; j++) { -//calm[i][j] = 0.5; p->cal_A[1][j][2+i] = calm[i][j]; } } @@ -2236,6 +2242,104 @@ spyd4_comp_calmat( } +/* Preset the calibration to a spectral sample type. */ +/* ccmat[][] is set to unity */ +static inst_code +spyd2_set_speccal( + spyd2 *p, + xspect *samples, /* Array of nsamp spectral samples, or RGBcmfs for MIbLSr */ + int nsamp /* Number of samples */ +) { + int i; + + /* Save a the spectral samples to the current state */ + if (p->samples != NULL) + free(p->samples); + p->nsamp = 0; + if ((p->samples = (xspect *)calloc(sizeof(xspect), nsamp)) == NULL) { + a1loge(p->log, inst_internal_error, "spyd2_set_speccal: malloc failed\n"); + return inst_internal_error; + } + for (i = 0; i < nsamp; i++ ) + p->samples[i] = samples[i]; /* Struct copy */ + p->nsamp = nsamp; + + p->icx = (99 << 1) | 1; /* Out of range index */ + + icmSetUnity3x3(p->ccmat); /* No matrix */ + + return inst_ok; +} + + +/* 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) { + 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; + } + icmCpy3x3(p->ccmat, mtx); + } + + return inst_ok; +} + + +/* Set the calibration to the currently preset type */ +static inst_code +spyd2_set_cal(spyd2 *p) { + inst_code ev = inst_ok; + + if (p->samples != NULL && p->nsamp > 0) { + + /* Create matrix for specified samples */ + if ((ev = spyd4_comp_calmat(p, p->obType, p->custObserver, p->samples, p->nsamp)) + != inst_ok) { + a1logd(p->log, 1, "spyd2_set_cal: comp_calmat ccss failed with rv = 0x%x\n",ev); + return ev; + } + + p->icx = (99 << 1) | 1; /* Out of range index */ + icmSetUnity3x3(p->ccmat); /* to be sure to be sure... */ + + } else { + + if (p->hwver >= 7) { + if ((p->icx >> 1) > spyd4_nocals) + return inst_unsupported; + + /* Create the calibration matrix from internal spectral data */ + if ((ev = spyd4_set_cal_ix(p, p->icx >> 1)) != inst_ok) + return ev; + } + + } + + if (p->log->debug >= 4) { + int i; + if (p->hwver >= 7) { + a1logd(p->log,4,"Spectral calibration matrix:\n"); + for (i = 0; i < 7; i++) { + a1logd(p->log,4," %f %f %f\n", + 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,"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,"\n"); + } + + return inst_ok; +} + /* ------------------------------------------------------------ */ /* Read all the relevant register values */ @@ -2472,7 +2576,7 @@ spyd2_download_pld( a1logd(p->log, 2, "spyd2_download_pld: called\n"); if (*spyder2_pld_size == 0 || *spyder2_pld_size == 0x11223344) { - a1logd(p->log, 1, "spyd2_download_pld: No PLD pattern available! (have you run spyd2en ?)\n"); + 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) ; } @@ -2652,8 +2756,8 @@ spyd2_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { a1logd(p->log, 2, "spyd2_init_coms: about to init coms\n"); if (p->icom->port_type(p->icom) != icomt_usb) { - a1logd(p->log, 1, "spyd2_init_coms: coms is not the right type!\n"); - return spyd2_interp_code((inst *)p, SPYD2_UNKNOWN_MODEL); + a1logd(p->log, 1, "spyd2_init_coms: wrong communications type for device!\n"); + return inst_coms_fail; } a1logd(p->log, 2, "spyd2_init_coms: about to init USB\n"); @@ -2864,8 +2968,9 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ } else { /* Check for abort */ if (p->uicallback != NULL - && (ev = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_trig) + && (ev = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) { return ev; /* Abort */ + } } if (IMODETST(p->mode, inst_mode_emis_ambient)) { @@ -2900,6 +3005,7 @@ instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ val->sp.spec_n = 0; val->duration = 0.0; + if (user_trig) return inst_user_trig; return ev; @@ -2913,23 +3019,17 @@ inst *pp, double mtx[3][3] ) { spyd2 *p = (spyd2 *)pp; + inst_code ev = inst_ok; if (!p->gotcoms) return inst_no_coms; if (!p->inited) return inst_no_init; - 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"); - inst_wrong_setup; - } - icmCpy3x3(p->ccmat, mtx); - } - - return inst_ok; + if ((ev = spyd2_set_matcal(p, mtx)) != inst_ok) + return ev; + + return spyd2_set_cal(p); } /* Use a Colorimeter Calibration Spectral Set to set the */ @@ -2955,11 +3055,12 @@ int no_sets if ((ev = set_default_disp_type(p)) != inst_ok) return ev; } else { - /* Use given spectral samples */ - if ((ev = spyd4_comp_calmat(p, p->obType, p->custObserver, sets, no_sets)) != inst_ok) - return ev; - p->icx = (99 << 1) | 1; /* Out of range index */ + if ((ev = spyd2_set_speccal(p, sets, no_sets)) != inst_ok) + return ev; + + ev = spyd2_set_cal(p); } + return ev; } @@ -3031,8 +3132,8 @@ char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */ if ((*calt & inst_calt_ref_freq) && p->refrmode != 0) { - if (*calc != inst_calc_emis_white) { - *calc = inst_calc_emis_white; + if (*calc != inst_calc_emis_80pc) { + *calc = inst_calc_emis_80pc; return inst_cal_setup; } @@ -3120,7 +3221,7 @@ spyd2_interp_error(inst *pp, int ec) { case SPYD2_BAD_EE_SIZE: return "Serial EEProm read size > 256"; case SPYD2_NO_PLD_PATTERN: - return "No PLD firmware pattern is available (have you run spyd2en ?)"; + return "No PLD firmware pattern is available (have you run oeminst ?)"; case SPYD2_NO_COMS: return "Communications hasn't been established"; case SPYD2_NOT_INITED: @@ -3206,6 +3307,8 @@ spyd2_del(inst *pp) { if (p->icom != NULL) p->icom->del(p->icom); inst_del_disptype_list(p->dtlist, p->ndtlist); + if (p->samples != NULL) + free(p->samples); free(p); } @@ -3235,7 +3338,8 @@ inst3_capability *pcap3) { cap2 |= inst2_prog_trig | inst2_user_trig | inst2_ccmx - | inst2_refresh_rate + | inst2_get_refresh_rate + | inst2_set_refresh_rate | inst2_emis_refr_meas ; @@ -3386,7 +3490,7 @@ inst_disptypesel spyd4_disptypesel[8] = { { inst_dtflags_default, 1, - "n", + "nl", "Generic Non-Refresh Display", 0, 1 @@ -3394,7 +3498,7 @@ inst_disptypesel spyd4_disptypesel[8] = { { inst_dtflags_none, /* flags */ 2, /* cbid */ - "r", /* sel */ + "rc", /* sel */ "Generic Refresh Display", /* desc */ 1, /* refr */ 1 /* ix = hw bit + spec table << 1 */ @@ -3512,35 +3616,22 @@ static inst_code set_disp_type(spyd2 *p, inst_disptypesel *dentry) { } p->refrmode = refrmode; - if (dentry->flags & inst_dtflags_ccss) { + if (dentry->flags & inst_dtflags_ccss) { /* Spectral sample */ - if ((ev = spyd4_comp_calmat(p, p->obType, p->custObserver, dentry->sets, dentry->no_sets)) - != inst_ok) { - a1logd(p->log, 1, "spyd4_set_disp_type: comp_calmat ccss failed with rv = 0x%x\n",ev); + if ((ev = spyd2_set_speccal(p, dentry->sets, dentry->no_sets)) != inst_ok) return ev; - } - p->icx = (99 << 1) | 1; /* Out of range index */ - icmSetUnity3x3(p->ccmat); - } else { - - if (p->hwver >= 7) { - if ((p->icx >> 1) > spyd4_nocals) - return inst_unsupported; - - /* Create the calibration matrix */ - if ((ev = spyd4_set_cal(p, p->icx >> 1)) != inst_ok) - return ev; - } + } else { /* Matrix */ if (dentry->flags & inst_dtflags_ccmx) { - icmCpy3x3(p->ccmat, dentry->mat); + if ((ev = spyd2_set_matcal(p, dentry->mat)) != inst_ok) + return ev; } else { - icmSetUnity3x3(p->ccmat); + if ((ev = spyd2_set_matcal(p, NULL)) != inst_ok) /* Noop */ + return ev; } } - - return inst_ok; + return spyd2_set_cal(p); } @@ -3662,7 +3753,7 @@ spyd2_get_set_opt(inst *pp, inst_opt_type m, ...) { p->custObserver[2] = custObserver[2]; } - return inst_ok; + return spyd2_set_cal(p); /* Recompute calibration */ } /* Operate the LED */ diff --git a/spectro/spyd2.h b/spectro/spyd2.h index d3a208c..2c34d88 100644 --- a/spectro/spyd2.h +++ b/spectro/spyd2.h @@ -147,9 +147,11 @@ struct _spyd2 { double refrate; /* Current refresh rate. Set to DEFREFR if not measurable */ int refrvalid; /* nz if refrate was measured */ double gain; /* hwver == 5 gain value (default 4) */ - double ccmat[3][3]; /* Colorimeter correction matrix */ icxObserverType obType; /* ccss observer to use */ xspect custObserver[3]; /* Custom ccss observer to use */ + double ccmat[3][3]; /* Colorimeter correction matrix, unity if none */ + xspect *samples; /* Copy of current calibration spectral samples, NULL if none */ + int nsamp; /* Number of samples, 0 if none */ int prevraw[8]; /* Previous raw reading values */ int prevrawinv; /* Previous raw readings invalid flag - after an abort */ diff --git a/spectro/spyd2PLD.h b/spectro/spyd2PLD.h index 4a71cd8..19d2a48 100644 --- a/spectro/spyd2PLD.h +++ b/spectro/spyd2PLD.h @@ -2,7 +2,7 @@ /* Spyder 2 Colorimeter Xilinx XCS05XL firmware pattern */ /* needs to be transfered here for the instrument to work. */ -/* The end user should see the spyd2en utility.*/ +/* The end user should see the oeminst utility.*/ static unsigned int pld_size = 0x11223344; /* Endian indicator */ static unsigned int pld_space = 6824; diff --git a/spectro/spyd2setup.h b/spectro/spyd2setup.h index def83b6..60375dd 100644 --- a/spectro/spyd2setup.h +++ b/spectro/spyd2setup.h @@ -63,7 +63,7 @@ int setup_spyd2() { 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, "etup_spyd2: failed to find PLD file\n"); + a1logd(g_log, 1, "setup_spyd2: failed to find PLD file\n"); break; } diff --git a/spectro/ss_imp.c b/spectro/ss_imp.c index b4c5d8a..359d4c4 100644 --- a/spectro/ss_imp.c +++ b/spectro/ss_imp.c @@ -512,7 +512,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, p->_rbuf, SS_MAX_RD_SIZE, "\n", 1, tmo)) != 0) { p->snerr = icoms2ss_err(se); return; } diff --git a/spectro/strange.cal b/spectro/strange.cal index 23c1e87..c7f0179 100644 --- a/spectro/strange.cal +++ b/spectro/strange.cal @@ -2,7 +2,7 @@ CAL DESCRIPTOR "Argyll Device Calibration Curves" ORIGINATOR "Argyll synthcal" -CREATED "Sat Mar 09 18:33:22 2013" +CREATED "Fri Jan 31 13:50:43 2014" KEYWORD "DEVICE_CLASS" DEVICE_CLASS "DISPLAY" KEYWORD "COLOR_REP" @@ -16,260 +16,260 @@ END_DATA_FORMAT NUMBER_OF_SETS 256 BEGIN_DATA -0.0000 0.0000 0.0000 0.0000 -3.9216e-003 5.6752e-005 0.011879 0.018606 -7.8431e-003 1.8439e-004 0.020682 0.030226 -0.011765 3.6735e-004 0.028607 0.040147 -0.015686 5.9908e-004 0.036009 0.049103 -0.019608 8.7544e-004 0.043047 0.057404 -0.023529 1.1935e-003 0.049807 0.065218 -0.027451 1.5511e-003 0.056344 0.072650 -0.031373 1.9464e-003 0.062696 0.079768 -0.035294 2.3779e-003 0.068891 0.086623 -0.039216 2.8443e-003 0.074949 0.093253 -0.043137 3.3446e-003 0.080888 0.099687 -0.047059 3.8778e-003 0.086719 0.10595 -0.050980 4.4431e-003 0.092453 0.11205 -0.054902 5.0396e-003 0.098100 0.11802 -0.058824 5.6668e-003 0.10367 0.12386 -0.062745 6.3239e-003 0.10916 0.12958 -0.066667 7.0104e-003 0.11458 0.13520 -0.070588 7.7258e-003 0.11995 0.14072 -0.074510 8.4696e-003 0.12525 0.14615 -0.078431 9.2413e-003 0.13049 0.15149 -0.082353 0.010040 0.13569 0.15675 -0.086275 0.010867 0.14083 0.16194 -0.090196 0.011720 0.14593 0.16706 -0.094118 0.012599 0.15099 0.17211 -0.098039 0.013504 0.15600 0.17710 -0.10196 0.014436 0.16097 0.18203 -0.10588 0.015392 0.16590 0.18690 -0.10980 0.016374 0.17080 0.19172 -0.11373 0.017380 0.17567 0.19649 -0.11765 0.018411 0.18049 0.20121 -0.12157 0.019467 0.18529 0.20588 -0.12549 0.020546 0.19006 0.21051 -0.12941 0.021650 0.19480 0.21509 -0.13333 0.022777 0.19950 0.21963 -0.13725 0.023927 0.20418 0.22414 -0.14118 0.025101 0.20884 0.22860 -0.14510 0.026298 0.21347 0.23303 -0.14902 0.027518 0.21807 0.23742 -0.15294 0.028760 0.22265 0.24177 -0.15686 0.030025 0.22720 0.24610 -0.16078 0.031312 0.23174 0.25039 -0.16471 0.032621 0.23625 0.25465 -0.16863 0.033953 0.24074 0.25888 -0.17255 0.035306 0.24521 0.26308 -0.17647 0.036681 0.24965 0.26725 -0.18039 0.038077 0.25408 0.27139 -0.18431 0.039495 0.25849 0.27551 -0.18824 0.040935 0.26288 0.27960 -0.19216 0.042395 0.26725 0.28366 -0.19608 0.043876 0.27161 0.28770 -0.20000 0.045378 0.27595 0.29172 -0.20392 0.046901 0.28027 0.29571 -0.20784 0.048445 0.28457 0.29968 -0.21176 0.050009 0.28886 0.30363 -0.21569 0.051594 0.29313 0.30755 -0.21961 0.053198 0.29738 0.31146 -0.22353 0.054824 0.30163 0.31534 -0.22745 0.056469 0.30585 0.31920 -0.23137 0.058134 0.31006 0.32304 -0.23529 0.059819 0.31426 0.32687 -0.23922 0.061523 0.31844 0.33067 -0.24314 0.063248 0.32261 0.33446 -0.24706 0.064992 0.32677 0.33822 -0.25098 0.066755 0.33091 0.34197 -0.25490 0.068538 0.33504 0.34570 -0.25882 0.070340 0.33916 0.34942 -0.26275 0.072162 0.34326 0.35312 -0.26667 0.074002 0.34736 0.35680 -0.27059 0.075862 0.35144 0.36046 -0.27451 0.077740 0.35551 0.36411 -0.27843 0.079638 0.35956 0.36774 -0.28235 0.081554 0.36361 0.37136 -0.28627 0.083489 0.36764 0.37496 -0.29020 0.085442 0.37167 0.37855 -0.29412 0.087415 0.37568 0.38213 -0.29804 0.089405 0.37968 0.38569 -0.30196 0.091414 0.38367 0.38923 -0.30588 0.093442 0.38765 0.39276 -0.30980 0.095487 0.39162 0.39628 -0.31373 0.097551 0.39559 0.39979 -0.31765 0.099633 0.39954 0.40328 -0.32157 0.10173 0.40348 0.40676 -0.32549 0.10385 0.40741 0.41022 -0.32941 0.10599 0.41133 0.41368 -0.33333 0.10814 0.41524 0.41712 -0.33725 0.11031 0.41915 0.42055 -0.34118 0.11250 0.42304 0.42396 -0.34510 0.11471 0.42693 0.42737 -0.34902 0.11693 0.43080 0.43076 -0.35294 0.11918 0.43467 0.43414 -0.35686 0.12144 0.43853 0.43752 -0.36078 0.12371 0.44238 0.44088 -0.36471 0.12601 0.44623 0.44422 -0.36863 0.12832 0.45006 0.44756 -0.37255 0.13065 0.45389 0.45089 -0.37647 0.13300 0.45770 0.45421 -0.38039 0.13536 0.46151 0.45751 -0.38431 0.13774 0.46532 0.46081 -0.38824 0.14014 0.46911 0.46410 -0.39216 0.14255 0.47290 0.46737 -0.39608 0.14499 0.47668 0.47064 -0.40000 0.14743 0.48045 0.47390 -0.40392 0.14990 0.48421 0.47715 -0.40784 0.15238 0.48797 0.48038 -0.41176 0.15488 0.49172 0.48361 -0.41569 0.15740 0.49546 0.48683 -0.41961 0.15993 0.49920 0.49004 -0.42353 0.16248 0.50293 0.49324 -0.42745 0.16505 0.50665 0.49644 -0.43137 0.16763 0.51037 0.49962 -0.43529 0.17023 0.51407 0.50279 -0.43922 0.17284 0.51778 0.50596 -0.44314 0.17547 0.52147 0.50912 -0.44706 0.17812 0.52516 0.51227 -0.45098 0.18079 0.52884 0.51541 -0.45490 0.18347 0.53252 0.51854 -0.45882 0.18616 0.53619 0.52167 -0.46275 0.18888 0.53985 0.52479 -0.46667 0.19161 0.54351 0.52789 -0.47059 0.19435 0.54716 0.53100 -0.47451 0.19711 0.55080 0.53409 -0.47843 0.19989 0.55444 0.53718 -0.48235 0.20268 0.55807 0.54025 -0.48627 0.20549 0.56170 0.54332 -0.49020 0.20832 0.56532 0.54639 -0.49412 0.21116 0.56894 0.54944 -0.49804 0.21402 0.57255 0.55249 -0.50196 0.21689 0.57615 0.55553 -0.50588 0.21978 0.57975 0.55857 -0.50980 0.22268 0.58334 0.56160 -0.51373 0.22560 0.58693 0.56462 -0.51765 0.22854 0.59051 0.56763 -0.52157 0.23149 0.59409 0.57064 -0.52549 0.23445 0.59766 0.57364 -0.52941 0.23744 0.60122 0.57663 -0.53333 0.24043 0.60478 0.57962 -0.53725 0.24345 0.60834 0.58260 -0.54118 0.24648 0.61189 0.58557 -0.54510 0.24952 0.61543 0.58854 -0.54902 0.25258 0.61897 0.59150 -0.55294 0.25565 0.62251 0.59445 -0.55686 0.25874 0.62603 0.59740 -0.56078 0.26185 0.62956 0.60034 -0.56471 0.26497 0.63308 0.60328 -0.56863 0.26811 0.63659 0.60621 -0.57255 0.27126 0.64010 0.60913 -0.57647 0.27442 0.64361 0.61205 -0.58039 0.27760 0.64711 0.61496 -0.58431 0.28080 0.65060 0.61787 -0.58824 0.28401 0.65410 0.62077 -0.59216 0.28724 0.65758 0.62366 -0.59608 0.29048 0.66106 0.62655 -0.60000 0.29373 0.66454 0.62943 -0.60392 0.29701 0.66801 0.63231 -0.60784 0.30029 0.67148 0.63518 -0.61176 0.30359 0.67494 0.63805 -0.61569 0.30691 0.67840 0.64091 -0.61961 0.31024 0.68186 0.64376 -0.62353 0.31358 0.68531 0.64661 -0.62745 0.31694 0.68875 0.64945 -0.63137 0.32032 0.69220 0.65229 -0.63529 0.32371 0.69563 0.65513 -0.63922 0.32711 0.69907 0.65795 -0.64314 0.33053 0.70249 0.66078 -0.64706 0.33397 0.70592 0.66360 -0.65098 0.33741 0.70934 0.66641 -0.65490 0.34088 0.71276 0.66922 -0.65882 0.34435 0.71617 0.67202 -0.66275 0.34785 0.71958 0.67482 -0.66667 0.35135 0.72298 0.67761 -0.67059 0.35487 0.72638 0.68040 -0.67451 0.35841 0.72978 0.68318 -0.67843 0.36196 0.73317 0.68596 -0.68235 0.36552 0.73656 0.68873 -0.68627 0.36910 0.73994 0.69150 -0.69020 0.37269 0.74332 0.69426 -0.69412 0.37630 0.74670 0.69702 -0.69804 0.37992 0.75007 0.69977 -0.70196 0.38356 0.75344 0.70252 -0.70588 0.38721 0.75681 0.70527 -0.70980 0.39087 0.76017 0.70801 -0.71373 0.39455 0.76353 0.71075 -0.71765 0.39824 0.76688 0.71348 -0.72157 0.40195 0.77023 0.71620 -0.72549 0.40567 0.77358 0.71893 -0.72941 0.40940 0.77692 0.72164 -0.73333 0.41315 0.78026 0.72436 -0.73725 0.41692 0.78360 0.72707 -0.74118 0.42069 0.78693 0.72977 -0.74510 0.42448 0.79026 0.73247 -0.74902 0.42829 0.79359 0.73517 -0.75294 0.43211 0.79691 0.73786 -0.75686 0.43594 0.80023 0.74055 -0.76078 0.43979 0.80354 0.74323 -0.76471 0.44365 0.80686 0.74591 -0.76863 0.44752 0.81016 0.74859 -0.77255 0.45141 0.81347 0.75126 -0.77647 0.45531 0.81677 0.75393 -0.78039 0.45923 0.82007 0.75659 -0.78431 0.46316 0.82336 0.75925 -0.78824 0.46710 0.82666 0.76191 -0.79216 0.47106 0.82994 0.76456 -0.79608 0.47503 0.83323 0.76721 -0.80000 0.47902 0.83651 0.76985 -0.80392 0.48302 0.83979 0.77249 -0.80784 0.48703 0.84307 0.77512 -0.81176 0.49105 0.84634 0.77776 -0.81569 0.49509 0.84961 0.78038 -0.81961 0.49915 0.85287 0.78301 -0.82353 0.50321 0.85614 0.78563 -0.82745 0.50729 0.85940 0.78825 -0.83137 0.51139 0.86265 0.79086 -0.83529 0.51550 0.86591 0.79347 -0.83922 0.51962 0.86916 0.79608 -0.84314 0.52375 0.87241 0.79868 -0.84706 0.52790 0.87565 0.80128 -0.85098 0.53206 0.87889 0.80387 -0.85490 0.53624 0.88213 0.80646 -0.85882 0.54042 0.88537 0.80905 -0.86275 0.54463 0.88860 0.81163 -0.86667 0.54884 0.89183 0.81421 -0.87059 0.55307 0.89506 0.81679 -0.87451 0.55731 0.89828 0.81937 -0.87843 0.56157 0.90150 0.82194 -0.88235 0.56584 0.90472 0.82450 -0.88627 0.57012 0.90793 0.82707 -0.89020 0.57441 0.91115 0.82963 -0.89412 0.57872 0.91436 0.83218 -0.89804 0.58304 0.91756 0.83474 -0.90196 0.58738 0.92077 0.83729 -0.90588 0.59173 0.92397 0.83983 -0.90980 0.59609 0.92717 0.84238 -0.91373 0.60046 0.93036 0.84492 -0.91765 0.60485 0.93356 0.84745 -0.92157 0.60925 0.93675 0.84999 -0.92549 0.61366 0.93993 0.85252 -0.92941 0.61809 0.94312 0.85504 -0.93333 0.62253 0.94630 0.85757 -0.93725 0.62698 0.94948 0.86009 -0.94118 0.63145 0.95266 0.86261 -0.94510 0.63593 0.95583 0.86512 -0.94902 0.64042 0.95900 0.86763 -0.95294 0.64493 0.96217 0.87014 -0.95686 0.64945 0.96534 0.87264 -0.96078 0.65398 0.96850 0.87515 -0.96471 0.65852 0.97166 0.87765 -0.96863 0.66308 0.97482 0.88014 -0.97255 0.66765 0.97798 0.88263 -0.97647 0.67223 0.98113 0.88512 -0.98039 0.67683 0.98428 0.88761 -0.98431 0.68144 0.98743 0.89009 -0.98824 0.68606 0.99058 0.89258 -0.99216 0.69069 0.99372 0.89505 -0.99608 0.69534 0.99686 0.89753 -1.0000 0.70000 1.0000 0.90000 +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/synthread.c b/spectro/synthread.c index c4312f1..3a23c31 100644 --- a/spectro/synthread.c +++ b/spectro/synthread.c @@ -451,6 +451,9 @@ printf("~1 omax = %f %f %f\n", md.omax[0], md.omax[1], md.omax[2]); if ((ti = icg->find_kword(icg, 0, "FULL_SPREAD_PATCHES")) >= 0) ocg->add_kword(ocg, 0, "FULL_SPREAD_PATCHES",icg->t[0].kdata[ti], NULL); + if ((ti = icg->find_kword(icg, 0, "DARK_REGION_EMPHASIS")) >= 0) + ocg->add_kword(ocg, 0, "DARK_REGION_EMPHASIS",icg->t[0].kdata[ti], NULL); + /* Fields we want */ ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t); diff --git a/spectro/usbio.c b/spectro/usbio.c index 3db1535..f8a0529 100644 --- a/spectro/usbio.c +++ b/spectro/usbio.c @@ -14,7 +14,7 @@ * see the License2.txt file for licencing details. */ -/* These routines supliement the class code in ntio.c and unixio.c */ +/* These routines supliement the class code in icoms_nt.c and icoms_ux.c */ /* with common and USB specific routines */ #include <stdio.h> @@ -51,35 +51,42 @@ int in_usb_rw = 0; void usb_init_cancel(usb_cancelt *p) { amutex_init(p->cmtx); + amutex_init(p->cond); -#ifdef NATIVE_USB p->hcancel = NULL; -#else -# ifdef USE_LIBUSB1 - p->hcancel = NULL; -# else - p->hcancel = (void *)-1; -# endif -#endif } void usb_uninit_cancel(usb_cancelt *p) { amutex_del(p->cmtx); + amutex_del(p->cond); } -/* Used by implementation */ -static void usb_lock_cancel(usb_cancelt *p) { +/* Used by caller of icoms to re-init for wait_io */ +/* Must be called before icoms_usb_wait_io() */ +void usb_reinit_cancel(usb_cancelt *p) { + amutex_lock(p->cmtx); -} -static void usb_unlock_cancel(usb_cancelt *p) { + p->hcancel = NULL; + p->state = 0; + amutex_lock(p->cond); /* Block until IO is started */ + amutex_unlock(p->cmtx); } +/* Wait for the given transaction to be pending or complete. */ +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 */ + return ICOM_OK; +} + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Include the USB implementation dependent function implementations */ -#ifdef NATIVE_USB # ifdef NT # include "usbio_nt.c" # endif @@ -87,11 +94,12 @@ static void usb_unlock_cancel(usb_cancelt *p) { # include "usbio_ox.c" # endif # if defined(UNIX_X11) -# include "usbio_lx.c" +# if defined(__FreeBSD__) +# include "usbio_bsd.c" +# else +# include "usbio_lx.c" +# endif # endif -#else /* Using libusb */ -# include "usbio_lusb.c" -#endif /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* I/O routines supported by icoms - uses platform independent */ @@ -415,19 +423,20 @@ double tout) /* Read characters into the buffer */ /* Return string will be terminated with a nul */ -/* Read only in paket sized chunks, and retry if */ +/* Read only in packet sized chunks, and retry if */ /* the bytes requested aren'r 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 characer */ -int ntc, /* Number of terminating characters */ +char *tc, /* Terminating characers, NULL if none */ +int ntc, /* Number of terminating characters needed to terminate */ double tout) /* Time out in seconds */ { int j, rbytes; - long toc, i, top; /* Timout count, counter, timeout period */ + long ttop, top; /* Total timeout period, timeout period */ + unsigned int stime, etime; /* Start and end times of USB operation */ char *rrbuf = rbuf; /* Start of return buffer */ int ep = p->rd_ep; /* End point */ icom_usb_trantype type; /* bulk or interrupt */ @@ -463,50 +472,74 @@ double tout) /* Time out in seconds */ return ICOM_SYS; } - for (i = 0; i < bsize; i++) rbuf[i] = 0; + for (j = 0; j < bsize; j++) rbuf[j] = 0; - tout *= 1000.0; /* Timout in msec */ - bsize--; /* Allow space for null */ + bsize -= 1; /* Allow space for null */ + bsize -= p->ms_bytes; /* Allow space for modem status bytes */ - /* Have to do this in one go, because libusb has no way */ - /* of timing out and returning the number of characters read */ - /* up to the timeout, and it looses characters. */ - top = (int)(tout + 0.5); /* Timeout period in msecs */ - toc = (int)(tout/top + 0.5); /* Number of timout periods in timeout */ - if (toc < 1) - toc = 1; + /* 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. */ + + 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: end point 0x%x, read quanta %d\n",p->rd_ep,p->rd_qa); /* Until data is all read, we time out, or the user aborts */ - for (i = toc, j = 0; i > 0 && bsize > 1 && j < ntc ;) { + stime = msec_time(); + top = ttop; + for (j = 0; top > 0 && bsize > 1 && j < ntc ;) { int c, rv; int rsize = p->rd_qa < bsize ? p->rd_qa : bsize; - a1logd(p->log, 8, "icoms_usb_ser_read: attempting to read %d bytes from usb, top = %d, i = %d, j = %d\n",bsize > p->rd_qa ? p->rd_qa : bsize,top,i,j); - /* We read one read quanta at a time (usually 8 bytes), to avoid */ - /* problems with libusb loosing characters whenever it times out. */ + 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); + rv = icoms_usb_transaction(p, NULL, &rbytes, type, (unsigned char)ep, (unsigned char *)rbuf, rsize, top); - if (rv != 0 && rv != ICOM_SHORT) { - a1logd(p->log, 8, "icoms_usb_ser_read: read failed with 0x%x, rbuf = '%s'\n",rv,icoms_fix(rrbuf)); - if (rv != ICOM_TO) { - retrv |= rv; - break; + etime = msec_time(); + + if (rbytes > 0) { /* Account for bytes read */ + + /* Account for modem status bytes. Modem bytes are per usb read. */ + 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); } - i--; /* Timeout */ - } else { /* Account for bytes read */ - a1logd(p->log, 8, "icoms_usb_ser_read: read read %d bytes, rbuf = '%s'\n",rbytes,icoms_fix(rrbuf)); - i = toc; + + a1logd(p->log, 8, "icoms_usb_ser_read: read %d bytes, rbuf = '%s'\n",rbytes,icoms_fix(rrbuf)); + bsize -= rbytes; - while(rbytes) { /* Count termination characters */ - if (*rbuf++ == tc) - j++; - rbytes--; + if (tc != NULL) { + while(rbytes--) { /* Count termination characters */ + char ch = *rbuf++, *tcp = tc; + + while(*tcp != '\000') { + if (ch == *tcp) + j++; + tcp++; + } + } + } else { + rbuf += rbytes; } } - } - if (i <= 0) /* Must have timed out */ - retrv |= ICOM_TO; + /* Deal with any errors */ + if (rv != ICOM_OK && rv != ICOM_SHORT) { + a1logd(p->log, 8, "icoms_usb_ser_read: read failed with 0x%x, rbuf = '%s'\n",rv,icoms_fix(rrbuf)); + retrv |= rv; + break; + } + + 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'; @@ -533,7 +566,8 @@ char **pnames /* List of process names to try and kill before opening */ ) { a1logd(p->log, 8, "icoms_set_usb_port: About to set usb port characteristics\n"); - if (p->port_type(p) == icomt_usb) { + if (p->port_type(p) == icomt_usb + || p->port_type(p) == icomt_usbserial) { int rv; if (p->is_open) @@ -546,21 +580,12 @@ char **pnames /* List of process names to try and kill before opening */ p->write = icoms_usb_ser_write; p->read = icoms_usb_ser_read; + } else { + a1logd(p->log, 8, "icoms_set_usb_port: Not a USB port!\n"); + return ICOM_NOTS; } a1logd(p->log, 6, "icoms_set_usb_port: usb port characteristics set ok\n"); -#ifndef NATIVE_USB - /* libusb doesn't have any facility for re-directing its */ - /* debug messages. Since we're moving away from it, */ - /* ignore the problem. */ - if (p->log->debug >= 8) { /* Could this go inside usb_open_port ? */ -# ifdef USE_LIBUSB1 - libusb_set_debug(NULL, p->log->debug); -# else - usb_set_debug(p->debug); -# endif - } -#endif /* NATIVE_USB */ return ICOM_OK; } @@ -575,6 +600,7 @@ icoms *p p->usb_control = icoms_usb_control; p->usb_read = icoms_usb_rw; p->usb_write = icoms_usb_rw; + p->usb_wait_io = icoms_usb_wait_io; p->usb_cancel_io = icoms_usb_cancel_io; p->usb_resetep = icoms_usb_resetep; p->usb_clearhalt = icoms_usb_clearhalt; @@ -582,4 +608,5 @@ icoms *p /* ---------------------------------------------------------------------------------*/ + #endif /* ENABLE_USB */ diff --git a/spectro/usbio.h b/spectro/usbio.h index 5c982ae..1181801 100644 --- a/spectro/usbio.h +++ b/spectro/usbio.h @@ -23,8 +23,6 @@ #endif -#ifdef NATIVE_USB - /* Standard USB protocol defines */ # include "iusb.h" @@ -131,6 +129,22 @@ struct usb_idevice { # if defined(UNIX) && !defined(__APPLE__) +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + +/* BSD USB context */ +struct usb_idevice { + /* icompath stuff: */ + char *dpath; /* Device path */ + int nconfig; /* Number of configurations */ + int config; /* This config (always 1) */ + int nifce; /* Number of interfaces */ + usb_ep ep[32]; /* Information about each end point for general usb i/o */ + /* Stuff setup when device is open: */ + int fd; /* Device file descriptor */ +}; + +# else + /* Linux USB context */ struct usb_idevice { /* icompath stuff: */ @@ -150,43 +164,11 @@ struct usb_idevice { pthread_mutex_t lock; /* Protect reqs list */ struct _usbio_req *reqs; /* linked list of current reqs */ }; - -# endif /* Linux */ - -#else /* !NATIVE_USB - Using libusb */ - -# ifdef USE_LIBUSB1 - -# include "libusb.h" -# define usb_idevice libusb_device -# define IUSB_ENDPOINT_DIR_MASK LIBUSB_ENDPOINT_DIR_MASK -# define IUSB_ENDPOINT_IN LIBUSB_ENDPOINT_IN -# define IUSB_ENDPOINT_OUT LIBUSB_ENDPOINT_OUT -# define IUSB_REQ_RECIP_DEVICE LIBUSB_RECIPIENT_DEVICE -# define IUSB_REQ_RECIP_INTERFACE LIBUSB_RECIPIENT_INTERFACE -# define IUSB_REQ_RECIP_ENDPOINT LIBUSB_RECIPIENT_ENDPOINT -# define IUSB_REQ_TYPE_STANDARD LIBUSB_REQUEST_TYPE_STANDARD -# define IUSB_REQ_TYPE_CLASS LIBUSB_REQUEST_TYPE_CLASS -# define IUSB_REQ_TYPE_VENDOR LIBUSB_REQUEST_TYPE_VENDOR -# define IUSB_ENDPOINT_TYPE_MASK LIBUSB_TRANSFER_TYPE_MASK - -# else - -# include "usb.h" -# define usb_idevice usb_device -# define IUSB_ENDPOINT_DIR_MASK USB_ENDPOINT_DIR_MASK -# define IUSB_ENDPOINT_IN USB_ENDPOINT_IN -# define IUSB_ENDPOINT_OUT USB_ENDPOINT_OUT -# define IUSB_REQ_RECIP_DEVICE USB_RECIP_DEVICE -# define IUSB_REQ_RECIP_INTERFACE USB_RECIP_INTERFACE -# define IUSB_REQ_RECIP_ENDPOINT USB_RECIP_ENDPOINT -# define IUSB_REQ_TYPE_STANDARD USB_TYPE_STANDARD -# define IUSB_REQ_TYPE_CLASS USB_TYPE_CLASS -# define IUSB_REQ_TYPE_VENDOR USB_TYPE_VENDOR -# define IUSB_ENDPOINT_TYPE_MASK USB_ENDPOINT_TYPE_MASK - -# endif -#endif + +# endif /* Linux */ + +# endif /* UNIX */ + /* - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -202,6 +184,8 @@ typedef enum { /* Cancelation token. */ struct _usb_cancelt { amutex cmtx; + int state; /* 0 = init, 1 = pending, 2 = complete */ + amutex cond; /* Wait for state 0->1 sync. mutex */ void *hcancel; /* Pointer to implementation cancel handle */ }; @@ -237,6 +221,9 @@ void usb_install_signal_handlers(icoms *p); /* (used inside usb_close_port(), hid_close_port() */ void usb_delete_from_cleanup_list(icoms *p); +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + #ifdef __cplusplus } #endif diff --git a/spectro/usbio_bsd.c b/spectro/usbio_bsd.c new file mode 100644 index 0000000..7649caf --- /dev/null +++ b/spectro/usbio_bsd.c @@ -0,0 +1,764 @@ + +/* General USB I/O support, BSD native implementation. */ +/* This file is conditionaly #included into usbio.c */ + +/* + * Argyll Color Correction System + * + * Author: Graeme W. Gill + * Date: 2006/22/4 + * + * 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 driver is incomplete and non-functional !!!! + + BSD uses fd per end point, so simplifies things. + + No clear ep or abort i/o though, so we could try clear halt, + or close fd and see if that works in aborting transaction ? + + Posix aio would probably work, but it's not loaded by default :-( + + Could use libusb20 API, but not backwards or cross compatible, + and is very likely to be buggy ? + + */ + +#include <unistd.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <glob.h> +#include <sys/ioctl.h> +#if defined(__FreeBSD__) +# include <dev/usb/usb_ioctl.h> /* Not sure what's going on with FreeBSD... */ +#else +# include <dev/usb/usb.h> /* The usual include for BSD */ +#endif + +/* select() defined, but not poll(), so emulate poll() */ +#if defined(FD_CLR) && !defined(POLLIN) +#include "pollem.h" +#define poll_x pollem +#else +#include <sys/poll.h> /* Else assume poll() is native */ +#define poll_x poll +#endif + +/* Add paths to USB connected instruments */ +/* Return an icom error */ +int usb_get_paths( +icompaths *p +) { + int i, j; + char *paths[] = { +#if defined(__FreeBSD__) + "/dev/usb/[0-9]*.*.0", /* FreeBSD >= 8 */ + "/dev/ugen[0-9]*", /* FreeBSD < 8, but no .E */ +#else + "/dev/ugen/[0-9]*.00", /* NetBSD, OpenBSD */ +#endif + NULL + }; + int vid, pid; + int nconfig = 0, nep = 0; + char *dpath; + instType itype; + struct usb_idevice *usbd = NULL; + + a1logd(p->log, 6, "usb_get_paths: about to look through usb devices:\n"); + + /* See what device names to look for */ + for (j = 0; ; j++) { + glob_t g; + int fd; + struct usb_device_info di; + int rv, found = 0; + + if (paths[j] == NULL) + break; + + memset(&g, 0, sizeof(g)); + + if (glob(paths[j], GLOB_NOSORT, NULL, &g) != 0) { + continue; + } + + /* For all the nodes found by the glob */ + for (i = 0; i < g.gl_pathc; i++) { + +#if defined(__FreeBSD__) + /* Skip anything with an end point number */ + if (j == 1 && strchr(g.gl_pathv[i], '.') != NULL) + continue; +#endif + if ((fd = open(g.gl_pathv[i], O_RDONLY)) < 0) + continue; + + if (ioctl(fd, USB_GET_DEVICEINFO, &di) < 0) + continue; + + vid = di.udi_vendorNo; + pid = di.udi_productNo; + + a1logd(p->log, 6, "usb_get_paths: checking vid 0x%04x, pid 0x%04x\n",vid,pid); + + /* Do a preliminary match */ + if ((itype = inst_usb_match(vid, pid, 0)) == instUnknown) { + a1logd(p->log, 6 , "usb_get_paths: instrument not reconized\n"); + continue; + } + + // ~~99 need to check number of end points ~~~ + // ~~99 and number of configs + nconfig = 1; + +//USB_GET_DEVICEINFO struct usb_device_info +//USB_GET_DEVICE_DESC struct usb_device_descriptor + + /* Allocate an idevice so that we can fill in the end point information */ + if ((usbd = (struct usb_idevice *) calloc(sizeof(struct usb_idevice), 1)) == NULL) { + a1loge(p->log, ICOM_SYS, "icoms: calloc failed!\n"); + close(fd); + globfree(&g); + return ICOM_SYS; + } + + usbd->nconfig = nconfig; + + /* Found a known instrument ? */ + if ((itype = inst_usb_match(vid, pid, nep)) != instUnknown) { + char pname[400], *cp; + + a1logd(p->log, 2, "usb_get_paths: found instrument vid 0x%04x, pid 0x%04x\n",vid,pid); + + /* Create the base device path */ + dpath = g.gl_pathv[i]; +#if defined(__FreeBSD__) + if (j == 0) { /* Remove .0 */ + if ((cp = strrchr(dpath, '.')) != NULL + && cp[1] == '0' && cp[2] == '\000') + *cp = '\000'; + } +#else + /* Remove .00 */ + if ((cp = strrchr(dpath, '.')) != NULL + && cp[1] == '0' && cp[2] == '0' && cp[3] == '\000') + *cp = '\000'; +#endif + if ((usbd->dpath = strdup(dpath)) == NULL) { + a1loge(p->log, ICOM_SYS, "usb_get_paths: strdup path failed!\n"); + free(usbd); + close(fd); + globfree(&g); + return ICOM_SYS; + } + + /* Create a path/identification */ + sprintf(pname,"%s (%s)", dpath, inst_name(itype)); + if ((usbd->dpath = strdup(dpath)) == NULL) { + a1loge(p->log, ICOM_SYS, "usb_get_paths: strdup path failed!\n"); + free(usbd); + close(fd); + globfree(&g); + return ICOM_SYS; + } + + /* Add the path and ep info to the list */ + if ((rv = p->add_usb(p, pname, vid, pid, nep, usbd, itype)) != ICOM_OK) + return rv; + + + } else { + free(usbd); + } + + close(fd); + } + globfree(&g); + + /* Only try one glob string */ + break; + } + + a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->npaths); + return ICOM_OK; +} + +/* Copy usb_idevice contents from icompaths to icom */ +/* return icom error */ +int usb_copy_usb_idevice(icoms *d, icompath *s) { + int i; + if (s->usbd == NULL) { + d->usbd = NULL; + return ICOM_OK; + } + if ((d->usbd = calloc(sizeof(struct usb_idevice), 1)) == NULL) { + a1loge(d->log, ICOM_SYS, "usb_copy_usb_idevice: malloc\n"); + return ICOM_SYS; + } + if ((d->usbd->dpath = strdup(s->usbd->dpath)) == NULL) { + a1loge(d->log, ICOM_SYS, "usb_copy_usb_idevice: malloc\n"); + return ICOM_SYS; + } + /* Copy the ep info */ + d->nconfig = s->usbd->nconfig; + d->nifce = s->usbd->nifce; + d->config = s->usbd->config; + for (i = 0; i < 32; i++) + d->ep[i] = s->usbd->ep[i]; /* Struct copy */ + return ICOM_OK; +} + +/* Cleanup and then free a usb dev entry */ +void usb_del_usb_idevice(struct usb_idevice *usbd) { + + if (usbd == NULL) + return; + + if (usbd->dpath != NULL) + free(usbd->dpath); + free(usbd); +} + +/* Cleanup any USB specific icoms state */ +void usb_del_usb(icoms *p) { + + usb_del_usb_idevice(p->usbd); +} + +/* Close an open USB port */ +/* If we don't do this, the port and/or the device may be left in an unusable state. */ +void usb_close_port(icoms *p) { + + a1logd(p->log, 6, "usb_close_port: called\n"); + +#ifdef NEVER // ~~99 + if (p->is_open && p->usbd != NULL) { + struct usbdevfs_urb urb; + unsigned char buf[8+IUSB_DESC_TYPE_DEVICE_SIZE]; + int iface, rv; + + /* Release all the interfaces */ + for (iface = 0; iface < p->nifce; iface++) + ioctl(p->usbd->fd, USBDEVFS_RELEASEINTERFACE, &iface); + + /* Workaround for some bugs - reset device on close */ + if (p->uflags & icomuf_reset_before_close) { + if ((rv = ioctl(p->usbd->fd, USBDEVFS_RESET, NULL)) != 0) { + a1logd(p->log, 1, "usb_close_port: reset returned %d\n",rv); + } + } + + if (p->usbd->running) { /* If reaper is still running */ + unsigned char buf[1] = { 0 }; + + a1logd(p->log, 6, "usb_close_port: waking reaper thread to trigger exit\n"); + p->usbd->shutdown = 1; + + if (write(p->usbd->sd_pipe[1], buf, 1) < 1) { + a1logd(p->log, 1, "usb_close_port: writing to sd_pipe failed with '%s'\n", strerror(errno)); + /* Hmm. We could be in trouble ? */ + } + } + a1logd(p->log, 6, "usb_close_port: waiting for reaper thread\n"); + pthread_join(p->usbd->thread, NULL); /* Wait for urb reaper thread to exit */ + close(p->usbd->fd); + pthread_mutex_destroy(&p->usbd->lock); + close(p->usbd->sd_pipe[0]); + close(p->usbd->sd_pipe[1]); + + a1logd(p->log, 6, "usb_close_port: usb port has been released and closed\n"); + } + p->is_open = 0; +#endif // ~~99 + + /* Find it and delete it from our static cleanup list */ + usb_delete_from_cleanup_list(p); +} + +static void *urb_reaper(void *context); /* Declare */ + +/* Open a USB port for all our uses. */ +/* This always re-opens the port */ +/* return icom error */ +static int usb_open_port( +icoms *p, +int config, /* Configuration number */ +int wr_ep, /* Write end point */ +int rd_ep, /* Read end point */ +icomuflags usbflags,/* Any special handling flags */ +int retries, /* > 0 if we should retry set_configuration (100msec) */ +char **pnames /* List of process names to try and kill before opening */ +) { + int rv, tries = 0; + a1logd(p->log, 8, "usb_open_port: Make sure USB port is open, tries %d\n",retries); + + if (p->is_open) + p->close_port(p); + +#ifdef NEVER // ~~99 + /* Make sure the port is open */ + if (!p->is_open) { + int rv, i, iface; + kkill_nproc_ctx *kpc = NULL; + + if (config != 1) { + /* Nothing currently needs it, so we haven't implemented it yet... */ + a1loge(p->log, ICOM_NOTS, "usb_open_port: native driver cant handle config %d\n",config); + return ICOM_NOTS; + } + + /* Do open retries */ + for (tries = 0; retries >= 0; retries--, tries++) { + + a1logd(p->log, 8, "usb_open_port: About to open USB port '%s'\n",p->usbd->dpath); + + if (tries > 0) { + //msec_sleep(i_rand(50,100)); + msec_sleep(77); + } + + p->uflags = usbflags; + + /* We should only do a set configuration if the device has more than one */ + /* possible configuration and it is currently not the desired configuration, */ + /* but we should avoid doing a set configuration if the OS has already */ + /* selected the configuration we want, since two set configs seem to */ + /* mess up the Spyder2, BUT we can't do a get config because this */ + /* messes up the i1pro-D. */ + + /* Linux set_configuration(1) by default, so we don't need to do anything */ + p->cconfig = 1; + + if (p->cconfig != config) { + if ((rv = ioctl(p->usbd->fd, USBDEVFS_SETCONFIGURATION, &config)) != 0) { + a1logd(p->log, 1, "icoms_usb_setconfig failed with %d\n",rv); + return ICOM_SYS; + } + p->cconfig = config; + a1logd(p->log, 6, "usb_open_port: set config %d OK\n",config); + } + + /* We're done */ + break; + } + + if (kpc != NULL) + kpc->del(kpc); + + /* Claim all the interfaces */ + for (iface = 0; iface < p->nifce; iface++) { + + if ((rv = ioctl(p->usbd->fd, USBDEVFS_CLAIMINTERFACE, &iface)) < 0) { + struct usbdevfs_getdriver getd; + getd.interface = iface; + + a1logd(p->log, 1, "usb_open_port: Claiming USB port '%s' interface %d initally failed with %d\n",p->usbd->dpath,iface,rv); + + /* Detatch the existing interface if kernel driver is active. */ + if (p->uflags & icomuf_detach + && ioctl(p->usbd->fd, USBDEVFS_GETDRIVER, &getd) == 0) { + struct usbdevfs_ioctl cmd; + a1logd(p->log, 1, "usb_open_port: Attempting kernel detach\n"); + cmd.ifno = iface; + cmd.ioctl_code = USBDEVFS_DISCONNECT; + cmd.data = NULL; + ioctl(p->usbd->fd, USBDEVFS_IOCTL, &cmd); + if ((rv = ioctl(p->usbd->fd, USBDEVFS_CLAIMINTERFACE, &iface)) < 0) { + a1loge(p->log, ICOM_SYS, "usb_open_port: Claiming USB port '%s' interface %d failed after detach with %d\n",p->usbd->dpath,iface,rv); + return ICOM_SYS; + } + } else { + a1loge(p->log, ICOM_SYS, "usb_open_port: Claiming USB port '%s' interface %d failed with %d\n",p->usbd->dpath,iface,rv); + return ICOM_SYS; + } + } + } + + /* Clear any errors. */ + /* (Some I/F seem to hang if we do this, some seem to hang if we don't !) */ + /* The ColorMunki on Linux only starts every second time if we don't do this. */ + if (!(p->uflags & icomuf_no_open_clear)) { + for (i = 0; i < 32; i++) { + if (!p->ep[i].valid) + continue; + p->usb_clearhalt(p, p->ep[i].addr); + } + } + + /* Set "serial" coms values */ + p->wr_ep = wr_ep; + p->rd_ep = rd_ep; + p->rd_qa = p->EPINFO(rd_ep).packetsize; + if (p->rd_qa == 0) + p->rd_qa = 8; + a1logd(p->log, 8, "usb_open_port: 'serial' read quanta = packet size = %d\n",p->rd_qa); + + /* Start the reaper thread to handle URB completions */ + if ((rv = pipe(p->usbd->sd_pipe)) < 0) { + a1loge(p->log, ICOM_SYS, "usb_open_port: creat pipe failed with %d\n",rv); + return ICOM_SYS; + } + pthread_mutex_init(&p->usbd->lock, NULL); + + p->usbd->running = 1; + if ((rv = pthread_create(&p->usbd->thread, NULL, urb_reaper, (void*)p)) < 0) { + p->usbd->running = 0; + a1loge(p->log, ICOM_SYS, "usb_open_port: creating urb reaper thread failed with %s\n",rv); + return ICOM_SYS; + } + + p->is_open = 1; + a1logd(p->log, 8, "usb_open_port: USB port is now open\n"); + } + +#endif // ~~99 + /* Install the cleanup signal handlers, and add to our cleanup list */ + usb_install_signal_handlers(p); + + return ICOM_OK; +} + +/* - - - - - - - - - - - - - - - - - - - - - */ +/* Our universal USB transfer function */ +static int icoms_usb_transaction( + icoms *p, + usb_cancelt *cancelt, + int *transferred, + icom_usb_trantype ttype, /* transfer type */ + unsigned char endpoint, /* 0x80 for control write, 0x00 for control read */ + unsigned char *buffer, + int length, + unsigned int timeout /* In msec */ +) { + int type; + int remlen; + unsigned char *bp; + int xlength = 0; + int i; + int reqrv = ICOM_OK; + +#ifdef NEVER // ~~99 + in_usb_rw++; + a1logd(p->log, 8, "icoms_usb_transaction: req type 0x%x ep 0x%x size %d\n",ttype,endpoint,length); + + if (!p->usbd->running) { + in_usb_rw--; + a1logv(p->log, 1, "icoms_usb_transaction: reaper thread is not running\n"); + return ICOM_SYS; + } + + /* Translate icoms transfer type of Linux */ + switch (ttype) { + case icom_usb_trantype_command: + type = USBDEVFS_URB_TYPE_CONTROL; + break; + case icom_usb_trantype_interrutpt: + type = USBDEVFS_URB_TYPE_INTERRUPT; + break; + case icom_usb_trantype_bulk: + type = USBDEVFS_URB_TYPE_BULK; + break; + } + + /* Setup the icom req and urbs */ + req.urbs = NULL; + pthread_mutex_init(&req.lock, NULL); + pthread_cond_init(&req.cond, NULL); + + /* Linux historically only copes with 16384 length urbs, */ + /* so break up longer requests into multiple urbs */ + + req.cancelled = 0; + req.nourbs = req.nurbs = (length + (1 << 14)-1) >> 14; + if ((req.urbs = (usbio_urb *)calloc(sizeof(usbio_urb), req.nourbs)) == NULL) { + in_usb_rw--; + a1loge(p->log, ICOM_SYS, "icoms_usb_transaction: control transfer too big! (%d)\n",length); + return ICOM_SYS; + } + + bp = buffer; + remlen = length; + for (i = 0; i < req.nurbs; i++) { + req.urbs[i].req = &req; + req.urbs[i].urbno = i; + /* Setup Linux URB */ + req.urbs[i].urb.usercontext = &req.urbs[i]; + req.urbs[i].urb.type = type; + if (type != USBDEVFS_URB_TYPE_CONTROL) + req.urbs[i].urb.endpoint = endpoint; + if (remlen > 16384) + req.urbs[i].urb.buffer_length = 16384; + else + req.urbs[i].urb.buffer_length = remlen; + req.urbs[i].urb.buffer = (void *)bp; + remlen -= req.urbs[i].urb.buffer_length; + 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); + + /* Add our request to the req list so that it can be cancelled on reap failure */ + pthread_mutex_lock(&p->usbd->lock); + req.next = p->usbd->reqs; + p->usbd->reqs = &req; + pthread_mutex_unlock(&p->usbd->lock); + + /* submit the URBs */ + 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.nourbs--; + } + } + + if (cancelt != NULL) { + 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->cmtx); + } + + /* Wait for the reaper to wake us, or for a timeout, */ + /* or for the reaper to die. */ + pthread_mutex_lock(&req.lock); + if (req.nourbs > 0) { + 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) { + pthread_mutex_unlock(&req.lock); + a1logd(p->log, 1, "coms_usb_transaction: pthread_cond_timedwait failed with %d\n",rv); + rv = ICOM_SYS; + goto done; + } + + /* Timed out - cancel the remaining URB's */ + a1logd(p->log, 8, "coms_usb_transaction: time out - cancel remaining URB's\n"); + reqrv = ICOM_TO; + if (!req.cancelled && (rv = cancel_req(p, &req, -1)) != ICOM_OK) { + pthread_mutex_unlock(&req.lock); + reqrv = ICOM_SYS; + /* Since cancelling failed, we can't wait for them to be reaped */ + goto done; + } + + /* Wait for the cancelled URB's to be reaped */ + for (;req.nourbs > 0;) { /* Ignore spurious wakeups */ + 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); + reqrv = ICOM_SYS; + /* Waiting for reap failed, so give up */ + goto done; + } + } + } else { + a1logd(p->log, 8, "coms_usb_transaction: reap - %d left\n",req.nourbs); + } + if (req.nourbs <= 0) + break; /* All urbs's are done */ + } + } + pthread_mutex_unlock(&req.lock); + + /* Compute the overall result by going through the urbs. */ + for (i = 0; i < req.nurbs; i++) { + int stat = req.urbs[i].urb.status; + xlength += req.urbs[i].urb.actual_length; + + if (stat == ICOM_SYS) { /* 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) + reqrv = ICOM_USBW; + else + reqrv = ICOM_USBR; + } else if (reqrv == ICOM_OK && stat == -ECONNRESET) { /* Cancelled */ + reqrv = ICOM_CANC; + } else if (reqrv == ICOM_OK + && req.urbs[i].urb.actual_length < req.urbs[i].urb.buffer_length) { + /* Disregard any following urb's status - they are probably cancelled */ + break; + } + /* reqrv == ICOM_TO will ignore urb status */ + } + + if (ttype == icom_usb_trantype_command) + xlength += IUSB_REQ_HEADER_SIZE; /* Account for header - linux doesn't */ + + /* requested size wasn't transferred ? */ + if (reqrv == ICOM_OK && xlength != length) + reqrv = ICOM_SHORT; + + if (transferred != NULL) + *transferred = xlength; + +done:; + if (cancelt != NULL) { + amutex_lock(cancelt->cmtx); + cancelt->hcancel = (void *)NULL; + if (cancelt->state == 0) + amutex_unlock(cancelt->cond); + cancelt->state = 2; + amutex_unlock(cancelt->cmtx); + } + + /* Remove our request from the list */ + pthread_mutex_lock(&p->usbd->lock); + preq = &p->usbd->reqs; + while (*preq != &req && *preq != NULL) /* Find it */ + preq = &((*preq)->next); + if (*preq != NULL) + *preq = (*preq)->next; + pthread_mutex_unlock(&p->usbd->lock); + + if (req.urbs != NULL) + free(req.urbs); + pthread_cond_destroy(&req.cond); + pthread_mutex_destroy(&req.lock); + + if (in_usb_rw < 0) + exit(0); + + in_usb_rw--; + + a1logd(p->log, 8, "coms_usb_transaction: returning err 0x%x and %d bytes\n",reqrv, xlength); +#endif // ~~99 + + return reqrv; +} + + +/* Return error icom error code */ +static int icoms_usb_control_msg( +icoms *p, +int *transferred, +int requesttype, int request, +int value, int index, unsigned char *bytes, int size, +int timeout) { + int reqrv = ICOM_OK; + int dirw = (requesttype & IUSB_REQ_DIR_MASK) == IUSB_REQ_HOST_TO_DEV ? 1 : 0; + unsigned char *buf; + + a1logd(p->log, 8, "icoms_usb_control_msg: type 0x%x req 0x%x size %d\n",requesttype,request,size); + +#ifdef NEVER // ~~99 + /* Allocate a buffer for the ctrl header + payload */ + if ((buf = calloc(1, IUSB_REQ_HEADER_SIZE + size)) == NULL) { + a1loge(p->log, ICOM_SYS, "icoms_usb_control_msg: calloc failed\n"); + return ICOM_SYS; + } + + /* Setup the control header */ + buf[0] = requesttype; + buf[1] = request; + short2buf(buf + 2, value); + short2buf(buf + 4, index); + short2buf(buf + 6, size); + + /* If it's a write, copy the write data into the buffer */ + if (dirw) + memcpy(buf + IUSB_REQ_HEADER_SIZE, bytes, size); + + reqrv = icoms_usb_transaction(p, NULL, transferred, icom_usb_trantype_command, + dirw ? 0x80 : 0x00, buf, IUSB_REQ_HEADER_SIZE + size, timeout); + + /* If read, copy the data back */ + if (!dirw) + memcpy(bytes, buf + IUSB_REQ_HEADER_SIZE, size); + + if (transferred != NULL) /* Adjust for header size requested */ + *transferred -= IUSB_REQ_HEADER_SIZE; + + free(buf); + +#endif // ~~99 + a1logd(p->log, 8, "icoms_usb_control_msg: returning err 0x%x and %d bytes\n",reqrv, *transferred); + return reqrv; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Time out error return value */ + +#define USBIO_ERROR_TIMEOUT -ETIMEDOUT + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Cancel i/o in another thread */ +int icoms_usb_cancel_io( + icoms *p, + usb_cancelt *cancelt +) { + int rv = ICOM_OK; +#ifdef NEVER // ~~99 + a1logd(p->log, 8, "icoms_usb_cancel_io called\n"); + usb_lock_cancel(cancelt); + if (cancelt->hcancel != NULL) + rv = cancel_req(p, (usbio_req *)cancelt->hcancel, -1); + usb_unlock_cancel(cancelt); + + if (rv != ICOM_OK) /* Assume this could be because of faulty device */ + rv = ICOM_USBW; +#endif // ~~99 + + return rv; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Reset and end point data toggle to 0 */ +int icoms_usb_resetep( + icoms *p, + int ep /* End point address */ +) { + int rv = ICOM_OK; + +#ifdef NEVER // ~~99 + if ((rv = ioctl(p->usbd->fd, USBDEVFS_RESETEP, &ep)) != 0) { + a1logd(p->log, 1, "icoms_usb_resetep failed with %d\n",rv); + rv = ICOM_USBW; + } +#endif // ~~99 + return rv; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Clear a halt on an end point */ +int icoms_usb_clearhalt( + icoms *p, + int ep /* End point address */ +) { + int rv = ICOM_OK; + +#ifdef NEVER // ~~99 + if ((rv = ioctl(p->usbd->fd, USBDEVFS_CLEAR_HALT, &ep)) != 0) { + a1logd(p->log, 1, "icoms_usb_clearhalt failed with %d\n",rv); + rv = ICOM_USBW; + } +#endif // ~~99 + return rv; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - */ + diff --git a/spectro/usbio_lusb.c b/spectro/usbio_lusb.c deleted file mode 100644 index f78bcca..0000000 --- a/spectro/usbio_lusb.c +++ /dev/null @@ -1,897 +0,0 @@ - -/* General USB I/O support, legacy Libusb 0.1/1.0 implementation, no longer */ -/* used by default, but can be configured in the Jamfile. */ -/* The corresponding libusb code is not distributed with the current source */ -/* though, and would have to be copied from ArgyllCMS V1.4.0 */ - -/* This file is conditionaly #included into usbio.c */ - -/* - * Argyll Color Correction System - * - * Author: Graeme W. Gill - * Date: 2006/22/4 - * - * 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. - */ - -/* To simplify error messages: */ -#ifdef USE_LIBUSB1 -# define USB_STRERROR(RV) libusb_strerror(RV) -#else -# define USB_STRERROR(RV) usb_strerror() -#endif - -#ifdef USE_LIBUSB1 -# define usb_device libusb_device -# define usb_device_descriptor libusb_device_descriptor -# define usb_dev_handle libusb_device_handle -# define usb_config_descriptor libusb_config_descriptor -# define usb_strerror libusb_strerror -#endif - -#if defined(__FreeBSD__) /* Shut spurious warnings up */ -# define CASTFIX (intptr_t) -#else -# define CASTFIX -#endif - -/* Check a USB Vendor and product ID, and add the device */ -/* to the icoms path if it is supported. */ -/* (this is used to help implement usb_get_paths) */ -/* return icom error */ -static int usb_check_and_add( -icompaths *p, -struct usb_device *usbd -) { - instType itype; - int nep; /* Number of end points */ - - struct usb_device_descriptor descriptor; - -#ifdef USE_LIBUSB1 - enum libusb_error rv; - - if ((rv = libusb_get_device_descriptor(usbd, &descriptor)) != LIBUSB_SUCCESS) { - a1loge(p->log, ICOM_SYS, "usb_check_and_add: failed with %d (%s)\n", - rv,USB_STRERROR(rv)); - return ICOM_SYS; - } -#else - descriptor = usbd->descriptor; /* Copy */ -#endif - a1logd(p->log, 6, "usb_check_and_add: called with VID 0x%x, PID 0x%x\n",descriptor.idVendor, descriptor.idProduct); - -#ifdef USE_LIBUSB1 -#if defined(NT) || defined(__APPLE__) - /* Ignore libusb1 HID driver capability */ - { - struct libusb_config_descriptor *confdesc = NULL; - - /* If not obviously HID, we need to fetch the config descriptor */ - if (descriptor.bDeviceClass != LIBUSB_CLASS_HID - && (rv = libusb_get_config_descriptor(usbd, 0, &confdesc)) != LIBUSB_SUCCESS) { - /* This seems to happen for hubs etc., so ignore it */ - a1logd(p->log, 6 , "usb_check_and_add: get conf desc. failed - device not reconized\n"); - return ICOM_OK; - } - - if (descriptor.bDeviceClass == LIBUSB_CLASS_HID - || (confdesc->bNumInterfaces > 0 - && confdesc->interface[0]. num_altsetting > 0 - && confdesc->interface[0].altsetting[0].bInterfaceClass == LIBUSB_CLASS_HID)) { - int i; - /* See if this devices is already in the list via the HID interface */ - /* (This may not be 100% correct in the face of multiple instances - of the same device, if Windows allows different drivers for different - instances of the same device type.) */ - for (i = 0; i < p->npaths; i++) { - if (p->paths[i]->vid == descriptor.idVendor - && p->paths[i]->pid == descriptor.idProduct) - break; /* Yes */ - } - if (i < p->npaths) { - a1logd(p->log, 6, "Is an HID device and already added\n"); - if (confdesc != NULL) - libusb_free_config_descriptor(confdesc); - return ICOM_OK; - } - } - if (confdesc != NULL) - libusb_free_config_descriptor(confdesc); - } -#endif -#endif - - /* The i1pro2 is detected by checking the number of end points, */ - /* so set this value in icom now by looking at the descriptor */ - { -#ifdef USE_LIBUSB1 - struct libusb_config_descriptor *confdesc; - const struct libusb_interface_descriptor *ifd; - - if (libusb_get_config_descriptor(usbd, 0, &confdesc) != LIBUSB_SUCCESS) { - /* This seems to happen for hubs etc., so ignore it */ - a1logd(p->log, 6 , "usb_check_and_add: get conf desc. failed - device not reconized\n"); - return ICOM_OK; - } - - ifd = &confdesc->interface[0].altsetting[0]; - nep = ifd->bNumEndpoints; - if (confdesc != NULL) - libusb_free_config_descriptor(confdesc); -#else - struct usb_interface_descriptor *ifd; - ifd = &usbd->config[0].interface[0].altsetting[0]; - nep = ifd->bNumEndpoints; -#endif - } - - if ((itype = inst_usb_match((unsigned int)descriptor.idVendor, - (unsigned int)descriptor.idProduct, nep)) != instUnknown) { - char pname[400]; - - a1logd(p->log, 2, "usb_check_and_add: found known instrument VID 0x%x, PID 0x%x\n", - descriptor.idVendor, descriptor.idProduct); - - /* Create a path/identification */ - /* (devnum doesn't seem valid ?) */ -#ifdef USE_LIBUSB1 - libusb_ref_device(usbd); /* Keep it */ - sprintf(pname,"usb:/bus%d/dev%d/ (%s)",libusb_get_bus_number(usbd),libusb_get_device_address(usbd), inst_name(itype)); -#else -# if defined(UNIX) - sprintf(pname,"usb:/bus%d/dev%d (%s)",usbd->bus->location >> 24, usbd->devnum, inst_name(itype)); -# else - sprintf(pname,"usb:/bus%lu/dev%d (%s)",usbd->bus->location, usbd->devnum, inst_name(itype)); -# endif -#endif - - /* Add the path to the list */ - p->add_usb(p, pname, descriptor.idVendor, descriptor.idProduct, nep, usbd, itype); - return ICOM_OK; - } - a1logd(p->log, 6 , "usb_check_and_add: device not reconized\n"); - - return ICOM_OK; -} - -#ifdef USE_LIBUSB1 - -/* Add paths of USB connected instruments */ -/* return icom error */ -int usb_get_paths( -icompaths *p -) { - ssize_t i, nlist; - struct libusb_device **list; - - /* Scan the USB busses for instruments we recognise */ - /* We're not expecting any of our instruments to be an interface on a device. */ - - /* Use the default context to avoid worying about versions */ - /* of libusb1 that don't reference count them. (ie. would need */ - /* copies in both icompaths and icoms) */ - - libusb_init(NULL); /* Use default context */ - - /* Enable lower level debugging of device finding */ - if (p->log->debug >= 8) - libusb_set_debug(NULL, p->log->debug); - - if ((nlist = libusb_get_device_list(NULL, &list)) < 0) { - a1loge(p->log, ICOM_SYS, "usb_get_paths: get_device_list failed with %d (%s)\n", - nlist,USB_STRERROR(nlist)); - return ICOM_SYS; - } - - a1logd(p->log, 6, "usb_get_paths: about to look through devices:\n"); - - for (i = 0; i < nlist; i++) { - usb_check_and_add(p, list[i]); - } - - libusb_free_device_list(list, 1); - - a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->npaths); - return ICOM_OK; -} - -#else /* !USE_LIBUSB1 */ - -/* Add paths of USB connected instruments */ -/* return icom error */ -int usb_get_paths( -icompaths *p -) { - struct usb_bus *bus; - int rv; - - /* Enable lower level debugging of device finding */ - if (p->log->debug >= 8) - usb_set_debug(p->log->debug); - - /* Scan the USB busses for instruments we recognise */ - /* We're not expecting any of our instruments to be an interface on a device. */ - - usb_init(); - if ((rv = usb_find_busses()) < 0) { - a1loge(p->log, ICOM_SYS, "usb_get_paths: find_busses failed with %d (%s)\n", - rv,USB_STRERROR(rv)); - return ICOM_SYS; - } - if ((rv = usb_find_devices()) < 0) { - a1loge(p->log, ICOM_SYS, "usb_get_paths: usb_find_devices failed with %d (%s)\n", - rv,USB_STRERROR(rv)); - return ICOM_SYS; - } - - a1logd(p->log, 6, "usb_get_paths: about to look through busses:\n"); - - for (bus = usb_get_busses(); bus != NULL; bus = bus->next) { - struct usb_device *dev; - a1logd(p->log, 6, "usb_get_paths: about to look through devices:\n"); - for (dev = bus->devices; dev != NULL; dev = dev->next) { - if ((rv = usb_check_and_add(p, dev)) != ICOM_OK) - return rv; - } - } - a1logd(p->log, 8, "usb_get_paths: returning %d paths and ICOM_OK\n",p->npaths); - return ICOM_OK; -} -#endif /* !USE_LIBUSB1 */ - -/* Copy usb_idevice contents from icompaths to icom */ -/* return icom error */ -int usb_copy_usb_idevice(icoms *d, icompath *s) { - if (s->usbd == NULL) { - d->usbd = NULL; - return ICOM_OK; - } - - d->usbd = s->usbd; /* Copy pointer */ -#ifdef USE_LIBUSB1 - libusb_ref_device(d->usbd); -#endif - return ICOM_OK; -} - -/* Cleanup and then free a usb dev entry */ -void usb_del_usb_idevice(struct usb_idevice *usbd) { - - if (usbd == NULL) - return; - -#ifdef USE_LIBUSB1 - libusb_unref_device(usbd); -#endif -} - -/* Cleanup any USB specific icoms state */ -void usb_del_usb(icoms *p) { - -#ifdef USE_LIBUSB1 - usb_del_usb_idevice(p->usbd); -#endif /* USE_LIBUSB1 */ -} - -/* Close an open USB port */ -/* If we don't do this, the port and/or the device may be left in an unusable state. */ -void usb_close_port(icoms *p) { - - a1logd(p->log, 8, "usb_close_port: called\n"); - - if (p->is_open && p->usbh != NULL) { - int iface; - - for (iface = 0; iface < p->nifce; iface++) { -#ifdef USE_LIBUSB1 - libusb_release_interface(p->usbh, iface); -#else - usb_release_interface(p->usbh, iface); -#endif - } - - /* Workaround for some bugs */ - if (p->uflags & icomuf_reset_before_close) { -#ifdef USE_LIBUSB1 - libusb_reset_device(p->usbh); -#else - usb_reset(p->usbh); -#endif - } - - /* Close as well, othewise we can't re-open */ - { -#ifdef USE_LIBUSB1 - libusb_close(p->usbh); -#else - usb_close(p->usbh); -#endif - } - p->usbh = NULL; - - a1logd(p->log, 8, "usb_close_port: port has been released and closed\n"); - } - p->is_open = 0; - - /* Find it and delete it from our static cleanup list */ - usb_delete_from_cleanup_list(p); - -} - -/* Open a USB port for all our uses. */ -/* This always re-opens the port */ -/* return icom error */ -static int usb_open_port( -icoms *p, -int config, /* Configuration number */ -int wr_ep, /* Write end point */ -int rd_ep, /* Read end point */ -icomuflags usbflags,/* Any special handling flags */ -int retries, /* > 0 if we should retry set_configuration (100msec) */ -char **pnames /* List of process names to try and kill before opening */ -) { - int tries = 0; - a1logd(p->log, 8, "usb_open_port: Make sure USB port is open, tries %d\n",retries); - - if (p->is_open) - p->close_port(p); - - /* Make sure the port is open */ - if (!p->is_open) { - struct usb_device_descriptor descriptor; -#ifdef USE_LIBUSB1 - const struct libusb_interface_descriptor *ifd; - struct libusb_config_descriptor *confdesc; -#else - struct usb_interface_descriptor *ifd; -#endif - int rv, i, iface; - kkill_nproc_ctx *kpc = NULL; - - /* Do open retries */ - for (tries = 0; retries >= 0; retries--, tries++) { - - a1logd(p->log, 8, "usb_open_port: About to open USB port '%s'\n",p->name); - - if (tries > 0) { - //msec_sleep(i_rand(50,100)); - msec_sleep(77); - } - if (tries > 0 && pnames != NULL && kpc == NULL) { -#if defined(__APPLE__) || defined(NT) - if ((kpc = kkill_nprocess(pnames, p->log)) == NULL) - a1logd(p->log, 1, "usb_open_port: kkill_nprocess returned error!\n"); -#endif /* __APPLE__ */ - } - -#ifdef USE_LIBUSB1 - if ((rv = libusb_open(p->usbd, &p->usbh)) != LIBUSB_SUCCESS) -#else - if ((p->usbh = usb_open(p->usbd)) == NULL) -#endif - { - a1logd(p->log, 8, "usb_open_port: open '%s' config %d failed (%s) (Permissions ?)\n",p->name,config,USB_STRERROR(rv)); - if (retries <= 0) { - if (kpc != NULL) - kpc->del(kpc); - a1loge(p->log, ICOM_SYS, "usb_open_port: open '%s' config %d failed (%s) (Permissions ?)\n",p->name,config,USB_STRERROR(rv)); - return ICOM_SYS; - } - continue; - } else if (p->debug) - a1logd(p->log, 2, "usb_open_port: open port '%s' succeeded\n",p->name); - - /* Get a copy of the device descriptor so we can see device params */ -#ifdef USE_LIBUSB1 - if (libusb_get_device_descriptor(p->usbd, &descriptor) != LIBUSB_SUCCESS) { - a1loge(p->log, ICOM_SYS, "usb_open_port: get device descriptor on '%s' failed with %d (%s)\n",p->name,rv,USB_STRERROR(rv)); - return ICOM_SYS; - } -#else - descriptor = p->usbd->descriptor; /* Copy */ -#endif - - p->uflags = usbflags; - - a1logd(p->log, 8, "usb_open_port: Number of configurations = %d\n", - descriptor.bNumConfigurations); - p->nconfig = descriptor.bNumConfigurations; - p->config = config; - -#if defined(UNIX_X11) - /* only call set_configuration on Linux if the device has more than one */ - /* possible configuration, because Linux does a set_configuration by default, */ - /* and two of them mess up instruments like the Spyder2 */ - - if (descriptor.bNumConfigurations > 1) { -#endif - - /* Can't skip this, as it is needed to setup the interface and end points on OS X */ -#ifdef USE_LIBUSB1 - if ((rv = libusb_set_configuration(p->usbh, config)) < 0) -#else - if ((rv = usb_set_configuration(p->usbh, config)) < 0) -#endif - { - a1logd(p->log, 8, "usb_open_port: configuring '%s' to %d failed with %d (%s)\n",p->name,config,rv,USB_STRERROR(rv)); - if (retries <= 0) { - if (kpc != NULL) - kpc->del(kpc); - a1loge(p->log, ICOM_SYS, "usb_open_port: configuring '%s' to %d failed with %d (%s)\n",p->name,config,rv,USB_STRERROR(rv)); - return ICOM_SYS; - } - /* reset the port and retry */ -#ifdef USE_LIBUSB1 - libusb_reset_device(p->usbh); // ~~999 ????? - libusb_close(p->usbh); -#else - usb_reset(p->usbh); - usb_close(p->usbh); -#endif - continue; - } -#if defined(UNIX_X11) - } /* End of if bNumConfigurations > 1 */ -#endif - - /* We're done */ - break; - } - - if (kpc != NULL) - kpc->del(kpc); - - /* Claim all interfaces of this configuration */ -#ifdef USE_LIBUSB1 - if ((rv = libusb_get_active_config_descriptor(p->usbd, &confdesc)) != LIBUSB_SUCCESS) { - a1loge(p->log, ICOM_SYS, "usb_open_port: get config desc. for '%s' failed with %d (%s)\n",p->name,rv,USB_STRERROR(rv)); - return ICOM_SYS; - } - p->nifce = confdesc->bNumInterfaces; -#else - p->nifce = p->usbd->config->bNumInterfaces; -#endif - -// a1logv(p->log, 8, "usb_open_port: Number of interfaces = %d\n",p->nifce); - - /* Claim all the interfaces */ - for (iface = 0; iface < p->nifce; iface++) { - /* (Second parameter is bInterfaceNumber) */ - -#ifdef USE_LIBUSB1 - if ((rv = libusb_claim_interface(p->usbh, iface)) < 0) { - /* Detatch the existing interface if kernel driver is active. */ - if (p->uflags & icomuf_detach - && libusb_kernel_driver_active(p->usbh, iface) == 1) { - libusb_detach_kernel_driver (p->usbh, iface); - if ((rv = libusb_claim_interface(p->usbh, iface)) < 0) { - a1loge(p->log, ICOM_SYS, "usb_open_port: Claiming USB port '%s' interface %d failed after detach with %d (%s)\n",p->name,iface,rv,USB_STRERROR(rv)); - return ICOM_SYS; - } - } else { - a1loge(p->log, ICOM_SYS, "usb_open_port: Claiming USB port '%s' interface %d failed with %d (%s)\n",p->name,iface,rv,USB_STRERROR(rv)); - return ICOM_SYS; - } - } -#else - if ((rv = usb_claim_interface(p->usbh, iface)) < 0) { -# if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP == 1 - /* Detatch the existing interface. */ - if (p->uflags & icomuf_detach) { - usb_detach_kernel_driver_np(p->usbh, iface); - if ((rv = usb_claim_interface(p->usbh, iface)) < 0) { - a1loge(p->log, ICOM_SYS, "usb_open_port: Claiming USB port '%s' interface %d failed after detach with %d (%s)\n",p->name,iface,rv,USB_STRERROR(rv)); - return ICOM_SYS; - } - } -# else - a1loge(p->log, ICOM_SYS, "usb_open_port: Claiming USB port '%s' interface %d failed with %d (%s)\n",p->name,iface,rv,USB_STRERROR(rv)); - return ICOM_SYS; -# endif - } -#endif - - /* Fill in the end point details */ -#ifdef USE_LIBUSB1 - ifd = &confdesc->interface[iface].altsetting[0]; -#else - ifd = &p->usbd->config[p->config-1].interface[iface].altsetting[0]; -#endif -// a1logv(p->log, 8, "usb_open_port: Number of endpoints on iface %d = %d\n",iface, ifd->bNumEndpoints); - p->nep = ifd->bNumEndpoints; - for (i = 0; i < ifd->bNumEndpoints; i++) { - int ad = ifd->endpoint[i].bEndpointAddress; - p->EPINFO(ad).valid = 1; - p->EPINFO(ad).addr = ad; - p->EPINFO(ad).packetsize = ifd->endpoint[i].wMaxPacketSize; - p->EPINFO(ad).type = ifd->endpoint[i].bmAttributes & IUSB_ENDPOINT_TYPE_MASK; - /* Some I/F seem to hang if we do this, some seem to hang if we don't ! */ - if (!(p->uflags & icomuf_no_open_clear)) -#ifdef USE_LIBUSB1 - libusb_clear_halt(p->usbh, (unsigned char)ad); -#else - usb_clear_halt(p->usbh, ad); -#endif -// a1logv(p->log, 8, "usb_open_port: ep %d: endpoint addr %02x pktsze %d, type %d\n",i,ad,ifd->endpoint[i].wMaxPacketSize,p->EPINFO(ad).type); - } - -#ifdef NEVER - /* Get the serial number */ - { - int rv; - struct usb_device_descriptor descriptor; - - a1logd(p->log, 8, "usb_open_port: About to get device serial number\n"); -# ifdef USE_LIBUSB1 - if ((rv = libusb_get_device_descriptor(p->usbd, &descriptor)) != LIBUSB_SUCCESS) { - a1loge(p->log, ICOM_SYS, "usb_open_port: get_device_descriptor failed with %d USB port '%s'\n",rv,p->name); - return ICOM_SYS; - } -# else - descriptor = dev->descriptor; /* Copy */ -# endif - if ((rv = libusb_get_string_descriptor_ascii(p->usbh, descriptor.iSerialNumber, p->serialno, 32)) <= 0) { - a1logd(p->log, 1, "usb_open_port: Failed to get device serial number %d (%s)\n",rv,USB_STRERROR(rv)); - p->serialno[0] = '\000'; - } else { - a1logd(p->log, 1, "usb_open_port: Device serial number = '%s'\n",p->serialno); - } - } -#endif /* NEVER */ - } - - /* Set "serial" coms values */ - p->wr_ep = wr_ep; - p->rd_ep = rd_ep; - p->rd_qa = p->EPINFO(rd_ep).packetsize; - if (p->rd_qa == 0) - p->rd_qa = 8; - a1logd(p->log, 8, "usb_open_port: 'serial' read quanta = packet size = %d\n",p->rd_qa); - -#ifdef USE_LIBUSB1 - if (confdesc != NULL) - libusb_free_config_descriptor(confdesc); -#endif - - p->is_open = 1; - a1logd(p->log, 8, "usb_open_port: USB port is now open\n"); - } - - /* Install the cleanup signal handlers, and add to our cleanup list */ - usb_install_signal_handlers(p); - - return ICOM_OK; -} - -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -/* Time out error return value */ - -#ifdef USE_LIBUSB1 -#define USBIO_ERROR_TIMEOUT LIBUSB_ERROR_TIMEOUT -#else -# if defined(UNIX) -#define USBIO_ERROR_TIMEOUT -ETIMEDOUT -# else -#define USBIO_ERROR_TIMEOUT -116 /* libusb-win32 code */ -# endif /* UNIX */ -#endif - -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -#ifdef USE_LIBUSB1 - -/* Callback functions */ -static void bulk_transfer_cb(struct libusb_transfer *transfer) -{ - int *completed = transfer->user_data; - *completed = 1; - /* caller interprets results and frees transfer */ -} - -/* Version of libusb1 sync to async code that returns the pointer */ -/* to the transfer structure so that the transfer can be cancelled */ -/* by another thread. */ -/* Return an icoms error code */ -static int do_sync_usb_transfer( - icoms *p, - struct libusb_device_handle *dev_handle, - usb_cancelt *cancelt, - int *transferred, - icom_usb_trantype ttype, - unsigned char endpoint, - unsigned char *buffer, - int length, - unsigned int timeout -) { - enum libusb_transfer_type type; - struct libusb_transfer *transfer = libusb_alloc_transfer(0); - int completed = 0; - int rv; - - if (!transfer) { - a1logd(p->log, 1, "do_sync_usb_transfer: transfer is NULL!\n"); - return ICOM_SYS; - } - - /* Translate icoms transfer type of libusb1 */ - switch (ttype) { - case icom_usb_trantype_command: - type = LIBUSB_TRANSFER_TYPE_CONTROL; - break; - case icom_usb_trantype_interrutpt: - type = LIBUSB_TRANSFER_TYPE_INTERRUPT; - break; - case icom_usb_trantype_bulk: - type = LIBUSB_TRANSFER_TYPE_BULK; - break; - } - - libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, - bulk_transfer_cb, &completed, timeout); - transfer->type = type; - - if ((rv = libusb_submit_transfer(transfer)) < 0) { - libusb_free_transfer(transfer); - a1logd(p->log, 1, "do_sync_usb_transfer: Submitting transfer failed with %d (%s)\n",rv,USB_STRERROR(rv)); - if ((endpoint & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT) - return ICOM_USBW; - else - return ICOM_USBR; - } - - if (cancelt != NULL) { - usb_lock_cancel(cancelt); - cancelt->hcancel = (void *)transfer; - usb_unlock_cancel(cancelt); - } - - while (!completed) { - if ((rv = libusb_handle_events_check(NULL, &completed)) < 0) { - if (rv == LIBUSB_ERROR_INTERRUPTED) - continue; /* Retry */ - /* Give up - cancel transfer and wait until complete */ - libusb_cancel_transfer(transfer); - while (!completed) { - if (libusb_handle_events_check(NULL, &completed) < 0) - break; - } - if (cancelt != NULL) { - usb_lock_cancel(cancelt); - cancelt->hcancel = NULL; - usb_unlock_cancel(cancelt); - } - libusb_free_transfer(transfer); - a1loge(p->log, ICOM_SYS, "do_sync_usb_transfer: handle_events failed with %d (%s)\n",rv,USB_STRERROR(rv)); - return ICOM_SYS; - } - } - - *transferred = transfer->actual_length; - switch (transfer->status) { - case LIBUSB_TRANSFER_COMPLETED: - rv = ICOM_OK; - break; - case LIBUSB_TRANSFER_TIMED_OUT: - rv = ICOM_TO; - break; - case LIBUSB_TRANSFER_STALL: - case LIBUSB_TRANSFER_OVERFLOW: - case LIBUSB_TRANSFER_NO_DEVICE: - if ((endpoint & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT) - rv = ICOM_USBW; - else - rv = ICOM_USBR; - break; - case LIBUSB_TRANSFER_CANCELLED: - rv = ICOM_CANC; - break; - default: - a1loge(p->log, ICOM_SYS, "do_sync_usb_transfer: transfer faile with %d (%s)\n",rv,USB_STRERROR(rv)); - rv = ICOM_SYS; - } - - if (cancelt != NULL) { - usb_lock_cancel(cancelt); - cancelt->hcancel = NULL; - usb_unlock_cancel(cancelt); - } - libusb_free_transfer(transfer); - - /* requested size wasn't transferred */ - if (rv == ICOM_OK && *transferred != length) - rv = ICOM_SHORT; - - return rv; -} -#endif /* USE_LIBUSB1 */ - -/* Return icom error code */ -static int icoms_usb_control_msg( -icoms *p, -int *transferred, -int requesttype, int request, -int value, int index, unsigned char *bytes, int size, -int timeout) { - int rv; - - in_usb_rw++; - a1logd(p->log, 8, "icoms_usb_control_msg: type 0x%x, req 0x%x, size %d\n",requesttype,request,size); - - if (transferred != NULL) - *transferred = 0; -#ifdef USE_LIBUSB1 - rv = libusb_control_transfer(p->usbh, (uint8_t)requesttype, (uint8_t)request, - (uint16_t)value, (uint16_t)index, bytes, (uint16_t)size, timeout); -#else - rv = usb_control_msg(p->usbh, requesttype, request, value, index, (char *)bytes, size, timeout); -#endif - if (in_usb_rw < 0) /* interrupt recurssion */ - exit(0); - - in_usb_rw--; - - if (rv < 0) { - if (rv == USBIO_ERROR_TIMEOUT) { /* Not a timeout */ - rv = ICOM_TO; - } else { - if (requesttype & IUSB_ENDPOINT_IN) /* Device to host */ - rv = ICOM_USBR; /* Read error */ - else - rv = ICOM_USBW; /* Write error */ - } - } else { - if (transferred != NULL) - *transferred = rv; - if (rv != size) { - rv = ICOM_SHORT; - } else - rv = ICOM_OK; - } - a1logd(p->log, 8, "icoms_usb_control_msg: returning err 0x%x and %d bytes\n",rv, *transferred); - return rv; -} - -/* Our versions of usblib read/write, that exit if a signal was caught */ -/* This is so that MSWindows works properly */ -/* return an icom error */ -static int icoms_usb_transaction(icoms *p, usb_cancelt *cancelt, int *xbytes, - icom_usb_trantype type, int ep, unsigned char *bytes, int size, int timeout) { - int rv; - - in_usb_rw++; - - a1logd(p->log, 8, "coms_usb_transaction: req type 0x%x ep 0x%x size %d\n",type,ep,size); -#ifdef USE_LIBUSB1 - rv = do_sync_usb_transfer(p, p->usbh, cancelt, xbytes, type, - (unsigned char)ep, bytes, size, timeout); -#else - if (xbytes != NULL) - *xbytes = 0; - if (cancelt != NULL) { - usb_lock_cancel(cancelt); - cancelt->hcancel = (void *) CASTFIX ep; - usb_lock_cancel(cancelt); - } - if (type == icom_usb_trantype_interrutpt) { - if ((ep & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT) - rv = usb_interrupt_write(p->usbh, ep, (char *)bytes, size, timeout); - else - rv = usb_interrupt_read(p->usbh, ep, (char *)bytes, size, timeout); - } else { /* bulk */ - if ((ep & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT) - rv = usb_bulk_write(p->usbh, ep, (char *)bytes, size, timeout); - else - rv = usb_bulk_read(p->usbh, ep, (char *)bytes, size, timeout); - } - if (cancelt != NULL) { - usb_lock_cancel(cancelt); - cancelt->hcancel = (void *)-1; - usb_lock_cancel(cancelt); - } - if (rv < 0) { - if (rv == USBIO_ERROR_TIMEOUT) - rv = ICOM_TO; - else { - if ((ep & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_OUT) - rv = ICOM_USBW; - else - rv = ICOM_USBR; - } - } else { - if (xbytes != NULL) - *xbytes = rv; - if (rv != *xbytes) - rv = ICOM_SHORT; - else - rv = ICOM_OK; - } -#endif - if (in_usb_rw < 0) /* Signal handler recursion error */ - exit(0); - - in_usb_rw--; - - a1logd(p->log, 8, "coms_usb_transaction: returning err 0x%x and %d bytes\n",rv, *xbytes); - - return rv; -} - -/* - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* Cancel i/o in another thread */ -int icoms_usb_cancel_io( - icoms *p, - usb_cancelt *cancelt -) { - int rv = 0; - usb_lock_cancel(cancelt); -#ifdef USE_LIBUSB1 - if (cancelt->hcancel != NULL) { - rv = libusb_cancel_transfer((struct libusb_transfer *)cancelt->hcancel); - if (rv == LIBUSB_ERROR_NOT_FOUND) - rv = 0; - } -#else - if ((int) CASTFIX cancelt->hcancel >= 0) { -// msec_sleep(1); /* Let device recover ? */ - rv = usb_resetep(p->usbh, (int) CASTFIX cancelt->hcancel); /* Not reliable ? */ -// msec_sleep(1); /* Let device recover ? */ - } -#endif - usb_unlock_cancel(cancelt); - - if (rv == 0) - return ICOM_OK; - - a1logd(p->log, 1, "icoms_usb_cancel_io: failed with %d (%s)\n", rv,USB_STRERROR(rv)); - return ICOM_SYS; -} - -/* - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* Reset and end point data toggle to 0 */ -int icoms_usb_resetep( - icoms *p, - int ep /* End point address */ -) { - int rv; -#ifdef USE_LIBUSB1 - rv = libusb_resetep(p->usbh, (unsigned char)ep); /* Is this the same though ? */ -#else - rv = usb_resetep(p->usbh, ep); /* Not reliable ? */ -#endif - - if (rv == 0) - return ICOM_OK; - - a1logd(p->log, 1, "icoms_usb_resetep: failed with %d (%s)\n", rv,USB_STRERROR(rv)); - return ICOM_USBW; -} - -/* - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* Clear a halt on an end point */ -int icoms_usb_clearhalt( - icoms *p, - int ep /* End point address */ -) { - int rv; -#ifdef USE_LIBUSB1 - rv = libusb_clear_halt(p->usbh, (unsigned char)ep); -#else - rv = usb_clear_halt(p->usbh, ep); -#endif - - if (rv == 0) - return ICOM_OK; - - a1logd(p->log, 1, "icoms_usb_clearhalt: failed with %d (%s)\n", rv,USB_STRERROR(rv)); - return ICOM_USBW; -} - -/* - - - - - - - - - - - - - - - - - - - - - - - - - */ - diff --git a/spectro/usbio_lx.c b/spectro/usbio_lx.c index 359f483..44307fd 100644 --- a/spectro/usbio_lx.c +++ b/spectro/usbio_lx.c @@ -71,12 +71,11 @@ static void short2buf(unsigned char *buf, int inv) { /* Check a USB Vendor and product ID by reading the device descriptors, */ /* and add the device to the icoms path if it is supported. */ /* Return icom nz error code on fatal error */ -int usb_check_and_add_fd( +static +int usb_check_and_add( a1log *log, -icompaths *pp, /* icompaths to add to, or if NULL */ -icompath *p, /* icompath to set. */ -char *dpath, /* path to device - may be NULL */ -int fd /* device file descriptor */ +icompaths *pp, /* icompaths to add to */ +char *dpath /* path to device */ ) { int rv; unsigned char buf[IUSB_DESC_TYPE_DEVICE_SIZE]; @@ -84,8 +83,15 @@ int fd /* device file descriptor */ unsigned int configix, nconfig, totlen; instType itype; struct usb_idevice *usbd = NULL; + int fd; /* device file descriptor */ - a1logd(log, 6, "usb_check_and_add_fd: with fd %d\n",fd); + a1logd(log, 6, "usb_check_and_add: givem '%s'\n",dpath); + + /* Open the device so that we can read it */ + if ((fd = open(dpath, O_RDONLY)) < 0) { + a1logd(log, 1, "usb_check_and_add: failed to open '%s'\n",dpath); + return ICOM_OK; + } /* Read the device descriptor */ if ((rv = read(fd, buf, IUSB_DESC_TYPE_DEVICE_SIZE)) < 0 @@ -93,6 +99,7 @@ int fd /* device file descriptor */ || buf[0] != IUSB_DESC_TYPE_DEVICE_SIZE || buf[1] != IUSB_DESC_TYPE_DEVICE) { a1logd(log, 1, "usb_check_and_add: failed to read device descriptor\n"); + close(fd); return ICOM_OK; } @@ -106,12 +113,14 @@ int fd /* device file descriptor */ /* Do a preliminary match */ if ((itype = inst_usb_match(vid, pid, 0)) == instUnknown) { a1logd(log, 6 , "usb_check_and_add: instrument not reconized\n"); + close(fd); return ICOM_OK; } /* Allocate an idevice so that we can fill in the end point information */ if ((usbd = (struct usb_idevice *) calloc(sizeof(struct usb_idevice), 1)) == NULL) { a1loge(log, ICOM_SYS, "icoms: calloc failed!\n"); + close(fd); return ICOM_SYS; } @@ -130,16 +139,19 @@ int fd /* device file descriptor */ || buf[1] != IUSB_DESC_TYPE_CONFIG) { a1logd(log, 1, "usb_check_and_add: failed to read device config\n"); free(usbd); + close(fd); return ICOM_OK; } if ((totlen = buf2ushort(buf + 2)) < 6) { a1logd(log, 1, "usb_check_and_add: config desc size strange\n"); free(usbd); + close(fd); return ICOM_OK;; } if ((buf2 = calloc(1, totlen)) == NULL) { a1loge(log, ICOM_SYS, "usb_check_and_add: calloc of descriptor failed!\n"); + close(fd); return ICOM_SYS; } @@ -149,6 +161,7 @@ int fd /* device file descriptor */ a1logd(log, 1, "usb_check_and_add: failed to read device config details\n"); free(buf2); free(usbd); + close(fd); return ICOM_SYS; } @@ -194,6 +207,7 @@ int fd /* device file descriptor */ if (nep10 == 0xffff) { /* Hmm. Failed to find number of end points */ a1logd(log, 1, "usb_check_and_add: failed to find number of end points\n"); free(usbd); + close(fd); return ICOM_SYS; } @@ -207,57 +221,28 @@ int fd /* device file descriptor */ /* Create a path/identification */ /* (devnum doesn't seem valid ?) */ - if (dpath == NULL) { - sprintf(pname,"%s", inst_name(itype)); - if ((usbd->dpath = strdup("no_path")) == NULL) { - a1loge(log, ICOM_SYS, "usb_check_and_add: strdup path failed!\n"); - free(usbd); - return ICOM_SYS; - } - } else { - sprintf(pname,"%s (%s)", dpath, inst_name(itype)); - if ((usbd->dpath = strdup(dpath)) == NULL) { - a1loge(log, ICOM_SYS, "usb_check_and_add: strdup path failed!\n"); - free(usbd); - return ICOM_SYS; - } + sprintf(pname,"%s (%s)", dpath, inst_name(itype)); + if ((usbd->dpath = strdup(dpath)) == NULL) { + a1loge(log, ICOM_SYS, "usb_check_and_add: strdup path failed!\n"); + free(usbd); + close(fd); + return ICOM_SYS; } /* Add the path and ep info to the list */ - if (pp != NULL) { - if ((rv = pp->add_usb(pp, pname, vid, pid, nep10, usbd, itype)) != ICOM_OK) - return rv; - } else { - usbd->fd = fd; - if ((rv = icompath_set_usb(log, p, pname, vid, pid, nep10, usbd, itype)) != ICOM_OK) - return rv; + if ((rv = pp->add_usb(pp, pname, vid, pid, nep10, usbd, itype)) != ICOM_OK) { + close(fd); + return rv; } - } else { - free(usbd); - } - return ICOM_OK; -} -/* Same as above, starting with the path */ -static int usb_check_and_add( -icompaths *p, -char *dpath -) { - int fd; - int rv; - a1logd(p->log, 6, "usb_check_and_add: givem '%s'\n",dpath); - /* Open the device so that we can read it */ - if ((fd = open(dpath, O_RDONLY)) < 0) { - a1logd(p->log, 1, "usb_check_and_add: failed to open '%s'\n",dpath); - return ICOM_OK; + } else { + free(usbd); } - rv = usb_check_and_add_fd(p->log, p, NULL, dpath, fd); - close(fd); - return rv; + return ICOM_OK; } /* Add paths to USB connected instruments */ @@ -268,7 +253,6 @@ icompaths *p int vid, pid; a1logd(p->log, 6, "usb_get_paths: about to look through buses:\n"); - { int j; char *paths[3] = { "/dev/bus/usb", /* current, from udev */ @@ -307,7 +291,7 @@ icompaths *p a1logd(p->log, 8, "usb_get_paths: about to stat %s\n",path2); if (stat(path2, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) { found = 1; - if ((rv = usb_check_and_add(p, path2)) != ICOM_OK) { + if ((rv = usb_check_and_add(p->log, p, path2)) != ICOM_OK) { closedir(d1); return rv; } @@ -325,7 +309,7 @@ icompaths *p a1logd(p->log, 8, "usb_get_paths: about to stat %s\n",path2); if (stat(path2, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) { found = 1; - if ((rv = usb_check_and_add(p, path2)) != ICOM_OK) { + if ((rv = usb_check_and_add(p->log, p, path2)) != ICOM_OK) { closedir(d1); return rv; } @@ -342,7 +326,6 @@ icompaths *p return ICOM_OK; } - /* Copy usb_idevice contents from icompaths to icom */ /* return icom error */ int usb_copy_usb_idevice(icoms *d, icompath *s) { @@ -424,9 +407,6 @@ void usb_close_port(icoms *p) { pthread_mutex_destroy(&p->usbd->lock); close(p->usbd->sd_pipe[0]); close(p->usbd->sd_pipe[1]); - free(p->usbd->dpath); - free(p->usbd); - p->usbd = NULL; a1logd(p->log, 6, "usb_close_port: usb port has been released and closed\n"); } @@ -690,7 +670,7 @@ static void *urb_reaper(void *context) { iurb = (usbio_urb *)out->usercontext; req = iurb->req; - a1logd(p->log, 8, "urb_reaper: urb reap URB %d with status %d bytes %d, usrbs left %d\n",iurb->urbno, out->status, out->actual_length, req->nourbs-1); + a1logd(p->log, 8, "urb_reaper: urb reap URB %d with status %d bytes %d, urbs left %d\n",iurb->urbno, out->status, out->actual_length, req->nourbs-1); pthread_mutex_lock(&req->lock); /* Stop requester from missing reap */ req->nourbs--; /* We're reaped one */ @@ -841,9 +821,11 @@ a1logd(p->log, 8, "icoms_usb_transaction: reset req 0x%p nourbs to %d\n",&req,re } if (cancelt != NULL) { - usb_lock_cancel(cancelt); + amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)&req; - usb_unlock_cancel(cancelt); + cancelt->state = 1; + amutex_unlock(cancelt->cond); /* Signal any thread waiting for IO start */ + amutex_unlock(cancelt->cmtx); } /* Wait for the reaper to wake us, or for a timeout, */ @@ -934,9 +916,12 @@ a1logd(p->log, 8, "icoms_usb_transaction: reset req 0x%p nourbs to %d\n",&req,re done:; if (cancelt != NULL) { - usb_lock_cancel(cancelt); + amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)NULL; - usb_unlock_cancel(cancelt); + if (cancelt->state == 0) + amutex_unlock(cancelt->cond); + cancelt->state = 2; + amutex_unlock(cancelt->cmtx); } /* Remove our request from the list */ @@ -1024,10 +1009,10 @@ int icoms_usb_cancel_io( ) { int rv = ICOM_OK; a1logd(p->log, 8, "icoms_usb_cancel_io called\n"); - usb_lock_cancel(cancelt); + amutex_lock(cancelt->cmtx); if (cancelt->hcancel != NULL) rv = cancel_req(p, (usbio_req *)cancelt->hcancel, -1); - usb_unlock_cancel(cancelt); + amutex_unlock(cancelt->cmtx); if (rv != ICOM_OK) /* Assume this could be because of faulty device */ rv = ICOM_USBW; diff --git a/spectro/usbio_nt.c b/spectro/usbio_nt.c index 1711f0d..de9b265 100644 --- a/spectro/usbio_nt.c +++ b/spectro/usbio_nt.c @@ -25,7 +25,7 @@ #include <setupapi.h> #include <driver_api.h> -#define DEBUG /* Turn on debug messages */ +#undef DEBUG /* Turn on debug messages */ #define LIBUSBW1_MAX_DEVICES 255 #define LIBUSBW1_PATH_MAX 512 @@ -117,7 +117,8 @@ icompaths *p unsigned char buf[IUSB_DESC_TYPE_DEVICE_SIZE]; _snprintf(dpath, LIBUSBW1_PATH_MAX - 1,"\\\\.\\libusb0-%04d", i+1); - a1logd(p->log, 6, "usb_get_paths opening device '%s'\n",dpath); + if (i < 16) /* Suppress messages on unlikely ports */ + a1logd(p->log, 6, "usb_get_paths opening device '%s'\n",dpath); if ((handle = CreateFile(dpath, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)) == INVALID_HANDLE_VALUE) { @@ -323,6 +324,8 @@ icompaths *p /* Add the path and ep info to the list */ if ((rv = p->add_usb(p, pname, vid, pid, nep10, usbd, itype)) != ICOM_OK) return rv; + + } else { free(usbd); } @@ -409,10 +412,6 @@ void usb_close_port(icoms *p) { } CloseHandle(p->usbd->handle); - free(p->usbd->dpath); - free(p->usbd); - p->usbd = NULL; - a1logd(p->log, 6, "usb_close_port: usb port has been released and closed\n"); } p->is_open = 0; @@ -565,6 +564,7 @@ char **pnames /* List of process names to try and kill before opening */ /* -------------------------------------------------------------- */ /* Our universal USB transfer function */ +/* It appears that we may return a timeout with valid characters. */ static int icoms_usb_transaction( icoms *p, usb_cancelt *cancelt, @@ -596,12 +596,6 @@ static int icoms_usb_transaction( if ((olaps.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) return ICOM_SYS; - if (cancelt != NULL) { - usb_lock_cancel(cancelt); - cancelt->hcancel = (void *)&endpoint; - usb_unlock_cancel(cancelt); - } - memset(&req, 0, sizeof(libusb_request)); req.endpoint.endpoint = endpoint; @@ -616,9 +610,17 @@ static int icoms_usb_transaction( goto done; } + if (cancelt != NULL) { + 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->cmtx); + } + if (WaitForSingleObject(olaps.hEvent, timeout) == WAIT_TIMEOUT) { - /* Cancel the operation */ + /* Cancel the operation, because it timed out */ memset(&req, 0, sizeof(libusb_request)); req.endpoint.endpoint = endpoint; req.timeout = LIBUSBW1_DEFAULT_TIMEOUT; @@ -638,9 +640,12 @@ static int icoms_usb_transaction( } done:; if (cancelt != NULL) { - usb_lock_cancel(cancelt); + amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)NULL; - usb_unlock_cancel(cancelt); + if (cancelt->state == 0) + amutex_unlock(cancelt->cond); /* Make sure this gets unlocked */ + cancelt->state = 2; + amutex_unlock(cancelt->cmtx); } CloseHandle(olaps.hEvent); @@ -815,7 +820,7 @@ int icoms_usb_cancel_io( usb_cancelt *cancelt ) { int rv = ICOM_OK; - usb_lock_cancel(cancelt); + amutex_lock(cancelt->cmtx); if (cancelt->hcancel != NULL) { libusb_request req; @@ -828,7 +833,7 @@ int icoms_usb_cancel_io( a1logd(p->log, 1, "icoms_usb_cancel_io: failed with 0x%x\n",rv); } } - usb_unlock_cancel(cancelt); + amutex_unlock(cancelt->cmtx); return rv; } diff --git a/spectro/usbio_ox.c b/spectro/usbio_ox.c index 24498fb..c92aed5 100644 --- a/spectro/usbio_ox.c +++ b/spectro/usbio_ox.c @@ -186,6 +186,7 @@ icompaths *p IOObjectRelease(mit); /* Release the itterator */ return rv; } + } } else { IOObjectRelease(ioob); /* Release found object */ @@ -402,7 +403,7 @@ char **pnames /* List of process names to try and kill before opening */ /* mess up the Spyder2, BUT we can't do a get config because this */ /* messes up the i1pro-D. */ - /* OS X doesn't do a set_configuration() by default */ + /* OS X doesn't do a set_configuration() by default, so force one. */ p->cconfig = 0; if (p->cconfig != config) { @@ -462,7 +463,7 @@ char **pnames /* List of process names to try and kill before opening */ return ICOM_SYS; } (*pluginref)->Stop(pluginref); - IODestroyPlugInInterface(pluginref); + IODestroyPlugInInterface(pluginref); // this stops IOServices though ?? if ((rv = (*(p->usbd->interfaces[i]))->USBInterfaceOpen( p->usbd->interfaces[i])) != kIOReturnSuccess) { @@ -703,9 +704,11 @@ static int icoms_usb_transaction( } if (cancelt != NULL) { - usb_lock_cancel(cancelt); + amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)&req; - usb_unlock_cancel(cancelt); + cancelt->state = 1; + amutex_unlock(cancelt->cond); /* Signal any thread waiting for IO start */ + amutex_unlock(cancelt->cmtx); } /* Wait for the callback to complete */ @@ -757,7 +760,7 @@ static int icoms_usb_transaction( /* If io was aborted, ClearPipeStall */ if (req.result == kIOReturnAborted) { -#if defined(NEVER) && (InterfaceVersion > 182) +#if (InterfaceVersion > 182) (*p->usbd->interfaces[iix])->ClearPipeStallBothEnds(p->usbd->interfaces[iix], pno); #else (*p->usbd->interfaces[iix])->ClearPipeStall(p->usbd->interfaces[iix], pno); @@ -787,9 +790,12 @@ static int icoms_usb_transaction( done:; if (cancelt != NULL) { - usb_lock_cancel(cancelt); + amutex_lock(cancelt->cmtx); cancelt->hcancel = (void *)NULL; - usb_unlock_cancel(cancelt); + if (cancelt->state == 0) + amutex_unlock(cancelt->cond); + cancelt->state = 2; + amutex_unlock(cancelt->cmtx); } pthread_cond_destroy(&req.cond); @@ -865,7 +871,7 @@ int icoms_usb_cancel_io( ) { int reqrv = ICOM_OK; - usb_lock_cancel(cancelt); + amutex_lock(cancelt->cmtx); usbio_req *req = (usbio_req *)cancelt->hcancel; if (req != NULL) { IOReturn rv; @@ -875,7 +881,7 @@ int icoms_usb_cancel_io( reqrv = ICOM_USBW; } } - usb_unlock_cancel(cancelt); + amutex_unlock(cancelt->cmtx); return reqrv; } @@ -911,7 +917,7 @@ int icoms_usb_clearhalt( IOReturn rv; int irv; -#if defined(NEVER) && (InterfaceVersion > 182) +#if (InterfaceVersion > 182) if ((rv = (*p->usbd->interfaces[iix])->ClearPipeStallBothEnds( p->usbd->interfaces[iix], pno)) != kIOReturnSuccess) { a1logd(p->log, 1, "icoms_usb_clearhalt failed with 0x%x\n",rv); diff --git a/spectro/webwin.c b/spectro/webwin.c index d90239a..1de010a 100644 --- a/spectro/webwin.c +++ b/spectro/webwin.c @@ -35,6 +35,7 @@ #include "cgats.h" #include "conv.h" #include "dispwin.h" +#include "webwin.h" #include "conv.h" #include "mongoose.h" @@ -217,22 +218,35 @@ dispwin *p, 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 */ debugr("webwin_set_color called\n"); if (p->nowin) return 1; - p->rgb[0] = r; - p->rgb[1] = g; - p->rgb[2] = b; + 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->rgb[j]; + 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... */ @@ -242,12 +256,44 @@ 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 time for */ - /* the browser to update the background color, the CRT */ - /* refresh or LCD processing/update time, + */ + /* 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(200); + msec_sleep(update_delay); return 0; } @@ -309,6 +355,13 @@ int webdisp, /* Port number */ double width, double height, /* Width and height in mm */ 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 */ @@ -326,21 +379,30 @@ int ddebug /* >0 to print debug statements to stderr */ return NULL; } - /* !!!! Make changes in dispwin.c as well !!!! */ + /* !!!! Make changes in dispwin.c & madvrwin.c as well !!!! */ p->name = strdup("Web Window"); p->nowin = nowin; - p->native = 0; + 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->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->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->set_callout = webwin_set_callout; + p->del = webwin_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 */ @@ -361,6 +423,9 @@ int ddebug /* >0 to print debug statements to stderr */ p->ncix = 1; + p->pdepth = 8; /* Assume this by API */ + p->edepth = 8; + /* Basic object is initialised, so create a web server */ options[0] = "listening_ports"; diff --git a/spectro/webwin.h b/spectro/webwin.h index b298397..59b1d22 100644 --- a/spectro/webwin.h +++ b/spectro/webwin.h @@ -22,6 +22,13 @@ int webdisp, /* Port number */ double width, double height, /* Width and height in mm */ 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 */ |