summaryrefslogtreecommitdiff
path: root/spectro
diff options
context:
space:
mode:
Diffstat (limited to 'spectro')
-rw-r--r--spectro/Jamfile43
-rw-r--r--spectro/Makefile.OSX4
-rw-r--r--spectro/Makefile.SA11
-rw-r--r--spectro/Makefile.UNIX2
-rw-r--r--spectro/afiles6
-rw-r--r--spectro/average.c2
-rw-r--r--spectro/ccxxmake.c49
-rw-r--r--spectro/chartread.c28
-rw-r--r--spectro/colorhug.c60
-rw-r--r--spectro/conv.c61
-rw-r--r--spectro/conv.h6
-rw-r--r--spectro/dispcal.c625
-rw-r--r--spectro/dispread.c114
-rw-r--r--spectro/dispsup.c572
-rw-r--r--spectro/dispsup.h41
-rw-r--r--spectro/dispwin.c999
-rw-r--r--spectro/dispwin.h67
-rw-r--r--spectro/dtp20.c13
-rw-r--r--spectro/dtp22.c16
-rw-r--r--spectro/dtp41.c14
-rw-r--r--spectro/dtp51.c22
-rw-r--r--spectro/dtp92.c101
-rw-r--r--spectro/fakeread.c362
-rw-r--r--spectro/hcfr.c9
-rw-r--r--spectro/hidio.c433
-rw-r--r--spectro/hidio.h9
-rw-r--r--spectro/huey.c20
-rw-r--r--spectro/i1d3.c732
-rw-r--r--spectro/i1d3.h13
-rw-r--r--spectro/i1disp.c35
-rw-r--r--spectro/i1pro.c33
-rw-r--r--spectro/i1pro_imp.c1703
-rw-r--r--spectro/i1pro_imp.h39
-rw-r--r--spectro/icoms.c134
-rw-r--r--spectro/icoms.h81
-rw-r--r--spectro/icoms_nt.c280
-rw-r--r--spectro/icoms_ux.c146
-rw-r--r--spectro/ifiles4
-rw-r--r--spectro/illumread.c115
-rw-r--r--spectro/inst.c222
-rw-r--r--spectro/inst.h114
-rw-r--r--spectro/instappsup.c13
-rw-r--r--spectro/instlib.ksh4
-rw-r--r--spectro/insttypeinst.h1
-rw-r--r--spectro/insttypes.c19
-rw-r--r--spectro/insttypes.h6
-rw-r--r--spectro/linear.cal514
-rw-r--r--spectro/madvrwin.c589
-rw-r--r--spectro/madvrwin.h37
-rw-r--r--spectro/munki.c36
-rw-r--r--spectro/munki_imp.c1263
-rw-r--r--spectro/munki_imp.h45
-rw-r--r--spectro/oemarch.c562
-rw-r--r--spectro/oemarch.h4
-rw-r--r--spectro/oeminst.c6
-rw-r--r--spectro/spec2cie.c1
-rw-r--r--spectro/specbos.c1674
-rw-r--r--spectro/specbos.h160
-rw-r--r--spectro/spotread.c206
-rw-r--r--spectro/spyd2.c199
-rw-r--r--spectro/spyd2.h4
-rw-r--r--spectro/spyd2PLD.h2
-rw-r--r--spectro/spyd2setup.h2
-rw-r--r--spectro/ss_imp.c2
-rw-r--r--spectro/strange.cal514
-rw-r--r--spectro/synthread.c3
-rw-r--r--spectro/usbio.c159
-rw-r--r--spectro/usbio.h65
-rw-r--r--spectro/usbio_bsd.c764
-rw-r--r--spectro/usbio_lusb.c897
-rw-r--r--spectro/usbio_lx.c107
-rw-r--r--spectro/usbio_nt.c39
-rw-r--r--spectro/usbio_ox.c26
-rw-r--r--spectro/webwin.c99
-rw-r--r--spectro/webwin.h7
75 files changed, 11217 insertions, 4152 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/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 */