1 
2 /*
3  * This file contains resources to translate colors to/from
4  * X-Rites XRGA calibration standard. This only applies to
5  * reflective measurements from historical Gretag-Macbeth & X-Rite
6  * instruments, and current X-Rite instruments.
7  */
8 
9 /*
10  * Author:  Graeme W. Gill
11  * Date:    9/2/2016
12  * Version: 1.00
13  *
14  * Copyright 2016 Graeme W. Gill
15  * All rights reserved.
16  *
17  * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
18  * see the License2.txt file for licencing details.
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <ctype.h>
24 #include <string.h>
25 #include <time.h>
26 #include <fcntl.h>
27 #if defined(UNIX)
28 # include <utime.h>
29 #else
30 # include <sys/utime.h>
31 #endif
32 #include <sys/stat.h>
33 #include <stdarg.h>
34 #ifndef SALONEINSTLIB
35 #include "copyright.h"
36 #include "aconfig.h"
37 #include "numlib.h"
38 #else	/* !SALONEINSTLIB */
39 #include "sa_config.h"
40 #include "numsup.h"
41 #endif /* !SALONEINSTLIB */
42 #ifndef SALONEINSTLIB
43 #  include "plot.h"
44 #endif
45 #include "xspect.h"
46 #include "insttypes.h"
47 #include "conv.h"
48 #include "icoms.h"
49 #include "inst.h"
50 #include "rspec.h"
51 
52 #include "xrga.h"
53 
54 /* A conversion equation */
55 typedef struct {
56 	double  gain0, gain1;		/* Destination gain at 550 nm, slope */
57 	double  wl0;				/* Wavlength shift at 550nm to source - assumed constant */
58 } xrga_eqn;
59 
60 /* XRGA conversion values in order [pol][src][dst] */
61 /* Parameters are gain at 550nm, slope of gain per nm, src wavelengh offset in nm. */
62 /* These values were inferred from the results one gets from processing spectral */
63 /* values though X-Rite's conversion routines. */
64 
65 xrga_eqn xrga_equations[2][3][3] = {
66 	{	/* Unpolarized */
67 		{
68 			{ 1.000000,  0.000000,  0.000000 },	/* XRDI -> XRDI */
69 			{ 1.000046, -0.000050, -1.400568 },	/* XRDI -> GMDI */
70 			{ 1.000005, -0.000025, -0.400084 }	/* XRDI -> XRGA */
71 		},
72 		{
73 			{ 1.000046, 0.000050, 1.400568 },	/* GMDI -> XRDI */
74 			{ 1.000000, 0.000000, 0.000000 },	/* GMDI -> GMDI */
75 			{ 1.000015, 0.000025, 1.000207 }	/* GMDI -> XRGA */
76 		},
77 		{
78 			{ 1.000005,  0.000025,  0.400084 },	/* XRGA -> XRDI */
79 			{ 1.000015, -0.000025, -1.000207 },	/* XRGA -> GMDI */
80 			{ 1.000000,  0.000000,  0.000000 }	/* XRGA -> XRGA */
81 		}
82 	},
83 	{	/* Polarized */
84 		{
85 			{ 1.000000,  0.000000,  0.000000 },	/* XRDI -> XRDI */
86 			{ 1.028710, -0.000081, -1.399477 },	/* XRDI -> GMDI */
87 			{ 1.000005, -0.000025, -0.400084 }	/* XRDI -> XRGA */
88 		},
89 		{
90 			{ 0.971957, 0.000072, 1.398829 },	/* GMDI -> XRDI */
91 			{ 1.000000, 0.000000, 0.000000 },	/* GMDI -> GMDI */
92 			{ 0.971975, 0.000049, 0.998938 }	/* GMDI -> XRGA */
93 		},
94 		{
95 			{ 1.000005,  0.000025,  0.400084 },	/* XRGA -> XRDI */
96 			{ 1.028711, -0.000056, -0.999421 },	/* XRGA -> GMDI */
97 			{ 1.000000,  0.000000,  0.000000 }	/* XRGA -> XRGA */
98 		}
99 	}
100 };
101 
102 
103 /* Core conversion code. dst and src are assumed to be different xspect's */
convert_xrga(xspect * dst,xspect * src,xrga_eqn * eq)104 static void convert_xrga(xspect *dst, xspect *src, xrga_eqn *eq) {
105 	int j;
106 
107 	XSPECT_COPY_INFO(dst, src);		/* Copy parameters */
108 
109 	for (j = 0; j < dst->spec_n; j++) {
110 		double dw, sw, ga;
111 		double spcing, f;
112 		double y[4], yw;
113 		double x[4];
114 		int i;
115 
116 		dw = XSPECT_XWL(dst, j);			/* Destination wavelength */
117 		sw = dw + eq->wl0;					/* Source wavelength */
118 		ga = eq->gain0 + (dw - 550.0) * eq->gain1;	/* Gain at this dest wl */
119 
120 		/* Compute fraction 0.0 - 1.0 out of known spectrum. */
121 		/* Place it so that the target wavelength lands in middle section */
122 		/* of Lagrange basis points. */
123 		spcing = (src->spec_wl_long - src->spec_wl_short)/(src->spec_n-1.0);
124 		f = (sw - src->spec_wl_short) / (src->spec_wl_long - src->spec_wl_short);
125 		f *= (src->spec_n - 1.0);
126 		i = (int)floor(f);			/* Base grid coordinate */
127 
128 		if (i < 1)					/* Limit to valid Lagrange basis index range, */
129 			i = 1;					/* and extrapolate from that at the ends. */
130 		else if (i > (src->spec_n - 3))
131 			i = (src->spec_n - 3);
132 
133 		/* Setup the surrounding values */
134 		x[0] = src->spec_wl_short + (i-1) * spcing;
135 		y[0] = src->spec[i-1];
136 		x[1] = src->spec_wl_short + i * spcing;
137 		y[1] = src->spec[i];
138 		x[2] = src->spec_wl_short + (i+1) * spcing;
139 		y[2] = src->spec[i+1];
140 		x[3] = src->spec_wl_short + (i+2) * spcing;
141 		y[3] = src->spec[i+2];
142 
143 
144 		/* Compute interpolated value using Lagrange: */
145 		yw = y[0] * (sw-x[1]) * (sw-x[2]) * (sw-x[3])/((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3]))
146 		   + y[1] * (sw-x[0]) * (sw-x[2]) * (sw-x[3])/((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3]))
147 		   + y[2] * (sw-x[0]) * (sw-x[1]) * (sw-x[3])/((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3]))
148 		   + y[3] * (sw-x[0]) * (sw-x[1]) * (sw-x[2])/((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2]));
149 
150 		yw *= ga;
151 
152 		dst->spec[j] = yw;
153 	}
154 }
155 
156 /* Apply a conversion from one calibration standard to another to an xspect */
xspec_convert_xrga(xspect * dst,xspect * srcp,xcalpol pol,xcalstd dsp,xcalstd ssp)157 void xspec_convert_xrga(xspect *dst, xspect *srcp, xcalpol pol, xcalstd dsp, xcalstd ssp) {
158 	xrga_eqn *eq;
159 	xspect tmp, *src = srcp;
160 
161 	/* If no conversion and no copy needed */
162 	if ((ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp)
163 	  && dst == src)
164 		return;
165 
166 	/* If no conversion needed */
167 	if (ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp) {
168 		*dst = *src;		/* Struct copy */
169 		return;
170 	}
171 
172 	/* If the dest is the same as the src, make a temporary copy */
173 	if (dst == src) {
174 		tmp = *src;			/* Struct copy */
175 		src = &tmp;
176 
177 	} else {
178 		XSPECT_COPY_INFO(dst, src);		/* Copy parameters */
179 	}
180 
181 	eq = &xrga_equations[pol][ssp][dsp];
182 	convert_xrga(dst, src, eq);
183 }
184 
185 /* Apply a conversion from one calibration standard to another to an array of ipatch's */
ipatch_convert_xrga(ipatch * vals,int nvals,xcalpol pol,xcalstd dsp,xcalstd ssp,int clamp)186 void ipatch_convert_xrga(ipatch *vals, int nvals,
187                          xcalpol pol, xcalstd dsp, xcalstd ssp, int clamp) {
188 	xrga_eqn *eq;
189 	xspect tmp;
190 	xsp2cie *conv = NULL;	/* Spectral to XYZ conversion object */
191 	int i;
192 
193 	/* If no conversion needed */
194 	if (ssp == xcalstd_native || dsp == xcalstd_native
195 	 || dsp == ssp || nvals <= 0)
196 		return;
197 
198 	/* Conversion to use */
199 	eq = &xrga_equations[pol][ssp][dsp];
200 
201 	for (i = 0; i < nvals; i++) {
202 		if (vals[i].mtype != inst_mrt_reflective
203 		 || vals[i].sp.spec_n <= 0) {
204 			continue;
205 		}
206 		tmp = vals[i].sp;		// Struct copy */
207 		convert_xrga(&vals[i].sp, &tmp, eq);
208 
209 		/* Re-compute XYZ */
210 		if (vals[i].XYZ_v) {
211 			if (conv == NULL) {
212 				conv = new_xsp2cie(icxIT_D50, NULL, icxOT_CIE_1931_2,
213 				                   NULL, icSigXYZData, (icxClamping)clamp);
214 			}
215 			conv->convert(conv, vals[i].XYZ, &vals[i].sp);
216 			vals[i].XYZ_v = 1;
217 			vals[i].XYZ[0] *= 100.0;	/* Convert to % */
218 			vals[i].XYZ[1] *= 100.0;
219 			vals[i].XYZ[2] *= 100.0;
220 		}
221 	}
222 	if (conv != NULL)
223 		conv->del(conv);
224 }
225