summaryrefslogtreecommitdiff
path: root/spectro/xrga.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/xrga.c')
-rw-r--r--spectro/xrga.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/spectro/xrga.c b/spectro/xrga.c
new file mode 100644
index 0000000..0bf22fa
--- /dev/null
+++ b/spectro/xrga.c
@@ -0,0 +1,224 @@
+
+/*
+ * This file contains resources to translate colors to/from
+ * X-Rites XRGA calibration standard. This only applies to
+ * reflective measurements from historical Gretag-Macbeth & X-Rite
+ * instruments, and current X-Rite instruments.
+ */
+
+/*
+ * Author: Graeme W. Gill
+ * Date: 9/2/2016
+ * Version: 1.00
+ *
+ * Copyright 2016 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
+ * see the License2.txt file for licencing details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#if defined(UNIX)
+# include <utime.h>
+#else
+# include <sys/utime.h>
+#endif
+#include <sys/stat.h>
+#include <stdarg.h>
+#ifndef SALONEINSTLIB
+#include "copyright.h"
+#include "aconfig.h"
+#include "numlib.h"
+#else /* !SALONEINSTLIB */
+#include "sa_config.h"
+#include "numsup.h"
+#endif /* !SALONEINSTLIB */
+#ifndef SALONEINSTLIB
+# include "plot.h"
+#endif
+#include "xspect.h"
+#include "insttypes.h"
+#include "conv.h"
+#include "icoms.h"
+#include "inst.h"
+#include "rspec.h"
+
+#include "xrga.h"
+
+/* A conversion equation */
+typedef struct {
+ double gain0, gain1; /* Destination gain at 550 nm, slope */
+ double wl0; /* Wavlength shift at 550nm to source - assumed constant */
+} xrga_eqn;
+
+/* XRGA conversion values in order [pol][src][dst] */
+/* Parameters are gain at 550nm, slope of gain per nm, src wavelengh offset in nm. */
+/* These values were inferred from the results one gets from processing spectral */
+/* values though X-Rite's conversion routines. */
+
+xrga_eqn xrga_equations[2][3][3] = {
+ { /* Unpolarized */
+ {
+ { 1.000000, 0.000000, 0.000000 }, /* XRDI -> XRDI */
+ { 1.000046, -0.000050, -1.400568 }, /* XRDI -> GMDI */
+ { 1.000005, -0.000025, -0.400084 } /* XRDI -> XRGA */
+ },
+ {
+ { 1.000046, 0.000050, 1.400568 }, /* GMDI -> XRDI */
+ { 1.000000, 0.000000, 0.000000 }, /* GMDI -> GMDI */
+ { 1.000015, 0.000025, 1.000207 } /* GMDI -> XRGA */
+ },
+ {
+ { 1.000005, 0.000025, 0.400084 }, /* XRGA -> XRDI */
+ { 1.000015, -0.000025, -1.000207 }, /* XRGA -> GMDI */
+ { 1.000000, 0.000000, 0.000000 } /* XRGA -> XRGA */
+ }
+ },
+ { /* Polarized */
+ {
+ { 1.000000, 0.000000, 0.000000 }, /* XRDI -> XRDI */
+ { 1.028710, -0.000081, -1.399477 }, /* XRDI -> GMDI */
+ { 1.000005, -0.000025, -0.400084 } /* XRDI -> XRGA */
+ },
+ {
+ { 0.971957, 0.000072, 1.398829 }, /* GMDI -> XRDI */
+ { 1.000000, 0.000000, 0.000000 }, /* GMDI -> GMDI */
+ { 0.971975, 0.000049, 0.998938 } /* GMDI -> XRGA */
+ },
+ {
+ { 1.000005, 0.000025, 0.400084 }, /* XRGA -> XRDI */
+ { 1.028711, -0.000056, -0.999421 }, /* XRGA -> GMDI */
+ { 1.000000, 0.000000, 0.000000 } /* XRGA -> XRGA */
+ }
+ }
+};
+
+
+/* Core conversion code. dst and src are assumed to be different xspect's */
+static void convert_xrga(xspect *dst, xspect *src, xrga_eqn *eq) {
+ int j;
+
+ XSPECT_COPY_INFO(dst, src); /* Copy parameters */
+
+ for (j = 0; j < dst->spec_n; j++) {
+ double dw, sw, ga;
+ double spcing, f;
+ double y[4], yw;
+ double x[4];
+ int i;
+
+ dw = XSPECT_XWL(dst, j); /* Destination wavelength */
+ sw = dw + eq->wl0; /* Source wavelength */
+ ga = eq->gain0 + (dw - 550.0) * eq->gain1; /* Gain at this dest wl */
+
+ /* Compute fraction 0.0 - 1.0 out of known spectrum. */
+ /* Place it so that the target wavelength lands in middle section */
+ /* of Lagrange basis points. */
+ spcing = (src->spec_wl_long - src->spec_wl_short)/(src->spec_n-1.0);
+ f = (sw - src->spec_wl_short) / (src->spec_wl_long - src->spec_wl_short);
+ f *= (src->spec_n - 1.0);
+ i = (int)floor(f); /* Base grid coordinate */
+
+ if (i < 1) /* Limit to valid Lagrange basis index range, */
+ i = 1; /* and extrapolate from that at the ends. */
+ else if (i > (src->spec_n - 3))
+ i = (src->spec_n - 3);
+
+ /* Setup the surrounding values */
+ x[0] = src->spec_wl_short + (i-1) * spcing;
+ y[0] = src->spec[i-1];
+ x[1] = src->spec_wl_short + i * spcing;
+ y[1] = src->spec[i];
+ x[2] = src->spec_wl_short + (i+1) * spcing;
+ y[2] = src->spec[i+1];
+ x[3] = src->spec_wl_short + (i+2) * spcing;
+ y[3] = src->spec[i+2];
+
+
+ /* Compute interpolated value using Lagrange: */
+ yw = y[0] * (sw-x[1]) * (sw-x[2]) * (sw-x[3])/((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3]))
+ + y[1] * (sw-x[0]) * (sw-x[2]) * (sw-x[3])/((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3]))
+ + y[2] * (sw-x[0]) * (sw-x[1]) * (sw-x[3])/((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3]))
+ + y[3] * (sw-x[0]) * (sw-x[1]) * (sw-x[2])/((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2]));
+
+ yw *= ga;
+
+ dst->spec[j] = yw;
+ }
+}
+
+/* Apply a conversion from one calibration standard to another to an xspect */
+void xspec_convert_xrga(xspect *dst, xspect *srcp, xcalpol pol, xcalstd dsp, xcalstd ssp) {
+ xrga_eqn *eq;
+ xspect tmp, *src = srcp;
+
+ /* If no conversion and no copy needed */
+ if ((ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp)
+ && dst == src)
+ return;
+
+ /* If no conversion needed */
+ if (ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp) {
+ *dst = *src; /* Struct copy */
+ return;
+ }
+
+ /* If the dest is the same as the src, make a temporary copy */
+ if (dst == src) {
+ tmp = *src; /* Struct copy */
+ src = &tmp;
+
+ } else {
+ XSPECT_COPY_INFO(dst, src); /* Copy parameters */
+ }
+
+ eq = &xrga_equations[pol][ssp][dsp];
+ convert_xrga(dst, src, eq);
+}
+
+/* Apply a conversion from one calibration standard to another to an array of ipatch's */
+void ipatch_convert_xrga(ipatch *vals, int nvals,
+ xcalpol pol, xcalstd dsp, xcalstd ssp, int clamp) {
+ xrga_eqn *eq;
+ xspect tmp;
+ xsp2cie *conv = NULL; /* Spectral to XYZ conversion object */
+ int i;
+
+ /* If no conversion needed */
+ if (ssp == xcalstd_native || dsp == xcalstd_native
+ || dsp == ssp || nvals <= 0)
+ return;
+
+ /* Conversion to use */
+ eq = &xrga_equations[pol][ssp][dsp];
+
+ for (i = 0; i < nvals; i++) {
+ if (vals[i].mtype != inst_mrt_reflective
+ || vals[i].sp.spec_n <= 0) {
+ continue;
+ }
+ tmp = vals[i].sp; // Struct copy */
+ convert_xrga(&vals[i].sp, &tmp, eq);
+
+ /* Re-compute XYZ */
+ if (vals[i].XYZ_v) {
+ if (conv == NULL) {
+ conv = new_xsp2cie(icxIT_D50, NULL, icxOT_CIE_1931_2,
+ NULL, icSigXYZData, (icxClamping)clamp);
+ }
+ conv->convert(conv, vals[i].XYZ, &vals[i].sp);
+ vals[i].XYZ_v = 1;
+ vals[i].XYZ[0] *= 100.0; /* Convert to % */
+ vals[i].XYZ[1] *= 100.0;
+ vals[i].XYZ[2] *= 100.0;
+ }
+ }
+ if (conv != NULL)
+ conv->del(conv);
+}