1 
2 /*
3  * Argyll Color Correction System
4  *
5  * Author: Graeme W. Gill
6  * Date:   12/1/2009
7  *
8  * Copyright 2006 - 2014, Graeme W. Gill
9  * All rights reserved.
10  *
11  * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
12  * see the License2.txt file for licencing details.
13  *
14  * (Base on i1pro_imp.c)
15  */
16 
17 /*
18    If you make use of the instrument driver code here, please note
19    that it is the author(s) of the code who take responsibility
20    for its operation. Any problems or queries regarding driving
21    instruments with the Argyll drivers, should be directed to
22    the Argyll's author(s), and not to any other party.
23 
24    If there is some instrument feature or function that you
25    would like supported here, it is recommended that you
26    contact Argyll's author(s) first, rather than attempt to
27    modify the software yourself, if you don't have firm knowledge
28    of the instrument communicate protocols. There is a chance
29    that an instrument could be damaged by an incautious command
30    sequence, and the instrument companies generally cannot and
31    will not support developers that they have not qualified
32    and agreed to support.
33  */
34 
35 /* TTBD:
36 
37 */
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <time.h>
44 #include <stdarg.h>
45 #include <math.h>
46 #if defined(UNIX)
47 # include <utime.h>
48 #else
49 # include <sys/utime.h>
50 #endif
51 #include <sys/stat.h>
52 #ifndef SALONEINSTLIB
53 #include "copyright.h"
54 #include "aconfig.h"
55 #include "numlib.h"
56 #include "rspl.h"
57 #else /* SALONEINSTLIB */
58 #include <fcntl.h>
59 #include "sa_config.h"
60 #include "numsup.h"
61 #include "rspl1.h"
62 #endif /* SALONEINSTLIB */
63 #include "xspect.h"
64 #include "insttypes.h"
65 #include "conv.h"
66 #include "icoms.h"
67 #include "sort.h"
68 
69 /* Configuration */
70 #undef USE_HIGH_GAIN_MODE /* [Und] Make use of high gain mode in emissive measurements */
71 #define USE_THREAD		/* [Def] Need to use thread, or there are 1.5 second internal */
72 						/* instrument delays ! */
73 #define ENABLE_NONVCAL	/* [Def] Enable saving calibration state between program runs to a file */
74 #define ENABLE_NONLINCOR	/* [Def] Enable non-linear correction */
75 						/* NOTE :- high gain scaling will be stuffed if disabled! */
76 #define ENABLE_LEDTEMPC	/* [Def] Enable LED temperature compensation */
77 #define ENABLE_BKDRIFTC	/* [Def] Enable Emis. Black drift compensation using sheilded cell values */
78 #define HEURISTIC_BKDRIFTC	/* [Def] Use heusristic black drift correction version */
79 #undef  ENABLE_REFSTRAYC	/* [Und] Enable Reflective stray light compensation */
80 #define REFSTRAYC_FACTOR 0.000086 /* [0.00043] Compensation factor */
81 #undef  ENABLE_REFLEDINTER	/* [Und] Enable Reflective LED interference correction */
82 
83 #define ENABLE_SPOS_CHECK	/* [Def] Check the sensor position is reasonable for measurement */
84 #define FILTER_SPOS_EVENTS	/* [Def] Use a thread to filter SPOS event changes */
85 #define FILTER_TIME 500		/* [500] Filter time in msec */
86 #define DCALTOUT (1 * 60 * 60)		/* [1 Hrs] Dark Calibration timeout in seconds */
87 #define WCALTOUT (24 * 60 * 60)		/* [24 Hrs] White Calibration timeout in seconds */
88 #define MAXSCANTIME 20.0	/* [20 Sec] Maximum scan time in seconds */
89 #define SW_THREAD_TIMEOUT (10 * 60.0) 	/* [10 Min] Switch read thread timeout */
90 
91 #define SINGLE_READ		/* [Def] Use a single USB read for scan to eliminate latency issues. */
92 #define HIGH_RES		/* [Def] Enable high resolution spectral mode code. Disable */
93 						/* to break dependency on rspl library. */
94 # undef FAST_HIGH_RES_SETUP	/* Slightly better accuracy ? */
95 
96 /* Debug [Und] */
97 #undef DEBUG			/* Turn on extra messages & plots */
98 #undef PLOT_DEBUG		/* Use plot to show readings & processing */
99 #undef PLOT_REFRESH 	/* Plot refresh rate measurement info */
100 #undef PLOT_UPDELAY		/* Plot data used to determine display update delay */
101 #undef RAWR_DEBUG		/* Print out raw reading processing values */
102 #undef DUMP_SCANV		/* Dump scan readings to a file "mkdump.txt" */
103 #undef DUMP_DARKM		/* Append raw dark readings to file "mkddump.txt" */
104 #undef DUMP_BKLED		/* Save REFSTRAYC & REFLEDNOISE comp plot to "refbk1.txt" & "refbk2.txt" */
105 #undef APPEND_MEAN_EMMIS_VAL /* Append averaged uncalibrated reading to file "mkdump.txt" */
106 #undef IGNORE_WHITE_INCONS	/* Ignore define reference reading inconsistency */
107 #undef TEST_DARK_INTERP	/* Test out the dark interpolation (need DEBUG for plot) */
108 #undef PLOT_RCALCURVE	/* Plot the reflection reference curve */
109 #undef PLOT_ECALCURVES	/* Plot the emission reference curves */
110 #undef PLOT_TEMPCOMP	/* Plot before and after LED temp. compensation */
111 #undef PLOT_PATREC		/* Print & Plot patch/flash recognition information */
112 #undef HIGH_RES_DEBUG
113 #undef HIGH_RES_PLOT
114 #undef HIGH_RES_PLOT_STRAYL		/* Plot stray light upsample */
115 
116 
117 #define DISP_INTT 0.7			/* Seconds per reading in display spot mode */
118 								/* More improves repeatability in dark colors, but limits */
119 								/* the maximum brightness level befor saturation. */
120 								/* A value of 2.0 seconds has a limit of about 110 cd/m^2 */
121 #define DISP_INTT2 0.3			/* High brightness display spot mode seconds per reading, */
122 								/* Should be good up to 275 cd/m^2 */
123 #define DISP_INTT3 0.1			/* High brightness display spot mode seconds per reading, */
124 								/* Should be good up to 700 cd/m^2 */
125 
126 #define ADARKINT_MIN 0.01		/* Min cal time for adaptive dark cal */
127 #define ADARKINT_MAX 2.0		/* Max cal time for adaptive dark cal with high gain mode */
128 #define ADARKINT_MAX2 4.0		/* Max cal time for adaptive dark for no high gain */
129 
130 #define RDEAD_TIME 0.004432		/* Fudge figure to make reflecting intn. time scale linearly */
131 
132 #define EMIS_SCALE_FACTOR 1.0	/* Emission mode scale factor */
133 #define AMB_SCALE_FACTOR 1.0	/* Ambient mode scale factor for Lux */
134 
135 #define NSEN_MAX 140            /* Maximum nsen/raw value we can cope with */
136 
137 /* Wavelength to start duplicating values below, because it is too noisy */
138 #define WL_REF_MIN 420.0
139 #define WL_EMIS_MIN 400.0
140 
141 /* High res mode settings */
142 #define HIGHRES_SHORT 380		/* Wavelength to calculate. Too noisy to try expanding range. */
143 #define HIGHRES_LONG  730
144 #define HIGHRES_WIDTH  (10.0/3.0) /* (The 3.3333 spacing and lanczos2 seems a good combination) */
145 
146 
147 #include "munki.h"
148 #include "munki_imp.h"
149 #include "xrga.h"
150 
151 /* - - - - - - - - - - - - - - - - - - */
152 
153 #define PATCH_CONS_THR 0.05		/* Dark measurement consistency threshold */
154 #define DARKTHSCAMIN 5000.0		/* Dark threshold scaled/offset minimum */
155 
156 /* - - - - - - - - - - - - - - - - - - */
157 
158 /* Three levels of runtime debugging messages:
159 
160 	~~~ this is no longer accurate. a1logd calls
161 	~~~ probably need to be tweaked.
162    1 = default, typical I/O messages etc.
163    2 = more internal operation messages
164    3 = dump extra detailes
165    4 = dump EEPROM data
166    5 = dump tables etc
167 */
168 
169 /* ============================================================ */
170 /* Debugging plot support */
171 
172 #if defined(DEBUG) || defined(PLOT_DEBUG) || defined(PLOT_PATREC) || defined(HIGH_RES_PLOT) ||  defined(HIGH_RES_PLOT_STRAYL)
173 
174 # include <plot.h>
175 
176 static int disdebplot = 0;
177 
178 # define DISDPLOT disdebplot = 1;
179 # define ENDPLOT disdebplot = 0;
180 
181 /* Plot a CCD spectra */
plot_raw(double * data)182 void plot_raw(double *data) {
183 	int i;
184 	double xx[NSEN_MAX];
185 	double yy[NSEN_MAX];
186 
187 	if (disdebplot)
188 		return;
189 
190 	for (i = 0; i < 128; i++) {
191 		xx[i] = (double)i;
192 		yy[i] = data[i];
193 	}
194 	do_plot(xx, yy, NULL, NULL, 128);
195 }
196 
197 /* Plot two CCD spectra */
plot_raw2(double * data1,double * data2)198 void plot_raw2(double *data1, double *data2) {
199 	int i;
200 	double xx[NSEN_MAX];
201 	double y1[NSEN_MAX];
202 	double y2[NSEN_MAX];
203 
204 	if (disdebplot)
205 		return;
206 
207 	for (i = 0; i < 128; i++) {
208 		xx[i] = (double)i;
209 		y1[i] = data1[i];
210 		y2[i] = data2[i];
211 	}
212 	do_plot(xx, y1, y2, NULL, 128);
213 }
214 
215 /* Plot a converted spectra */
plot_wav(munkiimp * m,double * data)216 void plot_wav(munkiimp *m, double *data) {
217 	int i;
218 	double xx[NSEN_MAX];
219 	double yy[NSEN_MAX];
220 
221 	if (disdebplot)
222 		return;
223 
224 	for (i = 0; i < m->nwav; i++) {
225 		xx[i] = XSPECT_WL(m->wl_short, m->wl_long, m->nwav, i);
226 		yy[i] = data[i];
227 	}
228 	do_plot(xx, yy, NULL, NULL, m->nwav);
229 }
230 
231 /* Plot a standard res spectra */
plot_wav1(munkiimp * m,double * data)232 void plot_wav1(munkiimp *m, double *data) {
233 	int i;
234 	double xx[36];
235 	double yy[36];
236 
237 	if (disdebplot)
238 		return;
239 
240 	for (i = 0; i < m->nwav1; i++) {
241 		xx[i] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
242 		yy[i] = data[i];
243 	}
244 	do_plot(xx, yy, NULL, NULL, m->nwav1);
245 }
246 
247 /* Plot a high res spectra */
plot_wav2(munkiimp * m,double * data)248 void plot_wav2(munkiimp *m, double *data) {
249 	int i;
250 	double xx[NSEN_MAX];
251 	double yy[NSEN_MAX];
252 
253 	if (disdebplot)
254 		return;
255 
256 	for (i = 0; i < m->nwav2; i++) {
257 		xx[i] = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
258 		yy[i] = data[i];
259 	}
260 	do_plot(xx, yy, NULL, NULL, m->nwav2);
261 }
262 
263 #else	/* !PLOT_DEBUG */
264 
265 # define DISDPLOT
266 # define ENDPLOT
267 
268 #endif	/* !PLOT_DEBUG */
269 
270 /* ============================================================ */
271 
272 munki_code munki_touch_calibration(munki *p);
273 
274 /* Implementation struct */
275 
276 /* Add an implementation structure */
add_munkiimp(munki * p)277 munki_code add_munkiimp(munki *p) {
278 	munkiimp *m;
279 
280 	if ((m = (munkiimp *)calloc(1, sizeof(munkiimp))) == NULL) {
281 		a1logd(p->log,3,"add_munkiimp malloc %lu bytes failed (1)\n",sizeof(munkiimp));
282 		return MUNKI_INT_MALLOC;
283 	}
284 	m->p = p;
285 
286 	m->lo_secs = 2000000000;        /* A very long time */
287 
288 	p->m = (void *)m;
289 	return MUNKI_OK;
290 }
291 
292 /* Shutdown instrument, and then destroy */
293 /* implementation structure */
del_munkiimp(munki * p)294 void del_munkiimp(munki *p) {
295 
296 	a1logd(p->log,3,"munki_del called\n");
297 
298 #ifdef ENABLE_NONVCAL
299 	/* Touch it so that we know when the instrument was last open */
300 	munki_touch_calibration(p);
301 #endif /* ENABLE_NONVCAL */
302 
303 	if (p->m != NULL) {
304 		int i;
305 		munkiimp *m = (munkiimp *)p->m;
306 		munki_state *s;
307 
308 #ifdef FILTER_SPOS_EVENTS
309 		if (m->spos_th != NULL)
310 			m->spos_th_term = 1;
311 #endif
312 
313 		if (m->th != NULL) {		/* Terminate switch monitor thread by simulating an event */
314 			m->th_term = 1;			/* Tell thread to exit on error */
315 			munki_simulate_event(p, mk_eve_spos_change, 0);
316 			for (i = 0; m->th_termed == 0 && i < 5; i++)
317 				msec_sleep(50);	/* Wait for thread to terminate */
318 			if (i >= 5) {
319 				a1logd(p->log,3,"Munki switch thread termination failed\n");
320 			}
321 			m->th->del(m->th);
322 			usb_uninit_cancel(&m->sw_cancel);	/* Don't need cancel token now */
323 		}
324 
325 #ifdef FILTER_SPOS_EVENTS
326 		if (m->spos_th != NULL) {
327 			for (i = 0; m->spos_th_termed == 0 && i < 5; i++)
328 				msec_sleep(50);	/* Wait for thread to terminate */
329 			if (i >= 5) {
330 				a1logd(p->log,3,"Munki spos thread termination failed\n");
331 			}
332 			m->spos_th->del(m->spos_th);
333 		}
334 #endif
335 
336 		/* Free any per mode data */
337 		for (i = 0; i < mk_no_modes; i++) {
338 			s = &m->ms[i];
339 
340 			free_dvector(s->dark_data, -1, m->nraw-1);
341 			free_dvector(s->dark_data2, -1, m->nraw-1);
342 			free_dvector(s->dark_data3, -1, m->nraw-1);
343 			free_dvector(s->white_data, -1, m->nraw-1);
344 			free_dmatrix(s->iwhite_data, 0, 1, -1, m->nraw-1);
345 			free_dmatrix(s->idark_data, 0, 3, -1, m->nraw-1);
346 
347 			free_dvector(s->cal_factor1, 0, m->nwav1-1);
348 			free_dvector(s->cal_factor2, 0, m->nwav2-1);
349 		}
350 
351 		/* Free EEProm key data */
352 		if (m->data != NULL)
353 			m->data->del(m->data);
354 
355 		/* Free arrays */
356 
357 		if (m->lin0 != NULL)
358 			free(m->lin0);
359 		if (m->lin1 != NULL)
360 			free(m->lin1);
361 
362 		if (m->white_ref1 != NULL)
363 			free(m->white_ref1);
364 		if (m->emis_coef1 != NULL)
365 			free(m->emis_coef1);
366 		if (m->amb_coef1 != NULL)
367 			free(m->amb_coef1);
368 		if (m->proj_coef1 != NULL)
369 			free(m->proj_coef1);
370 
371 		if (m->white_ref2 != NULL)
372 			free(m->white_ref2);
373 		if (m->emis_coef2 != NULL)
374 			free(m->emis_coef2);
375 		if (m->amb_coef2 != NULL)
376 			free(m->amb_coef2);
377 		if (m->proj_coef2 != NULL)
378 			free(m->proj_coef2);
379 
380 		if (m->straylight1 != NULL)
381 			free_dmatrix(m->straylight1, 0, m->nwav1-1, 0, m->nwav1-1);
382 
383 		if (m->straylight2 != NULL)
384 			free_dmatrix(m->straylight2, 0, m->nwav1-2, 0, m->nwav1-2);
385 
386 		if (m->rmtx_index1 != NULL)
387 			free(m->rmtx_index1);
388 		if (m->rmtx_nocoef1 != NULL)
389 			free(m->rmtx_nocoef1);
390 		if (m->rmtx_coef1 != NULL)
391 			free(m->rmtx_coef1);
392 
393 		if (m->rmtx_index2 != NULL)
394 			free(m->rmtx_index2);
395 		if (m->rmtx_nocoef2 != NULL)
396 			free(m->rmtx_nocoef2);
397 		if (m->rmtx_coef2 != NULL)
398 			free(m->rmtx_coef2);
399 
400 		if (m->emtx_index1 != NULL)
401 			free(m->emtx_index1);
402 		if (m->emtx_nocoef1 != NULL)
403 			free(m->emtx_nocoef1);
404 		if (m->emtx_coef1 != NULL)
405 			free(m->emtx_coef1);
406 
407 		if (m->emtx_index2 != NULL)
408 			free(m->emtx_index2);
409 		if (m->emtx_nocoef2 != NULL)
410 			free(m->emtx_nocoef2);
411 		if (m->emtx_coef2 != NULL)
412 			free(m->emtx_coef2);
413 
414 		free(m);
415 		p->m = NULL;
416 	}
417 }
418 
419 /* ============================================================ */
420 /* Little endian wire format conversion routines */
421 
422 /* Take an int, and convert it into a byte buffer little endian */
int2buf(unsigned char * buf,int inv)423 static void int2buf(unsigned char *buf, int inv) {
424 	buf[3] = (inv >> 24) & 0xff;
425 	buf[2] = (inv >> 16) & 0xff;
426 	buf[1] = (inv >> 8) & 0xff;
427 	buf[0] = (inv >> 0) & 0xff;
428 }
429 
430 /* Take a short, and convert it into a byte buffer little endian */
short2buf(unsigned char * buf,int inv)431 static void short2buf(unsigned char *buf, int inv) {
432 	buf[1] = (inv >> 8) & 0xff;
433 	buf[0] = (inv >> 0) & 0xff;
434 }
435 
436 /* Take a word sized buffer, and convert it to an int */
buf2int(unsigned char * buf)437 static int buf2int(unsigned char *buf) {
438 	int val;
439 	val =      ((signed char *)buf)[3];
440 	val = ((val << 8) + (0xff & buf[2]));
441 	val = ((val << 8) + (0xff & buf[1]));
442 	val = ((val << 8) + (0xff & buf[0]));
443 	return val;
444 }
445 
446 /* Take a word sized buffer, and convert it to an unsigned int */
buf2uint(unsigned char * buf)447 static unsigned int buf2uint(unsigned char *buf) {
448 	unsigned int val;
449 	val =               (0xff & buf[3]);
450 	val = ((val << 8) + (0xff & buf[2]));
451 	val = ((val << 8) + (0xff & buf[1]));
452 	val = ((val << 8) + (0xff & buf[0]));
453 	return val;
454 }
455 
456 /* Take a short sized buffer, and convert it to an int */
buf2short(unsigned char * buf)457 static int buf2short(unsigned char *buf) {
458 	int val;
459 	val =      ((signed char *)buf)[1];
460 	val = ((val << 8) + (0xff & buf[0]));
461 	return val;
462 }
463 
464 /* Take a unsigned short sized buffer, and convert it to an int */
buf2ushort(unsigned char * buf)465 static int buf2ushort(unsigned char *buf) {
466 	int val;
467 	val =               (0xff & buf[1]);
468 	val = ((val << 8) + (0xff & buf[0]));
469 	return val;
470 }
471 
472 /* ============================================================ */
473 /* High level functions */
474 
475 /* Initialise our software state from the hardware */
munki_imp_init(munki * p)476 munki_code munki_imp_init(munki *p) {
477 	munkiimp *m = (munkiimp *)p->m;
478 	munki_code ev = MUNKI_OK;
479 	unsigned char buf[4];
480 	int calsize = 0, rucalsize;
481 	unsigned char *calbuf;	/* EEProm contents */
482 	char *envv;
483 
484 	a1logd(p->log,2,"munki_init:\n");
485 
486 	if (p->itype != instColorMunki)
487 		return MUNKI_UNKNOWN_MODEL;
488 
489 
490 	m->native_calstd = xcalstd_xrga;
491 	m->target_calstd = xcalstd_native;		/* Default to native calibration standard*/
492 
493 	/* Honour Environment override */
494 	if ((envv = getenv("ARGYLL_XCALSTD")) != NULL) {
495 		if (strcmp(envv, "XRGA") == 0)
496 			m->target_calstd = xcalstd_xrga;
497 		else if (strcmp(envv, "XRDI") == 0)
498 			m->target_calstd = xcalstd_xrdi;
499 		else if (strcmp(envv, "GMDI") == 0)
500 			m->target_calstd = xcalstd_gmdi;
501 	}
502 
503 #ifdef ENABLE_SPOS_CHECK
504 	m->nosposcheck = 0;
505 #else
506 # pragma message("####### ColorMunki Sensor Position Check is OFF! ########")
507 	m->nosposcheck = 1;
508 #endif
509 
510 	m->trig = inst_opt_trig_user;
511 	m->scan_toll_ratio = 1.0;
512 
513 	/* Get the firmware parameters so that we can check eeprom range. */
514 	if ((ev = munki_getfirm(p, &m->fwrev, &m->tickdur, &m->minintcount, &m->noeeblocks, &m->eeblocksize)) != MUNKI_OK)
515 		return ev;
516 	a1logd(p->log,2,"Firmware rev = %d.%d\n",m->fwrev/256, m->fwrev % 256);
517 
518 	/* Check the EEProm */
519 	if (m->noeeblocks != 2 || m->eeblocksize != 8192) {
520 		a1logw(p->log,"EEProm is unexpected size\n");
521 		return MUNKI_INT_ASSERT;
522 	}
523 
524 	/* Tick in seconds */
525 	m->intclkp = (double)m->tickdur * 1e-6;
526 
527 	/* Set these to reasonable values */
528 	m->min_int_time = m->intclkp * (double)m->minintcount;
529 	m->max_int_time = 4.5;
530 
531 	a1logd(p->log,3, "minintcount %d, min_int_time = %f\n", m->minintcount, m->min_int_time);
532 
533 	/* Get the Chip ID */
534 	if ((ev = munki_getchipid(p, m->chipid)) != MUNKI_OK)
535 		return ev;
536 
537 	/* Get the Version String */
538 	if ((ev = munki_getversionstring(p, m->vstring)) != MUNKI_OK)
539 		return ev;
540 
541 	/* Dump the eeprom contents as a block */
542 	if (p->log->debug >= 9) {
543 		int base, size;
544 
545 		a1logd(p->log,7, "EEPROM contents:\n");
546 
547 		size = 1024;	/* read size == buffer size */
548 		for (base = 0; base < (2 * 8192); base += size) {
549 			unsigned char eeprom[1024];
550 
551 			if ((ev = munki_readEEProm(p, eeprom, base, size)) != MUNKI_OK)
552 				return ev;
553 
554 			adump_bytes(p->log, "  ", eeprom, base, size);
555 		}
556 	}
557 
558 	/* Read the calibration size */
559 	if ((ev = munki_readEEProm(p, buf, 4, 4)) != MUNKI_OK)
560 		return ev;
561 	calsize = buf2int(buf);
562 	rucalsize = (calsize + 3) & ~3;	/* Round up to next 32 bits */
563 
564 	if (calsize < 12)
565 		return MUNKI_INT_CALTOOSMALL;
566 	if (calsize > (m->noeeblocks * m->eeblocksize))
567 		return MUNKI_INT_CALTOOBIG;
568 
569 	/* Read the calibration raw data from the EEProm */
570 	 if ((calbuf = (unsigned char *)calloc(rucalsize, sizeof(unsigned char))) == NULL) {
571 		a1logd(p->log,3,"munki_imp_init malloc %d bytes failed\n",rucalsize);
572 		return MUNKI_INT_MALLOC;
573 	}
574 	if ((ev = munki_readEEProm(p, calbuf, 0, calsize)) != MUNKI_OK)
575 		return ev;
576 
577 	if ((ev = munki_parse_eeprom(p, calbuf, rucalsize)) != MUNKI_OK)
578 		return ev;
579 
580 	free(calbuf);
581 	calbuf = NULL;
582 
583 #ifdef USE_THREAD
584 	/* Setup the switch monitoring thread */
585 	usb_init_cancel(&m->sw_cancel);			/* Get cancel token ready */
586 	if ((m->th = new_athread(munki_switch_thread, (void *)p)) == NULL)
587 		return MUNKI_INT_THREADFAILED;
588 #endif
589 
590 #ifdef FILTER_SPOS_EVENTS
591 	/* Setup the sensor position filter thread */
592 	if ((m->spos_th = new_athread(munki_spos_thread, (void *)p)) == NULL)
593 		return MUNKI_INT_THREADFAILED;
594 #endif
595 
596 	/* Set up the current state of each mode */
597 	{
598 		int i, j;
599 		munki_state *s;
600 
601 		/* First set state to basic configuration */
602 		for (i = 0; i < mk_no_modes; i++) {
603 			s = &m->ms[i];
604 
605 			s->mode = i;
606 
607 			/* Default to an emissive configuration */
608 			s->targoscale = 0.90;	/* Allow extra 10% margine by default */
609 			s->targmaxitime = 2.0;	/* Maximum integration time to aim for */
610 			s->targoscale2 = 0.15;	/* Proportion of targoscale to meed targmaxitime */
611 
612 #ifdef USE_HIGH_GAIN_MODE
613 			s->auto_gain = 1;		/* No high gain by default */
614 #else
615 			s->auto_gain = 0;		/* No high gain by default */
616 #endif
617 			s->gainmode = 0;		/* Normal gain mode */
618 			s->inttime = 0.5;		/* Initial integration time */
619 
620 
621 			s->dark_valid = 0;		/* Dark cal invalid */
622 			s->dark_data = dvectorz(-1, m->nraw-1);
623 			s->dark_data2 = dvectorz(-1, m->nraw-1);
624 			s->dark_data3 = dvectorz(-1, m->nraw-1);
625 
626 			s->cal_valid = 0;		/* Scale cal invalid */
627 			s->cal_factor1 = dvectorz(0, m->nwav1-1);
628 			s->cal_factor2 = dvectorz(0, m->nwav2-1);
629 			s->cal_factor = s->cal_factor1; /* Default to standard resolution */
630 			s->white_data = dvectorz(-1, m->nraw-1);
631 			s->iwhite_data = dmatrixz(0, 1, -1, m->nraw-1);
632 
633 			s->idark_valid = 0;		/* Interpolatable Dark cal invalid */
634 			s->idark_data = dmatrixz(0, 3, -1, m->nraw-1);
635 
636 			s->dark_int_time  = DISP_INTT;	/* 0.7 */
637 			s->dark_int_time2 = DISP_INTT2;	/* 0.3 */
638 			s->dark_int_time3 = DISP_INTT3;	/* 0.1 */
639 
640 			s->idark_int_time[0] = s->idark_int_time[2] = m->min_int_time;
641 			s->idark_int_time[1] = ADARKINT_MAX; /* 2.0 */
642 			s->idark_int_time[3] = ADARKINT_MAX2; /* 4.0 */
643 
644 			s->want_calib = 1;		/* By default want an initial calibration */
645 			s->want_dcalib = 1;
646 		}
647 
648 		/* Then add mode specific settings */
649 		for (i = 0; i < mk_no_modes; i++) {
650 			s = &m->ms[i];
651 			switch(i) {
652 				case mk_refl_spot:
653 					s->auto_gain = 0;			/* Don't automatically set gain */
654 					s->targoscale = 1.0;		/* Optimised sensor scaling to full */
655 					s->reflective = 1;
656 					s->adaptive = 0;
657 					s->inttime = s->targoscale * m->cal_int_time;
658 					s->dark_int_time = s->inttime;
659 
660 					s->dpretime = 0.20;			/* Pre-measure time */
661 					s->wpretime = 0.20;
662 					s->dcaltime = 0.5;			/* same as reading */
663 					s->wcaltime = 0.5;			/* same as reading */
664 					s->dreadtime = 0.5;			/* same as reading */
665 					s->wreadtime = 0.5;
666 					s->maxscantime = 0.0;
667 					break;
668 
669 				case mk_refl_scan:
670 					s->auto_gain = 0;				/* Don't automatically set gain */
671 					s->targoscale = 1.0;			/* Maximize level */
672 					s->reflective = 1;
673 					s->scan = 1;
674 					s->adaptive = 0;
675 //					s->inttime = (s->targoscale * m->cal_int_time - RDEAD_TIME) + RDEAD_TIME;
676 //					if (s->inttime < m->min_int_time)
677 //						s->inttime = m->min_int_time;
678 //					s->dark_int_time = s->inttime;
679 					s->inttime = s->targoscale * m->cal_int_time;
680 					s->dark_int_time = s->inttime;
681 
682 					s->dpretime = 0.20;		/* Pre-measure time */
683 					s->wpretime = 0.20;
684 					s->dcaltime = 0.5;
685 					s->wcaltime = 0.5;
686 					s->dreadtime = 0.10;
687 					s->wreadtime = 0.10;
688 					s->maxscantime = MAXSCANTIME;
689 					break;
690 
691 				case mk_emiss_spot_na:			/* Emissive spot not adaptive */
692 				case mk_tele_spot_na:			/* Tele spot not adaptive */
693 					s->targoscale = 0.90;		/* Allow extra 10% margine */
694 					if (i == mk_emiss_spot_na) {
695 						for (j = 0; j < m->nwav1; j++)
696 							s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->emis_coef1[j];
697 					} else {
698 						for (j = 0; j < m->nwav1; j++)
699 							s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->proj_coef1[j];
700 						s->projector = 1;
701 					}
702 					s->cal_valid = 1;
703 					s->emiss = 1;
704 					s->adaptive = 0;
705 
706 					s->inttime = DISP_INTT;		/* Default disp integration time (ie. 0.7 sec) */
707 					s->dark_int_time = s->inttime;
708 					s->dark_int_time2 = DISP_INTT2;	/* Alternate disp integration time (ie. 0.3) */
709 					s->dark_int_time3 = DISP_INTT3;	/* Alternate disp integration time (ie. 0.1) */
710 
711 					s->dpretime = 0.0;
712 					s->wpretime = 0.20;
713 					s->dcaltime = 1.0;		/* ie. determines number of measurements */
714 					s->dcaltime2 = 1.0;
715 					s->dcaltime3 = 1.0;
716 					s->wcaltime = 0.0;
717 					s->dreadtime = 0.0;
718 					s->wreadtime = DISP_INTT;
719 					s->maxscantime = 0.0;
720 					break;
721 
722 				case mk_emiss_spot:
723 				case mk_amb_spot:
724 				case mk_tele_spot:				/* Adaptive projector */
725 					s->targoscale = 0.90;		/* Allow extra 5% margine */
726 					if (i == mk_emiss_spot) {
727 						for (j = 0; j < m->nwav1; j++)
728 							s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->emis_coef1[j];
729 					} else if (i == mk_amb_spot) {
730 						for (j = 0; j < m->nwav1; j++)
731 							s->cal_factor1[j] = AMB_SCALE_FACTOR * m->amb_coef1[j];
732 						s->ambient = 1;
733 					} else {
734 						for (j = 0; j < m->nwav1; j++)
735 							s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->proj_coef1[j];
736 						s->projector = 1;
737 					}
738 
739 					s->cal_valid = 1;
740 					s->emiss = 1;
741 					s->adaptive = 1;
742 
743 					s->dpretime = 0.0;
744 					s->wpretime = 0.10;
745 					s->dcaltime = 1.0;
746 					s->wcaltime = 0.0;
747 					s->dreadtime = 0.0;
748 					s->wreadtime = 1.0;
749 					s->maxscantime = 0.0;
750 					break;
751 
752 				case mk_emiss_scan:
753 				case mk_amb_flash:
754 					s->targoscale = 0.90;		/* Allow extra 10% margine */
755 					if (i == mk_emiss_scan) {
756 						for (j = 0; j < m->nwav1; j++)
757 							s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->emis_coef1[j];
758 					} else {
759 						for (j = 0; j < m->nwav1; j++)
760 							s->cal_factor1[j] = AMB_SCALE_FACTOR * m->amb_coef1[j];
761 						s->ambient = 1;
762 						s->flash = 1;
763 					}
764 					s->cal_valid = 1;
765 					s->emiss = 1;
766 					s->scan = 1;
767 					s->adaptive = 0;
768 					s->inttime = m->min_int_time;
769 					s->dark_int_time = s->inttime;
770 
771 					s->dpretime = 0.0;
772 					s->wpretime = 0.10;
773 					s->dcaltime = 1.0;
774 					s->wcaltime = 0.0;
775 					s->dreadtime = 0.0;
776 					s->wreadtime = 0.10;
777 					s->maxscantime = MAXSCANTIME;
778 					break;
779 
780 				/* Transparency has a white reference, and interpolated dark ref. */
781 				case mk_trans_spot:
782 					s->targoscale = 0.90;		/* Allow extra 10% margine */
783 					s->trans = 1;
784 					s->adaptive = 1;
785 
786 					s->dpretime = 0.20;
787 					s->wpretime = 0.20;
788 					s->dcaltime = 1.0;
789 					s->wcaltime = 1.0;
790 					s->dreadtime = 0.0;
791 					s->wreadtime = 1.0;
792 					s->maxscantime = 0.0;
793 					break;
794 
795 				case mk_trans_scan:
796 					// ~~99 should we use high gain mode ??
797 					s->targoscale = 0.10;		/* Scan as fast as possible */
798 					s->trans = 1;
799 					s->scan = 1;
800 					s->inttime = s->targoscale * m->cal_int_time;
801 					if (s->inttime < m->min_int_time)
802 						s->inttime = m->min_int_time;
803 					s->dark_int_time = s->inttime;
804 					s->adaptive = 0;
805 
806 					s->dpretime = 0.20;
807 					s->wpretime = 0.20;
808 					s->dcaltime = 1.0;
809 					s->wcaltime = 1.0;
810 					s->dreadtime = 0.00;
811 					s->wreadtime = 0.10;
812 					s->maxscantime = MAXSCANTIME;
813 					break;
814 			}
815 		}
816 	}
817 
818 #ifdef ENABLE_NONVCAL
819 	/* Restore the all modes calibration from the local system */
820 	munki_restore_calibration(p);
821 	/* Touch it so that we know when the instrument was last opened */
822 	munki_touch_calibration(p);
823 #endif
824 
825 	a1logv(p->log, 1,
826 		"Instrument Type:   ColorMunki\n"	// ~~ should get this from version string ?
827 		"Serial Number:     %s\n"
828 		"Firmware version:  %d\n"
829 		"Chip ID:           %02X-%02X%02X%02X%02X%02X%02X%02X\n"
830 		"Version string:    '%s'\n"
831 		"Calibration Ver.:  %d\n"
832 		"Production No.:    %d\n",
833 		m->serno,
834 		m->fwrev,
835 	    m->chipid[0], m->chipid[1], m->chipid[2], m->chipid[3],
836 		m->chipid[4], m->chipid[5], m->chipid[6], m->chipid[7],
837 		m->vstring,
838 		m->calver,
839 		m->prodno);
840 
841 	/* Flash the LED, just cos we can! */
842 	if ((ev = munki_setindled(p, 1000,0,0,-1,0)) != MUNKI_OK)
843 		return ev;
844 	msec_sleep(200);
845 	if ((ev = munki_setindled(p, 0,0,0,0,0)) != MUNKI_OK)
846 		return ev;
847 
848 	return ev;
849 }
850 
851 /* Return a pointer to the serial number */
munki_imp_get_serial_no(munki * p)852 char *munki_imp_get_serial_no(munki *p) {
853 	munkiimp *m = (munkiimp *)p->m;
854 
855 	return m->serno;
856 }
857 
858 /* Set the measurement mode. It may need calibrating */
munki_imp_set_mode(munki * p,mk_mode mmode,inst_mode mode)859 munki_code munki_imp_set_mode(
860 	munki *p,
861 	mk_mode mmode,		/* Operating mode */
862 	inst_mode mode		/* Full mode mask for options */
863 ) {
864 	munkiimp *m = (munkiimp *)p->m;
865 
866 	a1logd(p->log,2,"munki_imp_set_mode called with mode no. %d and mask 0x%x\n",mmode,m);
867 	switch(mmode) {
868 		case mk_refl_spot:
869 		case mk_refl_scan:
870 		case mk_emiss_spot_na:
871 		case mk_tele_spot_na:
872 		case mk_emiss_spot:
873 		case mk_tele_spot:
874 		case mk_emiss_scan:
875 		case mk_amb_spot:
876 		case mk_amb_flash:
877 		case mk_trans_spot:
878 		case mk_trans_scan:
879 			m->mmode = mmode;
880 			break;
881 		default:
882 			return MUNKI_INT_ILLEGALMODE;
883 	}
884 	m->spec_en = (mode & inst_mode_spectral) != 0;
885 
886 	if ((mode & inst_mode_highres) != 0) {
887 		munki_code rv;
888 		if ((rv = munki_set_highres(p)) != MUNKI_OK)
889 			return rv;
890 	} else {
891 		munki_set_stdres(p);	/* Ignore any error */
892 	}
893 
894 	return MUNKI_OK;
895 }
896 
897 /* Return needed and available inst_cal_type's */
munki_imp_get_n_a_cals(munki * p,inst_cal_type * pn_cals,inst_cal_type * pa_cals)898 munki_code munki_imp_get_n_a_cals(munki *p, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
899 	munkiimp *m = (munkiimp *)p->m;
900 	munki_state *cs = &m->ms[m->mmode];
901 	time_t curtime = time(NULL);
902 	inst_cal_type n_cals = inst_calt_none;
903 	inst_cal_type a_cals = inst_calt_none;
904 	int idark_valid = cs->idark_valid;			/* Current state of calib */
905 	int dark_valid = cs->dark_valid;
906 	int cal_valid = cs->cal_valid;
907 
908 	a1logd(p->log,3,"munki_imp_get_n_a_cals: checking mode %d\n",m->mmode);
909 
910 	/* Timout calibrations that are too old */
911 	a1logd(p->log,4,"curtime %u, iddate %u, ddate %u, cfdate %u\n",curtime,cs->iddate,cs->ddate,cs->cfdate);
912 	if ((curtime - cs->iddate) > DCALTOUT) {
913 		a1logd(p->log,3,"Invalidating adaptive dark cal as %d secs from last cal\n",curtime - cs->iddate);
914 		idark_valid = 0;
915 	}
916 	if ((curtime - cs->ddate) > DCALTOUT) {
917 		a1logd(p->log,3,"Invalidating dark cal as %d secs from last cal\n",curtime - cs->ddate);
918 		dark_valid = 0;
919 	}
920 	if (!cs->emiss && (curtime - cs->cfdate) > WCALTOUT) {
921 		a1logd(p->log,3,"Invalidating white cal as %d secs from last cal\n",curtime - cs->cfdate);
922 		cal_valid = 0;
923 	}
924 
925 	if (cs->reflective) {
926 		if (!dark_valid
927 		 || (cs->want_dcalib && !m->noinitcalib))
928 			n_cals |= inst_calt_ref_dark;
929 		a_cals |= inst_calt_ref_dark;
930 
931 		if (!cal_valid
932 		 || (cs->want_calib && !m->noinitcalib))
933 			n_cals |= inst_calt_ref_white;
934 		a_cals |= inst_calt_ref_white;
935 	}
936 	if (cs->emiss) {
937 		if ((!cs->adaptive && !dark_valid)
938 		 || (cs->adaptive && !idark_valid)
939 		 || (cs->want_dcalib && !m->noinitcalib))
940 			n_cals |= inst_calt_em_dark;
941 		a_cals |= inst_calt_em_dark;
942 	}
943 	if (cs->trans) {
944 		if ((!cs->adaptive && !dark_valid)
945 		 || (cs->adaptive && !idark_valid)
946 	     || (cs->want_dcalib && !m->noinitcalib))
947 			n_cals |= inst_calt_trans_dark;
948 		a_cals |= inst_calt_trans_dark;
949 
950 		if (!cal_valid
951 	     || (cs->want_calib && !m->noinitcalib))
952 			n_cals |= inst_calt_trans_vwhite;
953 		a_cals |= inst_calt_trans_vwhite;
954 	}
955 	if (cs->emiss && !cs->scan && !cs->adaptive) {
956 		if (!cs->done_dintsel)
957 			n_cals |= inst_calt_emis_int_time;
958 		a_cals |= inst_calt_emis_int_time;
959 	}
960 
961 	if (pn_cals != NULL)
962 		*pn_cals = n_cals;
963 
964 	if (pa_cals != NULL)
965 		*pa_cals = a_cals;
966 
967 	a1logd(p->log,3,"munki_imp_get_n_a_cals: returning n_cals 0x%x, a_cals 0x%x\n",n_cals, a_cals);
968 
969 	return MUNKI_OK;
970 }
971 
972 /* - - - - - - - - - - - - - - - - */
973 /* Calibrate for the current mode. */
974 /* Request an instrument calibration of the current mode. */
munki_imp_calibrate(munki * p,inst_cal_type * calt,inst_cal_cond * calc,inst_calc_id_type * idtype,char id[CALIDLEN])975 munki_code munki_imp_calibrate(
976 	munki *p,
977 	inst_cal_type *calt,	/* Calibration type to do/remaining */
978 	inst_cal_cond *calc,	/* Current condition/desired condition */
979 	inst_calc_id_type *idtype,	/* Condition identifier type */
980 	char id[CALIDLEN]		/* Condition identifier (ie. white reference ID) */
981 ) {
982 	munki_code ev = MUNKI_OK;
983 	munkiimp *m = (munkiimp *)p->m;
984 	int mmode = m->mmode;					/* Current actual mode */
985 	munki_state *cs = &m->ms[m->mmode];
986 	int sx1, sx2, sx;
987 	time_t cdate = time(NULL);
988 	int nummeas = 0;
989 	mk_spos spos;
990 	int i, j, k;
991 	inst_cal_type needed, available;
992 
993 	a1logd(p->log,3,"munki_imp_calibrate called with calt 0x%x, calc 0x%x\n",*calt, *calc);
994 
995 	if ((ev = munki_imp_get_n_a_cals(p, &needed, &available)) != MUNKI_OK)
996 		return ev;
997 
998 	/* Translate inst_calt_all/needed into something specific */
999 	if (*calt == inst_calt_all
1000 	 || *calt == inst_calt_needed
1001 	 || *calt == inst_calt_available) {
1002 		if (*calt == inst_calt_all)
1003 			*calt = (needed & inst_calt_n_dfrble_mask) | inst_calt_ap_flag;
1004 		else if (*calt == inst_calt_needed)
1005 			*calt = needed & inst_calt_n_dfrble_mask;
1006 		else if (*calt == inst_calt_available)
1007 			*calt = available & inst_calt_n_dfrble_mask;
1008 
1009 		a1logd(p->log,4,"munki_imp_calibrate: doing calt 0x%x\n",*calt);
1010 
1011 		if ((*calt & inst_calt_n_dfrble_mask) == 0)		/* Nothing todo */
1012 			return MUNKI_OK;
1013 	}
1014 
1015 	/* See if it's a calibration we understand */
1016 	if (*calt & ~available & inst_calt_all_mask) {
1017 		return MUNKI_UNSUPPORTED;
1018 	}
1019 
1020 	/* Get current sensor position */
1021 	if ((ev = munki_getstatus(p, &spos, NULL)) != MUNKI_OK) {
1022 		return ev;
1023 	}
1024 	a1logd(p->log,4,"munki sensor position = 0x%x\n",spos);
1025 
1026 	/* We can set the *calc to the actual conditions, in which case */
1027 	/* the calibration will commence immediately. */
1028 	if (m->nocalibask && !m->nosposcheck && spos == mk_spos_calib) {
1029 		*calc = inst_calc_man_cal_smode;
1030 		a1logd(p->log,4,"munki set calc to cal conditions\n",spos);
1031 	}
1032 
1033 	/* Make sure that the instrument configuration matches the */
1034 	/* conditions */
1035 	if ((*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode) {
1036 		if (!m->nosposcheck && spos != mk_spos_calib) {
1037 			return MUNKI_SPOS_CALIB;
1038 		}
1039 	} else if ((*calc & inst_calc_cond_mask) == inst_calc_man_trans_white) {
1040 		if (!m->nosposcheck && spos != mk_spos_surf) {
1041 			return MUNKI_SPOS_SURF;
1042 		}
1043 	}
1044 
1045 	a1logd(p->log,4,"munki_imp_calibrate has right conditions\n");
1046 
1047 	if (*calt & inst_calt_ap_flag) {
1048 		sx1 = 0; sx2 = mk_no_modes;		/* Go through all the modes */
1049 	} else {
1050 		sx1 = m->mmode; sx2 = sx1 + 1;		/* Just current mode */
1051 	}
1052 
1053 	/* Go through the modes we are going to cover */
1054 	for (sx = sx1; sx < sx2; sx++) {
1055 		munki_state *s = &m->ms[sx];
1056 		m->mmode = sx;				/* A lot of functions we call rely on this */
1057 
1058 		a1logd(p->log,3,"\nCalibrating mode %d\n", s->mode);
1059 
1060 		/* Sanity check scan mode settings, in case something strange */
1061 		/* has been restored from the persistence file. */
1062 //		if (s->scan && s->inttime > (2.1 * m->min_int_time)) {
1063 //			s->inttime = m->min_int_time;	/* Maximize scan rate */
1064 //		}
1065 
1066 		/* We are now either in inst_calc_man_cal_smode, */
1067 		/* inst_calc_man_trans_white, inst_calc_disp_white or inst_calc_proj_white */
1068 		/* sequenced in that order, and in the appropriate condition for it. */
1069 
1070 		/* Fixed int. time black calibration: */
1071 		/* Reflective uses on the fly black, even for adaptive. */
1072 		/* Emiss and trans can use single black ref only for non-adaptive */
1073 		/* using the current inttime & gainmode, while display mode */
1074 		/* does an extra fallback black cal for bright displays. */
1075 		if ((*calt & (inst_calt_ref_dark
1076 		            | inst_calt_em_dark
1077 		            | inst_calt_trans_dark | inst_calt_ap_flag))
1078 		 && (*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode
1079 		 && ( s->reflective
1080 		  || (s->emiss && !s->adaptive && !s->scan)
1081 		  || (s->trans && !s->adaptive))) {
1082 			int stm;
1083 			int usesdct23 = 0;			/* Is a mode that uses dcaltime2 & 3 */
1084 
1085 			if (s->emiss && !s->adaptive && !s->scan)
1086 				usesdct23 = 1;
1087 
1088 			nummeas = munki_comp_nummeas(p, s->dcaltime, s->inttime);
1089 
1090 			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);
1091 			stm = msec_time();
1092 			if ((ev = munki_dark_measure(p, s->dark_data, nummeas, &s->inttime, s->gainmode))
1093 		                                                                         != MUNKI_OK) {
1094 				m->mmode = mmode;           /* Restore actual mode */
1095 				return ev;
1096 			}
1097 			a1logd(p->log,4,"Execution time of dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1098 
1099 			/* Special display mode alternate integration time black measurement */
1100 			if (usesdct23) {
1101 				nummeas = munki_comp_nummeas(p, s->dcaltime2, s->dark_int_time2);
1102 				a1logd(p->log,3,"Doing 2nd initial black calibration with dcaltime2 %f, dark_int_time2 %f, nummeas %d, gainmode %d\n", s->dcaltime2, s->dark_int_time2, nummeas, s->gainmode);
1103 				stm = msec_time();
1104 				if ((ev = munki_dark_measure(p, s->dark_data2, nummeas, &s->dark_int_time2,
1105 				                                                   s->gainmode)) != MUNKI_OK) {
1106 					m->mmode = mmode;           /* Restore actual mode */
1107 					return ev;
1108 				}
1109 				a1logd(p->log,4,"Execution time of 2nd dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1110 
1111 				nummeas = munki_comp_nummeas(p, s->dcaltime3, s->dark_int_time3);
1112 				a1logd(p->log,3,"Doing 3rd initial black calibration with dcaltime3 %f, dark_int_time3 %f, nummeas %d, gainmode %d\n", s->dcaltime3, s->dark_int_time3, nummeas, s->gainmode);
1113 				nummeas = munki_comp_nummeas(p, s->dcaltime3, s->dark_int_time3);
1114 				stm = msec_time();
1115 				if ((ev = munki_dark_measure(p, s->dark_data3, nummeas, &s->dark_int_time3,
1116 					                                                   s->gainmode)) != MUNKI_OK) {
1117 					m->mmode = mmode;           /* Restore actual mode */
1118 					return ev;
1119 				}
1120 				a1logd(p->log,4,"Execution time of 3rd dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1121 
1122 			}
1123 			s->dark_valid = 1;
1124 			s->want_dcalib = 0;
1125 			s->ddate = cdate;
1126 			s->dark_int_time = s->inttime;
1127 			s->dark_gain_mode = s->gainmode;
1128 			*calt &= ~(inst_calt_ref_dark
1129 		             | inst_calt_em_dark
1130 		             | inst_calt_trans_dark);
1131 
1132 			/* Save the calib to all similar modes */
1133 			for (i = 0; i < mk_no_modes; i++) {
1134 				munki_state *ss = &m->ms[i];
1135 				if (ss == s || ss->ddate == cdate)
1136 					continue;
1137 				if ( (s->reflective
1138 				  || (ss->emiss && !ss->adaptive && !ss->scan)
1139 				  || (ss->trans && !ss->adaptive))
1140 				 && ss->dark_int_time == s->dark_int_time
1141 				 && ss->dark_gain_mode == s->dark_gain_mode) {
1142 
1143 					ss->dark_valid = s->dark_valid;
1144 					ss->want_dcalib = s->want_dcalib;
1145 					ss->ddate = s->ddate;
1146 					ss->dark_int_time = s->dark_int_time;
1147 					ss->dark_gain_mode = s->dark_gain_mode;
1148 					for (k = -1; k < m->nraw; k++)
1149 						ss->dark_data[k] = s->dark_data[k];
1150 					/* If this is a mode with dark_data2/3, tranfer it too */
1151 					if (usesdct23 && ss->emiss && !ss->adaptive && !ss->scan) {
1152 						ss->dark_int_time2 = s->dark_int_time2;
1153 #ifndef NEVER	// ~~99
1154 if (ss->dark_int_time2 != s->dark_int_time2
1155  || ss->dark_int_time3 != s->dark_int_time3)
1156 	a1logd(p->log,1,"copying cal to mode with different cal/gain mode: %d -> %d\n",s->mode, ss->mode);
1157 #endif
1158 						ss->dark_int_time3 = s->dark_int_time2;
1159 						for (k = -1; k < m->nraw; k++) {
1160 							ss->dark_data2[k] = s->dark_data2[k];
1161 							ss->dark_data3[k] = s->dark_data3[k];
1162 						}
1163 					}
1164 				}
1165 			}
1166 		}
1167 
1168 		/* Emissive scan black calibration: */
1169 		/* Emsissive scan (flash) uses the fastest possible scan rate (??) */
1170 		if ((*calt & (inst_calt_em_dark | inst_calt_ap_flag))
1171 		 && (*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode
1172 		 && (s->emiss && !s->adaptive && s->scan)) {
1173 			int stm;
1174 
1175 			nummeas = munki_comp_nummeas(p, s->dcaltime, s->inttime);
1176 
1177 			a1logd(p->log,3,"\nDoing emissive (flash) black calibration with dcaltime %f, int_time %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode);
1178 			stm = msec_time();
1179 			if ((ev = munki_dark_measure(p, s->dark_data, nummeas, &s->inttime, s->gainmode))
1180 		                                                                         != MUNKI_OK) {
1181 				m->mmode = mmode;           /* Restore actual mode */
1182 				return ev;
1183 			}
1184 			a1logd(p->log,4,"Execution time of dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1185 
1186 			s->dark_valid = 1;
1187 			s->want_dcalib = 0;
1188 			s->ddate = cdate;
1189 			s->dark_int_time = s->inttime;
1190 			s->dark_gain_mode = s->gainmode;
1191 			*calt &= ~inst_calt_em_dark;
1192 
1193 			/* Save the calib to all similar modes */
1194 			for (i = 0; i < mk_no_modes; i++) {
1195 				munki_state *ss = &m->ms[i];
1196 				if (ss == s || ss->ddate == cdate)
1197 					continue;
1198 				if ((ss->emiss && !ss->adaptive && ss->scan)
1199 				 && ss->dark_int_time == s->dark_int_time
1200 				 && ss->dark_gain_mode == s->dark_gain_mode) {
1201 					ss->dark_valid = s->dark_valid;
1202 					ss->want_dcalib = s->want_dcalib;
1203 					ss->ddate = s->ddate;
1204 					ss->dark_int_time = s->dark_int_time;
1205 					ss->dark_gain_mode = s->dark_gain_mode;
1206 					for (k = -1; k < m->nraw; k++) {
1207 						ss->dark_data[k] = s->dark_data[k];
1208 					}
1209 				}
1210 			}
1211 		}
1212 
1213 		/* Adaptive black calibration: */
1214 		/* Emmissive adaptive and transmissive black reference. */
1215 		/* in non-scan mode, where the integration time and gain may vary. */
1216 		if ((*calt & (inst_calt_ref_dark
1217 		            | inst_calt_em_dark
1218 		            | inst_calt_trans_dark | inst_calt_ap_flag))
1219 		 && (*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode
1220 		 && ((s->emiss && s->adaptive && !s->scan)
1221 		  || (s->trans && s->adaptive && !s->scan))) {
1222 			/* Adaptive where we can't measure the black reference on the fly, */
1223 			/* so bracket it and interpolate. */
1224 			/* The black reference is probably temperature dependent, but */
1225 			/* there's not much we can do about this. */
1226 
1227 			s->idark_int_time[0] = m->min_int_time;
1228 			nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[0]);
1229 			a1logd(p->log,3,"\nDoing adaptive interpolated black calibration, dcaltime %f, idark_int_time[0] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[0], nummeas, 0);
1230 			if ((ev = munki_dark_measure(p, s->idark_data[0], nummeas, &s->idark_int_time[0], 0))
1231 			                                                                          != MUNKI_OK) {
1232 				m->mmode = mmode;           /* Restore actual mode */
1233 				return ev;
1234 			}
1235 
1236 			nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[1]);
1237 			a1logd(p->log,3,"Doing adaptive interpolated black calibration, dcaltime %f, idark_int_time[1] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[1], nummeas, 0);
1238 			if ((ev = munki_dark_measure(p, s->idark_data[1], nummeas, &s->idark_int_time[1], 0))
1239 			                                                                          != MUNKI_OK) {
1240 				m->mmode = mmode;           /* Restore actual mode */
1241 				return ev;
1242 			}
1243 
1244 			if (s->auto_gain) {			/* If high gain is permitted */
1245 				s->idark_int_time[2] = m->min_int_time;	/* 0.01 */
1246 				nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]);
1247 				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);
1248 				if ((ev = munki_dark_measure(p, s->idark_data[2], nummeas, &s->idark_int_time[2], 1))
1249 				                                                                          != MUNKI_OK) {
1250 					m->mmode = mmode;           /* Restore actual mode */
1251 					return ev;
1252 				}
1253 
1254 				s->idark_int_time[3] = ADARKINT_MAX; /* 2.0 */
1255 				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);
1256 				nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[3]);
1257 				if ((ev = munki_dark_measure(p, s->idark_data[3], nummeas, &s->idark_int_time[3], 1))
1258 				                                                                          != MUNKI_OK) {
1259 					m->mmode = mmode;           /* Restore actual mode */
1260 					return ev;
1261 				}
1262 			}
1263 
1264 			munki_prepare_idark(p);
1265 
1266 			s->idark_valid = 1;
1267 			s->iddate = cdate;
1268 
1269 			if ((ev = munki_interp_dark(p, s->dark_data, s->inttime, s->gainmode)) != MUNKI_OK) {
1270 				m->mmode = mmode;           /* Restore actual mode */
1271 				return ev;
1272 			}
1273 			s->dark_valid = 1;
1274 			s->want_dcalib = 0;
1275 			s->ddate = s->iddate;
1276 			s->dark_int_time = s->inttime;
1277 			s->dark_gain_mode = s->gainmode;
1278 			*calt &= ~(inst_calt_ref_dark
1279 		            | inst_calt_em_dark
1280 		            | inst_calt_trans_dark);
1281 
1282 			/* Save the calib to all similar modes */
1283 			/* We're assuming they have the same int times */
1284 			a1logd(p->log,3,"Saving adaptive black calib to similar modes\n");
1285 			for (i = 0; i < mk_no_modes; i++) {
1286 				munki_state *ss = &m->ms[i];
1287 				if (ss == s || ss->iddate == cdate)
1288 					continue;
1289 				if ((ss->emiss || ss->trans) && ss->adaptive && !ss->scan) {
1290 					ss->idark_valid = s->idark_valid;
1291 					ss->want_dcalib = s->want_dcalib;
1292 					ss->iddate = s->iddate;
1293 					ss->dark_int_time = s->dark_int_time;
1294 					ss->dark_gain_mode = s->dark_gain_mode;
1295 #ifndef NEVER	// ~~99
1296 if (ss->dark_int_time != s->dark_int_time
1297  || ss->dark_gain_mode != s->dark_gain_mode)
1298 	a1logd(p->log,1,"copying cal to mode with different cal/gain mode: %d -> %d\n",s->mode, ss->mode);
1299 #endif
1300 					for (j = 0; j < (s->auto_gain ? 4 : 2); j++) {
1301 						ss->idark_int_time[j] = s->idark_int_time[j];
1302 #ifndef NEVER	// ~~99
1303 if (ss->idark_int_time[j] != s->idark_int_time[j])
1304 	a1logd(p->log,1,"copying cal to mode with different cal/gain mode: %d -> %d\n",s->mode, ss->mode);
1305 #endif
1306 						for (k = -1; k < m->nraw; k++)
1307 							ss->idark_data[j][k] = s->idark_data[j][k];
1308 					}
1309 				}
1310 			}
1311 
1312 			a1logd(p->log,3,"Done adaptive interpolated black calibration\n");
1313 
1314 			/* Test accuracy of dark level interpolation */
1315 #ifdef TEST_DARK_INTERP
1316 			{
1317 				double tinttime;
1318 				double ref[NSEN_MAX], interp[NSEN_MAX];
1319 
1320 	//			fprintf(stderr,"Normal gain offsets, base:\n");
1321 	//			plot_raw(s->idark_data[0]);
1322 	//			fprintf(stderr,"Normal gain offsets, multiplier:\n");
1323 	//			plot_raw(s->idark_data[1]);
1324 
1325 #ifdef DUMP_DARKM
1326 				extern int ddumpdarkm;
1327 				ddumpdarkm = 1;
1328 #endif
1329 				for (tinttime = m->min_int_time; ; tinttime *= 2.0) {
1330 					if (tinttime >= m->max_int_time)
1331 						tinttime = m->max_int_time;
1332 
1333 					nummeas = munki_comp_nummeas(p, s->dcaltime, tinttime);
1334 					if ((ev = munki_dark_measure(p, ref, nummeas, &tinttime, 0)) != MUNKI_OK) {
1335 						m->mmode = mmode;           /* Restore actual mode */
1336 						return ev;
1337 					}
1338 					munki_interp_dark(p, interp, tinttime, 0);
1339 #ifdef DEBUG
1340 					fprintf(stderr,"Normal gain, int time %f:\n",tinttime);
1341 					plot_raw2(ref, interp);
1342 #endif
1343 					if ((tinttime * 1.1) > m->max_int_time)
1344 						break;
1345 				}
1346 #ifdef DUMP_DARKM
1347 				ddumpdarkm = 0;
1348 #endif
1349 
1350 				if (s->auto_gain) {
1351 //					fprintf(stderr,"High gain offsets, base:\n");
1352 //					plot_raw(s->idark_data[2]);
1353 //					fprintf(stderr,"High gain offsets, multiplier:\n");
1354 //					plot_raw(s->idark_data[3]);
1355 
1356 					for (tinttime = m->min_int_time; ; tinttime *= 2.0) {
1357 						if (tinttime >= m->max_int_time)
1358 							tinttime = m->max_int_time;
1359 
1360 						nummeas = munki_comp_nummeas(p, s->dcaltime, tinttime);
1361 						if ((ev = munki_dark_measure(p, ref, nummeas, &tinttime, 1)) != MUNKI_OK) {
1362 							m->mmode = mmode;           /* Restore actual mode */
1363 							return ev;
1364 						}
1365 						munki_interp_dark(p, interp, tinttime, 1);
1366 #ifdef DEBUG
1367 						printf("High gain, int time %f:\n",tinttime);
1368 						plot_raw2(ref, interp);
1369 #endif
1370 						if ((tinttime * 1.1) > m->max_int_time)
1371 							break;
1372 					}
1373 			}
1374 			}
1375 #endif	/* TEST_DARK_INTERP */
1376 
1377 		}
1378 
1379 		/* Deal with an emissive/transmisive adaptive black reference */
1380 		/* when in scan mode. */
1381 		if ((*calt & (inst_calt_ref_dark
1382 		            | inst_calt_em_dark
1383 		            | inst_calt_trans_dark | inst_calt_ap_flag))
1384 		 && (*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode
1385 		 && ((s->emiss && s->adaptive && s->scan)
1386 		  || (s->trans && s->adaptive && s->scan))) {
1387 			int j;
1388 			/* We know scan is locked to the minimum integration time, */
1389 			/* so we can measure the dark data at that integration time, */
1390 			/* but we don't know what gain mode will be used, so measure both, */
1391 			/* and choose the appropriate one on the fly. */
1392 
1393 			s->idark_int_time[0] = s->inttime;
1394 			nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[0]);
1395 			a1logd(p->log,3,"\nDoing adaptive scan black calibration, dcaltime %f, idark_int_time[0] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[0], nummeas, s->gainmode);
1396 			if ((ev = munki_dark_measure(p, s->idark_data[0], nummeas, &s->idark_int_time[0], 0))
1397 			                                                                          != MUNKI_OK) {
1398 				m->mmode = mmode;           /* Restore actual mode */
1399 				return ev;
1400 			}
1401 
1402 			if (s->auto_gain) {
1403 				s->idark_int_time[2] = s->inttime;
1404 				nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]);
1405 				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);
1406 				if ((ev = munki_dark_measure(p, s->idark_data[2], nummeas, &s->idark_int_time[2], 1))
1407 				                                                                          != MUNKI_OK) {
1408 					m->mmode = mmode;           /* Restore actual mode */
1409 					return ev;
1410 				}
1411 			}
1412 
1413 			s->idark_valid = 1;
1414 			s->iddate = cdate;
1415 
1416 			if (s->auto_gain && s->gainmode) {
1417 				for (j = -1; j < m->nraw; j++)
1418 					s->dark_data[j] = s->idark_data[2][j];
1419 			} else {
1420 				for (j = -1; j < m->nraw; j++)
1421 					s->dark_data[j] = s->idark_data[0][j];
1422 			}
1423 			s->dark_valid = 1;
1424 			s->want_dcalib = 0;
1425 			s->ddate = s->iddate;
1426 			s->dark_int_time = s->inttime;
1427 			s->dark_gain_mode = s->gainmode;
1428 			*calt &= ~(inst_calt_ref_dark
1429 		            | inst_calt_em_dark
1430 		            | inst_calt_trans_dark);
1431 
1432 			a1logd(p->log,3,"Done adaptive scan black calibration\n");
1433 
1434 			/* Save the calib to all similar modes */
1435 			/* We're assuming they have the same int times */
1436 			a1logd(p->log,3,"Saving adaptive scan black calib to similar modes\n");
1437 			for (i = 0; i < mk_no_modes; i++) {
1438 				munki_state *ss = &m->ms[i];
1439 				if (ss == s || s->iddate == cdate)
1440 					continue;
1441 				if ((ss->emiss || ss->trans) && ss->adaptive && s->scan) {
1442 					ss->idark_valid = s->idark_valid;
1443 					ss->want_dcalib = s->want_dcalib;
1444 					ss->iddate = s->iddate;
1445 					ss->dark_int_time = s->dark_int_time;
1446 					ss->dark_gain_mode = s->dark_gain_mode;
1447 
1448 					for (j = 0; j < (s->auto_gain ? 4 : 2); j += 2) {
1449 						ss->idark_int_time[j] = s->idark_int_time[j];
1450 						for (k = -1; k < m->nraw; k++)
1451 							ss->idark_data[j][k] = s->idark_data[j][k];
1452 					}
1453 				}
1454 			}
1455 		}
1456 
1457 		/* Now deal with white calibrations */
1458 
1459 		/* If we are doing a reflective white reference calibrate */
1460 		/* or a we are doing a tranmisive white reference calibrate */
1461 		if ((*calt & (inst_calt_ref_white
1462 		            | inst_calt_trans_vwhite | inst_calt_ap_flag))
1463 		 && (((*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode && s->reflective)
1464 		  || ((*calc & inst_calc_cond_mask) == inst_calc_man_trans_white && s->trans))) {
1465 //		 && s->cfdate < cdate)
1466 			double dead_time = 0.0;		/* Dead integration time */
1467 			double scale;
1468 			int i;
1469 			double ulimit = m->optsval / m->minsval;	/* Upper scale needed limit */
1470 			double fulimit = sqrt(ulimit);				/* Fast exit limit */
1471 			double llimit = m->optsval / m->maxsval;	/* Lower scale needed limit */
1472 			double fllimit = sqrt(llimit);				/* Fast exit limit */
1473 
1474 			a1logd(p->log,3,"\nDoing initial white calibration with current inttime %f, gainmode %d\n",
1475 			                                                   s->inttime, s->gainmode);
1476 			a1logd(p->log,3,"ulimit %f, llimit %f\n",ulimit,llimit);
1477 			a1logd(p->log,3,"fulimit %f, fllimit %f\n",fulimit,fllimit);
1478 			if (s->reflective) {
1479 				dead_time = RDEAD_TIME;		/* Fudge value that makes int time calcs work */
1480 				/* Heat up the LED to put in in a nominal state for int time adjustment */
1481 				munki_heatLED(p, m->ledpreheattime);
1482 			}
1483 
1484 			/* Until we're done */
1485 			for (i = 0; i < 6; i++) {
1486 
1487 				a1logd(p->log,3,"Doing a white calibration with trial int_time %f, gainmode %d\n",
1488 				                                                 s->inttime,s->gainmode);
1489 
1490 				if (s->trans && s->adaptive) {
1491 					/* compute interpolated dark refence for chosen inttime & gainmode */
1492 					a1logd(p->log,3,"Interpolate dark calibration reference\n");
1493 					if ((ev = munki_interp_dark(p, s->dark_data, s->inttime, s->gainmode))
1494 					                                                             != MUNKI_OK) {
1495 						m->mmode = mmode;           /* Restore actual mode */
1496 						return ev;
1497 					}
1498 					s->dark_valid = 1;
1499 					s->ddate = s->iddate;
1500 					s->dark_int_time = s->inttime;
1501 					s->dark_gain_mode = s->gainmode;
1502 				}
1503 				nummeas = munki_comp_nummeas(p, s->wcaltime, s->inttime);
1504 				ev = munki_whitemeasure(p, s->white_data, &scale, nummeas, &s->inttime, s->gainmode,
1505 				                                                                      s->targoscale);
1506 				a1logd(p->log,3,"Needed scale is %f\n",scale);
1507 
1508 				if (ev == MUNKI_RD_SENSORSATURATED) {
1509 					scale = 0.0;			/* Signal it this way */
1510 					ev = MUNKI_OK;
1511 				}
1512 				if (ev != MUNKI_OK) {
1513 					m->mmode = mmode;           /* Restore actual mode */
1514 					return ev;
1515 				}
1516 
1517 				if (scale >= fllimit && scale <= fulimit) {
1518 					a1logd(p->log,3,"Close enough for early exit\n");
1519 					break;			/* OK, we can stop straight away */
1520 				}
1521 
1522 				if (scale == 0.0) {		/* If sensor was saturated */
1523 					s->inttime = m->min_int_time;
1524 					s->gainmode = 0;
1525 					s->dark_valid = 0;
1526 				} else {
1527 					double ninttime;
1528 
1529 					/* Compute a new integration time and gain mode */
1530 					/* in order to optimise the sensor values. Error if can't get */
1531 					/* scale we want. */
1532 					if ((ev = munki_optimise_sensor(p, &ninttime, &s->gainmode, s->inttime,
1533 					    s->gainmode, s->trans, 0, &s->targoscale, scale, dead_time)) != MUNKI_OK) {
1534 						m->mmode = mmode;           /* Restore actual mode */
1535 						return ev;
1536 					}
1537 					s->inttime = ninttime;
1538 					a1logd(p->log,3,"New inttime = %f\n",s->inttime);
1539 				}
1540 			}
1541 			if (i >= 6) {
1542 				if (scale == 0.0) {		/* If sensor was saturated */
1543 					a1logd(p->log,1, "White calibration failed - sensor is saturated\n");
1544 					m->mmode = mmode;           /* Restore actual mode */
1545 					return MUNKI_RD_SENSORSATURATED;
1546 				}
1547 				if (scale > ulimit || scale < llimit) {
1548 					a1logd(p->log,1,"White calibration failed - didn't converge (%f %f %f)\n",llimit,scale,ulimit);
1549 					m->mmode = mmode;           /* Restore actual mode */
1550 					return MUNKI_RD_REFWHITENOCONV;
1551 				}
1552 			}
1553 
1554 			/* We've settled on the inttime and gain mode to get a good white reference. */
1555 			if (s->reflective) {	/* We read the write reference - check it */
1556 
1557 				/* Let the LED cool down */
1558 				a1logd(p->log,3,"Waiting %f secs for LED to cool\n",m->ledwaittime);
1559 				msec_sleep((int)(m->ledwaittime * 1000.0 + 0.5));
1560 
1561 				/* Re-calibrate the black with the given integration time */
1562 				nummeas = munki_comp_nummeas(p, s->dcaltime, s->inttime);
1563 
1564 				a1logd(p->log,3,"Doing another reflective black calibration with dcaltime %f, int_time %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode);
1565 				if ((ev = munki_dark_measure(p, s->dark_data, nummeas, &s->inttime, s->gainmode))
1566 			                                                                         != MUNKI_OK) {
1567 					m->mmode = mmode;           /* Restore actual mode */
1568 					return ev;
1569 				}
1570 
1571 				/* Take a reflective white reference measurement, */
1572 				/* subtracts black and decompose into base + LED temperature components, */
1573 				/* and compute reftemp white reference. */
1574 				nummeas = munki_comp_nummeas(p, m->calscantime, s->inttime);
1575 				if ((ev = munki_ledtemp_whitemeasure(p, s->white_data, s->iwhite_data, &s->reftemp,
1576 				                                  nummeas, s->inttime, s->gainmode)) != MUNKI_OK) {
1577 					m->mmode = mmode;           /* Restore actual mode */
1578 					return ev;
1579 				}
1580 
1581 				/* Compute wavelength white readings from ref temp sensor reading */
1582 				if ((ev = munki_compute_wav_whitemeas(p, s->cal_factor1, s->cal_factor2,
1583 				                                             s->white_data)) != MUNKI_OK) {
1584 					m->mmode = mmode;           /* Restore actual mode */
1585 					return ev;
1586 				}
1587 
1588 				/* We don't seem to sanity check the white reference. Presumably */
1589 				/* this is because a LED isn't going to burn out... */
1590 
1591 				/* Compute a calibration factor given the reading of the white reference. */
1592 				munki_compute_white_cal(p, s->cal_factor1, m->white_ref1, s->cal_factor1,
1593 				                           s->cal_factor2, m->white_ref2, s->cal_factor2);
1594 
1595 			} else {
1596 				/* Compute wavelength white readings from sensor */
1597 				if ((ev = munki_compute_wav_whitemeas(p, s->cal_factor1, s->cal_factor2,
1598 				                                             s->white_data)) != MUNKI_OK) {
1599 					m->mmode = mmode;           /* Restore actual mode */
1600 					return ev;
1601 				}
1602 
1603 				/* Compute a calibration factor given the reading of the white reference. */
1604 				m->transwarn |= munki_compute_white_cal(p, s->cal_factor1, NULL, s->cal_factor1,
1605 				                                       s->cal_factor2, NULL, s->cal_factor2);
1606 			}
1607 			s->cal_valid = 1;
1608 			s->cfdate = cdate;
1609 			s->want_calib = 0;
1610 			*calt &= ~(inst_calt_ref_white
1611 			         | inst_calt_trans_vwhite);
1612 		}
1613 
1614 		/* Deal with a display integration time selection */
1615 		if ((*calt & (inst_calt_emis_int_time | inst_calt_ap_flag))
1616 		 && (*calc & inst_calc_cond_mask) == inst_calc_emis_white
1617 //		 && s->cfdate < cdate
1618 		 && (s->emiss && !s->adaptive && !s->scan)) {
1619 			double scale;
1620 			double *data;
1621 			double *tt, tv;
1622 
1623 			data = dvectorz(-1, m->nraw-1);
1624 
1625 			a1logd(p->log,3,"\nDoing display integration time calibration\n");
1626 
1627 			/* Undo any previous swaps */
1628 			if (s->dispswap == 1) {
1629 				tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
1630 				tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
1631 			} else if (s->dispswap == 2) {
1632 				tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
1633 				tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
1634 			}
1635 			s->dispswap = 0;
1636 
1637 			/* Simply measure the full display white, and if it's close to */
1638 			/* saturation, switch to the alternate display integration time */
1639 			nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
1640 			ev = munki_whitemeasure(p, data , &scale, nummeas,
1641 			           &s->inttime, s->gainmode, s->targoscale);
1642 			/* Switch to the alternate if things are too bright */
1643 			/* We do this simply by swapping the alternate values in. */
1644 			if (ev == MUNKI_RD_SENSORSATURATED || scale < 1.0) {
1645 				a1logd(p->log,3,"Switching to alternate display integration time %f seconds\n",s->dark_int_time2);
1646 				tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
1647 				tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
1648 				s->dispswap = 1;
1649 
1650 				/* Do another measurement of the full display white, and if it's close to */
1651 				/* saturation, switch to the 3rd alternate display integration time */
1652 				nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
1653 				ev = munki_whitemeasure(p, data , &scale, nummeas,
1654 				           &s->inttime, s->gainmode, s->targoscale);
1655 				/* Switch to the 3rd alternate if things are too bright */
1656 				/* We do this simply by swapping the alternate values in. */
1657 				if (ev == MUNKI_RD_SENSORSATURATED || scale < 1.0) {
1658 					a1logd(p->log,3,"Switching to 3rd alternate display integration time %f seconds\n",s->dark_int_time3);
1659 					/* Undo previous swap */
1660 					tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
1661 					tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
1662 					/* swap in 2nd alternate */
1663 					tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
1664 					tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
1665 					s->dispswap = 2;
1666 				}
1667 			}
1668 			free_dvector(data, -1, m->nraw-1);
1669 			if (ev != MUNKI_OK) {
1670 				m->mmode = mmode;           /* Restore actual mode */
1671 				return ev;
1672 			}
1673 			s->done_dintsel = 1;
1674 			s->diseldate = cdate;
1675 			*calt &= ~inst_calt_emis_int_time;
1676 
1677 			a1logd(p->log,3,"Done display integration time selection\n");
1678 		}
1679 
1680 	}	/* Look at next mode */
1681 	m->mmode = mmode;           /* Restore actual mode */
1682 
1683 	/* Make sure there's the right condition for the calibration */
1684 	if (*calt & (inst_calt_ref_dark | inst_calt_ref_white)) {	/* Reflective calib */
1685 		if ((*calc & inst_calc_cond_mask) != inst_calc_man_cal_smode) {
1686 			*calc = inst_calc_man_cal_smode;
1687 			return MUNKI_CAL_SETUP;
1688 		}
1689 	} else if (*calt & inst_calt_em_dark) {		/* Emissive Dark calib */
1690 		*idtype = inst_calc_id_none;
1691 		id[0] = '\000';
1692 		if ((*calc & inst_calc_cond_mask) != inst_calc_man_cal_smode) {
1693 			*calc = inst_calc_man_cal_smode;
1694 			return MUNKI_CAL_SETUP;
1695 		}
1696 	} else if (*calt & inst_calt_trans_dark) {	/* Transmissive dark */
1697 		*idtype = inst_calc_id_none;
1698 		id[0] = '\000';
1699 		if ((*calc & inst_calc_cond_mask) != inst_calc_man_cal_smode) {
1700 			*calc = inst_calc_man_cal_smode;
1701 			return MUNKI_CAL_SETUP;
1702 		}
1703 	} else if (*calt & inst_calt_trans_vwhite) {	/* Transmissive white for emulated trans. */
1704 		*idtype = inst_calc_id_none;
1705 		id[0] = '\000';
1706 		if ((*calc & inst_calc_cond_mask) != inst_calc_man_trans_white) {
1707 			*calc = inst_calc_man_trans_white;
1708 			return MUNKI_CAL_SETUP;
1709 		}
1710 	} else if (*calt & inst_calt_emis_int_time) {
1711 		*idtype = inst_calc_id_none;
1712 		id[0] = '\000';
1713 		if ((*calc & inst_calc_cond_mask) != inst_calc_emis_white) {
1714 			*calc = inst_calc_emis_white;
1715 			return MUNKI_CAL_SETUP;
1716 		}
1717 	}
1718 
1719 	/* Go around again if we've still got calibrations to do */
1720 	if (*calt & inst_calt_all_mask) {
1721 		return MUNKI_CAL_SETUP;
1722 	}
1723 
1724 	/* We must be done */
1725 
1726 #ifdef ENABLE_NONVCAL
1727 	/* Save the calibration to a file */
1728 	munki_save_calibration(p);
1729 #endif
1730 
1731 	if (m->transwarn) {
1732 		*calc = inst_calc_message;
1733 		if (m->transwarn & 2) {
1734 			*idtype = inst_calc_id_trans_low;
1735 			strcpy(id, "Warning: Transmission light source is too low for accuracy!");
1736 		} else {
1737 			*idtype = inst_calc_id_trans_wl;
1738 			strcpy(id, "Warning: Transmission light source is low at some wavelengths!");
1739 		}
1740 		m->transwarn = 0;
1741 		return MUNKI_OK;
1742 	}
1743 
1744 	a1logd(p->log,3,"Finished cal with dark_valid = %d, cal_valid = %d\n",cs->dark_valid, cs->cal_valid);
1745 
1746 	return ev;
1747 }
1748 
1749 /* Interpret an icoms error into a MUNKI error */
icoms2munki_err(int se)1750 int icoms2munki_err(int se) {
1751 	if (se != ICOM_OK)
1752 		return MUNKI_COMS_FAIL;
1753 	return MUNKI_OK;
1754 }
1755 
1756 /* - - - - - - - - - - - - - - - - */
1757 /* Measure a display update delay. It is assumed that */
1758 /* white_stamp(init) has been called, and then a */
1759 /* white to black change has been made to the displayed color, */
1760 /* and this will measure the time it took for the update to */
1761 /* be noticed by the instrument, up to 2.0 seconds. */
1762 /* (It is assumed that white_change() will be called at the time the patch */
1763 /* changes color.) */
1764 /* inst_misread will be returned on failure to find a transition to black. */
1765 #define NDMXTIME 2.0		/* Maximum time to take */
1766 #define NDSAMPS 500			/* Debug samples */
1767 
1768 typedef struct {
1769 	double sec;
1770 	double rgb[3];
1771 	double tot;
1772 } i1rgbdsamp;
1773 
munki_imp_meas_delay(munki * p,int * pdispmsec,int * pinstmsec)1774 munki_code munki_imp_meas_delay(
1775 munki *p,
1776 int *pdispmsec,      /* Return display update delay in msec */
1777 int *pinstmsec) {    /* Return instrument reaction time in msec */
1778 	munki_code ev = MUNKI_OK;
1779 	munkiimp *m = (munkiimp *)p->m;
1780 	munki_state *s = &m->ms[m->mmode];
1781 	int i, j, k, mm;
1782 	double **multimeas;			/* Spectral measurements */
1783 	int nummeas;
1784 	double rgbw[3] = { 610.0, 520.0, 460.0 };
1785 	double ucalf = 1.0;				/* usec_time calibration factor */
1786 	double inttime;
1787 	double rstart;
1788 	i1rgbdsamp *samp;
1789 	double stot, etot, del, thr;
1790 	double stime, etime;
1791 	int isdeb;
1792 	int dispmsec, instmsec;
1793 
1794 	if (pinstmsec != NULL)
1795 		*pinstmsec = 0;
1796 
1797 	if ((rstart = usec_time()) < 0.0) {
1798 		a1loge(p->log, inst_internal_error, "munki_imp_meas_delay: No high resolution timers\n");
1799 		return inst_internal_error;
1800 	}
1801 
1802 	/* Read the samples */
1803 	inttime = m->min_int_time;
1804 	nummeas = (int)(NDMXTIME/inttime + 0.5);
1805 	multimeas = dmatrix(0, nummeas-1, -1, m->nwav-1);
1806 	if ((samp = (i1rgbdsamp *)calloc(sizeof(i1rgbdsamp), nummeas)) == NULL) {
1807 		a1logd(p->log, 1, "munki_meas_delay: malloc failed\n");
1808 		return MUNKI_INT_MALLOC;
1809 	}
1810 
1811 	if ((ev = munki_read_patches_all(p, multimeas, nummeas, &inttime, 0)) != inst_ok) {
1812 		free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav-1);
1813 		free(samp);
1814 		return ev;
1815 	}
1816 
1817 	if (m->whitestamp < 0.0) {
1818 		a1logd(p->log, 1, "munki_meas_delay: White transition wasn't timestamped\n");
1819 		return inst_internal_error;
1820 	}
1821 
1822 	/* Convert the samples to RGB */
1823 	/* Add 10 msec fudge factor */
1824 	for (i = 0; i < nummeas; i++) {
1825 		samp[i].sec = i * inttime + (m->trigstamp - m->whitestamp)/1000000.0 + 0.01;
1826 		samp[i].rgb[0] = samp[i].rgb[1] = samp[i].rgb[2] = 0.0;
1827 		for (j = 0; j < m->nwav; j++) {
1828 			double wl = XSPECT_WL(m->wl_short, m->wl_long, m->nwav, j);
1829 
1830 //printf("~1 multimeas %d %d = %f\n",i, j, multimeas[i][j]);
1831 			for (k = 0; k < 3; k++) {
1832 				double tt = (double)(wl - rgbw[k]);
1833 				tt = (50.0 - fabs(tt))/50.0;
1834 				if (tt < 0.0)
1835 					tt = 0.0;
1836 				samp[i].rgb[k] += sqrt(tt) * multimeas[i][j];
1837 			}
1838 		}
1839 		samp[i].tot = samp[i].rgb[0] + samp[i].rgb[1] + samp[i].rgb[2];
1840 	}
1841 	free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav-1);
1842 
1843 	a1logd(p->log, 3, "munki_measure_refresh: Read %d samples for refresh calibration\n",nummeas);
1844 
1845 	/* Over the first 100msec, locate the maximum value */
1846 	stime = samp[0].sec;
1847 	stot = -1e9;
1848 	for (i = 0; i < nummeas; i++) {
1849 		if (samp[i].tot > stot)
1850 			stot = samp[i].tot;
1851 		if ((samp[i].sec - stime) > 0.1)
1852 			break;
1853 	}
1854 
1855 	/* Over the last 100msec, locate the maximum value */
1856 	etime = samp[nummeas-1].sec;
1857 	etot = -1e9;
1858 	for (i = nummeas-1; i >= 0; i--) {
1859 		if (samp[i].tot > etot)
1860 			etot = samp[i].tot;
1861 		if ((etime - samp[i].sec) > 0.1)
1862 			break;
1863 	}
1864 
1865 	del = etot - stot;
1866 	thr = stot + 0.30 * del;		/* 30% of transition threshold */
1867 
1868 #ifdef PLOT_UPDELAY
1869 	a1logd(p->log, 0, "munki_meas_delay: start tot %f end tot %f del %f, thr %f\n", stot, etot, del, thr);
1870 #endif
1871 
1872 #ifdef PLOT_UPDELAY
1873 	/* Plot the raw sensor values */
1874 	{
1875 		double xx[NDSAMPS];
1876 		double y1[NDSAMPS];
1877 		double y2[NDSAMPS];
1878 		double y3[NDSAMPS];
1879 		double y4[NDSAMPS];
1880 
1881 		for (i = 0; i < nummeas && i < NDSAMPS; i++) {
1882 			xx[i] = samp[i].sec;
1883 			y1[i] = samp[i].rgb[0];
1884 			y2[i] = samp[i].rgb[1];
1885 			y3[i] = samp[i].rgb[2];
1886 			y4[i] = samp[i].tot;
1887 //printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].tot);
1888 		}
1889 		printf("Display update delay measure sensor values and time (sec)\n");
1890 		do_plot6(xx, y1, y2, y3, y4, NULL, NULL, nummeas);
1891 	}
1892 #endif
1893 
1894 	/* Check that there has been a transition */
1895 	if (del < 5.0) {
1896 		free(samp);
1897 		a1logd(p->log, 1, "munki_meas_delay: can't detect change from black to white\n");
1898 		return MUNKI_RD_NOTRANS_FOUND;
1899 	}
1900 
1901 	/* Working from the start, locate the time at which the level was above the threshold */
1902 	for (i = 0; i < (nummeas-1); i++) {
1903 		if (samp[i].tot > thr)
1904 			break;
1905 	}
1906 
1907 	a1logd(p->log, 2, "munki_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec);
1908 
1909 	/* Compute overall delay and subtract patch change delay */
1910 	dispmsec = (int)(samp[i].sec * 1000.0 + 0.5);
1911 	instmsec = (int)((m->trigstamp - rstart)/1000.0 + 0.5);
1912 
1913 #ifdef PLOT_UPDELAY
1914 	a1logd(p->log, 0, "munki_meas_delay: disp %d, inst %d msec\n",dispmsec,instmsec);
1915 #else
1916 	a1logd(p->log, 2, "munki_meas_delay: disp %d, inst %d msec\n",dispmsec,instmsec);
1917 #endif
1918 
1919 	if (dispmsec < 0) 		/* This can happen if the patch generator delays it's return */
1920 		dispmsec = 0;
1921 
1922 	if (pdispmsec != NULL)
1923 		*pdispmsec = dispmsec;
1924 
1925 	if (pinstmsec != NULL)
1926 		*pinstmsec = instmsec;
1927 
1928 #ifdef PLOT_UPDELAY
1929 	a1logd(p->log, 0, "munki_meas_delay: returning %d & %d msec\n",dispmsec,instmsec);
1930 #else
1931 	a1logd(p->log, 2, "munki_meas_delay: returning %d & %d msec\n",dispmsec,instmsec);
1932 #endif
1933 	free(samp);
1934 
1935 	return MUNKI_OK;
1936 }
1937 #undef NDSAMPS
1938 #undef NDMXTIME
1939 
1940 /* Timestamp the white patch change during meas_delay() */
munki_imp_white_change(munki * p,int init)1941 inst_code munki_imp_white_change(munki *p, int init) {
1942 	munkiimp *m = (munkiimp *)p->m;
1943 
1944 	if (init)
1945 		m->whitestamp = -1.0;
1946 	else {
1947 		if ((m->whitestamp = usec_time()) < 0.0) {
1948 			a1loge(p->log, inst_internal_error, "munki_imp_wite_change: No high resolution timers\n");
1949 			return inst_internal_error;
1950 		}
1951 	}
1952 
1953 	return inst_ok;
1954 }
1955 
1956 /* - - - - - - - - - - - - - - - - */
1957 /* Measure a patch or strip or flash in the current mode. */
1958 /* To try and speed up the reaction time between */
1959 /* triggering a scan measurement and being able to */
1960 /* start moving the instrument, we pre-allocate */
1961 /* all the buffers and arrays, and pospone processing */
1962 /* until after the scan is complete. */
munki_imp_measure(munki * p,ipatch * vals,int nvals,instClamping clamp)1963 munki_code munki_imp_measure(
1964 	munki *p,
1965 	ipatch *vals,		/* Pointer to array of instrument patch value */
1966 	int nvals,			/* Number of values */
1967 	instClamping clamp	/* Clamp XYZ/Lab to be +ve */
1968 ) {
1969 	munki_code ev = MUNKI_OK;
1970 	munkiimp *m = (munkiimp *)p->m;
1971 	munki_state *s = &m->ms[m->mmode];
1972 	unsigned char *buf = NULL;	/* Raw USB reading buffer for reflection dark cal */
1973 	unsigned int bsize;
1974 	unsigned char *mbuf = NULL;	/* Raw USB reading buffer for measurement */
1975 	unsigned int mbsize;
1976 	int nummeas = 0, maxnummeas = 0;
1977 	int nmeasuered = 0;			/* Number actually measured */
1978 	double invsampt = 0.0;		/* Invalid sample time */
1979 	int ninvmeas = 0;			/* Number of invalid measurements */
1980 	double **specrd = NULL;		/* Cooked spectral patch values */
1981 	double duration = 0.0;		/* Possible flash duration value */
1982 	mk_spos spos;
1983 	int user_trig = 0;
1984 
1985 	a1logd(p->log,2,"munki_imp_measure called\n");
1986 	a1logd(p->log,3,"Taking %d measurments in %s%s%s%s%s mode called\n", nvals,
1987 		        s->emiss ? "Emission" : s->trans ? "Trans" : "Refl",
1988 		        s->emiss && s->ambient ? " Ambient" : "",
1989 		        s->scan ? " Scan" : "",
1990 		        s->flash ? " Flash" : "",
1991 		        s->adaptive ? " Adaptive" : "");
1992 
1993 
1994 	if ((s->emiss && s->adaptive && !s->idark_valid)
1995 	 || ((!s->emiss || !s->adaptive) && !s->dark_valid)
1996 	 || !s->cal_valid) {
1997 		a1logd(p->log,3,"emis %d, adaptive %d, idark_valid %d\n",s->emiss,s->adaptive,s->idark_valid);
1998 		a1logd(p->log,3,"dark_valid %d, cal_valid %d\n",s->dark_valid,s->cal_valid);
1999 		a1logd(p->log,3,"munki_imp_measure need calibration\n");
2000 		return MUNKI_RD_NEEDS_CAL;
2001 	}
2002 
2003 	if (nvals <= 0
2004 	 || (!s->scan && nvals > 1)) {
2005 		a1logd(p->log,3,"munki_imp_measure wrong number of patches\n");
2006 		return MUNKI_INT_WRONGPATCHES;
2007 	}
2008 
2009 	if (s->reflective) {
2010 		/* Number of invalid samples to allow for LED warmup */
2011 		invsampt = m->refinvalidsampt;
2012 		ninvmeas = munki_comp_ru_nummeas(p, invsampt, s->inttime);
2013 	}
2014 
2015 	/* Notional number of measurements, befor adaptive and not counting scan */
2016 	nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2017 
2018 	/* Allocate buffer for dark measurement */
2019 	if (s->reflective) {
2020 		bsize = m->nsen * 2 * nummeas;
2021 		if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
2022 			a1logd(p->log,1,"munki_imp_measure malloc %d bytes failed (5)\n",bsize);
2023 			return MUNKI_INT_MALLOC;
2024 		}
2025 	}
2026 
2027 	/* Allocate buffer for measurement */
2028 	maxnummeas = munki_comp_nummeas(p, s->maxscantime, s->inttime);
2029 	if (maxnummeas < (ninvmeas + nummeas))
2030 		maxnummeas = (ninvmeas + nummeas);
2031 	mbsize = m->nsen * 2 * maxnummeas;
2032 	if ((mbuf = (unsigned char *)malloc(sizeof(unsigned char) * mbsize)) == NULL) {
2033 		if (buf != NULL)
2034 			free(buf);
2035 		a1logd(p->log,1,"munki_imp_measure malloc %d bytes failed (6)\n",mbsize);
2036 		return MUNKI_INT_MALLOC;
2037 	}
2038 	specrd = dmatrix(0, nvals-1, 0, m->nwav-1);
2039 
2040 	if (m->trig == inst_opt_trig_user_switch) {
2041 		m->hide_switch = 1;						/* Supress switch events */
2042 
2043 #ifdef USE_THREAD
2044 		{
2045 			int currcount = m->switch_count;		/* Variable set by thread */
2046 			while (currcount == m->switch_count) {
2047 				inst_code rc;
2048 				int cerr;
2049 
2050 				/* Don't trigger on user key if scan, only trigger */
2051 				/* on instrument switch */
2052 				if (p->uicallback != NULL
2053 				 && (rc = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
2054 					if (rc == inst_user_abort) {
2055 						ev = MUNKI_USER_ABORT;
2056 						break;						/* Abort */
2057 					}
2058 					if (!s->scan && rc == inst_user_trig) {
2059 						ev = MUNKI_USER_TRIG;
2060 						user_trig = 1;
2061 						break;						/* Trigger */
2062 					}
2063 				}
2064 				msec_sleep(100);
2065 			}
2066 		}
2067 #else
2068 		/* Throw one away in case the switch was pressed prematurely */
2069 		munki_waitfor_switch_th(p, NULL, NULL, 0.01);
2070 
2071 		for (;;) {
2072 			mk_eve ecode;
2073 			int cerr;
2074 
2075 			if ((ev = munki_waitfor_switch_th(p, &ecode, NULL, 0.1)) != MUNKI_OK
2076 			 && ev != MUNKI_INT_BUTTONTIMEOUT)
2077 				break;			/* Error */
2078 
2079 			if (ev == MUNKI_OK && ecode == mk_eve_switch_press)
2080 				break;			/* switch triggered */
2081 
2082 			/* Don't trigger on user key if scan, only trigger */
2083 			/* on instrument switch */
2084 			if (p->uicallback != NULL
2085 			  && (rc = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
2086 				if (rc == inst_user_abort) {
2087 					ev = MUNKI_USER_ABORT;
2088 					break;						/* Abort */
2089 				}
2090 				if (!s->scan && rc == inst_user_trig) {
2091 					ev = MUNKI_USER_TRIG;
2092 					user_trig = 1;
2093 					break;						/* Trigger */
2094 				}
2095 			}
2096 		}
2097 #endif
2098 		a1logd(p->log,3,"############# triggered ##############\n");
2099 		if (p->uicallback)	/* Notify of trigger */
2100 			p->uicallback(p->uic_cntx, inst_triggered);
2101 
2102 		m->hide_switch = 0;						/* Enable switch events again */
2103 
2104 	} else if (m->trig == inst_opt_trig_user) {
2105 
2106 		if (p->uicallback == NULL) {
2107 			a1logd(p->log, 1, "hcfr: inst_opt_trig_user but no uicallback function set!\n");
2108 			ev = MUNKI_UNSUPPORTED;
2109 
2110 		} else {
2111 
2112 			for (;;) {
2113 				inst_code rc;
2114 				if ((rc = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
2115 					if (rc == inst_user_abort) {
2116 						ev = MUNKI_USER_ABORT;	/* Abort */
2117 						break;
2118 					}
2119 					if (rc == inst_user_trig) {
2120 						ev = MUNKI_USER_TRIG;
2121 						user_trig = 1;
2122 						break;						/* Trigger */
2123 					}
2124 				}
2125 				msec_sleep(200);
2126 			}
2127 		}
2128 		a1logd(p->log,3,"############# triggered ##############\n");
2129 		if (p->uicallback)	/* Notify of trigger */
2130 			p->uicallback(p->uic_cntx, inst_triggered);
2131 
2132 	/* Progromatic Trigger */
2133 	} else {
2134 		/* Check for abort */
2135 		if (p->uicallback != NULL
2136 		 && (ev = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort)
2137 			ev = MUNKI_USER_ABORT;	/* Abort */
2138 	}
2139 
2140 	if (ev != MUNKI_OK && ev != MUNKI_USER_TRIG) {
2141 		free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2142 		free(mbuf);
2143 		if (buf != NULL)
2144 			free(buf);
2145 		a1logd(p->log,3,"munki_imp_measure user aborted, terminated, command, or failure\n");
2146 		return ev;		/* User abort, term, command or failure */
2147 	}
2148 
2149 	/* Get current sensor position */
2150 	if ((ev = munki_getstatus(p, &spos, NULL)) != MUNKI_OK) {
2151 		free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2152 		free(mbuf);
2153 		if (buf != NULL)
2154 			free(buf);
2155 		a1logd(p->log,3,"munki_imp_measure getstatus failed\n");
2156 		return ev;
2157 	}
2158 
2159 	/* Check the current sensor position */
2160 	if (!m->nosposcheck) {
2161 		if (s->emiss) {
2162 			if (s->ambient) {
2163 				if (spos != mk_spos_amb)
2164 					ev = MUNKI_SPOS_AMB;
2165 			} else if (s->projector) {
2166 				if (spos != mk_spos_proj)
2167 					ev = MUNKI_SPOS_PROJ;
2168 			} else {	/* Display */
2169 				if (spos != mk_spos_surf)
2170 					ev = MUNKI_SPOS_SURF;
2171 			}
2172 		} else {	/* Reflective or transmissive */
2173 			if (spos != mk_spos_surf)
2174 				ev = MUNKI_SPOS_SURF;
2175 		}
2176 		if (ev != MUNKI_OK) {
2177 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2178 			free(mbuf);
2179 			if (buf != NULL)
2180 				free(buf);
2181 			a1logd(p->log,3,"munki_imp_measure: Sensor in wrong position\n");
2182 			return ev;
2183 		}
2184 	}
2185 
2186 	/* Emissive adaptive, non-scan */
2187 	if (s->emiss && !s->scan && s->adaptive) {
2188 		int saturated = 0;
2189 		double optscale = 1.0;
2190 		s->inttime = 0.25;
2191 		s->gainmode = 0;
2192 		s->dark_valid = 0;
2193 
2194 		a1logd(p->log,3,"Trial measure emission with inttime %f, gainmode %d\n",s->inttime,s->gainmode);
2195 
2196 		/* Take a trial measurement reading using the current mode. */
2197 		/* Used to determine if sensor is saturated, or not optimal */
2198 		nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2199 		if ((ev = munki_trialmeasure(p, &saturated, &optscale, nummeas, &s->inttime, s->gainmode,
2200 		                                                            s->targoscale)) != MUNKI_OK) {
2201 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2202 			free(mbuf);
2203 			a1logd(p->log,3,"munki_imp_measure trial measure failed\n");
2204 			return ev;
2205 		}
2206 
2207 		if (saturated) {
2208 			s->inttime = m->min_int_time;
2209 
2210 			a1logd(p->log,3,"2nd trial measure emission with inttime %f, gainmode %d\n",
2211 			                                         s->inttime,s->gainmode);
2212 			/* Take a trial measurement reading using the current mode. */
2213 			/* Used to determine if sensor is saturated, or not optimal */
2214 			nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2215 			if ((ev = munki_trialmeasure(p, &saturated, &optscale, nummeas, &s->inttime,
2216 			                                          s->gainmode, s->targoscale)) != MUNKI_OK) {
2217 				free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2218 				free(mbuf);
2219 				a1logd(p->log,3,"munki_imp_measure trial measure failed\n");
2220 				return ev;
2221 			}
2222 		}
2223 
2224 		a1logd(p->log,3,"Compute optimal integration time\n");
2225 		/* For adaptive mode, compute a new integration time and gain mode */
2226 		/* in order to optimise the sensor values. */
2227 		if ((ev = munki_optimise_sensor(p, &s->inttime, &s->gainmode,
2228 		         s->inttime, s->gainmode, 1, 1, &s->targoscale, optscale, 0.0)) != MUNKI_OK) {
2229 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2230 			free(mbuf);
2231 			a1logd(p->log,3,"munki_imp_measure optimise sensor failed\n");
2232 			return ev;
2233 		}
2234 		a1logd(p->log,3,"Computed optimal emiss inttime %f and gainmode %d\n",s->inttime,s->gainmode);
2235 
2236 		a1logd(p->log,3,"Interpolate dark calibration reference\n");
2237 		if ((ev = munki_interp_dark(p, s->dark_data, s->inttime, s->gainmode)) != MUNKI_OK) {
2238 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2239 			free(mbuf);
2240 			a1logd(p->log,3,"munki_imp_measure interplate dark ref failed\n");
2241 			return ev;
2242 		}
2243 		s->dark_valid = 1;
2244 		s->dark_int_time = s->inttime;
2245 		s->dark_gain_mode = s->gainmode;
2246 
2247 		/* Recompute number of measurements and realloc measurement buffer */
2248 		free(mbuf);
2249 		nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2250 		maxnummeas = munki_comp_nummeas(p, s->maxscantime, s->inttime);
2251 		if (maxnummeas < nummeas)
2252 			maxnummeas = nummeas;
2253 		mbsize = m->nsen * 2 * maxnummeas;
2254 		if ((mbuf = (unsigned char *)malloc(sizeof(unsigned char) * mbsize)) == NULL) {
2255 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2256 			a1logd(p->log,1,"munki_imp_measure malloc %d bytes failed (7)\n",mbsize);
2257 			return MUNKI_INT_MALLOC;
2258 		}
2259 
2260 	} else if (s->reflective) {
2261 
2262 		DISDPLOT
2263 
2264 		a1logd(p->log,3,"Doing on the fly black calibration_1 with nummeas %d int_time %f, gainmode %d\n",
2265 		                                                   nummeas, s->inttime, s->gainmode);
2266 
2267 		if ((ev = munki_dark_measure_1(p, nummeas, &s->inttime, s->gainmode, buf, bsize))
2268 		                                                                           != MUNKI_OK) {
2269 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2270 			free(buf);
2271 			free(mbuf);
2272 			a1logd(p->log,3,"munki_imp_measure dak measure 1 failed\n");
2273 			return ev;
2274 		}
2275 
2276 		ENDPLOT
2277 	}
2278 	/* Take a measurement reading using the current mode. */
2279 	/* Converts to completely processed output readings. */
2280 
2281 	a1logd(p->log,3,"Do main measurement reading\n");
2282 
2283 	/* Indicate to the user that they can now scan the instrument, */
2284 	/* after a little delay that allows for the instrument reaction time. */
2285 	if (s->scan) {
2286 		int delay = 100 + (int)(invsampt * 1000.0 + 0.9);
2287 		if (p->eventcallback != NULL) {
2288 			issue_scan_ready((inst *)p, delay);
2289 		} else {
2290 			/* delay then 1KHz for 200 msec */
2291 			msec_beep(delay, 1000, 200);
2292 		}
2293 	}
2294 
2295 	/* Retry loop in case a display read is saturated */
2296 	for (;;) {
2297 
2298 		/* Trigger measure and gather raw readings */
2299 		if ((ev = munki_read_patches_1(p, ninvmeas, nummeas, maxnummeas, &s->inttime, s->gainmode,
2300 		                                       &nmeasuered, mbuf, mbsize)) != MUNKI_OK) {
2301 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2302 			if (buf != NULL)
2303 				free(buf);
2304 			free(mbuf);
2305 			a1logd(p->log,3,"munki_imp_measure failed at munki_read_patches_1\n");
2306 			return ev;
2307 		}
2308 
2309 		/* Complete processing of dark readings now that main measurement has been taken */
2310 		if (s->reflective) {
2311 			a1logd(p->log,3,"Calling black calibration_2 calc with nummeas %d, inttime %f, gainmode %d\n", nummeas, s->inttime,s->gainmode);
2312 			if ((ev = munki_dark_measure_2(p, s->dark_data, nummeas, s->inttime,
2313 			                                  s->gainmode, buf, bsize)) != MUNKI_OK) {
2314 				free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2315 				free(buf);
2316 				free(mbuf);
2317 				a1logd(p->log,3,"munki_imp_measure failed at munki_dark_measure_2\n");
2318 				return ev;
2319 			}
2320 			s->dark_valid = 1;
2321 			s->dark_int_time = s->inttime;
2322 			s->dark_gain_mode = s->gainmode;
2323 			free(buf);
2324 		}
2325 
2326 		/* Process the raw measurement readings into final spectral readings */
2327 		ev = munki_read_patches_2(p, &duration, specrd, nvals, s->inttime, s->gainmode,
2328 		                                              ninvmeas, nmeasuered, mbuf, mbsize);
2329 		/* Special case display mode read. If the sensor is saturated, and */
2330 		/* we haven't already done so, switch to the alternate integration time */
2331 		/* and try again. */
2332 		if (s->emiss && !s->scan && !s->adaptive
2333 		 && ev == MUNKI_RD_SENSORSATURATED
2334 		 && s->dispswap < 2) {
2335 			double *tt, tv;
2336 
2337 			if (s->dispswap == 0) {
2338 				a1logd(p->log,3,"Switching to alternate display integration time %f seconds\n",s->dark_int_time2);
2339 				tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
2340 				tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
2341 				s->dispswap = 1;
2342 			} else if (s->dispswap == 1) {
2343 				a1logd(p->log,3,"Switching to 2nd alternate display integration time %f seconds\n",s->dark_int_time3);
2344 				/* Undo first swap */
2345 				tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
2346 				tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
2347 				/* Do 2nd swap */
2348 				tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
2349 				tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
2350 				s->dispswap = 2;
2351 			}
2352 			/* Recompute number of measurements and realloc measurement buffer */
2353 			free(mbuf);
2354 			nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2355 			maxnummeas = munki_comp_nummeas(p, s->maxscantime, s->inttime);
2356 			if (maxnummeas < nummeas)
2357 				maxnummeas = nummeas;
2358 			mbsize = m->nsen * 2 * maxnummeas;
2359 			if ((mbuf = (unsigned char *)malloc(sizeof(unsigned char) * mbsize)) == NULL) {
2360 				free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2361 				a1logd(p->log,1,"munki_imp_measure malloc %d bytes failed (7)\n",mbsize);
2362 				return MUNKI_INT_MALLOC;
2363 			}
2364 			continue;			/* Do the measurement again */
2365 		}
2366 
2367 		if (ev != MUNKI_OK) {
2368 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2369 			free(mbuf);
2370 			a1logd(p->log,3,"munki_imp_measure failed at munki_read_patches_2\n");
2371 			return ev;
2372 		}
2373 		break;		/* Don't repeat */
2374 	}
2375 	free(mbuf);
2376 
2377 	/* Transfer spectral and convert to XYZ */
2378 	if ((ev = munki_conv2XYZ(p, vals, nvals, specrd, clamp)) != MUNKI_OK) {
2379 		free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2380 		a1logd(p->log,3,"munki_imp_measure failed at munki_conv2XYZ\n");
2381 		return ev;
2382 	}
2383 	free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2384 
2385 	if (nvals > 0)
2386 		vals[0].duration = duration;	/* Possible flash duration */
2387 
2388 	a1logd(p->log,3,"munki_imp_measure sucessful return\n");
2389 	if (user_trig)
2390 		return MUNKI_USER_TRIG;
2391 	return ev;
2392 }
2393 
2394 /* - - - - - - - - - - - - - - - - - - - - - - - - - - */
2395 /*
2396 
2397 	Determining the refresh rate for a refresh type display.
2398 
2399 	This is easy when the max sample rate of the i1 is above
2400 	the nyquist of the display, and will always be the case
2401 	for the range we are prepared to measure (up to 100Hz)
2402 	when using an Rev B, D or E, but is a problem for the
2403 	rev A and ColorMunki, which can only sample at 113Hz.
2404 
2405 	We work around this problem by detecting when
2406 	we are measuring an alias of the refresh rate, and
2407 	average the aliasing corrected measurements.
2408 
2409 	If there is no aparent refresh, or the refresh rate is not determinable,
2410 	return a period of 0.0 and inst_ok;
2411 */
2412 
2413 munki_code munki_measure_rgb(munki *p, double *inttime, double *rgb);
2414 
2415 #ifndef PSRAND32L
2416 # define PSRAND32L(S) ((S) * 1664525L + 1013904223L)
2417 #endif
2418 #undef FREQ_SLOW_PRECISE	/* [und] Interpolate then autocorrelate, else autc & filter */
2419 #define NFSAMPS 80		/* Number of samples to read */
2420 #define NFMXTIME 6.0	/* Maximum time to take (2000 == 6) */
2421 #define PBPMS 20		/* bins per msec */
2422 #define PERMIN ((1000 * PBPMS)/40)	/* 40 Hz */
2423 #define PERMAX ((1000 * PBPMS)/4)	/* 4 Hz*/
2424 #define NPER (PERMAX - PERMIN + 1)
2425 #define PWIDTH (8 * PBPMS)			/* 8 msec bin spread to look for peak in */
2426 #define MAXPKS 20					/* Number of peaks to find */
2427 #define TRIES 8	 					/* Number of different sample rates to try */
2428 
munki_imp_meas_refrate(munki * p,double * ref_rate)2429 munki_code munki_imp_meas_refrate(
2430 	munki *p,
2431 	double *ref_rate
2432 ) {
2433 	munki_code ev = MUNKI_OK;
2434 	munkiimp *m = (munkiimp *)p->m;
2435 	munki_state *s = &m->ms[m->mmode];
2436 	int i, j, k, mm;
2437 	double **multimeas;			/* Spectral measurements */
2438 	int nummeas;
2439 	double rgbw[3] = { 610.0, 520.0, 460.0 };
2440 	double ucalf = 1.0;				/* usec_time calibration factor */
2441 	double inttime;
2442 	static unsigned int randn = 0x12345678;
2443 	struct {
2444 		double sec;
2445 		double rgb[3];
2446 	} samp[NFSAMPS * 2];
2447 	int nfsamps;			/* Actual samples read */
2448 	double minv[3];			/* Minimum reading */
2449 	double maxv[3];			/* Maximum reading */
2450 	double maxt;			/* Time range */
2451 #ifdef FREQ_SLOW_PRECISE
2452 	int nbins;
2453 	double *bins[3];		/* PBPMS sample bins */
2454 #else
2455 	double tcorr[NPER];		/* Temp for initial autocorrelation */
2456 	int ntcorr[NPER];		/* Number accumulated */
2457 #endif
2458 	double corr[NPER];		/* Filtered correlation for each period value */
2459 	double mincv, maxcv;	/* Max and min correlation values */
2460 	double crange;			/* Correlation range */
2461 	double peaks[MAXPKS];	/* Peak wavelength */
2462 	double peakh[MAXPKS];	/* Peak heighheight */
2463 	int npeaks;				/* Number of peaks */
2464 	double pval;			/* Period value */
2465 	double rfreq[TRIES];	/* Computed refresh frequency for each try */
2466 	double rsamp[TRIES];	/* Sampling rate used to measure frequency */
2467 	int tix = 0;			/* try index */
2468 
2469 	a1logd(p->log,2,"munki_imp_meas_refrate called\n");
2470 
2471 	if (ref_rate != NULL)
2472 		*ref_rate = 0.0;
2473 
2474 	if (!s->emiss) {
2475 		a1logd(p->log,2,"munki_imp_meas_refrate not in emissive mode\n");
2476 		return MUNKI_UNSUPPORTED;
2477 	}
2478 
2479 	for (mm = 0; mm < TRIES; mm++) {
2480 		rfreq[mm] = 0.0;
2481 		npeaks = 0;			/* Number of peaks */
2482 		nummeas = NFSAMPS;
2483 		multimeas = dmatrix(0, nummeas-1, -1, m->nwav-1);
2484 
2485 		if (mm == 0)
2486 			inttime = m->min_int_time;
2487 		else {
2488 			double rval, dmm;
2489 			randn = PSRAND32L(randn);
2490 			rval = (double)randn/4294967295.0;
2491 			dmm = ((double)mm + rval - 0.5)/(TRIES - 0.5);
2492 			inttime = m->min_int_time * (1.0 + dmm * 0.80);
2493 		}
2494 
2495 		if ((ev = munki_read_patches_all(p, multimeas, nummeas, &inttime, 0)) != inst_ok) {
2496 			free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav-1);
2497 			return ev;
2498 		}
2499 
2500 		rsamp[tix] = 1.0/inttime;
2501 
2502 		/* Convert the samples to RGB */
2503 		for (i = 0; i < nummeas && i < NFSAMPS; i++) {
2504 			samp[i].sec = i * inttime;
2505 			samp[i].rgb[0] = samp[i].rgb[1] = samp[i].rgb[2] = 0.0;
2506 			for (j = 0; j < m->nwav; j++) {
2507 				double wl = XSPECT_WL(m->wl_short, m->wl_long, m->nwav, j);
2508 
2509 //printf("~1 multimeas %d %d = %f\n",i, j, multimeas[i][j]);
2510 				for (k = 0; k < 3; k++) {
2511 					double tt = (double)(wl - rgbw[k]);
2512 					tt = (40.0 - fabs(tt))/40.0;
2513 					if (tt < 0.0)
2514 						tt = 0.0;
2515 					samp[i].rgb[k] += sqrt(tt) * multimeas[i][j];
2516 				}
2517 			}
2518 		}
2519 		nfsamps = i;
2520 
2521 		a1logd(p->log, 3, "munki_measure_refresh: Read %d samples for refresh calibration\n",nfsamps);
2522 
2523 #ifdef NEVER
2524 		/* Plot the raw sensor values */
2525 		{
2526 			double xx[NFSAMPS];
2527 			double y1[NFSAMPS];
2528 			double y2[NFSAMPS];
2529 			double y3[NFSAMPS];
2530 
2531 			for (i = 0; i < nfsamps; i++) {
2532 				xx[i] = samp[i].sec;
2533 				y1[i] = samp[i].rgb[0];
2534 				y2[i] = samp[i].rgb[1];
2535 				y3[i] = samp[i].rgb[2];
2536 //			printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].rgb[0]);
2537 			}
2538 			printf("Fast scan sensor values and time (sec)\n");
2539 			do_plot6(xx, y1, y2, y3, NULL, NULL, NULL, nfsamps);
2540 		}
2541 #endif
2542 
2543 		/* Locate the smallest values and maximum time */
2544 		maxt = -1e6;
2545 		minv[0] = minv[1] = minv[2] = 1e20;
2546 		maxv[0] = maxv[1] = maxv[2] = -11e20;
2547 		for (i = nfsamps-1; i >= 0; i--) {
2548 			if (samp[i].sec > maxt)
2549 				maxt = samp[i].sec;
2550 			for (j = 0; j < 3; j++) {
2551 				if (samp[i].rgb[j] < minv[j])
2552 					minv[j] = samp[i].rgb[j];
2553 				if (samp[i].rgb[j] > maxv[j])
2554 					maxv[j] = samp[i].rgb[j];
2555 			}
2556 		}
2557 		/* Re-zero the sample times, and normalise the readings */
2558 		for (i = nfsamps-1; i >= 0; i--) {
2559 			samp[i].sec -= samp[0].sec;
2560 			samp[i].sec *= ucalf;
2561 			if (samp[i].sec > maxt)
2562 				maxt = samp[i].sec;
2563 			for (j = 0; j < 3; j++) {
2564 				samp[i].rgb[j] -= minv[j];
2565 			}
2566 		}
2567 
2568 #ifdef FREQ_SLOW_PRECISE	/* Interpolate then autocorrelate */
2569 
2570 		/* Create PBPMS bins and interpolate readings into them */
2571 		nbins = 1 + (int)(maxt * 1000.0 * PBPMS + 0.5);
2572 		for (j = 0; j < 3; j++) {
2573 			if ((bins[j] = (double *)calloc(sizeof(double), nbins)) == NULL) {
2574 				a1loge(p->log, inst_internal_error, "munki_measure_refresh: malloc failed\n");
2575 				return MUNKI_INT_MALLOC;
2576 			}
2577 		}
2578 
2579 		/* Do the interpolation */
2580 		for (k = 0; k < (nfsamps-1); k++) {
2581 			int sbin, ebin;
2582 			sbin = (int)(samp[k].sec * 1000.0 * PBPMS + 0.5);
2583 			ebin = (int)(samp[k+1].sec * 1000.0 * PBPMS + 0.5);
2584 			for (i = sbin; i <= ebin; i++) {
2585 				double bl;
2586 #if defined(__APPLE__) && defined(__POWERPC__)
2587 				gcc_bug_fix(i);
2588 #endif
2589 				bl = (i - sbin)/(double)(ebin - sbin);	/* 0.0 to 1.0 */
2590 				for (j = 0; j < 3; j++) {
2591 					bins[j][i] = (1.0 - bl) * samp[k].rgb[j] + bl * samp[k+1].rgb[j];
2592 				}
2593 			}
2594 		}
2595 
2596 #ifdef NEVER
2597 		/* Plot interpolated values */
2598 		{
2599 			double *xx;
2600 			double *y1;
2601 			double *y2;
2602 			double *y3;
2603 
2604 			xx = malloc(sizeof(double) * nbins);
2605 			y1 = malloc(sizeof(double) * nbins);
2606 			y2 = malloc(sizeof(double) * nbins);
2607 			y3 = malloc(sizeof(double) * nbins);
2608 
2609 			if (xx == NULL || y1 == NULL || y2 == NULL || y3 == NULL) {
2610 				a1loge(p->log, inst_internal_error, "munki_measure_refresh: malloc failed\n");
2611 				for (j = 0; j < 3; j++)
2612 					free(bins[j]);
2613 				return MUNKI_INT_MALLOC;
2614 			}
2615 			for (i = 0; i < nbins; i++) {
2616 				xx[i] = i / (double)PBPMS;			/* msec */
2617 				y1[i] = bins[0][i];
2618 				y2[i] = bins[1][i];
2619 				y3[i] = bins[2][i];
2620 			}
2621 			printf("Interpolated fast scan sensor values and time (msec) for inttime %f\n",inttime);
2622 			do_plot6(xx, y1, y2, y3, NULL, NULL, NULL, nbins);
2623 
2624 			free(xx);
2625 			free(y1);
2626 			free(y2);
2627 			free(y3);
2628 		}
2629 #endif /* PLOT_REFRESH */
2630 
2631 		/* Compute auto-correlation at 1/PBPMS msec intervals */
2632 		/* from 25 msec (40Hz) to 100msec (10 Hz) */
2633 		mincv = 1e48, maxcv = -1e48;
2634 		for (i = 0; i < NPER; i++) {
2635 			int poff = PERMIN + i;		/* Offset to corresponding sample */
2636 
2637 			corr[i] = 0;
2638 			for (k = 0; (k + poff) < nbins; k++) {
2639 				corr[i] += bins[0][k] * bins[0][k + poff]
2640 				        +  bins[1][k] * bins[1][k + poff]
2641 				        +  bins[2][k] * bins[2][k + poff];
2642 			}
2643 			corr[i] /= (double)k;		/* Normalize */
2644 
2645 			if (corr[i] > maxcv)
2646 				maxcv = corr[i];
2647 			if (corr[i] < mincv)
2648 				mincv = corr[i];
2649 		}
2650 		/* Free the bins */
2651 		for (j = 0; j < 3; j++)
2652 			free(bins[j]);
2653 
2654 #else /* !FREQ_SLOW_PRECISE  Fast - autocorrellate then filter */
2655 
2656 	/* Upsample by a factor of 2 */
2657 	for (i = nfsamps-1; i >= 0; i--) {
2658 		j = 2 * i;
2659 		samp[j].sec = samp[i].sec;
2660 		samp[j].rgb[0] = samp[i].rgb[0];
2661 		samp[j].rgb[1] = samp[i].rgb[1];
2662 		samp[j].rgb[2] = samp[i].rgb[2];
2663 		if (i > 0) {
2664 			j--;
2665 			samp[j].sec = 0.5 * (samp[i].sec + samp[i-1].sec);
2666 			samp[j].rgb[0] = 0.5 * (samp[i].rgb[0] + samp[i-1].rgb[0]);
2667 			samp[j].rgb[1] = 0.5 * (samp[i].rgb[1] + samp[i-1].rgb[1]);
2668 			samp[j].rgb[2] = 0.5 * (samp[i].rgb[2] + samp[i-1].rgb[2]);
2669 		}
2670 	}
2671 	nfsamps = 2 * nfsamps - 1;
2672 
2673 	/* Do point by point correllation of samples */
2674 	for (i = 0; i < NPER; i++) {
2675 		tcorr[i] = 0.0;
2676 		ntcorr[i] = 0;
2677 	}
2678 
2679 	for (j = 0; j < (nfsamps-1); j++) {
2680 
2681 		for (k = j+1; k < nfsamps; k++) {
2682 			double del, cor;
2683 			int bix;
2684 
2685 			del = samp[k].sec - samp[j].sec;
2686 			bix = (int)(del * 1000.0 * PBPMS + 0.5);
2687 			if (bix < PERMIN)
2688 				continue;
2689 			if (bix > PERMAX)
2690 				break;
2691 			bix -= PERMIN;
2692 
2693 			cor = samp[j].rgb[0] * samp[k].rgb[0]
2694 	            + samp[j].rgb[1] * samp[k].rgb[1]
2695 	            + samp[j].rgb[2] * samp[k].rgb[2];
2696 
2697 //printf("~1 j %d k %d, del %f bix %d cor %f\n",j,k,del,bix,cor);
2698 			tcorr[bix] += cor;
2699 			ntcorr[bix]++;
2700 		}
2701 	}
2702 	/* Divide out count and linearly interpolate */
2703 	j = 0;
2704 	for (i = 0; i < NPER; i++) {
2705 		if (ntcorr[i] > 0) {
2706 			tcorr[i] /= ntcorr[i];
2707 			if ((i - j) > 1) {
2708 				if (j == 0) {
2709 					for (k = j; k < i; k++)
2710 						tcorr[k] = tcorr[i];
2711 
2712 				} else {		/* Linearly interpolate from last value */
2713 					double ww = (double)i-j;
2714 					for (k = j+1; k < i; k++) {
2715 						double bl = (k-j)/ww;
2716 						tcorr[k] = (1.0 - bl) * tcorr[j] + bl * tcorr[i];
2717 					}
2718 				}
2719 			}
2720 			j = i;
2721 		}
2722 	}
2723 	if (j < (NPER-1)) {
2724 		for (k = j+1; k < NPER; k++) {
2725 			tcorr[k] = tcorr[j];
2726 		}
2727 	}
2728 
2729 #ifdef PLOT_REFRESH
2730 	/* Plot unfiltered auto correlation */
2731 	{
2732 		double xx[NPER];
2733 		double y1[NPER];
2734 
2735 		for (i = 0; i < NPER; i++) {
2736 			xx[i] = (i + PERMIN) / (double)PBPMS;			/* msec */
2737 			y1[i] = tcorr[i];
2738 		}
2739 		printf("Unfiltered auto correlation (msec)\n");
2740 		do_plot6(xx, y1, NULL, NULL, NULL, NULL, NULL, NPER);
2741 	}
2742 #endif /* PLOT_REFRESH */
2743 
2744 	/* Apply a gausian filter */
2745 #define FWIDTH 100
2746 	{
2747 		double gaus_[2 * FWIDTH * PBPMS + 1];
2748 		double *gaus = &gaus_[FWIDTH * PBPMS];
2749 		double bb = 1.0/pow(2, 5.0);
2750 		double fw = inttime * 1000.0;
2751 		int ifw;
2752 
2753 //printf("~1 sc = %f = %f msec\n",1.0/inttime, fw);
2754 //printf("~1 fw = %f, ifw = %d\n",fw,ifw);
2755 
2756 		fw *= 0.9;
2757 		ifw = (int)ceil(fw * PBPMS);
2758 		if (ifw > FWIDTH * PBPMS)
2759 			error("munki: Not enough space for lanczos 2 filter");
2760 		for (j = -ifw; j <= ifw; j++) {
2761 			double x, y;
2762 			x = j/(PBPMS * fw);
2763 			if (fabs(x) > 1.0)
2764 				y = 0.0;
2765 			else
2766 				y = 1.0/pow(2, 5.0 * x * x) - bb;
2767 			gaus[j] = y;
2768 //printf("~1 gaus[%d] = %f\n",j,y);
2769 		}
2770 
2771 		for (i = 0; i < NPER; i++) {
2772 			double sum = 0.0;
2773 			double wght = 0.0;
2774 
2775 			for (j = -ifw; j <= ifw; j++) {
2776 				double w;
2777 				int ix = i + j;
2778 				if (ix < 0)
2779 					ix = -ix;
2780 				if (ix > (NPER-1))
2781 					ix = 2 * NPER-1 - ix;
2782 				w = gaus[j];
2783 				sum += w * tcorr[ix];
2784 				wght += w;
2785 			}
2786 //printf("~1 corr[%d] wgt = %f\n",i,wght);
2787 			corr[i] = sum / wght;
2788 		}
2789 	}
2790 
2791 	/* Compute min & max */
2792 	mincv = 1e48, maxcv = -1e48;
2793 	for (i = 0; i < NPER; i++) {
2794 		if (corr[i] > maxcv)
2795 			maxcv = corr[i];
2796 		if (corr[i] < mincv)
2797 			mincv = corr[i];
2798 	}
2799 
2800 #endif /* !FREQ_SLOW_PRECISE  Fast - autocorrellate then filter */
2801 
2802 		crange = maxcv - mincv;
2803 		a1logd(p->log,3,"Correlation value range %f - %f = %f = %f%%\n",mincv, maxcv,crange, 100.0 * (maxcv-mincv)/maxcv);
2804 
2805 #ifdef PLOT_REFRESH
2806 		/* Plot this measuremnts auto correlation */
2807 		{
2808 			double xx[NPER];
2809 			double y1[NPER];
2810 
2811 			for (i = 0; i < NPER; i++) {
2812 				xx[i] = (i + PERMIN) / (double)PBPMS;			/* msec */
2813 				y1[i] = corr[i];
2814 			}
2815 			printf("Auto correlation (msec)\n");
2816 			do_plot6(xx, y1, NULL, NULL, NULL, NULL, NULL, NPER);
2817 		}
2818 #endif /* PLOT_REFRESH */
2819 
2820 #define PFDB 4	// normally 4
2821 		/* If there is sufficient level and distict correlations */
2822 		if (crange/maxcv >= 0.1) {
2823 
2824 			a1logd(p->log,PFDB,"Searching for peaks\n");
2825 
2826 			/* Locate all the peaks starting at the longest correllation */
2827 			for (i = (NPER-1-PWIDTH); i >= 0 && npeaks < MAXPKS; i--) {
2828 				double v1, v2, v3;
2829 				v1 = corr[i];
2830 				v2 = corr[i + PWIDTH/2];	/* Peak */
2831 				v3 = corr[i + PWIDTH];
2832 
2833 				if (fabs(v3 - v1)/crange < 0.05
2834 				 && (v2 - v1)/crange > 0.025
2835 				 && (v2 - v3)/crange > 0.025
2836 				 && (v2 - mincv)/crange > 0.5) {
2837 					double pkv;			/* Peak value */
2838 					int pki;			/* Peak index */
2839 					double ii, bl;
2840 
2841 #ifdef PLOT_REFRESH
2842 					a1logd(p->log,PFDB,"Max between %f and %f msec\n",
2843 					       (i + PERMIN)/(double)PBPMS,(i + PWIDTH + PERMIN)/(double)PBPMS);
2844 #endif
2845 
2846 					/* Locate the actual peak */
2847 					pkv = -1.0;
2848 					pki = 0;
2849 					for (j = i; j < (i + PWIDTH); j++) {
2850 						if (corr[j] > pkv) {
2851 							pkv = corr[j];
2852 							pki = j;
2853 						}
2854 					}
2855 #ifdef PLOT_REFRESH
2856 					a1logd(p->log,PFDB,"Peak is at %f msec, %f corr\n", (pki + PERMIN)/(double)PBPMS, pkv);
2857 #endif
2858 
2859 					/* Interpolate the peak value for higher precision */
2860 					/* j = bigest */
2861 					if (corr[pki-1] > corr[pki+1])  {
2862 						j = pki-1;
2863 						k = pki+1;
2864 					} else {
2865 						j = pki+1;
2866 						k = pki-1;
2867 					}
2868 					bl = (corr[pki] - corr[j])/(corr[pki] - corr[k]);
2869 					bl = (bl + 1.0)/2.0;
2870 					ii = bl * pki + (1.0 - bl) * j;
2871 					pval = (ii + PERMIN)/(double)PBPMS;
2872 #ifdef PLOT_REFRESH
2873 					a1logd(p->log,PFDB,"Interpolated peak is at %f msec\n", pval);
2874 #endif
2875 					peaks[npeaks] = pval;
2876 					peakh[npeaks] = corr[pki];
2877 					npeaks++;
2878 
2879 					i -= PWIDTH;
2880 				}
2881 #ifdef NEVER
2882 				if (v2 > v1 && v2 > v3) {
2883 					printf("Peak rehjected:\n");
2884 					printf("(v3 - v1)/crange = %f < 0.05 ?\n",fabs(v3 - v1)/crange);
2885 					printf("(v2 - v1)/crange = %f > 0.025 ?\n",(v2 - v1)/crange);
2886 					printf("(v2 - v3)/crange = %f > 0.025 ?\n",(v2 - v3)/crange);
2887 					printf("(v2 - mincv)/crange = %f > 0.5 ?\n",(v2 - mincv)/crange);
2888 				}
2889 #endif
2890 			}
2891 			a1logd(p->log,3,"Number of peaks located = %d\n",npeaks);
2892 
2893 		} else {
2894 			a1logd(p->log,3,"All rejected, crange/maxcv = %f < 0.06\n",crange/maxcv);
2895 		}
2896 #undef PFDB
2897 
2898 		a1logd(p->log,3,"Number of peaks located = %d\n",npeaks);
2899 
2900 		if (npeaks > 1) {		/* Compute aparent refresh rate */
2901 			int nfails;
2902 			double div, avg, ano;
2903 			/* Try and locate a common divisor amongst all the peaks. */
2904 			/* This is likely to be the underlying refresh rate. */
2905 			for (k = 0; k < npeaks; k++) {
2906 				for (j = 1; j < 25; j++) {
2907 					avg = ano = 0.0;
2908 					div = peaks[k]/(double)j;
2909 					if (div < 5.0)
2910 						continue;		/* Skip anything higher than 200Hz */
2911 //printf("~1 trying %f Hz\n",1000.0/div);
2912 					for (nfails = i = 0; i < npeaks; i++) {
2913 						double rem, cnt;
2914 
2915 						rem = peaks[i]/div;
2916 						cnt = floor(rem + 0.5);
2917 						rem = fabs(rem - cnt);
2918 
2919 #ifdef PLOT_REFRESH
2920 						a1logd(p->log, 3, "remainder for peak %d = %f\n",i,rem);
2921 #endif
2922 						if (rem > 0.06) {
2923 							if (++nfails > 2)
2924 								break;				/* Fail this divisor */
2925 						} else {
2926 							avg += peaks[i];		/* Already weighted by cnt */
2927 							ano += cnt;
2928 						}
2929 					}
2930 
2931 					if (nfails == 0 || (nfails <= 2 && npeaks >= 6))
2932 						break;		/* Sucess */
2933 					/* else go and try a different divisor */
2934 				}
2935 				if (j < 25)
2936 					break;			/* Success - found common divisor */
2937 			}
2938 			if (k >= npeaks) {
2939 				a1logd(p->log,3,"Failed to locate common divisor\n");
2940 
2941 			} else {
2942 				pval = 0.001 * avg/ano;
2943 				if (pval < inttime) {
2944 					a1logd(p->log,3,"Discarding frequency %f > sample rate %f\n",1.0/pval, 1.0/inttime);
2945 				} else {
2946 					pval = 1.0/pval;		/* Convert to frequency */
2947 					rfreq[tix++] = pval;
2948 					a1logd(p->log,3,"Located frequency %f sum %f dif %f\n",pval, pval + 1.0/inttime, fabs(pval - 1.0/inttime));
2949 				}
2950 			}
2951 		}
2952 	}
2953 
2954 	if (tix >= 3) {
2955 
2956 		for (mm = 0; mm < tix; mm++) {
2957 			a1logd(p->log, 3, "Try %d, samp %f Hz, Meas %f Hz, Sum %f Hz, Dif %f Hz\n",mm,rsamp[mm],rfreq[mm], rsamp[mm] + rfreq[mm], fabs(rsamp[mm] - rfreq[mm]));
2958 		}
2959 
2960 		/* Decide if we are above the nyquist, or whether */
2961 		/* we have aliases of the fundamental */
2962 		{
2963 			double brange = 1e38;
2964 			double brate = 0.0;
2965 			int bsplit = -1;
2966 			double min, max, avg, range;
2967 			int split, mul, niia;
2968 
2969 			/* Compute fundamental and sub aliases at all possible splits. */
2970 			/* Skip the reading at the split. */
2971 			for (split = tix; split >= -1; split--) {
2972 				min = 1e38; max = -1e38; avg = 0.0; niia = 0;
2973 				for (mm = 0; mm < tix; mm++) {
2974 					double alias;
2975 
2976 					if (mm == split)
2977 						continue;
2978 					if (mm < split)
2979 						alias = rfreq[mm];
2980 					else
2981 						alias = fabs(rsamp[mm] - rfreq[mm]);
2982 
2983 					avg += alias;
2984 					niia++;
2985 
2986 					if (alias < min)
2987 						min = alias;
2988 					if (alias > max)
2989 						max = alias;
2990 				}
2991 				avg /= (double)niia;
2992 				range = (max - min)/(max + min);
2993 //printf("~1 split %d avg = %f, range = %f\n",split,avg,range);
2994 				if (range < brange) {
2995 					brange = range;
2996 					brate = avg;
2997 					bsplit = split;
2998 				}
2999 			}
3000 
3001 			/* Compute sub and add aliases at all possible splits */
3002 			/* Skip the reading at the split. */
3003 			for (split = tix; split >= -1; split--) {
3004 				min = 1e38; max = -1e38; avg = 0.0; niia = 0;
3005 				for (mm = 0; mm < tix; mm++) {
3006 					double alias;
3007 
3008 					if (mm == split)
3009 						continue;
3010 					if (mm < split)
3011 						alias = fabs(rsamp[mm] - rfreq[mm]);
3012 					else
3013 						alias = rsamp[mm] + rfreq[mm];
3014 
3015 					avg += alias;
3016 					niia++;
3017 
3018 					if (alias < min)
3019 						min = alias;
3020 					if (alias > max)
3021 						max = alias;
3022 				}
3023 				avg /= (double)niia;
3024 				range = (max - min)/(max + min);
3025 //printf("~1 split %d avg = %f, range = %f\n",100 + split,avg,range);
3026 				if (range < brange) {
3027 					brange = range;
3028 					brate = avg;
3029 					bsplit = 100 + split;
3030 				}
3031 			}
3032 
3033 			a1logd(p->log, 3, "Selected split %d range %f\n",bsplit,brange);
3034 
3035 			/* Hmm. Could reject result and re-try if brange is too large ? ( > 0.005 ?) */
3036 
3037 			if (brange > 0.05) {
3038 				a1logd(p->log, 3, "Readings are too inconsistent (brange %.1f%%) - should retry ?\n",brange * 100.0);
3039 			} else {
3040 				if (ref_rate != NULL)
3041 					*ref_rate = brate;
3042 
3043 				/* Error against my 85Hz CRT - GWG */
3044 //				a1logd(p->log, 1, "Refresh rate %f Hz, error = %.4f%%\n",brate,100.0 * fabs(brate - 85.0)/(85.0));
3045 				return MUNKI_OK;
3046 			}
3047 		}
3048 	} else {
3049 		a1logd(p->log, 3, "Not enough tries suceeded to determine refresh rate\n");
3050 	}
3051 
3052 	return MUNKI_RD_NOREFR_FOUND;
3053 }
3054 #undef NFSAMPS
3055 #undef PBPMS
3056 #undef PERMIN
3057 #undef PERMAX
3058 #undef NPER
3059 #undef PWIDTH
3060 
3061 /* - - - - - - - - - - - - - - - - - - - - - - - - - - */
3062 /* Save the calibration for all modes, stored on local system */
3063 
3064 #ifdef ENABLE_NONVCAL
3065 
3066 /* non-volatile save/restor state to/from a file */
3067 typedef struct {
3068 	int ef;					/* Error flag, 1 = write failed, 2 = close failed */
3069 	unsigned int chsum;		/* Checksum */
3070 } mknonv;
3071 
update_chsum(mknonv * x,unsigned char * p,int nn)3072 static void update_chsum(mknonv *x, unsigned char *p, int nn) {
3073 	int i;
3074 	for (i = 0; i < nn; i++, p++)
3075 		x->chsum = ((x->chsum << 13) | (x->chsum >> (32-13))) + *p;
3076 }
3077 
3078 /* Write an array of chars to the file. Set the error flag to nz on error */
write_chars(mknonv * x,FILE * fp,char * dp,int n)3079 static void write_chars(mknonv *x, FILE *fp, char *dp, int n) {
3080 
3081 	if (fwrite((void *)dp, sizeof(char), n, fp) != n) {
3082 		x->ef = 1;
3083 	} else {
3084 		update_chsum(x, (unsigned char *)dp, n * sizeof(char));
3085 	}
3086 }
3087 
3088 /* Write an array of ints to the file. Set the error flag to nz on error */
write_ints(mknonv * x,FILE * fp,int * dp,int n)3089 static void write_ints(mknonv *x, FILE *fp, int *dp, int n) {
3090 
3091 	if (fwrite((void *)dp, sizeof(int), n, fp) != n) {
3092 		x->ef = 1;
3093 	} else {
3094 		update_chsum(x, (unsigned char *)dp, n * sizeof(int));
3095 	}
3096 }
3097 
3098 /* Write an array of doubles to the file. Set the error flag to nz on error */
write_doubles(mknonv * x,FILE * fp,double * dp,int n)3099 static void write_doubles(mknonv *x, FILE *fp, double *dp, int n) {
3100 
3101 	if (fwrite((void *)dp, sizeof(double), n, fp) != n) {
3102 		x->ef = 1;
3103 	} else {
3104 		update_chsum(x, (unsigned char *)dp, n * sizeof(double));
3105 	}
3106 }
3107 
3108 /* Write an array of time_t's to the file. Set the error flag to nz on error */
3109 /* (This will cause file checksum fail if different executables on the same */
3110 /*  system have different time_t values) */
write_time_ts(mknonv * x,FILE * fp,time_t * dp,int n)3111 static void write_time_ts(mknonv *x, FILE *fp, time_t *dp, int n) {
3112 
3113 	if (fwrite((void *)dp, sizeof(time_t), n, fp) != n) {
3114 		x->ef = 1;
3115 	} else {
3116 		update_chsum(x, (unsigned char *)dp, n * sizeof(time_t));
3117 	}
3118 }
3119 
3120 /* Read an array of ints from the file. Set the error flag to nz on error */
read_ints(mknonv * x,FILE * fp,int * dp,int n)3121 static void read_ints(mknonv *x, FILE *fp, int *dp, int n) {
3122 
3123 	if (fread((void *)dp, sizeof(int), n, fp) != n) {
3124 		x->ef = 1;
3125 	} else {
3126 		update_chsum(x, (unsigned char *)dp, n * sizeof(int));
3127 	}
3128 }
3129 
3130 /* Read an array of chars from the file. Set the error flag to nz on error */
read_chars(mknonv * x,FILE * fp,char * dp,int n)3131 static void read_chars(mknonv *x, FILE *fp, char *dp, int n) {
3132 
3133 	if (fread((void *)dp, sizeof(char), n, fp) != n) {
3134 		x->ef = 1;
3135 	} else {
3136 		update_chsum(x, (unsigned char *)dp, n * sizeof(char));
3137 	}
3138 }
3139 
3140 
3141 /* Read an array of doubles from the file. Set the error flag to nz on error */
read_doubles(mknonv * x,FILE * fp,double * dp,int n)3142 static void read_doubles(mknonv *x, FILE *fp, double *dp, int n) {
3143 
3144 	if (fread((void *)dp, sizeof(double), n, fp) != n) {
3145 		x->ef = 1;
3146 	} else {
3147 		update_chsum(x, (unsigned char *)dp, n * sizeof(double));
3148 	}
3149 }
3150 
3151 /* Read an array of time_t's from the file. Set the error flag to nz on error */
3152 /* (This will cause file checksum fail if different executables on the same */
3153 /*  system have different time_t values) */
read_time_ts(mknonv * x,FILE * fp,time_t * dp,int n)3154 static void read_time_ts(mknonv *x, FILE *fp, time_t *dp, int n) {
3155 
3156 	if (fread((void *)dp, sizeof(time_t), n, fp) != n) {
3157 		x->ef = 1;
3158 	} else {
3159 		update_chsum(x, (unsigned char *)dp, n * sizeof(time_t));
3160 	}
3161 }
3162 
munki_save_calibration(munki * p)3163 munki_code munki_save_calibration(munki *p) {
3164 	munkiimp *m = (munkiimp *)p->m;
3165 	munki_code ev = MUNKI_OK;
3166 	munki_state *s;
3167 	int i;
3168 	char nmode[10];
3169 	char cal_name[100];		/* Name */
3170 	char **cal_paths = NULL;
3171 	int no_paths = 0;
3172 	FILE *fp;
3173 	mknonv x;
3174 	int ss;
3175 	int argyllversion = ARGYLL_VERSION;
3176 
3177 	strcpy(nmode, "w");
3178 #if !defined(O_CREAT) && !defined(_O_CREAT)
3179 # error "Need to #include fcntl.h!"
3180 #endif
3181 #if defined(O_BINARY) || defined(_O_BINARY)
3182 	strcat(nmode, "b");
3183 #endif
3184 
3185 	sprintf(cal_name, "ArgyllCMS/.mk_%s.cal", m->serno);
3186 	if ((no_paths = xdg_bds(NULL, &cal_paths, xdg_cache, xdg_write, xdg_user, cal_name)) < 1) {
3187 		a1logd(p->log,1,"munki_save_calibration xdg_bds returned no paths\n");
3188 		return MUNKI_INT_CAL_SAVE;
3189 	}
3190 
3191 	a1logd(p->log,3,"munki_save_calibration saving to file '%s'\n",cal_paths[0]);
3192 
3193 	if (create_parent_directories(cal_paths[0])
3194 	 || (fp = fopen(cal_paths[0], nmode)) == NULL) {
3195 		a1logd(p->log,3,"munki_save_calibration failed to open file for writing\n");
3196 		xdg_free(cal_paths, no_paths);
3197 		return MUNKI_INT_CAL_SAVE;
3198 	}
3199 
3200 	x.ef = 0;
3201 	x.chsum = 0;
3202 
3203 	/* A crude structure signature */
3204 	ss = sizeof(munki_state) + sizeof(munkiimp);
3205 
3206 	/* Some file identification */
3207 	write_ints(&x, fp, &argyllversion, 1);
3208 	write_ints(&x, fp, &ss, 1);
3209 	write_chars(&x, fp, m->serno, 17);
3210 	write_ints(&x, fp, &m->nraw, 1);
3211 	write_ints(&x, fp, (int *)&m->nwav1, 1);
3212 	write_ints(&x, fp, (int *)&m->nwav2, 1);
3213 
3214 	/* For each mode, save the calibration if it's valid */
3215 	for (i = 0; i < mk_no_modes; i++) {
3216 		s = &m->ms[i];
3217 
3218 		/* Mode identification */
3219 		write_ints(&x, fp, &s->emiss, 1);
3220 		write_ints(&x, fp, &s->trans, 1);
3221 		write_ints(&x, fp, &s->reflective, 1);
3222 		write_ints(&x, fp, &s->scan, 1);
3223 		write_ints(&x, fp, &s->flash, 1);
3224 		write_ints(&x, fp, &s->ambient, 1);
3225 		write_ints(&x, fp, &s->projector, 1);
3226 		write_ints(&x, fp, &s->adaptive, 1);
3227 
3228 		/* Configuration calibration is valid for */
3229 		write_ints(&x, fp, &s->gainmode, 1);
3230 		write_doubles(&x, fp, &s->inttime, 1);
3231 
3232 		/* Calibration information */
3233 		write_ints(&x, fp, &s->dark_valid, 1);
3234 		write_time_ts(&x, fp, &s->ddate, 1);
3235 		write_doubles(&x, fp, &s->dark_int_time, 1);
3236 		write_doubles(&x, fp, s->dark_data-1, m->nraw+1);
3237 		write_doubles(&x, fp, &s->dark_int_time2, 1);
3238 		write_doubles(&x, fp, s->dark_data2-1, m->nraw+1);
3239 		write_doubles(&x, fp, &s->dark_int_time3, 1);
3240 		write_doubles(&x, fp, s->dark_data3-1, m->nraw+1);
3241 		write_ints(&x, fp, &s->dark_gain_mode, 1);
3242 
3243 		if (!s->emiss) {
3244 			write_ints(&x, fp, &s->cal_valid, 1);
3245 			write_time_ts(&x, fp, &s->cfdate, 1);
3246 			write_doubles(&x, fp, s->cal_factor1, m->nwav1);
3247 			write_doubles(&x, fp, s->cal_factor2, m->nwav2);
3248 			write_doubles(&x, fp, s->white_data-1, m->nraw+1);
3249 			write_doubles(&x, fp, &s->reftemp, 1);
3250 			write_doubles(&x, fp, s->iwhite_data[0]-1, m->nraw+1);
3251 			write_doubles(&x, fp, s->iwhite_data[1]-1, m->nraw+1);
3252 		}
3253 
3254 		write_ints(&x, fp, &s->idark_valid, 1);
3255 		write_time_ts(&x, fp, &s->iddate, 1);
3256 		write_doubles(&x, fp, s->idark_int_time, 4);
3257 		write_doubles(&x, fp, s->idark_data[0]-1, m->nraw+1);
3258 		write_doubles(&x, fp, s->idark_data[1]-1, m->nraw+1);
3259 		write_doubles(&x, fp, s->idark_data[2]-1, m->nraw+1);
3260 		write_doubles(&x, fp, s->idark_data[3]-1, m->nraw+1);
3261 	}
3262 
3263 	a1logd(p->log,3,"Checkum = 0x%x\n",x.chsum);
3264 	write_ints(&x, fp, (int *)&x.chsum, 1);
3265 
3266 	if (fclose(fp) != 0)
3267 		x.ef = 2;
3268 
3269 	if (x.ef != 0) {
3270 		a1logd(p->log,3,"Writing calibration file failed with %d\n",x.ef);
3271 		delete_file(cal_paths[0]);
3272 	} else {
3273 		a1logd(p->log,3,"Writing calibration file succeeded\n");
3274 	}
3275 	xdg_free(cal_paths, no_paths);
3276 
3277 	return ev;
3278 }
3279 
3280 /* Restore the all modes calibration from the local system */
munki_restore_calibration(munki * p)3281 munki_code munki_restore_calibration(munki *p) {
3282 	munkiimp *m = (munkiimp *)p->m;
3283 	munki_code ev = MUNKI_OK;
3284 	munki_state *s, ts;
3285 	int i, j;
3286 	char nmode[10];
3287 	char cal_name[100];		/* Name */
3288 	char **cal_paths = NULL;
3289 	int no_paths = 0;
3290 	FILE *fp;
3291 	mknonv x;
3292 	int argyllversion;
3293 	int ss, nraw, nwav1, nwav2, chsum1, chsum2;
3294 	char serno[17];
3295 
3296 	strcpy(nmode, "r");
3297 #if defined(O_BINARY) || defined(_O_BINARY)
3298 	strcat(nmode, "b");
3299 #endif
3300 
3301 	sprintf(cal_name, "ArgyllCMS/.mk_%s.cal" SSEPS "color/.mk_%s.cal", m->serno, m->serno);
3302 	if ((no_paths = xdg_bds(NULL, &cal_paths, xdg_cache, xdg_read, xdg_user, cal_name)) < 1) {
3303 		a1logd(p->log,1,"munki_restore_calibration xdg_bds returned no paths\n");
3304 		return MUNKI_INT_CAL_RESTORE;
3305 	}
3306 
3307 	a1logd(p->log,2,"munki_restore_calibration restoring from file '%s'\n",cal_paths[0]);
3308 
3309 	/* Check the last modification time */
3310 	{
3311 		struct sys_stat sbuf;
3312 
3313 		if (sys_stat(cal_paths[0], &sbuf) == 0) {
3314 			m->lo_secs = time(NULL) - sbuf.st_mtime;
3315 			a1logd(p->log,2,"munki_restore_calibration: %d secs from instrument last open\n",m->lo_secs);
3316 		} else {
3317 			a1logd(p->log,2,"munki_restore_calibration: stat on file failed\n");
3318 		}
3319 	}
3320 
3321 	if ((fp = fopen(cal_paths[0], nmode)) == NULL) {
3322 		a1logd(p->log,2,"munki_restore_calibration failed to open file for reading\n");
3323 		xdg_free(cal_paths, no_paths);
3324 		return MUNKI_INT_CAL_RESTORE;
3325 	}
3326 	xdg_free(cal_paths, no_paths);
3327 
3328 	x.ef = 0;
3329 	x.chsum = 0;
3330 
3331 	/* Check the file identification */
3332 	read_ints(&x, fp, &argyllversion, 1);
3333 	read_ints(&x, fp, &ss, 1);
3334 	read_chars(&x, fp, serno, 17);
3335 	read_ints(&x, fp, &nraw, 1);
3336 	read_ints(&x, fp, &nwav1, 1);
3337 	read_ints(&x, fp, &nwav2, 1);
3338 	if (x.ef != 0
3339 	 || argyllversion != ARGYLL_VERSION
3340 	 || ss != (sizeof(munki_state) + sizeof(munkiimp))
3341 	 || strcmp(serno, m->serno) != 0
3342 	 || nraw != m->nraw
3343 	 || nwav1 != m->nwav1
3344 	 || nwav2 != m->nwav2) {
3345 		a1logd(p->log,3,"Identification didn't verify\n");
3346 		goto reserr;
3347 	}
3348 
3349 	/* Do a dummy read to check the checksum */
3350 	for (i = 0; i < mk_no_modes; i++) {
3351 		int di;
3352 		double dd;
3353 		time_t dt;
3354 		int emiss, trans, reflective, ambient, projector, scan, flash, adaptive;
3355 
3356 		s = &m->ms[i];
3357 
3358 		/* Mode identification */
3359 		read_ints(&x, fp, &emiss, 1);
3360 		read_ints(&x, fp, &trans, 1);
3361 		read_ints(&x, fp, &reflective, 1);
3362 		read_ints(&x, fp, &scan, 1);
3363 		read_ints(&x, fp, &flash, 1);
3364 		read_ints(&x, fp, &ambient, 1);
3365 		read_ints(&x, fp, &projector, 1);
3366 		read_ints(&x, fp, &adaptive, 1);
3367 
3368 		/* Configuration calibration is valid for */
3369 		read_ints(&x, fp, &di, 1);					/* gainmode */
3370 		read_doubles(&x, fp, &dd, 1);				/* inttime */
3371 
3372 		/* Calibration information */
3373 		read_ints(&x, fp, &di, 1);					/* dark_valid */
3374 		read_time_ts(&x, fp, &dt, 1);				/* ddate */
3375 		read_doubles(&x, fp, &dd, 1);				/* dark_int_time */
3376 		for (j = -1; j < m->nraw; j++)
3377 			read_doubles(&x, fp, &dd, 1);			/* dark_data */
3378 		read_doubles(&x, fp, &dd, 1);				/* dark_int_time2 */
3379 		for (j = -1; j < m->nraw; j++)
3380 			read_doubles(&x, fp, &dd, 1);			/* dark_data2 */
3381 		read_doubles(&x, fp, &dd, 1);				/* dark_int_time3 */
3382 		for (j = -1; j < m->nraw; j++)
3383 			read_doubles(&x, fp, &dd, 1);			/* dark_data3 */
3384 		read_ints(&x, fp, &di, 1);					/* dark_gain_mode */
3385 
3386 		if (!s->emiss) {
3387 			read_ints(&x, fp, &di, 1);				/* cal_valid */
3388 			read_time_ts(&x, fp, &dt, 1);			/* cfdate */
3389 			for (j = 0; j < m->nwav1; j++)
3390 				read_doubles(&x, fp, &dd, 1);		/* cal_factor1 */
3391 			for (j = 0; j < m->nwav2; j++)
3392 				read_doubles(&x, fp, &dd, 1);		/* cal_factor2 */
3393 			for (j = -1; j < m->nraw; j++)
3394 				read_doubles(&x, fp, &dd, 1);		/* white_data */
3395 			read_doubles(&x, fp, &dd, 1);			/* reftemp */
3396 			for (j = -1; j < m->nraw; j++)
3397 				read_doubles(&x, fp, &dd, 1);		/* iwhite_data[0] */
3398 			for (j = -1; j < m->nraw; j++)
3399 				read_doubles(&x, fp, &dd, 1);		/* iwhite_data[1] */
3400 		}
3401 
3402 		read_ints(&x, fp, &di, 1);					/* idark_valid */
3403 		read_time_ts(&x, fp, &dt, 1);				/* iddate */
3404 		for (j = 0; j < 4; j++)
3405 			read_doubles(&x, fp, &dd, 1);			/* idark_int_time */
3406 		for (j = -1; j < m->nraw; j++)
3407 			read_doubles(&x, fp, &dd, 1);			/* idark_data[0] */
3408 		for (j = -1; j < m->nraw; j++)
3409 			read_doubles(&x, fp, &dd, 1);			/* idark_data[1] */
3410 		for (j = -1; j < m->nraw; j++)
3411 			read_doubles(&x, fp, &dd, 1);			/* idark_data[2] */
3412 		for (j = -1; j < m->nraw; j++)
3413 			read_doubles(&x, fp, &dd, 1);			/* idark_data[3] */
3414 	}
3415 
3416 	chsum1 = x.chsum;
3417 	read_ints(&x, fp, &chsum2, 1);
3418 
3419 	if (x.ef != 0
3420 	 || chsum1 != chsum2) {
3421 		a1logd(p->log,3,"Checksum didn't verify, got 0x%x, expected 0x%x\n",chsum1, chsum2);
3422 		goto reserr;
3423 	}
3424 
3425 	rewind(fp);
3426 
3427 	/* Allocate space in temp structure */
3428 
3429 	ts.dark_data = dvectorz(-1, m->nraw-1);
3430 	ts.dark_data2 = dvectorz(-1, m->nraw-1);
3431 	ts.dark_data3 = dvectorz(-1, m->nraw-1);
3432 	ts.cal_factor1 = dvectorz(0, m->nwav1-1);
3433 	ts.cal_factor2 = dvectorz(0, m->nwav2-1);
3434 	ts.white_data = dvectorz(-1, m->nraw-1);
3435 	ts.iwhite_data = dmatrixz(0, 2, -1, m->nraw-1);
3436 	ts.idark_data = dmatrixz(0, 3, -1, m->nraw-1);
3437 
3438 	/* Read the identification */
3439 	read_ints(&x, fp, &argyllversion, 1);
3440 	read_ints(&x, fp, &ss, 1);
3441 	read_chars(&x, fp, m->serno, 17);
3442 	read_ints(&x, fp, &m->nraw, 1);
3443 	read_ints(&x, fp, (int *)&m->nwav1, 1);
3444 	read_ints(&x, fp, (int *)&m->nwav2, 1);
3445 
3446 	/* For each mode, save the calibration if it's valid */
3447 	for (i = 0; i < mk_no_modes; i++) {
3448 		s = &m->ms[i];
3449 
3450 		/* Mode identification */
3451 		read_ints(&x, fp, &ts.emiss, 1);
3452 		read_ints(&x, fp, &ts.trans, 1);
3453 		read_ints(&x, fp, &ts.reflective, 1);
3454 		read_ints(&x, fp, &ts.scan, 1);
3455 		read_ints(&x, fp, &ts.flash, 1);
3456 		read_ints(&x, fp, &ts.ambient, 1);
3457 		read_ints(&x, fp, &ts.projector, 1);
3458 		read_ints(&x, fp, &ts.adaptive, 1);
3459 
3460 		/* Configuration calibration is valid for */
3461 		read_ints(&x, fp, &ts.gainmode, 1);
3462 		read_doubles(&x, fp, &ts.inttime, 1);
3463 
3464 		/* Calibration information: */
3465 
3466 		/* Static Dark */
3467 		read_ints(&x, fp, &ts.dark_valid, 1);
3468 		read_time_ts(&x, fp, &ts.ddate, 1);
3469 		read_doubles(&x, fp, &ts.dark_int_time, 1);
3470 		read_doubles(&x, fp, ts.dark_data-1, m->nraw+1);
3471 		read_doubles(&x, fp, &ts.dark_int_time2, 1);
3472 		read_doubles(&x, fp, ts.dark_data2-1, m->nraw+1);
3473 		read_doubles(&x, fp, &ts.dark_int_time3, 1);
3474 		read_doubles(&x, fp, ts.dark_data3-1, m->nraw+1);
3475 		read_ints(&x, fp, &ts.dark_gain_mode, 1);
3476 
3477 		if (!ts.emiss) {
3478 			/* Reflective */
3479 			read_ints(&x, fp, &ts.cal_valid, 1);
3480 			read_time_ts(&x, fp, &ts.cfdate, 1);
3481 			read_doubles(&x, fp, ts.cal_factor1, m->nwav1);
3482 			read_doubles(&x, fp, ts.cal_factor2, m->nwav2);
3483 			read_doubles(&x, fp, ts.white_data-1, m->nraw+1);
3484 			read_doubles(&x, fp, &ts.reftemp, 1);
3485 			read_doubles(&x, fp, ts.iwhite_data[0]-1, m->nraw+1);
3486 			read_doubles(&x, fp, ts.iwhite_data[1]-1, m->nraw+1);
3487 		}
3488 
3489 		/* Adaptive Dark */
3490 		read_ints(&x, fp, &ts.idark_valid, 1);
3491 		read_time_ts(&x, fp, &ts.iddate, 1);
3492 		read_doubles(&x, fp, ts.idark_int_time, 4);
3493 		read_doubles(&x, fp, ts.idark_data[0]-1, m->nraw+1);
3494 		read_doubles(&x, fp, ts.idark_data[1]-1, m->nraw+1);
3495 		read_doubles(&x, fp, ts.idark_data[2]-1, m->nraw+1);
3496 		read_doubles(&x, fp, ts.idark_data[3]-1, m->nraw+1);
3497 
3498 		/* If the configuration for this mode matches */
3499 		/* that of the calibration, restore the calibration */
3500 		/* for this mode. */
3501 		if (x.ef == 0				/* No read error */
3502 		 &&	s->emiss == ts.emiss
3503 		 && s->trans == ts.trans
3504 		 && s->reflective == ts.reflective
3505 		 && s->scan == ts.scan
3506 		 && s->flash == ts.flash
3507 		 && s->ambient == ts.ambient
3508 		 && s->projector == ts.projector
3509 		 && s->adaptive == ts.adaptive
3510 		 && (s->adaptive || fabs(s->inttime - ts.inttime) < 0.01)
3511 		 && (s->adaptive || fabs(s->dark_int_time - ts.dark_int_time) < 0.01)
3512 		 && (s->adaptive || fabs(s->dark_int_time2 - ts.dark_int_time2) < 0.01)
3513 		 && (s->adaptive || fabs(s->dark_int_time3 - ts.dark_int_time3) < 0.01)
3514 		 && (!s->adaptive || fabs(s->idark_int_time[0] - ts.idark_int_time[0]) < 0.01)
3515 		 && (!s->adaptive || fabs(s->idark_int_time[1] - ts.idark_int_time[1]) < 0.01)
3516 		 && (!s->adaptive || fabs(s->idark_int_time[2] - ts.idark_int_time[2]) < 0.01)
3517 		 && (!s->adaptive || fabs(s->idark_int_time[3] - ts.idark_int_time[3]) < 0.01)
3518 		) {
3519 			/* Copy all the fields read above */
3520 			s->emiss = ts.emiss;
3521 			s->trans = ts.trans;
3522 			s->reflective = ts.reflective;
3523 			s->scan = ts.scan;
3524 			s->flash = ts.flash;
3525 			s->ambient = ts.ambient;
3526 			s->projector = ts.projector;
3527 			s->adaptive = ts.adaptive;
3528 
3529 			s->gainmode = ts.gainmode;
3530 			s->inttime = ts.inttime;
3531 			s->dark_valid = ts.dark_valid;
3532 			s->ddate = ts.ddate;
3533 			s->dark_int_time = ts.dark_int_time;
3534 			for (j = -1; j < m->nraw; j++)
3535 				s->dark_data[j] = ts.dark_data[j];
3536 			s->dark_int_time2 = ts.dark_int_time2;
3537 			for (j = -1; j < m->nraw; j++)
3538 				s->dark_data2[j] = ts.dark_data2[j];
3539 			s->dark_int_time3 = ts.dark_int_time3;
3540 			for (j = -1; j < m->nraw; j++)
3541 				s->dark_data3[j] = ts.dark_data3[j];
3542 			s->dark_gain_mode = ts.dark_gain_mode;
3543 			if (!ts.emiss) {
3544 				s->cal_valid = ts.cal_valid;
3545 				s->cfdate = ts.cfdate;
3546 				for (j = 0; j < m->nwav1; j++)
3547 					s->cal_factor1[j] = ts.cal_factor1[j];
3548 				for (j = 0; j < m->nwav2; j++)
3549 					s->cal_factor2[j] = ts.cal_factor2[j];
3550 				for (j = -1; j < m->nraw; j++)
3551 					s->white_data[j] = ts.white_data[j];
3552 				s->reftemp = ts.reftemp;
3553 				for (j = -1; j < m->nraw; j++)
3554 					s->iwhite_data[0][j] = ts.iwhite_data[0][j];
3555 				for (j = -1; j < m->nraw; j++)
3556 					s->iwhite_data[1][j] = ts.iwhite_data[1][j];
3557 			}
3558 			s->idark_valid = ts.idark_valid;
3559 			s->iddate = ts.iddate;
3560 			for (j = 0; j < 4; j++)
3561 				s->idark_int_time[j] = ts.idark_int_time[j];
3562 			for (j = -1; j < m->nraw; j++)
3563 				s->idark_data[0][j] = ts.idark_data[0][j];
3564 			for (j = -1; j < m->nraw; j++)
3565 				s->idark_data[1][j] = ts.idark_data[1][j];
3566 			for (j = -1; j < m->nraw; j++)
3567 				s->idark_data[2][j] = ts.idark_data[2][j];
3568 			for (j = -1; j < m->nraw; j++)
3569 				s->idark_data[3][j] = ts.idark_data[3][j];
3570 
3571 		} else {
3572 //printf("~1 mode %d\n",i);
3573 //printf("~1 adaptive = %d\n",s->adaptive);
3574 //printf("~1 innttime %f %f\n",s->inttime, ts.inttime);
3575 //printf("~1 dark_int_time %f %f\n",s->dark_int_time,ts.dark_int_time);
3576 //printf("~1 dark_int_time2 %f %f\n",s->dark_int_time2,ts.dark_int_time2);
3577 //printf("~1 dark_int_time3 %f %f\n",s->dark_int_time3,ts.dark_int_time3);
3578 //printf("~1 idark_int_time0 %f %f\n",s->idark_int_time[0],ts.idark_int_time[0]);
3579 //printf("~1 idark_int_time1 %f %f\n",s->idark_int_time[1],ts.idark_int_time[1]);
3580 //printf("~1 idark_int_time2 %f %f\n",s->idark_int_time[2],ts.idark_int_time[2]);
3581 //printf("~1 idark_int_time3 %f %f\n",s->idark_int_time[3],ts.idark_int_time[3]);
3582 			a1logd(p->log,4,"Not restoring cal for mode %d since params don't match:\n",i);
3583 			a1logd(p->log,4,"emis = %d : %d, trans = %d : %d, ref = %d : %d\n",s->emiss,ts.emiss,s->trans,ts.trans,s->reflective,ts.reflective);
3584 			a1logd(p->log,4,"scan = %d : %d, flash = %d : %d, ambi = %d : %d, proj = %d : %d, adapt = %d : %d\n",s->scan,ts.scan,s->flash,ts.flash,s->ambient,ts.ambient,s->projector,ts.projector,s->adaptive,ts.adaptive);
3585 			a1logd(p->log,4,"inttime = %f : %f\n",s->inttime,ts.inttime);
3586 			a1logd(p->log,4,"darkit1 = %f : %f, 2 = %f : %f, 3 = %f : %f\n",s->dark_int_time,ts.dark_int_time,s->dark_int_time2,ts.dark_int_time2,s->dark_int_time3,ts.dark_int_time3);
3587 			a1logd(p->log,4,"idarkit0 = %f : %f, 1 = %f : %f, 2 = %f : %f, 3 = %f : %f\n",s->idark_int_time[0],ts.idark_int_time[0],s->idark_int_time[1],ts.idark_int_time[1],s->idark_int_time[2],ts.idark_int_time[2],s->idark_int_time[3],ts.idark_int_time[3]);
3588 		}
3589 	}
3590 
3591 	/* Free up temporary space */
3592 	free_dvector(ts.dark_data, -1, m->nraw-1);
3593 	free_dvector(ts.dark_data2, -1, m->nraw-1);
3594 	free_dvector(ts.dark_data3, -1, m->nraw-1);
3595 	free_dvector(ts.white_data, -1, m->nraw-1);
3596 	free_dmatrix(ts.iwhite_data, 0, 1, -1, m->nraw-1);
3597 	free_dmatrix(ts.idark_data, 0, 3, -1, m->nraw-1);
3598 
3599 	free_dvector(ts.cal_factor1, 0, m->nwav1-1);
3600 	free_dvector(ts.cal_factor2, 0, m->nwav2-1);
3601 
3602 	a1logd(p->log,3,"munki_restore_calibration done\n");
3603  reserr:;
3604 
3605 	fclose(fp);
3606 
3607 	return ev;
3608 }
3609 
munki_touch_calibration(munki * p)3610 munki_code munki_touch_calibration(munki *p) {
3611 	munkiimp *m = (munkiimp *)p->m;
3612 	munki_code ev = MUNKI_OK;
3613 	char cal_name[100];		/* Name */
3614 	char **cal_paths = NULL;
3615 	int no_paths = 0;
3616 	int rv;
3617 
3618 	sprintf(cal_name, "ArgyllCMS/.mk_%s.cal" SSEPS "color/.mk_%s.cal", m->serno, m->serno);
3619 	if ((no_paths = xdg_bds(NULL, &cal_paths, xdg_cache, xdg_read, xdg_user, cal_name)) < 1)
3620 		return MUNKI_INT_CAL_TOUCH;
3621 
3622 	a1logd(p->log,2,"munki_touch_calibration touching file '%s'\n",cal_paths[0]);
3623 
3624 	if ((rv = sys_utime(cal_paths[0], NULL)) != 0) {
3625 		a1logd(p->log,2,"munki_touch_calibration failed with %d\n",rv);
3626 		xdg_free(cal_paths, no_paths);
3627 		return MUNKI_INT_CAL_TOUCH;
3628 	}
3629 	xdg_free(cal_paths, no_paths);
3630 
3631 	return ev;
3632 }
3633 
3634 #endif /* ENABLE_NONVCAL */
3635 
3636 
3637 /* ============================================================ */
3638 /* Intermediate routines  - composite commands/processing */
3639 
3640 /* Take a dark reference measurement - part 1 */
munki_dark_measure_1(munki * p,int nummeas,double * inttime,int gainmode,unsigned char * buf,unsigned int bsize)3641 munki_code munki_dark_measure_1(
3642 	munki *p,
3643 	int nummeas,			/* Number of readings to take */
3644 	double *inttime, 		/* Integration time to use/used */
3645 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
3646 	unsigned char *buf,		/* USB reading buffer to use */
3647 	unsigned int bsize		/* Size of buffer */
3648 ) {
3649 	munki_code ev = MUNKI_OK;
3650 
3651 	if (nummeas <= 0)
3652 		return MUNKI_INT_ZEROMEASURES;
3653 
3654 	if ((ev = munki_trigger_one_measure(p, nummeas, inttime, gainmode, 1, 1)) != MUNKI_OK)
3655 		return ev;
3656 
3657 	if ((ev = munki_readmeasurement(p, nummeas, 0, buf, bsize, NULL, 1, 1)) != MUNKI_OK)
3658 		return ev;
3659 
3660 	return ev;
3661 }
3662 
3663 /* Take a dark reference measurement - part 2 */
munki_dark_measure_2(munki * p,double * sens,int nummeas,double inttime,int gainmode,unsigned char * buf,unsigned int bsize)3664 munki_code munki_dark_measure_2(
3665 	munki *p,
3666 	double *sens,		/* Return array [-1 nraw] of sens values */
3667 	int nummeas,			/* Number of readings to take */
3668 	double inttime, 		/* Integration time to use/used */
3669 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
3670 	unsigned char *buf,		/* raw USB reading buffer to process */
3671 	unsigned int bsize		/* Buffer size to process */
3672 ) {
3673 	munki_code ev = MUNKI_OK;
3674 	munkiimp *m = (munkiimp *)p->m;
3675 	double **multimes;		/* Multiple measurement results */
3676 	double darkthresh;		/* Dark threshold */
3677 	double sensavg;			/* Overall average of sensor readings */
3678 	int rv;
3679 
3680 	multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
3681 
3682 	/* Take a buffer full of raw readings, and convert them to */
3683 	/* floating point sensor readings. Check for saturation */
3684 	if ((rv = munki_sens_to_raw(p, multimes, NULL, buf, 0, nummeas, m->satlimit, &darkthresh))
3685 		                                                                        != MUNKI_OK) {
3686 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
3687 		return rv;
3688 	}
3689 
3690 	/* Average a set of measurements into one. */
3691 	/* Return nz if the readings are not consistent */
3692 	/* Return the overall average. */
3693 	rv = munki_average_multimeas(p, sens, multimes, nummeas, &sensavg, darkthresh);
3694 	free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
3695 
3696 #ifdef PLOT_DEBUG
3697 	a1logd(p->log,4,"dark_measure_2: Avg abs. sensor readings = %f, sh/darkthresh %f\n",sensavg,darkthresh);
3698 	printf("sens data:\n");
3699 	plot_raw(sens);
3700 #endif
3701 
3702 	if (rv) {
3703 		a1logd(p->log,3,"munki_dark_measure_2: readings are inconsistent\n");
3704 		return MUNKI_RD_DARKREADINCONS;
3705 	}
3706 
3707 	if (sensavg > (2.0 * darkthresh)) {
3708 		a1logd(p->log,3,"munki_dark_measure_2: Average %f is > 2 * darkthresh %f\n",sensavg,darkthresh);
3709 		return MUNKI_RD_DARKNOTVALID;
3710 	}
3711 	return ev;
3712 }
3713 
3714 #ifdef DUMP_DARKM
3715 int ddumpdarkm = 0;
3716 #endif
3717 
3718 /* Take a dark reference measurement (combined parts 1 & 2) */
munki_dark_measure(munki * p,double * raw,int nummeas,double * inttime,int gainmode)3719 munki_code munki_dark_measure(
3720 	munki *p,
3721 	double *raw,			/* Return array [-1 nraw] of raw values */
3722 	int nummeas,			/* Number of readings to take */
3723 	double *inttime, 		/* Integration time to use/used */
3724 	int gainmode			/* Gain mode to use, 0 = normal, 1 = high */
3725 ) {
3726 	munkiimp *m = (munkiimp *)p->m;
3727 	munki_code ev = MUNKI_OK;
3728 	unsigned char *buf;		/* Raw USB reading buffer */
3729 	unsigned int bsize;
3730 
3731 	a1logd(p->log,3, "munki_dark_measure with inttime %f\n",*inttime);
3732 	bsize = m->nsen * 2 * nummeas;
3733 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
3734 		a1logd(p->log,1,"munki_dark_measure malloc %d bytes failed (8)\n",bsize);
3735 		return MUNKI_INT_MALLOC;
3736 	}
3737 
3738 	if ((ev = munki_dark_measure_1(p, nummeas, inttime, gainmode, buf, bsize)) != MUNKI_OK) {
3739 		free(buf);
3740 		return ev;
3741 	}
3742 
3743 	if ((ev = munki_dark_measure_2(p, raw, nummeas, *inttime, gainmode, buf, bsize))
3744 	                                                                           != MUNKI_OK) {
3745 		free(buf);
3746 		return ev;
3747 	}
3748 	free(buf);
3749 
3750 #ifdef DUMP_DARKM
3751 	/* Dump raw dark readings to a file "mkddump.txt" */
3752 	if (ddumpdarkm) {
3753 		int j;
3754 		FILE *fp;
3755 
3756 		if ((fp = fopen("mkddump.txt", "a")) == NULL)
3757 			a1logw(p->log,"Unable to open debug file mkddump.txt\n");
3758 		else {
3759 			fprintf(fp, "\nDark measure: nummeas %d, inttime %f, gainmode %d, darkcells %f\n",nummeas,*inttime,gainmode, raw[-1]);
3760 			fprintf(fp,"\t\t\t{ ");
3761 			for (j = 0; j < (m->nraw-1); j++)
3762 				fprintf(fp, "%f, ",raw[j]);
3763 			fprintf(fp, "%f },\n",raw[j]);
3764 			fclose(fp);
3765 		}
3766 	}
3767 #endif
3768 	return ev;
3769 }
3770 
3771 /* Heat the LED up for given number of seconds by taking a reading */
munki_heatLED(munki * p,double htime)3772 munki_code munki_heatLED(
3773 	munki *p,
3774 	double htime		/* Heat up time */
3775 ) {
3776 	munkiimp *m = (munkiimp *)p->m;
3777 	double inttime = m->cal_int_time; 		/* Integration time to use/used */
3778 	int nummeas;
3779 	unsigned char *buf;		/* Raw USB reading buffer */
3780 	unsigned int bsize;
3781 	int rv;
3782 
3783 	a1logd(p->log,3,"munki_heatLED called \n");
3784 
3785 	nummeas = munki_comp_ru_nummeas(p, htime, inttime);
3786 
3787 	if (nummeas <= 0)
3788 		return MUNKI_OK;
3789 
3790 	/* Allocate temporaries */
3791 	bsize = m->nsen * 2 * nummeas;
3792 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
3793 		a1logd(p->log,1,"munki_heatLED malloc %d bytes failed (10)\n",bsize);
3794 		return MUNKI_INT_MALLOC;
3795 	}
3796 
3797 	a1logd(p->log,3,"Triggering measurement cycle, nummeas %d, inttime %f\n", nummeas, inttime);
3798 
3799 	if ((rv = munki_trigger_one_measure(p, nummeas, &inttime, 0, 1, 0))
3800 	                                                                        != MUNKI_OK) {
3801 		free(buf);
3802 		return rv;
3803 	}
3804 
3805 	a1logd(p->log,3,"Gathering readings\n");
3806 
3807 	rv = munki_readmeasurement(p, nummeas, 0, buf, bsize, NULL, 1, 0);
3808 
3809 	free(buf);
3810 
3811 	return rv;
3812 }
3813 
3814 
3815 /* Take a reflective or emissive white reference sens measurement, */
3816 /* subtracts black and processes into wavelenths. */
3817 /* (absraw is usually ->white_data) */
munki_whitemeasure(munki * p,double * absraw,double * optscale,int nummeas,double * inttime,int gainmode,double targoscale)3818 munki_code munki_whitemeasure(
3819 	munki *p,
3820 	double *absraw,			/* Return array [-1 nraw] of absraw values (may be NULL) */
3821 	double *optscale,		/* Return scale for gain/int time to make optimal (may be NULL) */
3822 	int nummeas,			/* Number of readings to take */
3823 	double *inttime, 		/* Integration time to use/used */
3824 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
3825 	double targoscale		/* Ratio of optimal sensor value to aim for */
3826 ) {
3827 	munki_code ev = MUNKI_OK;
3828 	munkiimp *m = (munkiimp *)p->m;
3829 	munki_state *s = &m->ms[m->mmode];
3830 	unsigned char *buf;		/* Raw USB reading buffer */
3831 	unsigned int bsize;
3832 	int ninvmeas = 0;			/* Number of invalid measurements */
3833 	double **multimes;		/* Multiple measurement results */
3834 	double sensavg;			/* Overall average of sensor readings */
3835 	double darkthresh;		/* Dark threshold */
3836 	double trackmax[3];		/* Track optimum target & darkthresh */
3837 	double maxval;			/* Maximum multimeas value */
3838 	int rv;
3839 
3840 	a1logd(p->log,3,"munki_whitemeasure called \n");
3841 
3842 	if (s->reflective) {
3843 		/* Compute invalid samples to allow for LED warmup */
3844 		ninvmeas = munki_comp_ru_nummeas(p, m->refinvalidsampt, *inttime);
3845 	}
3846 
3847 	if (nummeas <= 0)
3848 		return MUNKI_INT_ZEROMEASURES;
3849 
3850 	/* Allocate temporaries */
3851 	bsize = m->nsen * 2 * (ninvmeas + nummeas);
3852 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
3853 		a1logd(p->log,1,"munki_whitemeasure malloc %d bytes failed (10)\n",bsize);
3854 		return MUNKI_INT_MALLOC;
3855 	}
3856 
3857 	a1logd(p->log,3,"Triggering measurement cycle, ninvmeas %d, nummeas %d, inttime %f, gainmode %d\n",
3858 	                                              ninvmeas, nummeas, *inttime, gainmode);
3859 
3860 	if ((ev = munki_trigger_one_measure(p, ninvmeas + nummeas, inttime, gainmode, 1, 0))
3861 	                                                                        != MUNKI_OK) {
3862 		free(buf);
3863 		return ev;
3864 	}
3865 
3866 	a1logd(p->log,3,"Gathering readings\n");
3867 
3868 	if ((ev = munki_readmeasurement(p, ninvmeas + nummeas, 0, buf, bsize, NULL, 1, 0))
3869 	                                                                      != MUNKI_OK) {
3870 		free(buf);
3871 		return ev;
3872 	}
3873 
3874   	multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
3875 
3876 	/* Take a buffer full of raw readings, and convert them to */
3877 	/* floating point sensor readings. Check for saturation */
3878 	if ((rv = munki_sens_to_raw(p, multimes, NULL, buf, ninvmeas, nummeas, m->satlimit,
3879 		                                                     &darkthresh)) != MUNKI_OK) {
3880 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
3881 		return rv;
3882 	}
3883 
3884 #ifdef PLOT_DEBUG
3885 	printf("Dark data:\n");
3886 	plot_raw(s->dark_data);
3887 #endif
3888 
3889 	trackmax[0] = darkthresh;		/* Track the dark threshold value */
3890 	trackmax[1] = m->optsval;		/* Track the optimal sensor target value */
3891 	trackmax[2] = m->satlimit;		/* For debugging */
3892 
3893 	/* Subtract the black from sensor values and convert to */
3894 	/* absolute (integration & gain scaled), zero offset based, */
3895 	/* linearized sensor values. */
3896 	/* Return the highest individual element. */
3897 	munki_sub_raw_to_absraw(p, nummeas, *inttime, gainmode, multimes, s->dark_data,
3898 	                             trackmax, 3, &maxval);
3899 	darkthresh = trackmax[0];
3900 	free(buf);
3901 
3902 	if (absraw != NULL) {
3903 		/* Average a set of measurements into one. */
3904 		/* Return nz if the readings are not consistent */
3905 		/* Return the overall average. */
3906 		rv = munki_average_multimeas(p, absraw, multimes, nummeas, &sensavg, darkthresh);
3907 
3908 #ifndef IGNORE_WHITE_INCONS
3909 		if (rv) {
3910 			free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
3911 			return MUNKI_RD_WHITEREADINCONS;
3912 		}
3913 #endif /* IGNORE_WHITE_INCONS */
3914 
3915 		a1logd(p->log,3,"Average absolute sensor readings, avg %f, max %f, darkth %f satth %f\n",
3916 		               sensavg,maxval,darkthresh,trackmax[2]);
3917 #ifdef PLOT_DEBUG
3918 		printf("absraw whitemeas:\n");
3919 		plot_raw(absraw);
3920 #endif
3921 	}
3922 
3923 	if (optscale != NULL) {
3924 		double opttarget;       /* Optimal reading scale target value */
3925 
3926 		opttarget = targoscale * trackmax[1];
3927 		if (maxval < 0.01)		/* Could go -ve */
3928 			maxval = 0.01;
3929 		*optscale = opttarget/maxval;
3930 
3931 		a1logd(p->log,3,"Targscale %f, maxval %f, optimal target = %f, amount to scale = %f\n",
3932 		     targoscale, maxval, opttarget, *optscale);
3933 	}
3934 
3935 	free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);		/* Free after using *pmax */
3936 
3937 	return ev;
3938 }
3939 
3940 /* Given an absraw white reference measurement, */
3941 /* compute the wavelength equivalents. */
3942 /* (absraw is usually ->white_data) */
3943 /* (abswav1 is usually ->cal_factor1) */
3944 /* (abswav2 is usually ->cal_factor2) */
munki_compute_wav_whitemeas(munki * p,double * abswav1,double * abswav2,double * absraw)3945 munki_code munki_compute_wav_whitemeas(
3946 	munki *p,
3947 	double *abswav1,		/* Return array [nwav1] of abswav values (may be NULL) */
3948 	double *abswav2,		/* Return array [nwav2] of abswav values (if hr_init, may be NULL) */
3949 	double *absraw			/* Given array [-1 nraw] of absraw values */
3950 ) {
3951 	munkiimp *m = (munkiimp *)p->m;
3952 
3953 	/* Convert an absraw array from raw wavelengths to output wavelenths */
3954 	if (abswav1 != NULL) {
3955 		munki_absraw_to_abswav1(p, 1, &abswav1, &absraw);
3956 
3957 #ifdef PLOT_DEBUG
3958 		printf("White meas converted to wavelengths std res:\n");
3959 		plot_wav1(m, abswav1);
3960 #endif
3961 	}
3962 
3963 #ifdef HIGH_RES
3964 	if (abswav2 != NULL && m->hr_inited == 2) {
3965 		munki_absraw_to_abswav2(p, 1, &abswav2, &absraw);
3966 
3967 #ifdef PLOT_DEBUG
3968 		printf("Converted to wavelengths high res:\n");
3969 		plot_wav2(m, abswav2);
3970 #endif
3971 	}
3972 #endif /* HIGH_RES */
3973 
3974 	return MUNKI_OK;
3975 }
3976 
3977 /* Take a reflective white reference measurement, */
3978 /* subtracts black and decompose into base + LED temperature components */
munki_ledtemp_whitemeasure(munki * p,double * white,double ** iwhite,double * reftemp,int nummeas,double inttime,int gainmode)3979 munki_code munki_ledtemp_whitemeasure(
3980 	munki *p,
3981 	double *white,			/* Return [-1 nraw] of temperature compensated white reference */
3982 	double **iwhite,		/* Return array [-1 nraw][2] of absraw base and scale values */
3983 	double *reftemp,		/* Return a reference temperature to normalize to */
3984 	int nummeas,			/* Number of readings to take */
3985 	double inttime, 		/* Integration time to use/used */
3986 	int gainmode			/* Gain mode to use, 0 = normal, 1 = high */
3987 ) {
3988 	munki_code ev = MUNKI_OK;
3989 	munkiimp *m = (munkiimp *)p->m;
3990 	munki_state *s = &m->ms[m->mmode];
3991 	unsigned char *buf;		/* Raw USB reading buffer */
3992 	unsigned int bsize;
3993 	int ninvmeas = 0;		/* Number of invalid measurements */
3994 	double **multimes;		/* Multiple measurement results */
3995 	double *ledtemp;		/* LED temperature for each measurement */
3996 	double darkthresh;		/* Dark threshold */
3997 	int rv;
3998 
3999 	a1logd(p->log,3,"munki_ledtemp_whitemeasure called \n");
4000 
4001 	/* Compute invalid samples to allow for LED warmup */
4002 	ninvmeas = munki_comp_ru_nummeas(p, m->refinvalidsampt, inttime);
4003 
4004 	if (nummeas <= 0) {
4005 		return MUNKI_INT_ZEROMEASURES;
4006 	}
4007 
4008 	/* Allocate temporaries */
4009 	bsize = m->nsen * 2 * (ninvmeas + nummeas);
4010 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
4011 		a1logd(p->log,1,"munki_whitemeasure malloc %d bytes failed (10)\n",bsize);
4012 		return MUNKI_INT_MALLOC;
4013 	}
4014 
4015 	a1logd(p->log,3,"Triggering measurement cycle, ninvmeas %d, nummeas %d, inttime %f, gainmode %d\n",
4016 	                                              ninvmeas, nummeas, inttime, gainmode);
4017 
4018 	if ((ev = munki_trigger_one_measure(p, ninvmeas + nummeas, &inttime, gainmode, 1, 0))
4019 	                                                                        != MUNKI_OK) {
4020 		free(buf);
4021 		return ev;
4022 	}
4023 
4024 	a1logd(p->log,3,"Gathering readings\n");
4025 
4026 	if ((ev = munki_readmeasurement(p, ninvmeas + nummeas, 0, buf, bsize, NULL, 1, 0))
4027 	                                                                      != MUNKI_OK) {
4028 		free(buf);
4029 		return ev;
4030 	}
4031 
4032   	multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
4033   	ledtemp = dvector(0, nummeas-1);
4034 
4035 	/* Take a buffer full of raw readings, and convert them to */
4036 	/* floating point sensor readings. Check for saturation */
4037 	if ((ev = munki_sens_to_raw(p, multimes, ledtemp, buf, ninvmeas, nummeas, m->satlimit,
4038 	                                                           &darkthresh)) != MUNKI_OK) {
4039 		free(buf);
4040 		free_dvector(ledtemp, 0, nummeas-1);
4041 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4042 		return ev;
4043 	}
4044 	free(buf);
4045 
4046 	/* Make the reference temperature nominal */
4047 	*reftemp = 0.5 * (ledtemp[0] + ledtemp[nummeas-1]);
4048 
4049 #ifdef PLOT_DEBUG
4050 	printf("Ledtemp Dark data:\n");
4051 	plot_raw(s->dark_data);
4052 #endif
4053 
4054 	/* Subtract the black from sensor values and convert to */
4055 	/* absolute (integration & gain scaled), zero offset based, */
4056 	/* linearized sensor values. */
4057 	munki_sub_raw_to_absraw(p, nummeas, inttime, gainmode, multimes, s->dark_data,
4058 	                             &darkthresh, 1, NULL);
4059 
4060 
4061 	/* For each raw wavelength, compute a linear regression */
4062 	{
4063 		int i, w;
4064 		double tt, ss, sx, sy, sxdss, stt, b;
4065 
4066 		ss = (double)nummeas;
4067 		for (sx = 0.0, i = 0; i < nummeas; i++)
4068 			sx += ledtemp[i];
4069 		sxdss = sx/ss;
4070 
4071 		for (w = -1; w < m->nraw; w++) {
4072 			for (sy = 0.0, i = 0; i < nummeas; i++)
4073 				sy += multimes[i][w];
4074 
4075 			for (stt = b = 0.0, i = 0; i < nummeas; i++) {
4076 				tt = ledtemp[i] - sxdss;
4077 				stt += tt * tt;
4078 				b += tt * multimes[i][w];
4079 			}
4080 			b /= stt;
4081 
4082 			iwhite[0][w] = (sy - sx * b)/ss;
4083 			iwhite[1][w] = b;
4084 		}
4085 	}
4086 #ifdef DEBUG
4087 	{	/* Verify the linear regression */
4088 		int i, w;
4089 		double x, terr = 0.0, errc = 0.0;
4090 
4091 		for (w = -1; w < m->nraw; w++) {
4092 			for (i = 0; i < nummeas; i++) {
4093 				x = iwhite[0][w] + ledtemp[i] * iwhite[1][w];
4094 				terr += fabs(x -  multimes[i][w]);
4095 				errc++;
4096 			}
4097 		}
4098 		terr /= errc;
4099 		printf("Linear regression average error = %f\n",terr);
4100 	}
4101 #endif
4102 
4103 #ifdef PLOT_TEMPCOMP
4104 	/* Plot the raw spectra and model, 3 at a time */
4105 	{
4106 		int i, j, k;
4107 		double *indx;
4108 		double **mod;
4109 		indx = dvectorz(0, nummeas-1);
4110 	  	mod = dmatrix(0, 5, 0, nummeas-1);
4111 		for (i = 0; i < nummeas; i++)
4112 			indx[i] = 3.0 * i/(nummeas-1.0);
4113 
4114 		for (j = 0; (j+2) < (m->nraw-1); j += 3) {
4115 			for (i = 0; i < nummeas; i++) {
4116 				for (k = j; k < (j + 3); k++) {
4117 					mod[k-j][i] = iwhite[0][k] + ledtemp[i] * iwhite[1][k];
4118 				}
4119 				for (k = j; k < (j + 3); k++) {
4120 					mod[k-j+3][i] = multimes[i][k];
4121 				}
4122 			}
4123 
4124 			printf("Bands %d - %d\n",j, j+2);
4125 			do_plot6(indx, mod[0], mod[1], mod[2], mod[3], mod[4], mod[5], nummeas);
4126 		}
4127 		free_dvector(indx, 0, nummeas-1);
4128 	  	free_dmatrix(mod, 0, 5, 0, nummeas-1);
4129 	}
4130 #endif
4131 
4132 	a1logd(p->log,3,"Computed linear regression\n");
4133 
4134 #ifdef ENABLE_LEDTEMPC
4135 	/* Compute a temerature compensated set of white readings */
4136 	if ((ev = munki_ledtemp_comp(p, multimes, ledtemp, nummeas, *reftemp, iwhite)) != MUNKI_OK) {
4137 		free_dvector(ledtemp, 0, nummeas-1);
4138 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4139 		return ev;
4140 	}
4141 #endif /* ENABLE_LEDTEMPC */
4142 
4143 	/* Average a set of measurements into one. */
4144 	if ((rv = munki_average_multimeas(p, white, multimes, nummeas, NULL, darkthresh)) != 0) {
4145 		free_dvector(ledtemp, 0, nummeas-1);
4146 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4147 		a1logd(p->log,3,"munki_ledtemp_whitemeasure: readings are inconsistent\n");
4148 		return MUNKI_RD_DARKREADINCONS;
4149 	}
4150 
4151 	free_dvector(ledtemp, 0, nummeas-1);
4152 	free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4153 
4154 	return ev;
4155 }
4156 
4157 /* Given the ledtemp base and scale values, */
4158 /* return an absraw reflective white reference for the */
4159 /* given temperature */
munki_ledtemp_white(munki * p,double * absraw,double ** iwhite,double ledtemp)4160 munki_code munki_ledtemp_white(
4161 	munki *p,
4162 	double *absraw,			/* Return array [-1 nraw] of absraw base and scale values */
4163 	double **iwhite,		/* ledtemp base and scale */
4164 	double ledtemp			/* LED temperature value */
4165 ) {
4166 	munkiimp *m = (munkiimp *)p->m;
4167 	int w;
4168 
4169 	for (w = -1; w < m->nraw; w++)
4170 		absraw[w] = iwhite[0][w] + ledtemp * iwhite[1][w];
4171 
4172 	return MUNKI_OK;
4173 }
4174 
4175 /* Given a set of absraw sensor readings and the corresponding temperature, */
4176 /* compensate the readings to be at the nominated temperature. */
munki_ledtemp_comp(munki * p,double ** absraw,double * ledtemp,int nummeas,double reftemp,double ** iwhite)4177 munki_code munki_ledtemp_comp(
4178 	munki *p,
4179 	double **absraw,		/* [nummeas][raw] measurements to compensate */
4180 	double *ledtemp,		/* LED temperature for each measurement */
4181 	int nummeas,			/* Number of measurements */
4182 	double reftemp,			/* LED reference temperature to compensate to */
4183 	double **iwhite			/* ledtemp base and scale information */
4184 ) {
4185 	munkiimp *m = (munkiimp *)p->m;
4186 	int i, w;
4187 
4188 	for (i = 0; i < nummeas; i++) {
4189 		for (w = 0; w < m->nraw; w++) {		/* Don't try and compensate shielded values */
4190 			double targ, attemp;
4191 			targ   = iwhite[0][w] + reftemp * iwhite[1][w];
4192 			attemp = iwhite[0][w] + ledtemp[i] * iwhite[1][w];
4193 
4194 			absraw[i][w] *= targ/attemp;
4195 		}
4196 	}
4197 	return MUNKI_OK;
4198 }
4199 
4200 /* Trigger measure and gather raw readings using the current mode. */
munki_read_patches_1(munki * p,int ninvmeas,int minnummeas,int maxnummeas,double * inttime,int gainmode,int * nmeasuered,unsigned char * buf,unsigned int bsize)4201 munki_code munki_read_patches_1(
4202 	munki *p,
4203 	int ninvmeas,			/* Number of extra invalid measurements at start */
4204 	int minnummeas,			/* Minimum number of measurements to take */
4205 	int maxnummeas,			/* Maximum number of measurements to allow for */
4206 	double *inttime, 		/* Integration time to use/used */
4207 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
4208 	int *nmeasuered,		/* Number actually measured (excluding ninvmeas) */
4209 	unsigned char *buf,		/* Raw USB reading buffer */
4210 	unsigned int bsize		/* Raw USB readings buffer size in bytes */
4211 ) {
4212 	munki_code ev = MUNKI_OK;
4213 	munkiimp *m = (munkiimp *)p->m;
4214 
4215 	if ((ninvmeas + minnummeas) <= 0)
4216 		return MUNKI_INT_ZEROMEASURES;
4217 
4218 	if ((minnummeas + ninvmeas) > maxnummeas)
4219 		maxnummeas = (minnummeas - ninvmeas);
4220 
4221 	a1logd(p->log,3,"Triggering & gathering cycle, ninvmeas %d, minnummeas %d, inttime %f, gainmode %d\n",
4222 	                                              ninvmeas, minnummeas, *inttime, gainmode);
4223 
4224 	if ((ev = munki_trigger_one_measure(p, ninvmeas + minnummeas, inttime, gainmode, 0, 0))
4225 	                                                                           != MUNKI_OK) {
4226 		return ev;
4227 	}
4228 
4229 	if ((ev = munki_readmeasurement(p, ninvmeas + minnummeas, m->c_measmodeflags & MUNKI_MMF_SCAN,
4230 	                                             buf, bsize, nmeasuered, 0, 0)) != MUNKI_OK) {
4231 		return ev;
4232 	}
4233 
4234 	if (nmeasuered != NULL)
4235 		*nmeasuered -= ninvmeas;	/* Correct for invalid number */
4236 
4237 	return ev;
4238 }
4239 
4240 /* Given a buffer full of raw USB values, process them into */
4241 /* completely processed spectral output patch readings. */
munki_read_patches_2(munki * p,double * duration,double ** specrd,int numpatches,double inttime,int gainmode,int ninvmeas,int nummeas,unsigned char * buf,unsigned int bsize)4242 munki_code munki_read_patches_2(
4243 	munki *p,
4244 	double *duration,		/* Return flash duration in seconds */
4245 	double **specrd,		/* Return array [numpatches][nwav] of spectral reading values */
4246 	int numpatches,			/* Number of patches to return */
4247 	double inttime, 		/* Integration time to used */
4248 	int gainmode,			/* Gain mode useed, 0 = normal, 1 = high */
4249 	int ninvmeas,			/* Number of extra invalid measurements at start */
4250 	int nummeas,			/* Number of actual measurements */
4251 	unsigned char *buf,		/* Raw USB reading buffer */
4252 	unsigned int bsize		/* Raw USB reading buffer size */
4253 ) {
4254 	munki_code ev = MUNKI_OK;
4255 	munkiimp *m = (munkiimp *)p->m;
4256 	munki_state *s = &m->ms[m->mmode];
4257 	double **multimes;		/* Multiple measurement results [maxnummeas|nummeas][-1 nraw]*/
4258 	double **absraw;		/* Linearsised absolute sensor raw values [numpatches][-1 nraw]*/
4259 	double *ledtemp;		/* LED temperature values */
4260 	double darkthresh;		/* Dark threshold (for consistency checking) */
4261 	int rv = 0;
4262 
4263 	if (duration != NULL)
4264 		*duration = 0.0;	/* default value */
4265 
4266 	/* Allocate temporaries */
4267 	multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
4268   	ledtemp = dvector(0, nummeas-1);
4269 	absraw = dmatrix(0, numpatches-1, -1, m->nraw-1);
4270 
4271 	/* Take a buffer full of raw readings, and convert them to */
4272 	/* floating point sensor readings. Check for saturation */
4273 	if ((rv = munki_sens_to_raw(p, multimes, ledtemp, buf, ninvmeas, nummeas,
4274 	                                         m->satlimit, &darkthresh)) != MUNKI_OK) {
4275 		free_dvector(ledtemp, 0, nummeas-1);
4276 		free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4277 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4278 		return rv;
4279 	}
4280 
4281 	/* Subtract the black from sensor values and convert to */
4282 	/* absolute (integration & gain scaled), zero offset based, */
4283 	/* linearized sensor values. */
4284 	munki_sub_raw_to_absraw(p, nummeas, inttime, gainmode, multimes, s->dark_data,
4285 	                             &darkthresh, 1, NULL);
4286 
4287 #ifdef DUMP_SCANV
4288 	/* Dump raw scan readings to a file "mkdump.txt" */
4289 	{
4290 		int i, j;
4291 		FILE *fp;
4292 
4293 		if ((fp = fopen("mkdump.txt", "w")) == NULL)
4294 			a1logw(p->log,"Unable to open debug file mkdump.txt\n");
4295 		else {
4296 			for (i = 0; i < nummeas; i++) {
4297 				fprintf(fp, "%d	",i);
4298 				for (j = 0; j < m->nraw; j++) {
4299 					fprintf(fp, "%f	",multimes[i][j]);
4300 				}
4301 				fprintf(fp,"\n");
4302 			}
4303 			fclose(fp);
4304 		}
4305 	}
4306 #endif
4307 
4308 #ifdef ENABLE_LEDTEMPC
4309 
4310 
4311 	/* Do LED temperature compensation of absraw values */
4312 	if (s->reflective) {
4313 
4314 #ifdef PLOT_TEMPCOMP
4315 	/* Plot the raw spectra, 6 at a time */
4316 		{
4317 			int i, j, k;
4318 			double *indx;
4319 			double **mod;
4320 			indx = dvectorz(0, nummeas-1);
4321 		  	mod = dmatrix(0, 5, 0, nummeas-1);
4322 			for (i = 0; i < nummeas; i++)
4323 				indx[i] = (double)i;
4324 
4325 //		for (j = 0; (j+5) < m->nraw; j += 6) {
4326 			for (j = 50; (j+5) < 56; j += 6) {
4327 				for (i = 0; i < nummeas; i++) {
4328 					for (k = j; k < (j + 6); k++) {
4329 						mod[k-j][i] = multimes[i][k];
4330 					}
4331 				}
4332 
4333 				printf("Before temp comp, bands %d - %d\n",j, j+5);
4334 				do_plot6(indx, mod[0], mod[1], mod[2], mod[3], mod[4], mod[5], nummeas);
4335 			}
4336 			free_dvector(indx, 0, nummeas-1);
4337 		  	free_dmatrix(mod, 0, 5, 0, nummeas-1);
4338 		}
4339 #endif
4340 		/* Do the LED temperature compensation */
4341 		if ((ev = munki_ledtemp_comp(p, multimes, ledtemp, nummeas, s->reftemp, s->iwhite_data))
4342 		                                                                            != MUNKI_OK) {
4343 			free_dvector(ledtemp, 0, nummeas-1);
4344 			free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4345 			free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4346 			a1logd(p->log,3,"munki_read_patches_2 ledtemp comp failed\n");
4347 			return ev;
4348 		}
4349 #ifdef PLOT_TEMPCOMP
4350 		/* Plot the raw spectra, 6 at a time */
4351 		{
4352 			int i, j, k;
4353 			double *indx;
4354 			double **mod;
4355 			indx = dvectorz(0, nummeas-1);
4356 		  	mod = dmatrix(0, 5, 0, nummeas-1);
4357 			for (i = 0; i < nummeas; i++)
4358 				indx[i] = (double)i;
4359 
4360 //			for (j = 0; (j+5) < m->nraw; j += 6) {
4361 			for (j = 50; (j+5) < 56; j += 6) {
4362 				for (i = 0; i < nummeas; i++) {
4363 					for (k = j; k < (j + 6); k++) {
4364 						mod[k-j][i] = multimes[i][k];
4365 					}
4366 				}
4367 
4368 				printf("After temp comp, bands %d - %d\n",j, j+5);
4369 				do_plot6(indx, mod[0], mod[1], mod[2], mod[3], mod[4], mod[5], nummeas);
4370 			}
4371 			free_dvector(indx, 0, nummeas-1);
4372 		  	free_dmatrix(mod, 0, 5, 0, nummeas-1);
4373 		}
4374 #endif
4375 	}
4376 #endif /* ENABLE_LEDTEMPC */
4377 
4378 
4379 	if (!s->scan) {
4380 		if (numpatches != 1) {
4381 			free_dvector(ledtemp, 0, nummeas-1);
4382 			free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4383 			free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4384 			a1logd(p->log,3,"munki_read_patches_2 spot read failed because numpatches != 1\n");
4385 			return MUNKI_INT_WRONGPATCHES;
4386 		}
4387 
4388 		/* Average a set of measurements into one. */
4389 		/* Return zero if readings are consistent. */
4390 		/* Return nz if the readings are not consistent */
4391 		/* Return the overall average. */
4392 		rv = munki_average_multimeas(p, absraw[0], multimes, nummeas, NULL, darkthresh);
4393 	} else {
4394 
4395 		if (s->flash) {
4396 
4397 			if (numpatches != 1) {
4398 				free_dvector(ledtemp, 0, nummeas-1);
4399 				free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4400 				free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4401 				a1logd(p->log,3,"munki_read_patches_2 spot read failed because numpatches != 1\n");
4402 				return MUNKI_INT_WRONGPATCHES;
4403 			}
4404 			if ((ev = munki_extract_patches_flash(p, &rv, duration, absraw[0], multimes,
4405 			                                                 nummeas, inttime)) != MUNKI_OK) {
4406 				free_dvector(ledtemp, 0, nummeas-1);
4407 				free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4408 				free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4409 				a1logd(p->log,3,"munki_read_patches_2 spot read failed at munki_extract_patches_flash\n");
4410 				return ev;
4411 			}
4412 
4413 		} else {
4414 			a1logd(p->log,3,"Number of patches to be measured = %d\n",nummeas);
4415 
4416 			/* Recognise the required number of ref/trans patch locations, */
4417 			/* and average the measurements within each patch. */
4418 			if ((ev = munki_extract_patches_multimeas(p, &rv, absraw, numpatches, multimes,
4419 			                                   nummeas, inttime)) != MUNKI_OK) {
4420 				free_dvector(ledtemp, 0, nummeas-1);
4421 				free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4422 				free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4423 				a1logd(p->log,3,"munki_read_patches_2 spot read failed at munki_extract_patches_multimeas\n");
4424 				return ev;
4425 			}
4426 		}
4427 	}
4428 	free_dvector(ledtemp, 0, nummeas-1);
4429 	free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4430 
4431 	if (rv) {
4432 		free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4433 		a1logd(p->log,3,"munki_read_patches_2 spot read failed with inconsistent readings\n");
4434 		return MUNKI_RD_READINCONS;
4435 	}
4436 
4437 #ifdef ENABLE_REFSTRAYC	/* Enable Reflective stray light compensation */
4438 	if (s->reflective) {
4439 		int i, j;
4440 # if defined(PLOT_DEBUG) || defined(DUMP_BKLED)
4441 		double xx[140];
4442 		double yy[3][140];
4443 # endif
4444 
4445 		double fact = REFSTRAYC_FACTOR;		/* Slightly conservative */
4446 
4447 		for (i = 0; i < numpatches; i++) {
4448 
4449 			for (j = 0; j < m->nraw; j++) {
4450 # if defined(PLOT_DEBUG) || defined(DUMP_BKLED)
4451 				yy[0][j] = absraw[i][j];
4452 # endif
4453 				absraw[i][j] -= fact * s->white_data[j];
4454 # if defined(PLOT_DEBUG) || defined(DUMP_BKLED)
4455 				xx[j] = j;
4456 				yy[1][j] = fact * s->white_data[j];
4457 				yy[2][j] = absraw[i][j];
4458 # endif
4459 			}
4460 # ifdef PLOT_DEBUG
4461 			printf("Before/After subtracting stray ref. light %d:\n",i);
4462 			do_plot6(xx, yy[0], yy[1], yy[2], NULL, NULL, NULL, m->nraw);
4463 # endif
4464 # ifdef DUMP_BKLED		/* Save REFSTRAYC & REFLEDNOISE comp plot to "refbk1.txt" & "refbk2.txt" */
4465 		{
4466 			xspect sp[3];
4467 			for (i = 0; i < 3; i++) {
4468 				sp[i].spec_n = 128;
4469 				sp[i].spec_wl_short = 0.0;
4470 				sp[i].spec_wl_long = 127.0;
4471 				sp[i].norm = 1.0;
4472 				for (j = 0; j < 128; j++)
4473 					sp[i].spec[j] = yy[i][j];
4474 			}
4475 			write_nxspect("refbk2.txt", sp, 3, 0);
4476 # pragma message("######### munki DUMP_BKLED enabled! ########")
4477 		}
4478 # endif	/* DUMP_BKLED */
4479 		}
4480 	}
4481 #endif /* ENABLE_REFSTRAYC */
4482 
4483 	/* Convert an absraw array from raw wavelengths to output wavelenths */
4484 	munki_absraw_to_abswav(p, numpatches, specrd, absraw);
4485 	free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4486 
4487 #ifdef APPEND_MEAN_EMMIS_VAL
4488 	/* Append averaged emission reading to file "mkdump.txt" */
4489 	{
4490 		int i, j;
4491 		FILE *fp;
4492 
4493 		/* Create wavelegth label */
4494 		if ((fp = fopen("mkdump.txt", "r")) == NULL) {
4495 			if ((fp = fopen("mkdump.txt", "w")) == NULL)
4496 				a1logw(p->log,"Unable to reate debug file mkdump.txt\n");
4497 			else {
4498 				for (j = 0; j < m->nwav; j++)
4499 					fprintf(fp,"%f ",XSPECT_WL(m->wl_short, m->wl_long, m->nwav, j));
4500 				fprintf(fp,"\n");
4501 				fclose(fp);
4502 			}
4503 		}
4504 		if ((fp = fopen("mkdump.txt", "a")) == NULL)
4505 			a1logw(p->log,"Unable to open debug file mkdump.txt\n");
4506 		else {
4507 			for (j = 0; j < m->nwav; j++)
4508 				fprintf(fp, "%f	",specrd[0][j] * m->emis_coef[j]);
4509 			fprintf(fp,"\n");
4510 			fclose(fp);
4511 		}
4512 	}
4513 #endif
4514 
4515 #ifdef PLOT_DEBUG
4516 	printf("Converted to wavelengths:\n");
4517 	plot_wav(m, specrd[0]);
4518 #endif
4519 
4520 	/* Scale to the calibrated output values */
4521 	munki_scale_specrd(p, specrd, numpatches, specrd);
4522 
4523 #ifdef PLOT_DEBUG
4524 	printf("Calibrated measuerment spectra:\n");
4525 	plot_wav(m, specrd[0]);
4526 #endif
4527 
4528 	return ev;
4529 }
4530 
4531 /* Given a buffer full of raw USB values, process them into */
4532 /* completely processed spectral output readings, */
4533 /* but don't average together or extract patches or flash. */
4534 /* This is used for delay & refresh rate measurement. */
4535 /* !! Doesn't do LED temperature compensation for reflective !! */
4536 /* (! Note that we aren't currently detecting saturation here!) */
munki_read_patches_2a(munki * p,double ** specrd,int numpatches,double inttime,int gainmode,unsigned char * buf,unsigned int bsize)4537 munki_code munki_read_patches_2a(
4538 	munki *p,
4539 	double **specrd,		/* Return array [numpatches][nwav] of spectral reading values */
4540 	int numpatches,			/* Number of patches measured and to return */
4541 	double inttime, 		/* Integration time to used */
4542 	int gainmode,			/* Gain mode useed, 0 = normal, 1 = high */
4543 	unsigned char *buf,		/* Raw USB reading buffer */
4544 	unsigned int bsize
4545 ) {
4546 	munki_code ev = MUNKI_OK;
4547 	munkiimp *m = (munkiimp *)p->m;
4548 	munki_state *s = &m->ms[m->mmode];
4549 	double **absraw;		/* Linearsised absolute sensor raw values [numpatches][-1 nraw]*/
4550 	double *ledtemp;		/* LED temperature values */
4551 	double satthresh;		/* Saturation threshold */
4552 	double darkthresh;		/* Dark threshold for consistency scaling limit */
4553 
4554 	/* Allocate temporaries */
4555 	absraw = dmatrix(0, numpatches-1, -1, m->nraw-1);
4556 	ledtemp = dvector(0, numpatches-1);
4557 
4558 	/* Take a buffer full of raw readings, and convert them to */
4559 	/* floating point sensor readings. Check for saturation */
4560 	if ((ev = munki_sens_to_raw(p, absraw, ledtemp, buf, 0, numpatches,
4561 					                        m->satlimit, &darkthresh)) != MUNKI_OK) {
4562 		free_dvector(ledtemp, 0, numpatches-1);
4563 		free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4564 		return ev;
4565 	}
4566 
4567 	/* Subtract the black from sensor values and convert to */
4568 	/* absolute (integration & gain scaled), zero offset based, */
4569 	/* linearized sensor values. */
4570 	munki_sub_raw_to_absraw(p, numpatches, inttime, gainmode, absraw, s->dark_data,
4571                            &darkthresh, 1, NULL);
4572 
4573 	a1logd(p->log,3,"Number of patches measured = %d\n",numpatches);
4574 
4575 	/* Convert an absraw array from raw wavelengths to output wavelenths */
4576 	munki_absraw_to_abswav(p, numpatches, specrd, absraw);
4577 
4578 	free_dvector(ledtemp, 0, numpatches-1);
4579 	free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4580 
4581 #ifdef PLOT_DEBUG
4582 	printf("Converted to wavelengths:\n");
4583 	plot_wav(m, specrd[0]);
4584 #endif
4585 
4586 	/* Scale to the calibrated output values */
4587 	munki_scale_specrd(p, specrd, numpatches, specrd);
4588 
4589 #ifdef PLOT_DEBUG
4590 	printf("Calibrated measuerment spectra:\n");
4591 	plot_wav(m, specrd[0]);
4592 #endif
4593 
4594 	return ev;
4595 }
4596 
4597 /* Take a measurement reading using the current mode (combined parts 1 & 2a) */
4598 /* Converts to completely processed output readings, without averaging or extracting */
4599 /* sample patches, for emissive measurement mode. */
4600 /* This is used for delay & refresh rate measurement. */
munki_read_patches_all(munki * p,double ** specrd,int numpatches,double * inttime,int gainmode)4601 munki_code munki_read_patches_all(
4602 	munki *p,
4603 	double **specrd,		/* Return array [numpatches][nwav] of spectral reading values */
4604 	int numpatches,			/* Number of sample to measure */
4605 	double *inttime, 		/* Integration time to use/used */
4606 	int gainmode			/* Gain mode to use, 0 = normal, 1 = high */
4607 ) {
4608 	munki_code ev = MUNKI_OK;
4609 	munkiimp *m = (munkiimp *)p->m;
4610 	munki_state *s = &m->ms[m->mmode];
4611 	unsigned char *buf;		/* Raw USB reading buffer */
4612 	unsigned int bsize;
4613 	int rv = 0;
4614 
4615 	bsize = m->nsen * 2 * numpatches;
4616 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
4617 		a1logd(p->log,1,"munki_read_patches malloc %d bytes failed (11)\n",bsize);
4618 		return MUNKI_INT_MALLOC;
4619 	}
4620 
4621 	/* Trigger measure and gather raw readings */
4622 	if ((ev = munki_read_patches_1(p, 0, numpatches, numpatches, inttime, gainmode,
4623 	                                       NULL, buf, bsize)) != MUNKI_OK) {
4624 		free(buf);
4625 		return ev;
4626 	}
4627 
4628 	/* Process the raw readings without averaging or extraction */
4629 	if ((ev = munki_read_patches_2a(p, specrd, numpatches, *inttime, gainmode,
4630 	                                                 buf, bsize)) != MUNKI_OK) {
4631 		free(buf);
4632 		return ev;
4633 	}
4634 	free(buf);
4635 	return ev;
4636 }
4637 
4638 /* Take a trial emission measurement reading using the current mode. */
4639 /* Used to determine if sensor is saturated, or not optimal */
4640 /* in adaptive emission mode. */
munki_trialmeasure(munki * p,int * saturated,double * optscale,int nummeas,double * inttime,int gainmode,double targoscale)4641 munki_code munki_trialmeasure(
4642 	munki *p,
4643 	int *saturated,			/* Return nz if sensor is saturated */
4644 	double *optscale,		/* Return scale for gain/int time to make optimal (may be NULL) */
4645 	int nummeas,			/* Number of readings to take */
4646 	double *inttime, 		/* Integration time to use/used */
4647 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
4648 	double targoscale		/* Ratio of optimal sensor value to aim for */
4649 ) {
4650 	munki_code ev = MUNKI_OK;
4651 	munkiimp *m = (munkiimp *)p->m;
4652 	munki_state *s = &m->ms[m->mmode];
4653 	unsigned char *buf;		/* Raw USB reading buffer */
4654 	unsigned int bsize;
4655 	double **multimes;		/* Multiple measurement results */
4656 	double *absraw;		/* Linearsised absolute sensor raw values */
4657 	int nmeasuered;			/* Number actually measured */
4658 	double sensavg;			/* Overall average of sensor readings */
4659 	double darkthresh;		/* Dark threshold */
4660 	double trackmax[2];		/* Track optimum target */
4661 	double maxval;			/* Maximum multimeas value */
4662 	int rv;
4663 
4664 	if (s->reflective) {
4665 		a1logw(p->log, "munki_trialmeasure: Assert - not meant to be used for reflective read!\n");
4666 		return MUNKI_INT_ASSERT;
4667 	}
4668 
4669 	if (nummeas <= 0)
4670 		return MUNKI_INT_ZEROMEASURES;
4671 
4672 	/* Allocate up front to avoid delay between trigger and read */
4673 	bsize = m->nsen * 2 * nummeas;
4674 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
4675 		a1logd(p->log,1,"munki_trialmeasure malloc %d bytes failed (12)\n",bsize);
4676 		return MUNKI_INT_MALLOC;
4677 	}
4678 	multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
4679 	absraw = dvector(-1, m->nraw-1);
4680 
4681 	a1logd(p->log,3,"Triggering measurement cycle, nummeas %d, inttime %f, gainmode %d\n",
4682 	                                              nummeas, *inttime, gainmode);
4683 
4684 	if ((ev = munki_trigger_one_measure(p, nummeas, inttime, gainmode, 1, 0)) != MUNKI_OK) {
4685 		free_dvector(absraw, -1, m->nraw-1);
4686 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4687 		free(buf);
4688 		return ev;
4689 	}
4690 
4691 	a1logd(p->log,3,"Gathering readings\n");
4692 	if ((ev = munki_readmeasurement(p, nummeas, m->c_measmodeflags & MUNKI_MMF_SCAN,
4693 	                                             buf, bsize, &nmeasuered, 1, 0)) != MUNKI_OK) {
4694 		free_dvector(absraw, -1, m->nraw-1);
4695 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4696 		free(buf);
4697 		return ev;
4698 	}
4699 
4700 	if (saturated != NULL)			/* Initialize return flag */
4701 		*saturated = 0;
4702 
4703 	/* Take a buffer full of raw readings, and convert them to */
4704 	/* floating point sensor readings. Check for saturation */
4705 	if ((rv = munki_sens_to_raw(p, multimes, NULL, buf, 0, nmeasuered, m->satlimit,
4706 		                                                 &darkthresh)) != MUNKI_OK) {
4707 	 	if (rv != MUNKI_RD_SENSORSATURATED) {
4708 			free_dvector(absraw, -1, m->nraw-1);
4709 			free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4710 			free(buf);
4711 			return rv;
4712 		}
4713 	 	if (saturated != NULL)
4714 			*saturated = 1;
4715 	}
4716 	free(buf);
4717 
4718 	/* Comute dark subtraction for this trial's parameters */
4719 	if ((ev = munki_interp_dark(p, s->dark_data, *inttime, gainmode)) != MUNKI_OK) {
4720 		free_dvector(absraw, -1, m->nraw-1);
4721 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4722 		a1logd(p->log,3,"munki_imp_measure interplate dark ref failed\n");
4723 		return ev;
4724 	}
4725 
4726 	trackmax[0] = darkthresh;		/* Track dark threshold */
4727 	trackmax[1] = m->optsval;		/* Track the optimal sensor target value */
4728 
4729 	/* Subtract the black from sensor values and convert to */
4730 	/* absolute (integration & gain scaled), zero offset based, */
4731 	/* linearized sensor values. */
4732 	/* Return the highest individual element. */
4733 	munki_sub_raw_to_absraw(p, nmeasuered, *inttime, gainmode, multimes, s->dark_data,
4734 	                             trackmax, 2, &maxval);
4735 	darkthresh = trackmax[0];
4736 
4737 	/* Average a set of measurements into one. */
4738 	/* Return nz if the readings are not consistent */
4739 	/* Return the overall average. */
4740 	rv = munki_average_multimeas(p, absraw, multimes, nmeasuered, &sensavg, darkthresh);
4741 
4742 	/* Ignore iconsistent readings ?? */
4743 
4744 #ifdef PLOT_DEBUG
4745 	printf("Average absolute sensor readings, average = %f, max %f\n",sensavg,maxval);
4746 	plot_raw(absraw);
4747 #endif
4748 
4749 	if (optscale != NULL) {
4750 		double opttarget;		/* Optimal sensor target */
4751 
4752 		opttarget = targoscale * trackmax[1];
4753 		if (maxval < 0.01)		/* Could go -ve */
4754 			maxval = 0.01;
4755 		*optscale = opttarget/ maxval;
4756 		a1logd(p->log,4,"Targscale %f, maxval %f, optimal target = %f, amount to scale = %f\n",
4757 		     targoscale, maxval, opttarget, *optscale);
4758 	}
4759 
4760 	free_dvector(absraw, -1, m->nraw-1);
4761 	free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);		/* Free after using *pmax */
4762 
4763 	return ev;
4764 }
4765 
4766 /* Trigger a single measurement cycle. This could be a dark calibration, */
4767 /* a calibration, or a real measurement. This is used to create the */
4768 /* higher level "calibrate" and "take reading" functions. */
4769 /* The setup for the operation is in the current mode state. */
4770 /* Call munki_readmeasurement() to collect the results */
4771 munki_code
munki_trigger_one_measure(munki * p,int nummeas,double * inttime,int gainmode,int calib_measure,int dark_measure)4772 munki_trigger_one_measure(
4773 	munki *p,
4774 	int nummeas,			/* Minimum number of measurements to make */
4775 	double *inttime, 		/* Integration time to use/used */
4776 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
4777 	int calib_measure,		/* flag - nz if this is a calibration measurement */
4778 	int dark_measure		/* flag - nz if this is a dark measurement */
4779 ) {
4780 	munki_code ev = MUNKI_OK;
4781 	munkiimp *m = (munkiimp *)p->m;
4782 	munki_state *s = &m->ms[m->mmode];
4783 	double dintclocks;
4784 	int intclocks;		/* Number of integration clocks */
4785 	int measmodeflags;	/* Measurement mode command flags */
4786 	int holdtempduty;	/* Hold temperature duty cycle */
4787 
4788 	/* Compute integration clocks. Take account of (seeming) dead integration time */
4789 	dintclocks = floor((*inttime)/m->intclkp + 0.5);
4790 	intclocks = (int)dintclocks;
4791 	*inttime = (double)intclocks * m->intclkp;		/* Quantized integration time */
4792 
4793 	/* Create measurement mode flag byte for this operation */
4794 	measmodeflags = 0;
4795 	if (s->scan && !calib_measure)
4796 		measmodeflags |= MUNKI_MMF_SCAN;		/* Never scan on a calibration */
4797 	if (s->reflective && !dark_measure)
4798 		measmodeflags |= MUNKI_MMF_LAMP;		/* Need lamp if reflective and not dark measure */
4799 	if (gainmode == 1)
4800 		measmodeflags |= MUNKI_MMF_HIGHGAIN;	/* High gain mode */
4801 	holdtempduty = m->ledholdtempdc;			/* From the EEProm value */
4802 
4803 	/* Trigger a measurement */
4804 	if ((ev = munki_triggermeasure(p, intclocks, nummeas, measmodeflags, holdtempduty)) != MUNKI_OK)
4805 		return ev;
4806 
4807 	m->c_measmodeflags = measmodeflags;
4808 
4809 	m->c_inttime = *inttime;
4810 
4811 	return ev;
4812 }
4813 
4814 /* ============================================================ */
4815 /* lower level reading processing and computation */
4816 
4817 /* Take a buffer full of raw readings, and convert them to */
4818 /* directly to floating point raw values. Return MUNKI_RD_SENSORSATURATED if any is saturated */
4819 /* (No black subtraction or linearization is performed) */
munki_sens_to_raw(munki * p,double ** raw,double * ledtemp,unsigned char * buf,int ninvalid,int nummeas,double satthresh,double * pdarkthresh)4820 munki_code munki_sens_to_raw(
4821 	munki *p,
4822 	double **raw,			/* Array of [nummeas-ninvalid][-1 nraw] value to return */
4823 	double *ledtemp,		/* Optional array [nummeas-ninvalid] LED temperature values to return */
4824 	unsigned char *buf,		/* Raw measurement data must be 274 * nummeas */
4825 	int ninvalid,			/* Number of initial invalid readings to skip */
4826 	int nummeas,			/* Number of readings measured */
4827 	double satthresh,		/* Saturation threshold to trigger error in raw units (if > 0.0) */
4828 	double *pdarkthresh		/* Return a dark threshold value = sheilded cell values */
4829 ) {
4830 	munkiimp *m = (munkiimp *)p->m;
4831 	int i, j, k;
4832 	unsigned char *bp;
4833 	double maxval = -1e38;
4834 	double darkthresh = 0.0;
4835 	double ndarkthresh = 0.0;
4836 	int sskip = 2 * 6;		/* Bytes to skip at start */
4837 	int eskip = 2 * 3;		/* Bytes to skip at end */
4838 
4839 	if ((m->nraw * 2 + sskip + eskip) != (m->nsen * 2)) {
4840 		a1logw(p->log,"NRAW %d and NRAWB %d don't match!\n",m->nraw,m->nsen * 2);
4841 		return MUNKI_INT_ASSERT;
4842 	}
4843 
4844 	if (ninvalid > 0)
4845 		a1logd(p->log, 4, "munki_sens_to_raw: Skipping %d invalid readings\n",ninvalid);
4846 
4847 	/* Now process the buffer values */
4848 	for (bp = buf + ninvalid * m->nsen * 2, i = 0; i < nummeas; i++, bp += eskip) {
4849 
4850 		/* The first 4 readings are shielded cells, and we use them as an */								/* estimate of the dark reading consistency, as well as for */
4851 		/* compensating the dark level calibration for any temperature changes. */
4852 
4853 		/* raw average of all measurement shielded cell values */
4854 		for (k = 0; k < 4; k++) {
4855 			darkthresh += (double)buf2ushort(bp + k * 2);
4856 			ndarkthresh++;
4857 		}
4858 
4859 		/* raw of shielded cells per reading */
4860 		raw[i][-1] = 0.0;
4861 		for (k = 0; k < 4; k++) {
4862 			raw[i][-1] += (double)buf2ushort(bp + k * 2);
4863 		}
4864 		raw[i][-1] /= 4.0;
4865 
4866 		/* The LED voltage drop is the last 16 bits in each reading */
4867 		if (ledtemp != NULL)
4868 			ledtemp[i] = (double)buf2ushort(bp + (m->nsen * 2) - 2);
4869 
4870 		/* The 128 raw spectral values */
4871 		for (bp += sskip, j = 0; j < m->nraw; j++, bp += 2) {
4872 			unsigned int rval;
4873 			double fval;
4874 
4875 			rval = buf2ushort(bp);
4876 			fval = (double)rval;
4877 			raw[i][j] = fval;
4878 //			printf("~1 i = %d, j = %d, addr % 274 = %d, val = %f\n",i,j,(bp - buf) % 274, fval);
4879 
4880 			if (fval > maxval)
4881 				maxval = fval;
4882 		}
4883 	}
4884 
4885 	/* Check if any are over saturation threshold */
4886 	if (satthresh > 0.0) {
4887 		if (maxval > satthresh) {
4888 			a1logd(p->log,4,"munki_sens_to_raw: Max sens %f > satthresh %f\n",maxval,satthresh);
4889 			return MUNKI_RD_SENSORSATURATED;
4890 		}
4891 		a1logd(p->log,4,"munki_sens_to_raw: Max sens %f < satthresh %f\n",maxval,satthresh);
4892 	}
4893 
4894 	darkthresh /= ndarkthresh;
4895 	if (pdarkthresh != NULL)
4896 		*pdarkthresh = darkthresh;
4897 	a1logd(p->log,3,"munki_sens_to_raw: Dark thrheshold = %f\n",darkthresh);
4898 
4899 	return MUNKI_OK;
4900 }
4901 
4902 /* Subtract the black from raw values and convert to */
4903 /* absolute (integration & gain scaled), zero offset based, */
4904 /* linearized raw values. */
4905 /* Return the highest individual element. */
munki_sub_raw_to_absraw(munki * p,int nummeas,double inttime,int gainmode,double ** absraw,double * sub,double * trackmax,int ntrackmax,double * maxv)4906 void munki_sub_raw_to_absraw(
4907 	munki *p,
4908 	int nummeas,			/* Return number of readings measured */
4909 	double inttime, 		/* Integration time used */
4910 	int gainmode,			/* Gain mode, 0 = normal, 1 = high */
4911 	double **absraw,		/* Source/Desination array [-1 nraw] */
4912 	double *sub,			/* Value to subtract [-1 nraw] (ie. cal dark data) */
4913 	double *trackmax, 		/* absraw values that should be offset the same as max */
4914 	int ntrackmax,			/* Number of trackmax values */
4915 	double *maxv			/* If not NULL, return the maximum value */
4916 ) {
4917 	munkiimp *m = (munkiimp *)p->m;
4918 	munki_state *s = &m->ms[m->mmode];
4919 	int npoly;			/* Number of linearisation coefficients */
4920 	double *polys;		/* the coeficients */
4921 	double scale;		/* Absolute scale value */
4922 	double submax = -1e6;	/* Subtraction value maximum */
4923 	double asub[NSEN_MAX];
4924 	double avgscell, zero;
4925 	double rawmax, maxval = -1e38;
4926 	double maxzero = 0.0;
4927 	int i, j, k;
4928 
4929 	/* Heusristic correction for LED interference bump for 0.018 secs int_time */
4930 	int    pos[] = { 0,    20,   56,   62,   75,   127 };
4931 //	double off[] = { 0.7, 0.0,  0.6, -0.9,  -1.2, -0.7 };
4932 	double off[] = { 0.7, 0.0,  0.6, -0.9,  -0.8, -0.5 };
4933 
4934 	if (gainmode) {				/* High gain */
4935 		npoly = m->nlin1;		/* Encodes gain too */
4936 		polys = m->lin1;
4937 	} else {					/* Low gain */
4938 		npoly = m->nlin0;
4939 		polys = m->lin0;
4940 	}
4941 	scale = 1.0/inttime;
4942 
4943 	/* Adjust black to allow for temperature change by using the */
4944 	/* shielded cell values as a reference. */
4945 	/* We use a heuristic to compute a zero based scale for adjusting the */
4946 	/* black. It's not clear why it works best this way, or how */
4947 	/* dependent on the particular instrument the magic numbers are, */
4948 	/* but it reduces the black level error from over 10% to about 0.3% */
4949 
4950 	/* Locate largest of black */
4951 	for (j = 0; j < m->nraw; j++) {
4952 		if (sub[j] > submax)
4953 			submax = sub[j];
4954 	}
4955 
4956 	/* Average the shielded cell value of all the readings */
4957 	avgscell = 0.0;
4958 	for (i = 0; i < nummeas; i++)
4959 		avgscell += absraw[i][-1];
4960 	avgscell /= (double)nummeas;
4961 
4962 	/* Compute scaling zero */
4963 	zero = 1.08 * 0.5 * (avgscell + sub[-1]);
4964 
4965 	/* make sure that the zero point is above any black value */
4966 	if (zero < (1.005 * avgscell))
4967 		zero = 1.005 * avgscell;
4968 	if (zero < (1.005 * sub[-1]))
4969 		zero = 1.005 * sub[-1];
4970 	if (zero < (1.005 * submax))
4971 		zero = 1.005 * submax;
4972 
4973 	a1logd(p->log,4,"Black shielded value = %f, Reading shielded value = %f\n",sub[-1], avgscell);
4974 
4975 	/* Compute the adjusted black for each band */
4976 	if (s->reflective) {
4977 
4978 		/* It seems that having the LED on shifts the shielded cell values */
4979 		/* by about 2.5, and this stuffs up the reflective measurement. */
4980 		/* This seems to be from the LED PWM driver, which perhaps */
4981 		/* is synchronous to the sensor clock, and so switches */
4982 		/* at a certain point in the transfer of data from the sensor. */
4983 		/* The result is a step up from 0-60, and then down from 61-128. */
4984 		/* Perhaps altering the LED PWM setting and seeing if this point */
4985 		/* shifts would be a way of confirming this ? */
4986 		/* There is also some stray light reflected into the sensor */
4987 		/* from the LED, but due to the LED step, the sensor reading is */
4988 		/* less than the dark data at some wavelengths. */
4989 		/* The details of the LED step seem to be integration time dependent, */
4990 		/* but decresing the scanning rate therebye increasing integration */
4991 		/* time and light level reduces the impact of this error. */
4992 
4993 		/* Since we do an on the fly black measurement before each */
4994 		/* reflective measurement, ignoring the shielded cell values */
4995 		/* shouldn't affect accuracy so much. */
4996 
4997 #ifdef ENABLE_REFLEDINTER
4998 		/* A heuristic to correct for the LED noise. */
4999 		/* This is only valid for int_time of 0.0182 secs, */
5000 		/* and it's not clear how well it works across different */
5001 		/* temperatures or examples of the ColorMunki. */
5002 		/* in another revision ?? */
5003 		for (j = 0; j < m->nraw; j++) {
5004 			int ix;
5005 			double bl, val;
5006 
5007 			for (ix = 0; ; ix++) {
5008 				if (j >= pos[ix] && j <= pos[ix+1])
5009 					break;
5010 			}
5011 			bl = (j - pos[ix])/((double)pos[ix+1] - pos[ix]);
5012 			val = (1.0 - bl) * off[ix] + bl * off[ix+1];
5013 			asub[j] = sub[j] + val;
5014 		}
5015 #else
5016 		for (j = 0; j < m->nraw; j++)
5017 			asub[j] = sub[j];		/* Just use the calibration dark data */
5018 #endif
5019 
5020 	} else {
5021 		/* No LED on operation - use sheilded cell values */
5022 		for (j = 0; j < m->nraw; j++) {
5023 #ifdef ENABLE_BKDRIFTC
5024 
5025 # ifdef HEURISTIC_BKDRIFTC
5026 			/* heuristic scaled correction */
5027 			asub[j] = zero - (zero - sub[j]) * (zero - avgscell)/(zero - sub[-1]);
5028 # else
5029 			/* simple additive correction */
5030 #  pragma message("######### munki Simple shielded cell temperature correction! ########")
5031 			asub[j] = sub[j] + (avgscell - sub[-1]);
5032 # endif
5033 #else
5034 #  pragma message("######### munki No shielded cell temperature correction! ########")
5035 			asub[j] = sub[j];		/* Just use the calibration dark data */
5036 #endif
5037 		}
5038 	}
5039 
5040 #if defined(PLOT_DEBUG) || defined(DUMP_BKLED)
5041 	{
5042 	double xx[130];
5043 	double yy[3][130];
5044 
5045 	for (j = -1; j < m->nraw+1; j++)
5046 		yy[0][j+1] = 0.0;
5047 
5048 	for (i = 0; i < nummeas; i++) {
5049 		for (j = -1; j < m->nraw; j++)
5050 			yy[0][j+1] += absraw[i][j];
5051 	}
5052 	for (j = -1; j < m->nraw; j++)
5053 		yy[0][j+1] /= (double)nummeas;
5054 
5055 	for (j = -1; j < m->nraw; j++)
5056 		yy[1][j+1]= sub[j];
5057 
5058 	/* Show what ENABLE_REFLEDINTER would do */
5059 	for (j = 0; j < m->nraw; j++) {
5060 		int ix;
5061 		double bl, val;
5062 
5063 		for (ix = 0; ; ix++) {
5064 			if (j >= pos[ix] && j <= pos[ix+1])
5065 				break;
5066 		}
5067 		bl = (j - pos[ix])/((double)pos[ix+1] - pos[ix]);
5068 		val = (1.0 - bl) * off[ix] + bl * off[ix+1];
5069 		yy[2][j+1] = yy[0][j+1] - val;
5070 	}
5071 	yy[2][0] = yy[0][0];
5072 
5073 	for (j = -1; j < m->nraw; j++)
5074 		xx[j+1] = (double)j;
5075 
5076 	xx[0]= -10.0;
5077 
5078 # ifdef PLOT_DEBUG
5079 	printf("sub_raw_to_absraw %d samp avg - dark ref:\n",nummeas);
5080 	do_plot(xx, yy[0], yy[1], yy[2], 129);
5081 # endif
5082 # ifdef DUMP_BKLED
5083 	{
5084 		xspect sp[3];
5085 		for (i = 0; i < 3; i++) {
5086 			sp[i].spec_n = 128;
5087 			sp[i].spec_wl_short = 0.0;
5088 			sp[i].spec_wl_long = 127.0;
5089 			sp[i].norm = 1.0;
5090 			for (j = 0; j < 128; j++)
5091 				sp[i].spec[j] = yy[i][j+1];
5092 		}
5093 		write_nxspect("refbk1.txt", sp, 3, 0);
5094 	}
5095 # endif	/* DUMP_BKLED */
5096 	}
5097 #endif	/* PLOT_DEBUG || DUMP_BKLED */
5098 
5099 	/* For each measurement */
5100 	for (i = 0; i < nummeas; i++) {
5101 		double rval, sval, lval;
5102 
5103 		for (j = 0; j < m->nraw; j++) {
5104 
5105 			rval = absraw[i][j];
5106 
5107 			sval = rval - asub[j];		/* Make zero based */
5108 
5109 #ifdef ENABLE_NONLINCOR
5110 			/* Linearise */
5111 			for (lval = polys[npoly-1], k = npoly-2; k >= 0; k--)
5112 				lval = lval * sval + polys[k];
5113 #else
5114 			lval = sval;
5115 #endif
5116 			lval *= scale;
5117 			absraw[i][j] = lval;
5118 
5119 			/* Track the maximum value and the black that was subtracted from it */
5120 			if (lval > maxval) {
5121 				maxval = lval;
5122 				rawmax = rval;
5123 				maxzero = asub[j];
5124 				if (maxv != NULL)
5125 					*maxv = absraw[i][j];
5126 			}
5127 		}
5128 	}
5129 
5130 	/* Process the "tracked to max" values too */
5131 	if (ntrackmax > 0 && trackmax != NULL)  {
5132 		for (i = 0; i < ntrackmax; i++) {
5133 			double rval, fval, lval;
5134 
5135 			rval = trackmax[i];
5136 			fval = rval - maxzero;
5137 
5138 #ifdef ENABLE_NONLINCOR
5139 			/* Linearise */
5140 			for (lval = polys[npoly-1], k = npoly-2; k >= 0; k--)
5141 				lval = lval * fval + polys[k];
5142 #else
5143 			lval = fval;
5144 #endif
5145 			lval *= scale;
5146 			trackmax[i] = lval;
5147 //			printf("~1 trackmax[%d] = %f, maxzero = %f\n",i,lval,maxzero);
5148 		}
5149 	}
5150 }
5151 
5152 /* Average a set of sens or absens measurements into one. */
5153 /* (Make sure darkthresh is tracked if absens is being averaged!) */
5154 /* Return zero if readings are consistent and not saturated. */
5155 /* Return nz if the readings are not consistent */
5156 /* Return the overall average. */
munki_average_multimeas(munki * p,double * avg,double ** multimeas,int nummeas,double * poallavg,double darkthresh)5157 int munki_average_multimeas(
5158 	munki *p,
5159 	double *avg,			/* return average [-1 nraw] */
5160 	double **multimeas,		/* Array of [nummeas][-1 nraw] value to average */
5161 	int nummeas,			/* number of readings to be averaged */
5162 	double *poallavg,		/* If not NULL, return overall average of bands and measurements */
5163 	double darkthresh		/* Dark threshold (used for consistency check scaling) */
5164 ) {
5165 	munkiimp *m = (munkiimp *)p->m;
5166 	int i, j;
5167 	double oallavg = 0.0;
5168 	double maxavg = -1e38;		/* Track min and max averages of readings */
5169 	double minavg = 1e38;
5170 	double norm;
5171 	int rv = 0;
5172 
5173 	a1logd(p->log,3,"munki_average_multimeas %d readings (darkthresh %f)\n",nummeas,darkthresh);
5174 
5175 	for (j = -1; j < m->nraw; j++)
5176 		avg[j] = 0.0;
5177 
5178 	/* Now process the buffer values */
5179 	for (i = 0; i < nummeas; i++) {
5180 		double measavg = 0.0;
5181 
5182 		avg[-1] += multimeas[i][-1];			/* shielded cell value */
5183 
5184 		for (j = 0; j < m->nraw; j++) {
5185 			double val;
5186 
5187 			val = multimeas[i][j];
5188 
5189 			measavg += val;
5190 			avg[j] += val;
5191 		}
5192 		measavg /= (double)m->nraw;
5193 		oallavg += measavg;
5194 		if (measavg < minavg)
5195 			minavg = measavg;
5196 		if (measavg > maxavg)
5197 			maxavg = measavg;
5198 	}
5199 
5200 	for (j = -1; j < m->nraw; j++)
5201 		avg[j] /= (double)nummeas;
5202 	oallavg /= (double)nummeas;
5203 
5204 	if (poallavg != NULL)
5205 		*poallavg = oallavg;
5206 
5207 	norm = fabs(0.5 * (maxavg+minavg));
5208 	darkthresh = fabs(darkthresh);
5209 	if (darkthresh < DARKTHSCAMIN)
5210 		darkthresh = DARKTHSCAMIN;
5211 	a1logd(p->log,3,"norm = %f, dark thresh = %f\n",norm,darkthresh);
5212 	if (norm < (2.0 * darkthresh))
5213 		norm = 2.0 * darkthresh;
5214 
5215 	a1logd(p->log,4,"avg_multi: overall avg = %f, minavg = %f, maxavg = %f, variance %f, THR %f (darkth %f)\n",
5216 	                   oallavg,minavg,maxavg,(maxavg - minavg)/norm, PATCH_CONS_THR,darkthresh);
5217 	if ((maxavg - minavg)/norm > PATCH_CONS_THR) {
5218 		rv |= 1;
5219 	}
5220 	return rv;
5221 }
5222 
5223 /* Minimum number of scan samples in a patch */
5224 #define MIN_SAMPLES 2
5225 
5226 /* Range of bands to detect transitions */
5227 #define BL 5	/* Start */
5228 #define BH 105	/* End */
5229 #define BW 5	/* Width */
5230 
5231 /* Record of possible patch within a reading buffer */
5232 typedef struct {
5233 	int ss;				/* Start sample index */
5234 	int no;				/* Number of samples */
5235 	int use;			/* nz if patch is to be used */
5236 } munki_patch;
5237 
5238 /* Recognise the required number of ref/trans patch locations, */
5239 /* and average the measurements within each patch. */
5240 /* *flags returns zero if readings are consistent. */
5241 /* *flags returns nz if the readings are not consistent */
5242 /* (Doesn't extract [-1] shielded values, since they have already been used) */
munki_extract_patches_multimeas(munki * p,int * flags,double ** pavg,int tnpatch,double ** multimeas,int nummeas,double inttime)5243 munki_code munki_extract_patches_multimeas(
5244 	munki *p,
5245 	int *flags,				/* return flags */
5246 	double **pavg,			/* return patch average [naptch][-1 nraw] */
5247 	int tnpatch,			/* Target number of patches to recognise */
5248 	double **multimeas,		/* Array of [nummeas][-1 nraw] value to extract from */
5249 	int nummeas,			/* number of readings made */
5250 	double inttime			/* Integration time (used to adjust consistency threshold) */
5251 ) {
5252 	munkiimp *m = (munkiimp *)p->m;
5253 	int i, j, k, pix;
5254 	double **sslope;			/* Signed difference between i and i+1 */
5255 	double *slope;				/* Accumulated absolute difference between i and i+1 */
5256 	double *fslope;				/* Filtered slope */
5257 	munki_patch *pat;			/* Possible patch information */
5258 	int npat, apat = 0;
5259 	double *maxval;				/* Maximum input value for each wavelength */
5260 	double fmaxslope = 0.0;
5261 	double maxslope = 0.0;
5262 	double minslope =  1e38;
5263 	double thresh = 0.4;		/* Slope threshold */
5264 	int try;					/* Thresholding try */
5265 	double avglegth;			/* Average length of patches */
5266 	int *sizepop;				/* Size popularity of potential patches */
5267 	double median;				/* median potential patch width */
5268 	double window;				/* +/- around median to accept */
5269 	double white_avg;			/* Average of (aproximate) white data */
5270 	int rv = 0;
5271 	double patch_cons_thr = PATCH_CONS_THR * m->scan_toll_ratio;
5272 #ifdef PLOT_PATREC
5273 	double **plot;
5274 #endif
5275 
5276 	a1logd(p->log,3,"munki_extract_patches_multimeas: looking for %d patches out of %d samples\n",tnpatch,nummeas);
5277 
5278 	maxval = dvectorz(-1, m->nraw-1);
5279 
5280 	/* Loosen consistency threshold for short intergation time */
5281 	if (inttime < 0.012308)		/* Smaller than Rev A minimum int. time */
5282 		patch_cons_thr *= sqrt(0.012308/inttime);
5283 
5284 	/* Discover the maximum input value for normalisation */
5285 	for (j = 0; j < m->nraw; j ++) {
5286 		for (i = 0; i < nummeas; i++) {
5287 			if (multimeas[i][j] > maxval[j])
5288 				maxval[j] = multimeas[i][j];
5289 		}
5290 		if (maxval[j] < 1.0)
5291 			maxval[j] = 1.0;
5292 	}
5293 
5294 #ifdef PLOT_PATREC
5295 	/* Plot out 6 lots of 6 values each */
5296 	plot = dmatrixz(0, 6, 0, nummeas-1);
5297 //	for (j = 1; j < (m->nraw-6); j += 6) 			/* Plot all the bands */
5298 //	for (j = 45; j < (m->nraw-6); j += 100) 		/* Do just one band */
5299 	for (j = 5; j < (m->nraw-6); j += 30) {		/* Do four bands */
5300 		for (k = 0; k < 6; k ++) {
5301 			for (i = 0; i < nummeas; i++) {
5302 				plot[k][i] = multimeas[i][j+k]/maxval[j+k];
5303 			}
5304 		}
5305 		for (i = 0; i < nummeas; i++)
5306 			plot[6][i] = (double)i;
5307 		printf("Bands %d - %d\n",j,j+5);
5308 		do_plot6(plot[6], plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], nummeas);
5309 	}
5310 #endif
5311 
5312 #ifdef NEVER
5313 	/* Plot the shielded cell value */
5314 	for (i = 0; i < nummeas; i++) {
5315 		plot[0][i] = multimeas[i][-1];
5316 		plot[6][i] = (double)i;
5317 	}
5318 	printf("Sheilded values\n");
5319 	do_plot6(plot[6], plot[0], NULL, NULL, NULL, NULL, NULL, nummeas);
5320 #endif
5321 
5322 	sslope = dmatrixz(0, nummeas-1, -1, m->nraw-1);
5323 	slope = dvectorz(0, nummeas-1);
5324 	fslope = dvectorz(0, nummeas-1);
5325 	sizepop = ivectorz(0, nummeas-1);
5326 
5327 #ifndef NEVER		/* Good with this on */
5328 	/* Average bands together */
5329 	for (i = 0; i < nummeas; i++) {
5330 		for (j = BL + BW; j < (BH - BW); j++) {
5331 			for (k = -BL; k <= BW; k++)		/* Box averaging filter over bands */
5332 				 sslope[i][j] += multimeas[i][j + k]/maxval[j];
5333 		}
5334 	}
5335 #else
5336 	/* Don't average bands */
5337 	for (i = 0; i < nummeas; i++) {
5338 		for (j = 0; j < m->nraw; j++) {
5339 			sslope[i][j] = multimeas[i][j]/maxval[j];
5340 		}
5341 	}
5342 #endif
5343 
5344 	/* Compute slope result over readings and bands */
5345 	/* Compute signed slope result over readings and bands */
5346 
5347 #ifdef NEVER		/* Works well for non-noisy readings */
5348 	/* Median of 5 differences from 6 points */
5349 	for (i = 2; i < (nummeas-3); i++) {
5350 		for (j = BL; j < BH; j++) {
5351 			double sl, asl[5];
5352 			int r, s;
5353 			asl[0] = fabs(sslope[i-2][j] - sslope[i-1][j]);
5354 			asl[1] = fabs(sslope[i-1][j] - sslope[i-0][j]);
5355 			asl[2] = fabs(sslope[i-0][j] - sslope[i+1][j]);
5356 			asl[3] = fabs(sslope[i+1][j] - sslope[i+2][j]);
5357 			asl[4] = fabs(sslope[i+2][j] - sslope[i+3][j]);
5358 
5359 			/* Sort them */
5360 			for (r = 0; r < (5-1); r++) {
5361 				for (s = r+1; s < 5; s++) {
5362 					if (asl[s] < asl[r]) {
5363 						double tt;
5364 						tt = asl[s];
5365 						asl[s] = asl[r];
5366 						asl[r] = tt;
5367 					}
5368 				}
5369 			}
5370 			/* Pick middle one */
5371 			sl = asl[2];
5372 			if (sl > slope[i])
5373 				slope[i] = sl;
5374 		}
5375 	}
5376 
5377 #else	/* Works better for noisy readings */
5378 
5379 	/* Compute sliding window average and deviation that contains */
5380 	/* our output point, and chose the average with the minimum deviation. */
5381 #define FW 3		/* Number of delta's to average */
5382 	for (i = FW-1; i < (nummeas-FW); i++) {		/* Samples */
5383 		double basl, bdev;		/* Best average slope, Best deviation */
5384 		double sl[2 * FW -1];
5385 		double asl[FW], dev[FW];
5386 		int slopen = 0;
5387 		double slopeth = 0.0;
5388 		int m, pp;
5389 
5390 		for (pp = 0; pp < 2; pp++) { 			/* For each pass */
5391 
5392 			for (j = BL; j < BH; j++) {				/* Bands */
5393 
5394 				/* Compute differences for the range of our windows */
5395 				for (k = 0; k < (2 * FW -1); k++)
5396 					sl[k] = sslope[i+k-FW+1][j] - sslope[i+k+-FW+2][j];
5397 
5398 				/* For each window offset, compute average and deviation squared */
5399 				bdev = 1e38;
5400 				for (k = 0; k < FW; k++) {
5401 
5402 					/* Compute average of this window offset */
5403 					asl[k] = 0.0;
5404 					for (m = 0; m < FW; m++)	/* For slope in window */
5405 						asl[k] += sl[k+m];
5406 					asl[k] /= (double)FW;
5407 
5408 					/* Compute deviation squared */
5409 					dev[k] = 0.0;
5410 					for (m = 0; m < FW; m++) {
5411 						double tt;
5412 						tt = sl[k+m] - asl[k];
5413 						dev[k] += tt * tt;
5414 					}
5415 					if (dev[k] < bdev)
5416 						bdev = dev[k];
5417 				}
5418 
5419 #ifndef NEVER	/* Use this */
5420 				/* Weight the deviations with a triangular weighting */
5421 				/* to skew slightly towards the center */
5422 				for (k = 0; k < FW; k++) {
5423 					double wt;
5424 
5425 					wt = fabs(2.0 * k - (FW -1.0))/(FW-1.0);
5426 					dev[k] += wt * bdev;
5427 				}
5428 #endif
5429 
5430 				/* For each window offset, choose the one to use. */
5431 				bdev = 1e38;
5432 				basl = 0.0;
5433 				for (k = 0; k < FW; k++) {
5434 
5435 					/* Choose window average with smallest deviation squared */
5436 					if (dev[k] < bdev) {
5437 						bdev = dev[k];
5438 						basl = fabs(asl[k]);
5439 					}
5440 				}
5441 
5442 				if (pp == 0) {		/* First pass, compute average slope over bands */
5443 					slope[i] += basl;
5444 
5445 				} else {			/* Second pass, average slopes of bands over threshold */
5446 					if (basl > slopeth) {
5447 						slope[i] += basl;
5448 						slopen++;
5449 					}
5450 				}
5451 			}	/* Next band */
5452 
5453 			if (pp == 0) {
5454 				slopeth = 1.0 * slope[i]/j;		/* Compute threshold */
5455 				slope[i] = 0.0;
5456 			} else {
5457 				if (slopen > 0)
5458 					slope[i] /= slopen;			/* Compute average of those over threshold */
5459 			}
5460 		}		/* Next pass */
5461 	}
5462 #undef FW
5463 #endif
5464 
5465 #ifndef NEVER		/* Good with this on */
5466 	/* Normalise the slope values */
5467 	/* Locate the minumum and maximum values */
5468 	maxslope = 0.0;
5469 	minslope = 1e38;
5470 	for (i = 4; i < (nummeas-4); i++) {
5471 		double avs;
5472 
5473 		if (slope[i] > maxslope)
5474 			maxslope = slope[i];
5475 
5476 		/* Simple moving average for min comp. */
5477 		avs = 0.0;
5478 		for (j = -2; j <= 2; j++)
5479 			avs += slope[i+j];
5480 		avs /= 5.0;
5481 		if (avs < minslope)
5482 			minslope = avs;
5483 	}
5484 
5485 	/* Normalise the slope */
5486 	maxslope *= 0.5;
5487 	minslope *= 3.0;
5488 	for (i = 0; i < nummeas; i++) {
5489 		slope[i] = (slope[i] - minslope) / (maxslope - minslope);
5490 		if (slope[i] < 0.0)
5491 			slope[i] = 0.0;
5492 		else if (slope[i] > 1.0)
5493 			slope[i] = 1.0;
5494 	}
5495 
5496 	/* "Automatic Gain control" the raw slope information. */
5497 #define LFW 20		/* Half width of triangular filter */
5498 	for (i = 0; i < nummeas; i++) {
5499 		double sum, twt;
5500 
5501 		sum = twt = 0.0;
5502 		for (j = -LFW; j <= LFW; j++) {
5503 			double wt;
5504 			if ((i+j) < 0 || (i+j) >= nummeas)
5505 				continue;
5506 
5507 			wt = ((LFW-abs(j))/(double)LFW);
5508 
5509 			sum += wt * slope[i+j];
5510 			twt += wt;
5511 		}
5512 		fslope[i] = sum/twt;
5513 		if (fslope[i] > fmaxslope)
5514 			fmaxslope = fslope[i];
5515 	}
5516 #undef LFW
5517 
5518 #ifdef NEVER		/* Better with the off, for very noisy samples */
5519 	/* Apply AGC with limited gain */
5520 	for (i = 0; i < nummeas; i++) {
5521 		if (fslope[i] > fmaxslope/4.0)
5522 			slope[i] = slope[i]/fslope[i];
5523 		else
5524 			slope[i] = slope[i] * 4.0/fmaxslope;
5525 	}
5526 #endif
5527 #endif /* NEVER */
5528 
5529 	/* Locate the minumum and maximum values */
5530 	maxslope = 0.0;
5531 	minslope = 1e38;
5532 	for (i = 4; i < (nummeas-4); i++) {
5533 		double avs;
5534 
5535 		if (slope[i] > maxslope)
5536 			maxslope = slope[i];
5537 
5538 		/* Simple moving average for min comp. */
5539 		avs = 0.0;
5540 		for (j = -2; j <= 2; j++)
5541 			avs += slope[i+j];
5542 		avs /= 5.0;
5543 		if (avs < minslope)
5544 			minslope = avs;
5545 	}
5546 
5547 #ifndef NEVER		/* Good with this on */
5548 	/* Normalise the slope again */
5549 	maxslope *= 0.3;
5550 	minslope *= 3.0;
5551 	for (i = 0; i < nummeas; i++) {
5552 		slope[i] = (slope[i] - minslope) / (maxslope - minslope);
5553 		if (slope[i] < 0.0)
5554 			slope[i] = 0.0;
5555 		else if (slope[i] > 1.0)
5556 			slope[i] = 1.0;
5557 	}
5558 #endif
5559 
5560 #ifdef PLOT_PATREC
5561 	printf("Slope filter output\n");
5562 	for (i = 0; i < nummeas; i++) {
5563 		int jj;
5564 		for (jj = 0, j = BL; jj < 6 && j < BH; jj++, j += ((BH-BL)/6)) {
5565 			double sum = 0.0;
5566 			for (k = -BL; k <= BW; k++)		/* Box averaging filter over bands */
5567 				sum += multimeas[i][j + k];
5568 			plot[jj][i] = sum/((2.0 * BL + 1.0) * maxval[j+k]);
5569 		}
5570 	}
5571 	for (i = 0; i < nummeas; i++)
5572 		plot[6][i] = (double)i;
5573 	do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas);
5574 #endif
5575 
5576 	free_dvector(fslope, 0, nummeas-1);
5577 	free_dmatrix(sslope, 0, nummeas-1, -1, m->nraw-1);
5578 
5579 	/* Now threshold the measurements into possible patches */
5580 	apat = 2 * nummeas;
5581 	if ((pat = (munki_patch *)malloc(sizeof(munki_patch) * apat)) == NULL) {
5582 		a1logd(p->log,1,"munki: malloc of patch structures failed!\n");
5583 		free_ivector(sizepop, 0, nummeas-1);
5584 		free_dvector(slope, 0, nummeas-1);
5585 		free_dvector(maxval, -1, m->nraw-1);
5586 		return MUNKI_INT_MALLOC;
5587 	}
5588 
5589 	avglegth = 0.0;
5590 	for (npat = i = 0; i < (nummeas-1); i++) {
5591 		if (slope[i] > thresh)
5592 			continue;
5593 
5594 		/* Start of a new patch */
5595 		if (npat >= apat) {
5596 			apat *= 2;
5597 			if ((pat = (munki_patch *)realloc(pat, sizeof(munki_patch) * apat)) == NULL) {
5598 				free_ivector(sizepop, 0, nummeas-1);
5599 				free_dvector(slope, 0, nummeas-1);
5600 				free_dvector(maxval, -1, m->nraw-1);
5601 				a1logd(p->log,1,"munki: reallloc of patch structures failed!\n");
5602 				return MUNKI_INT_MALLOC;
5603 			}
5604 		}
5605 		pat[npat].ss = i;
5606 		pat[npat].no = 2;
5607 		pat[npat].use = 0;
5608 		for (i++; i < (nummeas-1); i++) {
5609 			if (slope[i] > thresh)
5610 				break;
5611 			pat[npat].no++;
5612 		}
5613 		avglegth += (double) pat[npat].no;
5614 		npat++;
5615 	}
5616 	a1logd(p->log,7,"Number of patches = %d\n",npat);
5617 
5618 	/* We don't count the first and last patches, as we assume they are white leader. */
5619 	/* (They are marked !use in list anyway) */
5620 	if (npat < (tnpatch + 2)) {
5621 		free_ivector(sizepop, 0, nummeas-1);
5622 		free_dvector(slope, 0, nummeas-1);
5623 		free_dvector(maxval, -1, m->nraw-1);
5624 		free(pat);
5625 		a1logd(p->log,1,"Patch recog failed - unable to detect enough possible patches\n");
5626 		return MUNKI_RD_NOTENOUGHPATCHES;
5627 	} else if (npat >= (2 * tnpatch) + 2) {
5628 		free_ivector(sizepop, 0, nummeas-1);
5629 		free_dvector(slope, 0, nummeas-1);
5630 		free_dvector(maxval, -1, m->nraw-1);
5631 		free(pat);
5632 		a1logd(p->log,1,"Patch recog failed - detecting too many possible patches\n");
5633 		return MUNKI_RD_TOOMANYPATCHES;
5634 	}
5635 	avglegth /= (double)npat;
5636 
5637 #ifdef PLOT_PATREC
5638 	for (i = 0; i < npat; i++) {
5639 		printf("Raw patch %d, start %d, length %d\n",i, pat[i].ss, pat[i].no);
5640 	}
5641 #endif
5642 
5643 	/* Accumulate popularity ccount of possible patches */
5644 	for (i = 1; i < (npat-1); i++)
5645 		sizepop[pat[i].no]++;
5646 
5647 	/* Locate the median potential patch width */
5648 	for (j = 0, i = 0; i < nummeas; i++) {
5649 		j += sizepop[i];
5650 		if (j > ((npat-2)/2))
5651 			break;
5652 	}
5653 	median = (double)i;
5654 
5655 	a1logd(p->log,7,"Median patch width %f\n",median);
5656 
5657 	/* Now decide which patches to use. */
5658 	/* Try a widening window around the median. */
5659 	for (window = 0.2, try = 0; try < 15; window *= 1.4, try++) {
5660 		int bgcount = 0, bgstart = 0;
5661 		int gcount, gstart;
5662 		double wmin = median/(1.0 + window);
5663 		double wmax = median * (1.0 + window);
5664 
5665 		a1logd(p->log,7,"Window = %f - %f\n",wmin, wmax);
5666 		/* Track which is the largest contiguous group that */
5667 		/* is within our window */
5668 		gcount = gstart = 0;
5669 		for (i = 1; i < npat; i++) {
5670 			if (i < (npat-1) && pat[i].no <= wmax) {		/* Small enough */
5671 				if (pat[i].no >= wmin) {	/* And big enough */
5672 					if (gcount == 0) {		/* Start of new group */
5673 						gcount++;
5674 						gstart = i;
5675 						a1logd(p->log,7,"Start group at %d\n",gstart);
5676 					} else {
5677 						gcount++;			/* Continuing new group */
5678 						a1logd(p->log,7,"Continue group at %d, count %d\n",gstart,gcount);
5679 					}
5680 				}
5681 			} else {	/* Too big or end of patches, end this group */
5682 				a1logd(p->log,7,"Terminating group group at %d, count %d\n",gstart,gcount);
5683 				if (gcount > bgcount) {		/* New biggest group */
5684 					bgcount = gcount;
5685 					bgstart = gstart;
5686 					a1logd(p->log,7,"New biggest\n");
5687 				}
5688 				gcount = gstart = 0;		/* End this group */
5689 			}
5690 		}
5691 		a1logd(p->log,7,"Biggest group is at %d, count %d\n",bgstart,bgcount);
5692 
5693 		if (bgcount == tnpatch) {			/* We're done */
5694 			for (i = bgstart, j = 0; i < npat && j < tnpatch; i++) {
5695 				if (pat[i].no <= wmax && pat[i].no >= wmin) {
5696 					pat[i].use = 1;
5697 					j++;
5698 					if (pat[i].no < MIN_SAMPLES) {
5699 						a1logd(p->log,7,"Too few samples\n");
5700 						free_ivector(sizepop, 0, nummeas-1);
5701 						free_dvector(slope, 0, nummeas-1);
5702 						free_dvector(maxval, -1, m->nraw-1);
5703 						free(pat);
5704 						a1logd(p->log,1,"Patch recog failed - patches sampled too sparsely\n");
5705 						return MUNKI_RD_NOTENOUGHSAMPLES;
5706 					}
5707 				}
5708 			}
5709 			break;
5710 
5711 		} else if (bgcount > tnpatch) {
5712 			a1logd(p->log,7,"Too many patches\n");
5713 			free_ivector(sizepop, 0, nummeas-1);
5714 			free_dvector(slope, 0, nummeas-1);
5715 			free_dvector(maxval, -1, m->nraw-1);
5716 			free(pat);
5717 			a1logd(p->log,1,"Patch recog failed - detected too many consistent patches\n");
5718 			return MUNKI_RD_TOOMANYPATCHES;
5719 		}
5720 	}
5721 	if (try >= 15) {
5722 		a1logd(p->log,7,"Not enough patches\n");
5723 		free_ivector(sizepop, 0, nummeas-1);
5724 		free_dvector(slope, 0, nummeas-1);
5725 		free_dvector(maxval, -1, m->nraw-1);
5726 		free(pat);
5727 		a1logd(p->log,1,"Patch recog failed - unable to find enough consistent patches\n");
5728 		return MUNKI_RD_NOTENOUGHPATCHES;
5729 	}
5730 
5731 #ifdef PLOT_PATREC
5732 	printf("Got %d patches out of potentional %d:\n",tnpatch, npat);
5733 	printf("Average patch legth %f\n",avglegth);
5734 	for (i = 1; i < (npat-1); i++) {
5735 		if (pat[i].use == 0)
5736 			continue;
5737 		printf("Patch %d, start %d, length %d, use %d\n",i, pat[i].ss, pat[i].no, pat[i].use);
5738 	}
5739 #endif
5740 
5741 	/* Now trim the patches simply by shrinking their windows */
5742 	for (k = 1; k < (npat-1); k++) {
5743 		int nno, trim;
5744 
5745 		if (pat[k].use == 0)
5746 			continue;
5747 
5748 //		nno = (pat[k].no * 3 + 0)/4;		/* Trim to 75% & round down */
5749 		nno = (pat[k].no * 2 + 0)/3;		/* Trim to 66% & round down [def] */
5750 //		nno = (pat[k].no * 2 + 0)/4;		/* Trim to 50% & round down */
5751 		trim = (pat[k].no - nno + 1)/2;
5752 
5753 		pat[k].ss += trim;
5754 		pat[k].no = nno;
5755 	}
5756 
5757 #ifdef PLOT_PATREC
5758 	printf("After trimming got:\n");
5759 	for (i = 1; i < (npat-1); i++) {
5760 		if (pat[i].use == 0)
5761 			continue;
5762 		printf("Patch %d, start %d, length %d, use %d\n",i, pat[i].ss, pat[i].no, pat[i].use);
5763 	}
5764 
5765 	/* Create fake "slope" value that marks patches */
5766 	for (i = 0; i < nummeas; i++)
5767 		slope[i] = 1.0;
5768 	for (k = 1; k < (npat-1); k++) {
5769 		if (pat[k].use == 0)
5770 			continue;
5771 		for (i = pat[k].ss; i < (pat[k].ss + pat[k].no); i++)
5772 			slope[i] = 0.0;
5773 	}
5774 
5775 	printf("Trimmed output - averaged bands:\n");
5776 	/* Plot box averaged bands */
5777 	for (i = 0; i < nummeas; i++) {
5778 		int jj;
5779 		for (jj = 0, j = BL; jj < 6 && j < BH; jj++, j += ((BH-BL)/6)) {
5780 			double sum = 0.0;
5781 			for (k = -BL; k <= BW; k++)		/* Box averaging filter over bands */
5782 				sum += multimeas[i][j + k];
5783 			plot[jj][i] = sum/((2.0 * BL + 1.0) * maxval[j+k]);
5784 		}
5785 	}
5786 	for (i = 0; i < nummeas; i++)
5787 		plot[6][i] = (double)i;
5788 	do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas);
5789 
5790 #ifdef NEVER
5791 	/* Plot all the bands */
5792 	printf("Trimmed output - all bands:\n");
5793 	for (j = 0; j < (m->nraw-5); j += 5) {
5794 		for (k = 0; k < 5; k ++) {
5795 			for (i = 0; i < nummeas; i++) {
5796 				plot[k][i] = multimeas[i][j+k]/maxval[j+k];
5797 			}
5798 		}
5799 		for (i = 0; i < nummeas; i++)
5800 			plot[6][i] = (double)i;
5801 		printf("Bands %d - %d\n",j,j+5);
5802 		do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas);
5803 	}
5804 #endif
5805 
5806 #endif
5807 
5808 	/* Now compute averaged patch values */
5809 
5810 	/* Compute average of (aproximate) white */
5811 	white_avg = 0.0;
5812 	for (j = 1; j < (m->nraw-1); j++)
5813 		white_avg += maxval[j];
5814 	white_avg /= (m->nraw - 2.0);
5815 
5816 	/* Now process the buffer values */
5817 	for (i = 0; i < tnpatch; i++)
5818 		for (j = 0; j < m->nraw; j++)
5819 			pavg[i][j] = 0.0;
5820 
5821 	for (pix = 0, k = 1; k < (npat-1); k++) {
5822 		double maxavg = -1e38;	/* Track min and max averages of readings for consistency */
5823 		double minavg = 1e38;
5824 		double cons;			/* Consistency */
5825 
5826 		if (pat[k].use == 0)
5827 			continue;
5828 
5829 		if (pat[k].no <= MIN_SAMPLES) {
5830 			a1logd(p->log,7,"Too few samples\n");
5831 			free_dvector(slope, 0, nummeas-1);
5832 			free_ivector(sizepop, 0, nummeas-1);
5833 			free_dvector(maxval, -1, m->nraw-1);
5834 			free(pat);
5835 			a1logd(p->log,1,"Patch recog failed - patches sampled too sparsely\n");
5836 			return MUNKI_RD_NOTENOUGHSAMPLES;
5837 		}
5838 
5839 		/* Measure samples that make up patch value */
5840 		for (i = pat[k].ss; i < (pat[k].ss + pat[k].no); i++) {
5841 			double measavg = 0.0;
5842 
5843 			for (j = 0; j < m->nraw; j++) {
5844 				double val;
5845 
5846 				val = multimeas[i][j];
5847 
5848 				measavg += val;
5849 				pavg[pix][j] += val;
5850 			}
5851 			measavg /= (m->nraw-2.0);
5852 			if (measavg < minavg)
5853 				minavg = measavg;
5854 			if (measavg > maxavg)
5855 				maxavg = measavg;
5856 		}
5857 
5858 		for (j = 0; j < m->nraw; j++)
5859 			pavg[pix][j] /= (double)pat[k].no;
5860 
5861 		cons = (maxavg - minavg)/white_avg;
5862 		a1logd(p->log,7,"Patch %d: consistency = %f%%, thresh = %f%%\n",pix,100.0 * cons, 100.0 * patch_cons_thr);
5863 		if (cons > patch_cons_thr) {
5864 			a1logd(p->log,1,"Patch recog failed - patch %d is inconsistent (%f%%)\n",pix, cons);
5865 			rv |= 1;
5866 		}
5867 		pix++;
5868 	}
5869 
5870 	if (flags != NULL)
5871 		*flags = rv;
5872 
5873 #ifdef PLOT_PATREC
5874 	free_dmatrix(plot, 0, 6, 0, nummeas-1);
5875 #endif
5876 
5877 	free_dvector(slope, 0, nummeas-1);
5878 	free_ivector(sizepop, 0, nummeas-1);
5879 	free_dvector(maxval, -1, m->nraw-1);
5880 	free(pat);		/* Otherwise caller will have to do it */
5881 
5882 	a1logd(p->log,3,"munki_extract_patches_multimeas done, sat = %s, inconsist = %s\n",
5883 	                  rv & 2 ? "true" : "false", rv & 1 ? "true" : "false");
5884 
5885 	a1logd(p->log,2,"Patch recognition returning OK\n");
5886 
5887 	return MUNKI_OK;
5888 }
5889 
5890 #undef BL
5891 #undef BH
5892 #undef BW
5893 
5894 /* Recognise any flashes in the readings, and */
5895 /* and average their values together as well as summing their duration. */
5896 /* The readings are integrated, so the the units are cd/m^2 seconds. */
5897 /* Return nz on an error */
5898 /* (Doesn't extract [-1] shielded values, since they have already been used) */
munki_extract_patches_flash(munki * p,int * flags,double * duration,double * pavg,double ** multimeas,int nummeas,double inttime)5899 munki_code munki_extract_patches_flash(
5900 	munki *p,
5901 	int *flags,				/* return flags */
5902 	double *duration,		/* return duration */
5903 	double *pavg,			/* return patch average [-1 nraw] */
5904 	double **multimeas,		/* Array of [nummeas][-1 nraw] value to extract from */
5905 	int nummeas,			/* number of readings made */
5906 	double inttime			/* Integration time (used to compute duration) */
5907 ) {
5908 	munkiimp *m = (munkiimp *)p->m;
5909 	int i, j, k;
5910 	double minval, maxval;		/* min and max input value at wavelength of maximum input */
5911 	double mean;				/* Mean of the max wavelength band */
5912 	int maxband;				/* Band of maximum value */
5913 	double thresh;				/* Level threshold */
5914 	int fsampl;					/* Index of the first sample over the threshold */
5915 	int nsampl;					/* Number of samples over the threshold */
5916 	double *aavg;				/* ambient average [-1 nraw] */
5917 	double finttime;			/* Flash integration time */
5918 	int rv = 0;
5919 #ifdef PLOT_PATREC
5920 	double **plot;
5921 #endif
5922 
5923 	a1logd(p->log,3,"munki_extract_patches_flash: %d measurements\n",nummeas);
5924 
5925 	/* Discover the maximum input value for flash dection */
5926 	maxval = -1e6;
5927 	maxband = 0;
5928 	for (j = 0; j < m->nraw; j ++) {
5929 		for (i = 0; i < nummeas; i++) {
5930 			if (multimeas[i][j] > maxval) {
5931 				maxval = multimeas[i][j];
5932 				maxband = j;
5933 			}
5934 		}
5935 	}
5936 
5937 	if (maxval <= 0.0) {
5938 		a1logd(p->log,1,"No flashes found in measurement\n");
5939 		return MUNKI_RD_NOFLASHES;
5940 	}
5941 
5942 	minval = 1e6;
5943 	mean = 0.0;
5944 	for (i = 0; i < nummeas; i++) {
5945 		mean += multimeas[i][maxband];
5946 		if (multimeas[i][maxband] < minval)
5947 			minval = multimeas[i][maxband];
5948 	}
5949 	mean /= (double)nummeas;
5950 
5951 	/* Set the threshold at 5% from mean towards max */
5952 	thresh = (3.0 * mean + maxval)/4.0;
5953 	a1logd(p->log,7,"munki_extract_patches_flash band %d minval %f maxval %f, mean = %f, thresh = %f\n",maxband,minval,maxval,mean, thresh);
5954 
5955 #ifdef PLOT_PATREC
5956 	/* Plot out 6 lots of 6 values each */
5957 	plot = dmatrixz(0, 6, 0, nummeas-1);
5958 	for (j = maxband -3; j>= 0 && j < (m->nraw-6); j += 100)		/* Do one set around max */
5959 	{
5960 		for (k = 0; k < 6; k ++) {
5961 			for (i = 0; i < nummeas; i++) {
5962 				plot[k][i] = multimeas[i][j+k]/maxval;
5963 			}
5964 		}
5965 		for (i = 0; i < nummeas; i++)
5966 			plot[6][i] = (double)i;
5967 		printf("Bands %d - %d\n",j,j+5);
5968 		do_plot6(plot[6], plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], nummeas);
5969 	}
5970 	free_dmatrix(plot,0,6,0,nummeas-1);
5971 #endif
5972 
5973 #ifdef PLOT_PATREC
5974 	/* Plot just the pulses */
5975 	{
5976 		int start, end;
5977 
5978 		plot = dmatrixz(0, 6, 0, nummeas-1);
5979 
5980 		for(j = 0, start = -1, end = 0;;) {
5981 
5982 			for (start = -1, i = end; i < nummeas; i++) {
5983 				if (multimeas[i][maxband] >= thresh) {
5984 					if (start < 0)
5985 						start = i;
5986 				} else if (start >= 0) {
5987 					end = i;
5988 					break;
5989 				}
5990 			}
5991 			if (start < 0)
5992 				break;
5993 			start -= 3;
5994 			if (start < 0)
5995 				start = 0;
5996 			end += 4;
5997 			if (end > nummeas)
5998 				end = nummeas;
5999 
6000 			for (i = start; i < end; i++, j++) {
6001 				int q;
6002 
6003 				plot[6][j] = (double)j;
6004 #ifdef NEVER	/* Plot +/-3 around maxband */
6005 				for (q = 0, k = maxband -3; k < (maxband+3) && k >= 0 && k < m->nraw; k++, q++) {
6006 					plot[q][j] = multimeas[i][k]/maxval;
6007 				}
6008 #else
6009 				/* plot max of bands in 6 segments */
6010 				for (q = 0; q < 6; q++) {
6011 					int ss, ee;
6012 
6013 					plot[q][j] = -1e60;
6014 					ss = q * (m->nraw/6);
6015 					ee = (q+1) * (m->nraw/6);
6016 					for (k = ss; k < ee; k++) {
6017 						if (multimeas[i][k]/maxval > plot[q][j])
6018 							plot[q][j] = multimeas[i][k]/maxval;
6019 					}
6020 				}
6021 #endif
6022 			}
6023 		}
6024 		do_plot6(plot[6], plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], j);
6025 		free_dmatrix(plot,0,6,0,nummeas-1);
6026 	}
6027 #endif
6028 
6029 	/* Locate the first sample over the threshold, and the */
6030 	/* total number of samples in the pulses. */
6031 	fsampl = -1;
6032 	for (nsampl = i = 0; i < nummeas; i++) {
6033 		for (j = 0; j < m->nraw-1; j++) {
6034 			if (multimeas[i][j] >= thresh)
6035 				break;
6036 		}
6037 		if (j < m->nraw-1) {
6038 			if (fsampl < 0)
6039 				fsampl = i;
6040 			nsampl++;
6041 		}
6042 	}
6043 	a1logd(p->log,7,"Number of flash patches = %d\n",nsampl);
6044 	if (nsampl == 0)
6045 		return MUNKI_RD_NOFLASHES;
6046 
6047 	/* See if there are as many samples before the first flash */
6048 	if (nsampl < 6)
6049 		nsampl = 6;
6050 
6051 	/* Average nsample samples of ambient */
6052 	i = (fsampl-3-nsampl);
6053 	if (i < 0)
6054 		return MUNKI_RD_NOAMBB4FLASHES;
6055 	a1logd(p->log,7,"Ambient samples %d to %d \n",i,fsampl-3);
6056 	aavg = dvectorz(-1, m->nraw-1);
6057 	for (nsampl = 0; i < (fsampl-3); i++) {
6058 		for (j = 0; j < m->nraw-1; j++)
6059 			aavg[j] += multimeas[i][j];
6060 		nsampl++;
6061 	}
6062 
6063 	/* Integrate all the values over the threshold, */
6064 	/* and also one either side of flash */
6065 	for (j = 0; j < m->nraw-1; j++)
6066 		pavg[j] = 0.0;
6067 
6068 	for (k = 0, i = 1; i < (nummeas-1); i++) {
6069 		int sample = 0;
6070 		for (j = 0; j < m->nraw-1; j++) {
6071 			if (multimeas[i-1][j] >= thresh) {
6072 				sample = 1;
6073 				break;
6074 			}
6075 			if (multimeas[i][j] >= thresh) {
6076 				sample = 1;
6077 				break;
6078 			}
6079 			if (multimeas[i+1][j] >= thresh) {
6080 				sample = 1;
6081 				break;
6082 			}
6083 		}
6084 		if (j < m->nraw-1) {
6085 			a1logd(p->log,7,"Integrating flash sample no %d \n",i);
6086 			for (j = 0; j < m->nraw-1; j++)
6087 				pavg[j] += multimeas[i][j];
6088 			k++;
6089 		}
6090 	}
6091 	for (j = 0; j < m->nraw-1; j++)
6092 		pavg[j] = pavg[j]/(double)k - aavg[j]/(double)nsampl;
6093 
6094 	a1logd(p->log,7,"Number of flash patches integrated = %d\n",k);
6095 
6096 	finttime = inttime * (double)k;
6097 	if (duration != NULL)
6098 		*duration = finttime;
6099 
6100 	/* Convert to cd/m^2 seconds */
6101 	for (j = 0; j < m->nraw-1; j++)
6102 		pavg[j] *= finttime;
6103 
6104 	if (flags != NULL)
6105 		*flags = rv;
6106 
6107 	free_dvector(aavg, -1, m->nraw-1);
6108 
6109 	return MUNKI_OK;
6110 }
6111 
6112 /* Convert an absraw array from raw wavelengths to output wavelenths */
6113 /* for the current resolution. Apply stray light compensation too. */
munki_absraw_to_abswav(munki * p,int nummeas,double ** abswav,double ** absraw)6114 void munki_absraw_to_abswav(
6115 	munki *p,
6116 	int nummeas,			/* Return number of readings measured */
6117 	double **abswav,		/* Desination array [nwav] */
6118 	double **absraw			/* Source array [-1 nraw] */
6119 ) {
6120 	munkiimp *m = (munkiimp *)p->m;
6121 	munki_state *s = &m->ms[m->mmode];
6122 	double *tm;			/* Temporary array */
6123 	int i, j, k, cx, sx;
6124 
6125 	tm = dvector(0, m->nwav-1);
6126 
6127 	/* For each measurement */
6128 	for (i = 0; i < nummeas; i++) {
6129 
6130 		/* For each output wavelength */
6131 		for (cx = j = 0; j < m->nwav; j++) {
6132 			double oval = 0.0;
6133 
6134 			/* For each matrix value */
6135 			if (s->reflective) {
6136 				sx = m->rmtx_index[j];		/* Starting index */
6137 				for (k = 0; k < m->rmtx_nocoef[j]; k++, cx++, sx++)
6138 					oval += m->rmtx_coef[cx] * absraw[i][sx];
6139 			} else {
6140 				sx = m->emtx_index[j];		/* Starting index */
6141 				for (k = 0; k < m->emtx_nocoef[j]; k++, cx++, sx++)
6142 					oval += m->emtx_coef[cx] * absraw[i][sx];
6143 			}
6144 			tm[j] = oval;
6145 		}
6146 
6147 		/* Now apply stray light compensation */
6148 		/* For each output wavelength */
6149 		for (j = 0; j < m->nwav; j++) {
6150 			double oval = 0.0;
6151 
6152 			/* For each matrix value */
6153 			for (k = 0; k < m->nwav; k++)
6154 				oval += m->straylight[j][k] * tm[k];
6155 			abswav[i][j] = oval;
6156 		}
6157 	}
6158 	free_dvector(tm, 0, m->nwav-1);
6159 }
6160 
6161 /* Convert an absraw array from raw wavelengths to output wavelenths */
6162 /* for the standard resolution. Apply stray light compensation too. */
munki_absraw_to_abswav1(munki * p,int nummeas,double ** abswav,double ** absraw)6163 void munki_absraw_to_abswav1(
6164 	munki *p,
6165 	int nummeas,			/* Return number of readings measured */
6166 	double **abswav,		/* Desination array [nwav1] */
6167 	double **absraw		/* Source array [-1 nraw] */
6168 ) {
6169 	munkiimp *m = (munkiimp *)p->m;
6170 	munki_state *s = &m->ms[m->mmode];
6171 	double *tm;			/* Temporary array */
6172 	int i, j, k, cx, sx;
6173 
6174 	tm = dvector(0, m->nwav1-1);
6175 
6176 	/* For each measurement */
6177 	for (i = 0; i < nummeas; i++) {
6178 
6179 		/* For each output wavelength */
6180 		for (cx = j = 0; j < m->nwav1; j++) {
6181 			double oval = 0.0;
6182 
6183 			/* For each matrix value */
6184 			if (s->reflective) {
6185 				sx = m->rmtx_index1[j];		/* Starting index */
6186 				for (k = 0; k < m->rmtx_nocoef1[j]; k++, cx++, sx++)
6187 					oval += m->rmtx_coef1[cx] * absraw[i][sx];
6188 			} else {
6189 				sx = m->emtx_index1[j];		/* Starting index */
6190 				for (k = 0; k < m->emtx_nocoef1[j]; k++, cx++, sx++)
6191 					oval += m->emtx_coef1[cx] * absraw[i][sx];
6192 			}
6193 			tm[j] = oval;
6194 		}
6195 
6196 		/* Now apply stray light compensation */
6197 		/* For each output wavelength */
6198 		for (j = 0; j < m->nwav1; j++) {
6199 			double oval = 0.0;
6200 
6201 			/* For each matrix value */
6202 			for (k = 0; k < m->nwav1; k++)
6203 				oval += m->straylight1[j][k] * tm[k];
6204 			abswav[i][j] = oval;
6205 		}
6206 	}
6207 	free_dvector(tm, 0, m->nwav1-1);
6208 }
6209 
6210 /* Convert an absraw array from raw wavelengths to output wavelenths */
6211 /* for the high resolution. Apply light compensation too. */
munki_absraw_to_abswav2(munki * p,int nummeas,double ** abswav,double ** absraw)6212 void munki_absraw_to_abswav2(
6213 	munki *p,
6214 	int nummeas,			/* Return number of readings measured */
6215 	double **abswav,		/* Desination array [nwav2] */
6216 	double **absraw			/* Source array [-1 nraw] */
6217 ) {
6218 	munkiimp *m = (munkiimp *)p->m;
6219 	munki_state *s = &m->ms[m->mmode];
6220 	double *tm;			/* Temporary array */
6221 	int i, j, k, cx, sx;
6222 
6223 	tm = dvector(0, m->nwav2-1);
6224 
6225 	/* For each measurement */
6226 	for (i = 0; i < nummeas; i++) {
6227 
6228 		/* For each output wavelength */
6229 		for (cx = j = 0; j < m->nwav2; j++) {
6230 			double oval = 0.0;
6231 
6232 			/* For each matrix value */
6233 			if (s->reflective) {
6234 				sx = m->rmtx_index2[j];		/* Starting index */
6235 				for (k = 0; k < m->rmtx_nocoef2[j]; k++, cx++, sx++)
6236 					oval += m->rmtx_coef2[cx] * absraw[i][sx];
6237 			} else {
6238 				sx = m->emtx_index2[j];		/* Starting index */
6239 				for (k = 0; k < m->emtx_nocoef2[j]; k++, cx++, sx++)
6240 					oval += m->emtx_coef2[cx] * absraw[i][sx];
6241 			}
6242 			tm[j] = oval;
6243 		}
6244 
6245 		/* Now apply stray light compensation */
6246 		/* For each output wavelength */
6247 		for (j = 0; j < m->nwav2; j++) {
6248 			double oval = 0.0;
6249 
6250 			/* For each matrix value */
6251 			for (k = 0; k < m->nwav2; k++)
6252 				oval += m->straylight2[j][k] * tm[k];
6253 			abswav[i][j] = oval;
6254 		}
6255 	}
6256 	free_dvector(tm, 0, m->nwav2-1);
6257 }
6258 
6259 /* Convert an abswav array of output wavelengths to scaled output readings. */
munki_scale_specrd(munki * p,double ** outspecrd,int numpatches,double ** inspecrd)6260 void munki_scale_specrd(
6261 	munki *p,
6262 	double **outspecrd,		/* Destination */
6263 	int numpatches,			/* Number of readings/patches */
6264 	double **inspecrd		/* Source */
6265 ) {
6266 	munkiimp *m = (munkiimp *)p->m;
6267 	munki_state *s = &m->ms[m->mmode];
6268 	int i, j;
6269 
6270 	/* For each measurement */
6271 	for (i = 0; i < numpatches; i++) {
6272 
6273 		/* For each output wavelength */
6274 		for (j = 0; j < m->nwav; j++) {
6275 			outspecrd[i][j] = inspecrd[i][j] * s->cal_factor[j];
6276 		}
6277 	}
6278 }
6279 
6280 
6281 /* =============================================== */
6282 #ifdef HIGH_RES
6283 
6284 /* High res congiguration */
6285 #undef EXISTING_SHAPE		/* [und] Else generate filter shape */
6286 #define USE_GAUSSIAN		/* [def] Use gaussian filter shape, else lanczos2 */
6287 
6288 #define DO_CCDNORM			/* [def] Normalise CCD values to original */
6289 #define DO_CCDNORMAVG		/* [und???] Normalise averages rather than per CCD bin */
6290 #define BOX_INTEGRATE    	/* [und] Integrate raw samples as if they were +/-0.5 boxes */
6291 							/*       (This improves coeficient consistency a bit ?) */
6292 
6293 #ifdef NEVER
6294 /* Plot the matrix coefficients */
munki_debug_plot_mtx_coef(munki * p,int ref)6295 void munki_debug_plot_mtx_coef(munki *p, int ref) {
6296 	munkiimp *m = (munkiimp *)p->m;
6297 	int i, j, k, cx, sx;
6298 	double *xx, *ss;
6299 	double **yy;
6300 
6301 	xx = dvectorz(-1, m->nraw-1);		/* X index */
6302 	yy = dmatrixz(0, 5, -1, m->nraw-1);	/* Curves distributed amongst 5 graphs */
6303 
6304 	for (i = 0; i < m->nraw; i++)
6305 		xx[i] = i;
6306 
6307 	/* For each output wavelength */
6308 	for (cx = j = 0; j < m->nwav; j++) {
6309 		i = j % 5;
6310 
6311 //		printf("Out wave = %d\n",j);
6312 		/* For each matrix value */
6313 		if (ref) {
6314 			sx = m->rmtx_index[j];		/* Starting index */
6315 //			printf("start index = %d, nocoef %d\n",sx,m->rmtx_nocoef[j]);
6316 			for (k = 0; k < m->rmtx_nocoef[j]; k++, cx++, sx++) {
6317 //				printf("offset %d, coef ix %d val %f from ccd %d\n",k, cx, m->rmtx_coef[cx], sx);
6318 				yy[5][sx] += 0.5 * m->rmtx_coef[cx];
6319 				yy[i][sx] = m->rmtx_coef[cx];
6320 			}
6321 		} else {
6322 			sx = m->emtx_index[j];		/* Starting index */
6323 //			printf("start index = %d, nocoef %d\n",sx,m->emtx_nocoef[j]);
6324 			for (k = 0; k < m->emtx_nocoef[j]; k++, cx++, sx++) {
6325 //				printf("offset %d, coef ix %d val %f from ccd %d\n",k, cx, m->emtx_coef[cx], sx);
6326 				yy[5][sx] += 0.5 * m->emtx_coef[cx];
6327 				yy[i][sx] = m->emtx_coef[cx];
6328 			}
6329 		}
6330 	}
6331 
6332 	if (ref)
6333 		printf("Reflective cooeficients\n");
6334 	else
6335 		printf("Emissive cooeficients\n");
6336 	do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
6337 	free_dvector(xx, -1, m->nraw-1);
6338 	free_dmatrix(yy, 0, 2, -1, m->nraw-1);
6339 }
6340 #endif
6341 
6342 /* Filter shape point */
6343 typedef	struct {
6344 	double wl, we;
6345 } munki_fs;
6346 
6347 /* Filter cooeficient values */
6348 typedef struct {
6349 	int ix;				/* Raw index */
6350 	double we;			/* Weighting */
6351 } munki_fc;
6352 
6353 /* Wavelenth calibration crossover point information */
6354 typedef struct {
6355 	double wav;				/* Wavelegth of point */
6356 	double raw;				/* Raw index of point */
6357 	double wei;				/* Weigting of the point */
6358 } munki_xp;
6359 
6360 /* Linearly interpolate the filter shape */
lin_fshape(munki_fs * fsh,int n,double x)6361 static double lin_fshape(munki_fs *fsh, int n, double x) {
6362 	int i;
6363 	double y;
6364 
6365 	if (x <= fsh[0].wl)
6366 		return fsh[0].we;
6367 	else if (x >= fsh[n-1].wl)
6368 		return fsh[n-1].we;
6369 
6370 	for (i = 0; i < (n-1); i++)
6371 		if (x >= fsh[i].wl && x <= fsh[i+1].wl)
6372 			break;
6373 
6374 	x = (x - fsh[i].wl)/(fsh[i+1].wl - fsh[i].wl);
6375 	y = fsh[i].we + (fsh[i+1].we - fsh[i].we) * x;
6376 
6377 	return y;
6378 }
6379 
6380 /* Generate a sample from a lanczos2 filter shape */
6381 /* wi is the width of the filter */
lanczos2(double wi,double x)6382 static double lanczos2(double wi, double x) {
6383 	double y;
6384 
6385 #ifdef USE_GAUSSIAN
6386 	/* gausian */
6387 	wi = wi/(sqrt(2.0 * log(2.0)));	/* Convert width at half max to std. dev. */
6388     x = x/wi;
6389 //	y = 1.0/(wi * sqrt(2.0 * DBL_PI)) * exp(-(x * x));		/* Unity area */
6390 	y = exp(-(x * x));										/* Center at 1.0 */
6391 #else
6392 
6393 
6394 	/* lanczos2 */
6395 	wi *= 1.05;			// Improves smoothness. Why ?
6396 	x = fabs(1.0 * x/wi);
6397 	if (x >= 2.0)
6398 		return 0.0;
6399 	if (x < 1e-5)
6400 		return 1.0;
6401 	y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/2.0)/(DBL_PI * x/2.0);
6402 #endif
6403 	return y;
6404 }
6405 
6406 #if defined(__APPLE__) && defined(__POWERPC__)
6407 
6408 /* Workaround for a ppc gcc 3.3 optimiser bug... */
gcc_bug_fix(int i)6409 static int gcc_bug_fix(int i) {
6410 	static int nn;
6411 	nn += i;
6412 	return nn;
6413 }
6414 #endif	/* APPLE */
6415 
6416 #ifdef SALONEINSTLIB
6417 # define ONEDSTRAYLIGHTUS
6418 #endif
6419 
6420 /* Create high resolution mode references, */
6421 /* Create Reflective if ref nz, else create Emissive */
6422 /* We expect this to be called twice, once for each. */
munki_create_hr(munki * p,int ref)6423 munki_code munki_create_hr(munki *p, int ref) {
6424 	munkiimp *m = (munkiimp *)p->m;
6425 	int i, j, jj, k, cx, sx;
6426 	munki_fc coeff[40][16];	/* Existing filter cooefficients */
6427 	int nwav1;					/* Number of filters */
6428 	double wl_short1, wl_long1;	/* Ouput wavelength of first and last filters */
6429 	double wl_step1;
6430 	munki_xp xp[41];			/* Crossover points each side of filter */
6431 	munki_code ev = MUNKI_OK;
6432 	rspl *raw2wav;				/* Lookup from CCD index to wavelength */
6433 	munki_fs fshape[40 * 16];  /* Existing filter shape */
6434 	int ncp = 0;				/* Number of shape points */
6435 	int *mtx_index1, **pmtx_index2, *mtx_index2;
6436 	int *mtx_nocoef1, **pmtx_nocoef2, *mtx_nocoef2;
6437 	double *mtx_coef1, **pmtx_coef2, *mtx_coef2;
6438 
6439 	double min_wl = ref ? WL_REF_MIN : WL_EMIS_MIN;
6440 
6441 	/* Start with nominal values. May alter these if filters are not unique */
6442 	nwav1 = m->nwav1;
6443 	wl_short1 = m->wl_short1;
6444 	wl_long1 = m->wl_long1;
6445 	wl_step1 = (wl_long1 - m->wl_short1)/(m->nwav1-1.0);
6446 
6447 	if (ref) {
6448 		mtx_index1 = m->rmtx_index1;
6449 		mtx_nocoef1 = m->rmtx_nocoef1;
6450 		mtx_coef1 = m->rmtx_coef1;
6451 		mtx_index2 = NULL;
6452 		mtx_nocoef2 = NULL;
6453 		mtx_coef2 = NULL;
6454 		pmtx_index2 = &m->rmtx_index2;
6455 		pmtx_nocoef2 = &m->rmtx_nocoef2;
6456 		pmtx_coef2 = &m->rmtx_coef2;
6457 	} else {
6458 		mtx_index1 = m->emtx_index1;
6459 		mtx_nocoef1 = m->emtx_nocoef1;
6460 		mtx_coef1 = m->emtx_coef1;
6461 		mtx_index2 = NULL;
6462 		mtx_nocoef2 = NULL;
6463 		mtx_coef2 = NULL;
6464 		pmtx_index2 = &m->emtx_index2;
6465 		pmtx_nocoef2 = &m->emtx_nocoef2;
6466 		pmtx_coef2 = &m->emtx_coef2;
6467 	}
6468 
6469 	/* Convert the native filter cooeficient representation to */
6470 	/* a 2D array we can randomly index. Skip any duplicated */
6471 	/* filter cooeficients. */
6472 	for (cx = j = jj = 0; j < m->nwav1; j++) { /* For each output wavelength */
6473 		if (j >= 40) {	/* Assert */
6474 			a1logw(p->log,"munki: number of output wavelenths is > 40\n");
6475 			return MUNKI_INT_ASSERT;
6476 		}
6477 
6478 		/* For each matrix value */
6479 		sx = mtx_index1[j];		/* Starting index */
6480 		if (j < (m->nwav1-1) && sx == mtx_index1[j+1]) {	/* Skip duplicates + last */
6481 //			printf("~1 skipping %d\n",j);
6482 			wl_short1 += wl_step1;
6483 			nwav1--;
6484 			cx += mtx_nocoef1[j];
6485 			continue;
6486 		}
6487 		for (k = 0; k < mtx_nocoef1[j]; k++, cx++, sx++) {
6488 			if (k >= 16) {	/* Assert */
6489 				a1logw(p->log,"munki: number of filter coeefs is > 16\n");
6490 				return MUNKI_INT_ASSERT;
6491 			}
6492 
6493 			coeff[jj][k].ix = sx;
6494 			coeff[jj][k].we = mtx_coef1[cx];
6495 		}
6496 		jj++;
6497 	}
6498 
6499 #ifdef HIGH_RES_PLOT
6500 	/* Plot original re-sampling curves */
6501 	{
6502 		double *xx, *ss;
6503 		double **yy;
6504 
6505 		xx = dvectorz(-1, m->nraw-1);		/* X index */
6506 		yy = dmatrixz(0, 5, -1, m->nraw-1);	/* Curves distributed amongst 5 graphs */
6507 
6508 		for (i = 0; i < m->nraw; i++)
6509 			xx[i] = i;
6510 
6511 		/* For each output wavelength */
6512 		for (j = 0; j < nwav1; j++) {
6513 			i = j % 5;
6514 
6515 			/* For each matrix value */
6516 			for (k = 0; k < mtx_nocoef1[j]; k++) {
6517 				yy[5][coeff[j][k].ix] += 0.5 * coeff[j][k].we;
6518 				yy[i][coeff[j][k].ix] = coeff[j][k].we;
6519 			}
6520 		}
6521 
6522 		if (ref)
6523 			printf("Original reflection wavelength sampling curves:\n");
6524 		else
6525 			printf("Original emission wavelength sampling curves:\n");
6526 		do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
6527 		free_dvector(xx, -1, m->nraw-1);
6528 		free_dmatrix(yy, 0, 2, -1, m->nraw-1);
6529 	}
6530 #endif /* HIGH_RES_PLOT */
6531 
6532 //	a1logd(p->log,3,"computing crossover points\n");
6533 	/* Compute the crossover points between each filter */
6534 	for (i = 0; i < (nwav1-1); i++) {
6535 		double den, y1, y2, y3, y4, yn, xn;	/* Location of intersection */
6536 		double eps = 1e-6;			/* Numerical tollerance */
6537 		double besty = -1e6;
6538 
6539 		/* between filter i and i+1, we want to find the two */
6540 		/* raw indexes where the weighting values cross over */
6541 		/* Do a brute force search to avoid making assumptions */
6542 		/* about the raw order. */
6543 		for (j = 0; j < (mtx_nocoef1[i]-1); j++) {
6544 			for (k = 0; k < (mtx_nocoef1[i+1]-1); k++) {
6545 				if (coeff[i][j].ix == coeff[i+1][k].ix
6546 				 && coeff[i][j+1].ix == coeff[i+1][k+1].ix) {
6547 
6548 //					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);
6549 
6550 					/* Compute the intersection of the two line segments */
6551 					y1 = coeff[i][j].we;
6552 					y2 = coeff[i][j+1].we;
6553 					y3 = coeff[i+1][k].we;
6554 					y4 = coeff[i+1][k+1].we;
6555 //					a1logd(p->log,3,"y1 %f, y2 %f, y3 %f, y4 %f\n",y1, y2, y3, y4);
6556 					den = -y4 + y3 + y2 - y1;
6557 					if (fabs(den) < eps)
6558 						continue;
6559 					yn = (y2 * y3 - y1 * y4)/den;
6560 					xn = (y3 - y1)/den;
6561 					if (xn < -eps || xn > (1.0 + eps))
6562 						continue;
6563 //					a1logd(p->log,3,"den = %f, yn = %f, xn = %f\n",den,yn,xn);
6564 					if (yn > besty) {
6565 						xp[i+1].wav = XSPECT_WL(wl_short1, wl_long1, nwav1, i + 0.5);
6566 						xp[i+1].raw = (1.0 - xn) * coeff[i][j].ix + xn * coeff[i][j+1].ix;
6567 						xp[i+1].wei = yn;
6568 						besty = yn;
6569 //					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);
6570 //					a1logd(p->log,3,"Found new best y %f\n",yn);
6571 					}
6572 //				a1logd(p->log,3,"\n");
6573 				}
6574 			}
6575 		}
6576 		if (besty < 0.0) {	/* Assert */
6577 			a1logw(p->log,"munki: failed to locate crossover between resampling filters\n");
6578 			return MUNKI_INT_ASSERT;
6579 		}
6580 //		a1logd(p->log,3,"\n");
6581 	}
6582 
6583 	/* Add the two points for the end filters */
6584 	{
6585 		double x5, x6, y5, y6;				/* Points on intesecting line */
6586 		double den, y1, y2, y3, y4, yn, xn;	/* Location of intersection */
6587 
6588 		x5 = xp[1].raw;
6589 		y5 = xp[1].wei;
6590 		x6 = xp[2].raw;
6591 		y6 = xp[2].wei;
6592 
6593 		/* Search for possible intersection point with first curve */
6594 		/* Create equation for line from next two intersection points */
6595 		for (j = 0; j < (mtx_nocoef1[0]-1); j++) {
6596 			/* Extrapolate line to this segment */
6597 			y3 = y5 + (coeff[0][j].ix - x5)/(x6 - x5) * (y6 - y5);
6598 			y4 = y5 + (coeff[0][j+1].ix - x5)/(x6 - x5) * (y6 - y5);
6599 			/* This segment of curve */
6600 			y1 = coeff[0][j].we;
6601 			y2 = coeff[0][j+1].we;
6602 			if ( ((  y1 >= y3 && y2 <= y4)		/* Segments overlap */
6603 			   || (  y1 <= y3 && y2 >= y4))
6604 			  && ((    coeff[0][j].ix < x5 && coeff[0][j].ix < x6
6605 			        && coeff[0][j+1].ix < x5 && coeff[0][j+1].ix < x6)
6606 			   || (    coeff[0][j+1].ix > x5 && coeff[0][j+1].ix > x6
6607 				    && coeff[0][j].ix > x5 && coeff[0][j].ix > x6))) {
6608 				break;
6609 			}
6610 		}
6611 		if (j >= mtx_nocoef1[0]) {	/* Assert */
6612 			a1logw(p->log,"munki: failed to find end crossover\n");
6613 			return MUNKI_INT_ASSERT;
6614 		}
6615 		den = -y4 + y3 + y2 - y1;
6616 		yn = (y2 * y3 - y1 * y4)/den;
6617 		xn = (y3 - y1)/den;
6618 //		a1logd(p->log,3,"den = %f, yn = %f, xn = %f\n",den,yn,xn);
6619 		xp[0].wav = XSPECT_WL(wl_short1, wl_long1, nwav1, -0.5);
6620 		xp[0].raw = (1.0 - xn) * coeff[0][j].ix + xn * coeff[0][j+1].ix;
6621 		xp[0].wei = yn;
6622 //		a1logd(p->log,3,"End 0 intersection %d: wav %f, raw %f, wei %f\n",0,xp[0].wav,xp[0].raw,xp[0].wei);
6623 //		a1logd(p->log,3,"\n");
6624 
6625 		x5 = xp[nwav1-2].raw;
6626 		y5 = xp[nwav1-2].wei;
6627 		x6 = xp[nwav1-1].raw;
6628 		y6 = xp[nwav1-1].wei;
6629 
6630 //		a1logd(p->log,3,"x5 %f, y5 %f, x6 %f, y6 %f\n",x5,y5,x6,y6);
6631 		/* Search for possible intersection point with first curve */
6632 		/* Create equation for line from next two intersection points */
6633 		for (j = 0; j < (mtx_nocoef1[0]-1); j++) {
6634 			/* Extrapolate line to this segment */
6635 			y3 = y5 + (coeff[nwav1-1][j].ix - x5)/(x6 - x5) * (y6 - y5);
6636 			y4 = y5 + (coeff[nwav1-1][j+1].ix - x5)/(x6 - x5) * (y6 - y5);
6637 			/* This segment of curve */
6638 			y1 = coeff[nwav1-1][j].we;
6639 			y2 = coeff[nwav1-1][j+1].we;
6640 			if ( ((  y1 >= y3 && y2 <= y4)		/* Segments overlap */
6641 			   || (  y1 <= y3 && y2 >= y4))
6642 			  && ((    coeff[nwav1-1][j].ix < x5 && coeff[nwav1-1][j].ix < x6
6643 			        && coeff[nwav1-1][j+1].ix < x5 && coeff[nwav1-1][j+1].ix < x6)
6644 			   || (    coeff[nwav1-1][j+1].ix > x5 && coeff[nwav1-1][j+1].ix > x6
6645 				    && coeff[nwav1-1][j].ix > x5 && coeff[nwav1-1][j].ix > x6))) {
6646 				break;
6647 			}
6648 		}
6649 		if (j >= mtx_nocoef1[nwav1-1]) {	/* Assert */
6650 			a1logw(p->log, "munki: failed to find end crossover\n");
6651 			return MUNKI_INT_ASSERT;
6652 		}
6653 		den = -y4 + y3 + y2 - y1;
6654 		yn = (y2 * y3 - y1 * y4)/den;
6655 		xn = (y3 - y1)/den;
6656 //		a1logd(p->log,3,"den = %f, yn = %f, xn = %f\n",den,yn,xn);
6657 		xp[nwav1].wav = XSPECT_WL(wl_short1, wl_long1, nwav1, nwav1-0.5);
6658 		xp[nwav1].raw = (1.0 - xn) * coeff[nwav1-1][j].ix + xn * coeff[nwav1-1][j+1].ix;
6659 		xp[nwav1].wei = yn;
6660 //		a1logd(p->log,3,"End 36 intersection %d: wav %f, raw %f, wei %f\n",nwav1+1,xp[nwav1].wav,xp[nwav1].raw,xp[nwav1].wei);
6661 //		a1logd(p->log,3,"\n");
6662 	}
6663 
6664 #ifdef HIGH_RES_PLOT
6665 	/* Plot original re-sampling curves + crossing points */
6666 	{
6667 		double *xx, *ss;
6668 		double **yy;
6669 		double *xc, *yc;
6670 
6671 		xx = dvectorz(-1, m->nraw-1);		/* X index */
6672 		yy = dmatrixz(0, 5, -1, m->nraw-1);	/* Curves distributed amongst 5 graphs */
6673 		xc = dvectorz(0, nwav1);		/* Crossover X */
6674 		yc = dvectorz(0, nwav1);		/* Crossover Y */
6675 
6676 		for (i = 0; i < m->nraw; i++)
6677 			xx[i] = i;
6678 
6679 		/* For each output wavelength */
6680 		for (j = 0; j < nwav1; j++) {
6681 			i = j % 5;
6682 
6683 			/* For each matrix value */
6684 			for (k = 0; k < mtx_nocoef1[j]; k++) {
6685 				yy[5][coeff[j][k].ix] += 0.5 * coeff[j][k].we;
6686 				yy[i][coeff[j][k].ix] = coeff[j][k].we;
6687 			}
6688 		}
6689 
6690 		/* Crosses at intersection points */
6691 		for (i = 0; i <= nwav1; i++) {
6692 			xc[i] = xp[i].raw;
6693 			yc[i] = xp[i].wei;
6694 		}
6695 
6696 		if (ref)
6697 			printf("Original reflection sampling curves + crossover points\n");
6698 		else
6699 			printf("Original emsission sampling curves + crossover points\n");
6700 		do_plot6p(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw, xc, yc, nwav1+1);
6701 		free_dvector(xx, -1, m->nraw-1);
6702 		free_dmatrix(yy, 0, 2, -1, m->nraw-1);
6703 		free_dvector(xc, 0, nwav1);
6704 		free_dvector(yc, 0, nwav1);
6705 	}
6706 #endif /* HIGH_RES_PLOT */
6707 
6708 #ifdef HIGH_RES_DEBUG
6709 	/* Check to see if the area of each filter curve is the same */
6710 	/* (yep, width times 2 * xover height is close to 1.0, and the */
6711 	/*  sum of the weightings is exactly 1.0) */
6712 	for (i = 0; i < nwav1; i++) {
6713 		double area1, area2;
6714 		area1 = fabs(xp[i].raw - xp[i+1].raw) * (xp[i].wei + xp[i+1].wei);
6715 
6716 		area2 = 0.0;
6717 		for (j = 0; j < (mtx_nocoef1[i]); j++)
6718 		    area2 += coeff[i][j].we;
6719 
6720 		printf("Area of curve %d = %f, %f\n",i,area1, area2);
6721 	}
6722 #endif /* HIGH_RES_DEBUG */
6723 
6724 	/* From our crossover data, create a rspl that maps raw CCD index */
6725 	/* value into wavelegth. */
6726 	{
6727 		co sd[101];				/* Scattered data points */
6728 		datai glow, ghigh;
6729 		datao vlow, vhigh;
6730 		int gres[1];
6731 		double avgdev[1];
6732 
6733 		if ((raw2wav = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) {
6734 			a1logd(p->log,3,"munki: creating rspl for high res conversion failed\n");
6735 			return MUNKI_INT_NEW_RSPL_FAILED;
6736 		}
6737 
6738 		vlow[0] = 1e6;
6739 		vhigh[0] = -1e6;
6740 
6741 		for (i = 0; i < (nwav1+1); i++) {
6742 			sd[i].p[0] = xp[i].raw;
6743 			sd[i].v[0] = xp[i].wav;
6744 
6745 			if (sd[i].v[0] < vlow[0])
6746 				vlow[0] = sd[i].v[0];
6747 			if (sd[i].v[0] > vhigh[0])
6748 				vhigh[0] = sd[i].v[0];
6749 		}
6750 		glow[0] = 0.0;
6751 		ghigh[0] = (double)(m->nraw-1);
6752 		gres[0] = m->nraw;
6753 		avgdev[0] = 0.0;
6754 
6755 		raw2wav->fit_rspl(raw2wav, 0, sd, nwav1+1, glow, ghigh, gres, vlow, vhigh, 1.0, avgdev, NULL);
6756 	}
6757 
6758 #ifdef EXISTING_SHAPE
6759 	/* Convert each weighting curves values into normalized values and */
6760 	/* accumulate into a single curve. */
6761 	/* This probably isn't quite correct - we really need to remove */
6762 	/* the effects of the convolution with the CCD cell widths. */
6763 	/* perhaps it's closer to a lanczos2 if this were done ? */
6764 	{
6765 		for (i = 0; i < nwav1; i++) {
6766 			double cwl;		/* center wavelegth */
6767 			double weight = 0.0;
6768 
6769 			for (j = 0; j < (mtx_nocoef1[i]); j++) {
6770 				double w1, w2, cellw;
6771 				co pp;
6772 
6773 				/* Translate CCD cell boundaries index to wavelength */
6774 				pp.p[0] = (double)coeff[i][j].ix - 0.5;
6775 				raw2wav->interp(raw2wav, &pp);
6776 				w1 = pp.v[0];
6777 
6778 				pp.p[0] = (double)coeff[i][j].ix + 0.5;
6779 				raw2wav->interp(raw2wav, &pp);
6780 				w2 = pp.v[0];
6781 
6782 				cellw = fabs(w2 - w1);
6783 
6784 				cwl = XSPECT_WL(wl_short1, wl_long1, nwav1, i);
6785 
6786 				/* Translate CCD index to wavelength */
6787 				pp.p[0] = (double)coeff[i][j].ix;
6788 				raw2wav->interp(raw2wav, &pp);
6789 				fshape[ncp].wl = pp.v[0] - cwl;
6790 				fshape[ncp].we = coeff[i][j].we / (0.09 * cellw);
6791 				ncp++;
6792 			}
6793 		}
6794 
6795 		/* Now sort by wavelength */
6796 #define HEAP_COMPARE(A,B) (A.wl < B.wl)
6797 		HEAPSORT(munki_fs, fshape, ncp)
6798 #undef HEAP_COMPARE
6799 
6800 		/* Strip out leading zero's */
6801 		for (i = 0; i < ncp; i++) {
6802 			if (fshape[i].we != 0.0)
6803 				break;
6804 		}
6805 		if (i > 1 && i < ncp) {
6806 			memmove(&fshape[0], &fshape[i-1], sizeof(munki_fs) * (ncp - i + 1));
6807 			ncp = ncp - i + 1;
6808 			for (i = 0; i < ncp; i++) {
6809 				if (fshape[i].we != 0.0)
6810 					break;
6811 			}
6812 		}
6813 
6814 #ifdef HIGH_RES_PLOT
6815 		/* Plot the shape of the accumulated curve */
6816 		{
6817 			double *x1 = dvectorz(0, ncp-1);
6818 			double *y1 = dvectorz(0, ncp-1);
6819 
6820 			for (i = 0; i < ncp; i++) {
6821 				double x;
6822 				x1[i] = fshape[i].wl;
6823 				y1[i] = fshape[i].we;
6824 			}
6825 			if (ref)
6826 				printf("Shape of existing reflection sampling curve:\n");
6827 			else
6828 				printf("Shape of existing emission sampling curve:\n");
6829 			do_plot(x1, y1, NULL, NULL, ncp);
6830 
6831 			free_dvector(x1, 0, ncp-1);
6832 			free_dvector(y1, 0, ncp-1);
6833 		}
6834 #endif /* HIGH_RES_PLOT */
6835 	}
6836 #endif /* EXISTING_SHAPE */
6837 
6838 #ifdef HIGH_RES_DEBUG
6839 	/* Check that the filter sums to a constant */
6840 	{
6841 		double x, sum;
6842 
6843 		for (x = 0.0; x < 10.0; x += 0.2) {
6844 			sum = 0;
6845 			sum += lin_fshape(fshape, ncp, x - 30.0);
6846 			sum += lin_fshape(fshape, ncp, x - 20.0);
6847 			sum += lin_fshape(fshape, ncp, x - 10.0);
6848 			sum += lin_fshape(fshape, ncp, x -  0.0);
6849 			sum += lin_fshape(fshape, ncp, x + 10.0);
6850 			sum += lin_fshape(fshape, ncp, x + 20.0);
6851 			printf("Offset %f, sum %f\n",x, sum);
6852 		}
6853 	}
6854 #endif /* HIGH_RES_DEBUG */
6855 
6856 	{
6857 		double fshmax;				/* filter shape max wavelength from center */
6858 #define MXNOWL 200      /* Max hires bands */
6859 #define MXNOFC 32
6860 		munki_fc coeff2[MXNOWL][MXNOFC];	/* New filter cooefficients */
6861 		double twidth;
6862 
6863 		/* Construct a set of filters that uses more CCD values */
6864 		twidth = HIGHRES_WIDTH;
6865 
6866 		if (m->nwav2 > MXNOWL) {		/* Assert */
6867 			a1logw(p->log,"High res filter has too many bands\n");
6868 			return MUNKI_INT_ASSERT;
6869 		}
6870 
6871 #ifdef EXISTING_SHAPE		/* Else generate filter shape */
6872 		/* Cut the filter width by half, to conver from 10nm to 5nm spacing */
6873 		for (i = 0; i < ncp; i++)
6874 			fshape[i].wl *= twidth/10.0;
6875 		fshmax = -fshape[0].wl;		/* aximum extent needed around zero */
6876 		if (fshape[ncp-1].wl > fshmax)
6877 			fshmax = fshape[ncp-1].wl;
6878 #else
6879 		/* Use a crude means of determining width */
6880 		for (fshmax = 50.0; fshmax >= 0.0; fshmax -= 0.1) {
6881 			if (fabs(lanczos2(twidth, fshmax)) > 0.001) {
6882 				fshmax += 0.1;
6883 				break;
6884 			}
6885 		}
6886 		if (fshmax <= 0.0) {
6887 			a1logw(p->log,"munki: fshmax search failed\n");
6888 			return MUNKI_INT_ASSERT;
6889 		}
6890 #endif
6891 //		a1logd(p->log,1,"fshmax = %f\n",fshmax);
6892 
6893 #ifdef HIGH_RES_DEBUG
6894 		/* Check that the filter sums to a constant */
6895 		{
6896 			double x, sum;
6897 
6898 			for (x = 0.0; x < 5.0; x += 0.1) {
6899 				sum = 0;
6900 				sum += lin_fshape(fshape, ncp, x - 15.0);
6901 				sum += lin_fshape(fshape, ncp, x - 10.0);
6902 				sum += lin_fshape(fshape, ncp, x -  5.0);
6903 				sum += lin_fshape(fshape, ncp, x -  0.0);
6904 				sum += lin_fshape(fshape, ncp, x +  5.0);
6905 				sum += lin_fshape(fshape, ncp, x + 10.0);
6906 				printf("Offset %f, sum %f\n",x, sum);
6907 			}
6908 		}
6909 #endif /* HIGH_RES_DEBUG */
6910 
6911 		/* Create all the filters */
6912 		if ((*pmtx_nocoef2 = mtx_nocoef2 = (int *)calloc(m->nwav2, sizeof(int))) == NULL) {
6913 			a1logd(p->log,1,"munki: malloc mtx_nocoef2 failed!\n");
6914 			return MUNKI_INT_MALLOC;
6915 		}
6916 
6917 		/* For all the useful CCD bands */
6918 		for (i = 0; i < m->nraw; i++) {
6919 			co pp;
6920 			double w1, wl, w2;
6921 
6922 			/* Translate CCD center to to wavelength */
6923 			pp.p[0] = (double)i;
6924 			raw2wav->interp(raw2wav, &pp);
6925 			wl = pp.v[0];
6926 
6927 			/* Translate CCD cell boundaries index to wavelength */
6928 			pp.p[0] = i - 0.5;
6929 			raw2wav->interp(raw2wav, &pp);
6930 			w1 = pp.v[0];
6931 
6932 			pp.p[0] = i + 0.5;
6933 			raw2wav->interp(raw2wav, &pp);
6934 			w2 = pp.v[0];
6935 
6936 			a1logd(p->log,1,"CCD %d, wl %f - %f\n",i,w1,w2);
6937 
6938 			/* For each filter */
6939 			for (j = 0; j < m->nwav2; j++) {
6940 				double cwl, rwl;		/* center, relative wavelegth */
6941 				double we;
6942 
6943 				cwl = m->wl_short2 + (double)j * (m->wl_long2 - m->wl_short2)/(m->nwav2-1.0);
6944 
6945 				if (cwl < min_wl)		/* Duplicate below this wl */
6946 					cwl = min_wl;
6947 
6948 				rwl = wl - cwl;			/* relative wavelgth to filter */
6949 
6950 				if (fabs(w1 - cwl) > fshmax && fabs(w2 - cwl) > fshmax)
6951 					continue;		/* Doesn't fall into this filter */
6952 
6953 #ifdef BOX_INTEGRATE
6954 				/* Integrate in 0.05 nm increments from filter shape */
6955 				{
6956 					int nn;
6957 					double lw, ll;
6958 #ifdef FAST_HIGH_RES_SETUP
6959 # define FINC 0.2
6960 #else
6961 # define FINC 0.05
6962 #endif
6963 					nn = (int)(fabs(w2 - w1)/FINC + 0.5);
6964 
6965 					lw = w1;
6966 #ifdef EXISTING_SHAPE
6967 					ll = lin_fshape(fshape, ncp, w1- cwl);
6968 #else
6969 					ll = lanczos2(twidth, w1- cwl);
6970 #endif
6971 					we = 0.0;
6972 					for (k = 0; k < nn; k++) {
6973 						double cw, cl;
6974 
6975 #if defined(__APPLE__) && defined(__POWERPC__)
6976 						gcc_bug_fix(k);
6977 #endif
6978 						cw = w1 + (k+1.0)/(nn +1.0) * (w2 - w1);
6979 #ifdef EXISTING_SHAPE
6980 						cl = lin_fshape(fshape, ncp, cw - cwl);
6981 #else
6982 						cl = lanczos2(twidth, cw- cwl);
6983 #endif
6984 						we += 0.5 * (cl + ll) * (lw - cw);
6985 						ll = cl;
6986 						lw = cw;
6987 					}
6988 				}
6989 
6990 
6991 #else			/* Point sample with weighting */
6992 
6993 #ifdef EXISTING_SHAPE
6994 				we = fabs(w2 - w1) * lin_fshape(fshape, ncp, rwl);
6995 #else
6996 				we = fabs(w2 - w1) * lanczos2(twidth, rwl);
6997 #endif
6998 
6999 #endif			/* Integrate/Point sample */
7000 
7001 				if (mtx_nocoef2[j] >= MXNOFC) {
7002 					a1logw(p->log,"munki: run out of high res filter space\n");
7003 					return MUNKI_INT_ASSERT;
7004 				}
7005 
7006 				coeff2[j][mtx_nocoef2[j]].ix = i;
7007 				coeff2[j][mtx_nocoef2[j]++].we = we;
7008 				a1logd(p->log,1,"filter %d, cwl %f, rwl %f, ix %d, we %f\n",j,cwl,rwl,i,we);
7009 			}
7010 		}
7011 
7012 		/* Dump the filter coefficients */
7013 		if (p->log->debug >= 1) {
7014 
7015 			/* For each output wavelength */
7016 			for (j = 0; j < m->nwav2; j++) {
7017 
7018 				a1logd(p->log,1,"filter %d, cwl %f\n",j,XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, j));
7019 				/* For each matrix value */
7020 				for (k = 0; k < mtx_nocoef2[j]; k++) {
7021 					a1logd(p->log,1," CCD %d, we %f\n",coeff2[j][k].ix,coeff2[j][k].we);
7022 				}
7023 			}
7024 		}
7025 
7026 #ifdef HIGH_RES_PLOT
7027 		/* Plot resampled curves */
7028 		{
7029 			double *xx, *ss;
7030 			double **yy;
7031 
7032 			xx = dvectorz(0, m->nraw-1);		/* X index */
7033 			yy = dmatrixz(0, 5, 0, m->nraw-1);	/* Curves distributed amongst 5 graphs */
7034 
7035 			for (i = 0; i < m->nraw; i++)
7036 				xx[i] = i;
7037 
7038 			/* For each output wavelength */
7039 			for (j = 0; j < m->nwav2; j++) {
7040 				i = j % 5;
7041 
7042 				/* For each matrix value */
7043 				for (k = 0; k < mtx_nocoef2[j]; k++) {
7044 					yy[5][coeff2[j][k].ix] += 0.5 * coeff2[j][k].we;
7045 					yy[i][coeff2[j][k].ix] = coeff2[j][k].we;
7046 				}
7047 			}
7048 
7049 			if (ref)
7050 				printf("Hi-Res reflection wavelength sampling curves:\n");
7051 			else
7052 				printf("Hi-Res emission wavelength sampling curves:\n");
7053 			do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
7054 			free_dvector(xx, 0, m->nraw-1);
7055 			free_dmatrix(yy, 0, 2, 0, m->nraw-1);
7056 		}
7057 #endif /* HIGH_RES_PLOT */
7058 
7059 		/* Convert into runtime format */
7060 		{
7061 			int xcount;
7062 
7063 			if ((*pmtx_index2 = mtx_index2 = (int *)calloc(m->nwav2, sizeof(int))) == NULL) {
7064 				a1logd(p->log,1,"munki: malloc mtx_index2 failed!\n");
7065 				return MUNKI_INT_MALLOC;
7066 			}
7067 
7068 			xcount = 0;
7069 			for (j = 0; j < m->nwav2; j++) {
7070 				mtx_index2[j] = coeff2[j][0].ix;
7071 				xcount += mtx_nocoef2[j];
7072 			}
7073 
7074 			if ((*pmtx_coef2 = mtx_coef2 = (double *)calloc(xcount, sizeof(double))) == NULL) {
7075 				a1logd(p->log,1,"munki: malloc mtx_coef2 failed!\n");
7076 				return MUNKI_INT_MALLOC;
7077 			}
7078 
7079 			for (i = j = 0; j < m->nwav2; j++)
7080 				for (k = 0; k < mtx_nocoef2[j]; k++, i++)
7081 					mtx_coef2[i] = coeff2[j][k].we;
7082 		}
7083 
7084 		/* Normalise the filters area in CCD space, while maintaining the */
7085 		/* total contribution of each CCD at the target too. */
7086 		/* Hmm. This will wreck super-sample. We should fix it */
7087 #ifdef DO_CCDNORM			/* Normalise CCD values to original */
7088 		{
7089 			double x[4], y[4];
7090 			double avg[2], max[2];
7091 			double ccdsum[2][128];			/* Target weight/actual for each CCD */
7092 			double dth[2];
7093 
7094 			avg[0] = avg[1] = 0.0;
7095 			max[0] = max[1] = 0.0;
7096 			for (j = 0; j < 128; j++) {
7097 				ccdsum[0][j] = 0.0;
7098 				ccdsum[1][j] = 0.0;
7099 			}
7100 
7101 			/* Compute the weighting of each CCD value in the normal output */
7102 			for (cx = j = 0; j < m->nwav1; j++) { /* For each wavelength */
7103 
7104 				/* For each matrix value */
7105 				sx = mtx_index1[j];		/* Starting index */
7106 				if (j < (m->nwav1-2) && sx == mtx_index1[j+1]) {
7107 					cx += mtx_nocoef1[j];
7108 					continue;			/* Skip all duplicate filters */
7109 				}
7110 				for (k = 0; k < mtx_nocoef1[j]; k++, cx++, sx++) {
7111 					ccdsum[0][sx] += mtx_coef1[cx];
7112 //printf("~1 Norm CCD [%d] %f += [%d] %f\n",sx,ccdsum[0][sx],cx, mtx_coef1[cx]);
7113 				}
7114 			}
7115 
7116 			/* Compute the weighting of each CCD value in the hires output */
7117 			for (cx = j = 0; j < m->nwav2; j++) { /* For each wavelength */
7118 
7119 				/* For each matrix value */
7120 				sx = mtx_index2[j];		/* Starting index */
7121 				if (j < (m->nwav2-2) && sx == mtx_index2[j+1]) {
7122 					cx += mtx_nocoef2[j];
7123 					continue;			/* Skip all duplicate filters */
7124 				}
7125 				for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) {
7126 					ccdsum[1][sx] += mtx_coef2[cx];
7127 //printf("~1 HiRes CCD [%d] %f += [%d] %f\n",sx,ccdsum[1][sx],cx, mtx_coef2[cx]);
7128 				}
7129 			}
7130 
7131 #ifdef HIGH_RES_PLOT
7132 			/* Plot target CCD values */
7133 			{
7134 				double xx[128], y1[128], y2[128];
7135 
7136 				for (i = 0; i < 128; i++) {
7137 					xx[i] = i;
7138 					y1[i] = ccdsum[0][i];
7139 					y2[i] = ccdsum[1][i];
7140 				}
7141 
7142 				printf("Raw target and actual CCD weight sums:\n");
7143 				do_plot(xx, y1, y2, NULL, 128);
7144 			}
7145 #endif
7146 
7147 			/* Figure valid range and extrapolate to edges */
7148 			dth[0] = 0.0;		/* ref */
7149 			dth[1] = 0.007;		/* hires */
7150 
7151 			for (k = 0; k < 2; k++) {
7152 
7153 				for (i = 0; i < 128; i++) {
7154 					if (ccdsum[k][i] > max[k])
7155 						max[k] = ccdsum[k][i];
7156 				}
7157 
7158 //printf("~1 max[%d] = %f\n",k, max[k]);
7159 				/* Figure out the valid range */
7160 				for (i = 64; i >= 0; i--) {
7161 					if (ccdsum[k][i] > (0.8 * max[k])) {
7162 						x[0] = (double)i;
7163 					} else {
7164 						break;
7165 					}
7166 				}
7167 				for (i = 64; i < 128; i++) {
7168 					if (ccdsum[k][i] > (0.8 * max[k])) {
7169 						x[3] = (double)i;
7170 					} else {
7171 						break;
7172 					}
7173 				}
7174 				/* Space off the last couple of entries */
7175 				x[0] += 2.0;
7176 				x[3] -= 6.0;
7177 				x[1] = floor((2 * x[0] + x[3])/3.0);
7178 				x[2] = floor((x[0] + 2 * x[3])/3.0);
7179 
7180 				for (i = 0; i < 4; i++)
7181 					y[i] = ccdsum[k][(int)x[i]];
7182 
7183 //printf("~1 extrap nodes %f, %f, %f, %f\n",x[0],x[1],x[2],x[3]);
7184 //printf("~1 extrap value %f, %f, %f, %f\n",y[0],y[1],y[2],y[3]);
7185 
7186 				for (i = 0; i < 128; i++) {
7187 					double xw, yw;
7188 
7189 					xw = (double)i;
7190 
7191 					/* Compute interpolated value using Lagrange: */
7192 					yw = y[0] * (xw-x[1]) * (xw-x[2]) * (xw-x[3])
7193 						      /((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3]))
7194 					   + y[1] * (xw-x[0]) * (xw-x[2]) * (xw-x[3])
7195 						      /((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3]))
7196 					   + y[2] * (xw-x[0]) * (xw-x[1]) * (xw-x[3])
7197 						      /((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3]))
7198 					   + y[3] * (xw-x[0]) * (xw-x[1]) * (xw-x[2])
7199 						      /((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2]));
7200 
7201 					if ((xw < x[0] || xw > x[3])
7202 					 && fabs(ccdsum[k][i] - yw)/yw > dth[k]) {
7203 						ccdsum[k][i] = yw;
7204 					}
7205 					avg[k] += ccdsum[k][i];
7206 				}
7207 				avg[k] /= 128.0;
7208 			}
7209 
7210 #ifdef HIGH_RES_PLOT
7211 			/* Plot target CCD values */
7212 			{
7213 				double xx[129], y1[129], y2[129];
7214 
7215 				for (i = 0; i < 128; i++) {
7216 					xx[i] = i;
7217 					y1[i] = ccdsum[0][i]/avg[0];
7218 					y2[i] = ccdsum[1][i]/avg[1];
7219 				}
7220 				xx[i] = i;
7221 				y1[i] = 0.0;
7222 				y2[i] = 0.0;
7223 
7224 				printf("Extrap. target and actual CCD weight sums:\n");
7225 				do_plot(xx, y1, y2, NULL, 129);
7226 			}
7227 #endif
7228 
7229 #ifdef DO_CCDNORMAVG	/* Just correct by average */
7230 			for (cx = j = 0; j < m->nwav2; j++) { /* For each wavelength */
7231 
7232 				/* For each matrix value */
7233 				sx = mtx_index2[j];		/* Starting index */
7234 				for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) {
7235 					mtx_coef2[cx] *= 10.0/twidth * avg[0]/avg[1];
7236 				}
7237 			}
7238 
7239 #else			/* Correct by CCD bin */
7240 
7241 			/* Correct the weighting of each CCD value in the hires output */
7242 			for (i = 0; i < 128; i++) {
7243 				ccdsum[1][i] = 10.0/twidth * ccdsum[0][i]/ccdsum[1][i];		/* Correction factor */
7244 			}
7245 			for (cx = j = 0; j < m->nwav2; j++) { /* For each wavelength */
7246 
7247 				/* For each matrix value */
7248 				sx = mtx_index2[j];		/* Starting index */
7249 				for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) {
7250 					mtx_coef2[cx] *= ccdsum[1][sx];
7251 				}
7252 			}
7253 #endif
7254 		}
7255 #endif /* DO_CCDNORM */
7256 
7257 #ifdef HIGH_RES_PLOT
7258 		{
7259 			static munki_fc coeff2[MXNOWL][MXNOFC];
7260 			double *xx, *ss;
7261 			double **yy;
7262 
7263 			/* Convert the native filter cooeficient representation to */
7264 			/* a 2D array we can randomly index. */
7265 			for (cx = j = 0; j < m->nwav2; j++) { /* For each output wavelength */
7266 				if (j >= MXNOWL) {	/* Assert */
7267 					a1loge(p->log,1,"munki: number of hires output wavelenths is > %d\n",MXNOWL);
7268 					return MUNKI_INT_ASSERT;
7269 				}
7270 
7271 				/* For each matrix value */
7272 				sx = mtx_index2[j];		/* Starting index */
7273 				for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) {
7274 					if (k >= MXNOFC) {	/* Assert */
7275 						a1loge(p->log,1,"munki: number of hires filter coeefs is > %d\n",MXNOFC);
7276 						return MUNKI_INT_ASSERT;
7277 					}
7278 					coeff2[j][k].ix = sx;
7279 					coeff2[j][k].we = mtx_coef2[cx];
7280 				}
7281 			}
7282 
7283 			xx = dvectorz(-1, m->nraw-1);		/* X index */
7284 			yy = dmatrixz(0, 5, -1, m->nraw-1);	/* Curves distributed amongst 5 graphs */
7285 
7286 			for (i = 0; i < m->nraw; i++)
7287 				xx[i] = i;
7288 
7289 			/* For each output wavelength */
7290 			for (j = 0; j < m->nwav2; j++) {
7291 				i = j % 5;
7292 
7293 				/* For each matrix value */
7294 				for (k = 0; k < mtx_nocoef2[j]; k++) {
7295 					yy[5][coeff2[j][k].ix] += 0.5 * coeff2[j][k].we;
7296 					yy[i][coeff2[j][k].ix] = coeff2[j][k].we;
7297 				}
7298 			}
7299 
7300 			printf("Normalized Hi-Res wavelength sampling curves: %s\n",ref ? "refl" : "emis");
7301 			do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
7302 			free_dvector(xx, -1, m->nraw-1);
7303 			free_dmatrix(yy, 0, 2, -1, m->nraw-1);
7304 		}
7305 #endif /* HIGH_RES_PLOT */
7306 #undef MXNOWL
7307 #undef MXNOFC
7308 
7309 		/* Basic capability is initialised */
7310 		m->hr_inited++;
7311 
7312 		/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
7313 		/* If both reflective and emissive samplings have been created, */
7314 		/* deal with upsampling the references and calibrations */
7315 		if (m->hr_inited == 2) {
7316 #ifdef ONEDSTRAYLIGHTUS
7317 			double **slp;			/* 2D Array of stray light values */
7318 #endif /* !ONEDSTRAYLIGHTUS */
7319 			rspl *trspl;			/* Upsample rspl */
7320 			cow sd[40 * 40];		/* Scattered data points of existing references */
7321 			datai glow, ghigh;
7322 			datao vlow, vhigh;
7323 			int gres[2];
7324 			double avgdev[2];
7325 			int ii, jj;
7326 			co pp;
7327 
7328 			/* First the 1D references */
7329 			if ((trspl = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) {
7330 				a1logd(p->log,3,"munki: creating rspl for high res conversion failed\n");
7331 				raw2wav->del(raw2wav);
7332 				return MUNKI_INT_NEW_RSPL_FAILED;
7333 			}
7334 
7335 			for (ii = 0; ii < 4; ii++) {
7336 				double **ref2, *ref1;
7337 
7338 				if (ii == 0) {
7339 					ref1 = m->white_ref1;
7340 					ref2 = &m->white_ref2;
7341 				} else if (ii == 1) {
7342 					ref1 = m->emis_coef1;
7343 					ref2 = &m->emis_coef2;
7344 				} else if (ii == 2) {
7345 					ref1 = m->amb_coef1;
7346 					ref2 = &m->amb_coef2;
7347 				} else {
7348 					ref1 = m->proj_coef1;
7349 					ref2 = &m->proj_coef2;
7350 				}
7351 
7352 				vlow[0] = 1e6;
7353 				vhigh[0] = -1e6;
7354 
7355 				/* Set scattered points */
7356 				for (i = 0; i < m->nwav1; i++) {
7357 
7358 					sd[i].p[0] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
7359 					sd[i].v[0] = ref1[i];
7360 					sd[i].w = 1.0;
7361 
7362 					if (sd[i].v[0] < vlow[0])
7363 						vlow[0] = sd[i].v[0];
7364 					if (sd[i].v[0] > vhigh[0])
7365 						vhigh[0] = sd[i].v[0];
7366 				}
7367 
7368 #ifdef NEVER
7369 				/* Add some corrections at short wavelengths */
7370 				/* (The combination of the diffraction grating and */
7371 				/*  LED light source doesn't give us much to work with here.) */
7372 				if (ii == 1) {	/* Emission */
7373 					sd[0].v[0] *= 10.0;		/* 380 */
7374 					sd[1].v[0] *= 3.0;		/* 390 */
7375 					sd[2].v[0] *= 1.0;		/* 400 */
7376 				}
7377 
7378 				if (ii == 2) {	/* Ambient */
7379 					sd[0].v[0] *= 5.0;		/* 380 */		/* Doesn't help much, because */
7380 					sd[1].v[0] *= 2.0;		/* 390 */		/* the diffuser absorbs short WL */
7381 					sd[2].v[0] *= 1.0;		/* 400 */
7382 				}
7383 
7384 				if (ii == 3) {	/* Projector */
7385 					sd[0].v[0] *= 0.1;		/* 380 */
7386 					sd[1].v[0] *= 1.0;		/* 390 */
7387 
7388 					sd[i].p[0] = 350.0; 	/* 350 */
7389 					sd[i].v[0] = 0.2 * sd[0].v[0];
7390 					sd[i++].w = 1.0;
7391 				}
7392 #endif
7393 
7394 				glow[0] = m->wl_short2;
7395 				ghigh[0] = m->wl_long2;
7396 				gres[0] = m->nwav2;
7397 				avgdev[0] = 0.0;
7398 
7399 				trspl->fit_rspl_w(trspl, 0, sd, i, glow, ghigh, gres, vlow, vhigh, 5.0, avgdev, NULL);
7400 
7401 				if ((*ref2 = (double *)calloc(m->nwav2, sizeof(double))) == NULL) {
7402 					raw2wav->del(raw2wav);
7403 					trspl->del(trspl);
7404 					a1logd(p->log,1,"munki: malloc mtx_coef2 failed!\n");
7405 					return MUNKI_INT_MALLOC;
7406 				}
7407 
7408 				/* Create upsampled version */
7409 				for (i = 0; i < m->nwav2; i++) {
7410 					pp.p[0] = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
7411 					if (pp.p[0] < min_wl)	/* Duplicate below this wl */
7412 						pp.p[0] = min_wl;
7413 					trspl->interp(trspl, &pp);
7414 					if (pp.v[0] < 0.0)
7415 						pp.v[0] = 0.0;
7416 					(*ref2)[i] = pp.v[0];
7417 				}
7418 
7419 
7420 #ifdef NEVER
7421 				/* Add some corrections at short wavelengths */
7422 				if (ii == 0) {
7423 					/* 376.67 - 470 */
7424 					double corr[5][29] = {
7425 						{ 4.2413, 4.0654, 3.6425, 3.2194, 2.8692, 2.3964,
7426 						  1.9678, 1.3527, 0.7978, 0.7823, 0.8474, 0.9227,
7427 						   0.9833, 1.0164, 1.0270, 1.0241, 1.0157, 1.0096,
7428 						   1.0060, 1.0, 1.0, 1.0, 1.0, 1.0,
7429 						   1.0, 1.0, 1.0, 1.0, 1.0 },
7430 
7431 					};
7432 
7433 					for (i = 0; i < m->nwav2; i++) {
7434 						double wl;
7435 						int ix;
7436 						wl = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
7437 						ix = XSPECT_IX(376.6666667, 470.0, 29, wl);
7438 
7439 						if (ix < 0)
7440 							ix = 0;
7441 						else if (ix >= 29)
7442 							ix = 28;
7443 						(*ref2)[i] *= corr[ii][ix];
7444 					}
7445 
7446 				}
7447 #endif
7448 
7449 #ifdef HIGH_RES_PLOT
7450 				/* Plot original and upsampled reference */
7451 				{
7452 					double *x1 = dvectorz(0, m->nwav2-1);
7453 					double *y1 = dvectorz(0, m->nwav2-1);
7454 					double *y2 = dvectorz(0, m->nwav2-1);
7455 
7456 					for (i = 0; i < m->nwav2; i++) {
7457 						double wl = m->wl_short2 + (double)i * (m->wl_long2 - m->wl_short2)/(m->nwav2-1.0);
7458 						x1[i] = wl;
7459 						y1[i] = (*ref2)[i];
7460 						if (wl < m->wl_short1 || wl > m->wl_long1) {
7461 							y2[i] = 0.0;
7462 						} else {
7463 							double x, wl1, wl2;
7464 							for (j = 0; j < (m->nwav1-1); j++) {
7465 								wl1 = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, j);
7466 								wl2 = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, j+1);
7467 								if (wl >= wl1 && wl <= wl2)
7468 									break;
7469 							}
7470 							x = (wl - wl1)/(wl2 - wl1);
7471 							y2[i] = ref1[j] + (ref1[j+1] - ref1[j]) * x;
7472 						}
7473 					}
7474 					printf("Original and up-sampled ");
7475 					if (ii == 0) {
7476 						printf("Reflective cal. curve:\n");
7477 					} else if (ii == 1) {
7478 						printf("Emission cal. curve:\n");
7479 					} else if (ii == 2) {
7480 						printf("Ambient cal. curve:\n");
7481 					} else {
7482 						printf("Projector cal. curve:\n");
7483 					}
7484 					do_plot(x1, y1, y2, NULL, m->nwav2);
7485 
7486 					free_dvector(x1, 0, m->nwav2-1);
7487 					free_dvector(y1, 0, m->nwav2-1);
7488 					free_dvector(y2, 0, m->nwav2-1);
7489 				}
7490 #endif /* HIGH_RES_PLOT */
7491 			}
7492 			trspl->del(trspl);
7493 
7494 #ifdef ONEDSTRAYLIGHTUS
7495 			/* Then the 2D stray light using linear interpolation */
7496 			slp = dmatrix(0, m->nwav1-1, 0, m->nwav1-1);
7497 
7498 			/* Set scattered points */
7499 			for (i = 0; i < m->nwav1; i++) {		/* Output wavelength */
7500 				for (j = 0; j < m->nwav1; j++) {	/* Input wavelength */
7501 
7502 					slp[i][j] = m->straylight1[i][j];
7503 
7504 					/* Use interpolate/extrapolate for middle points */
7505 					if (j == (i-1) || j == i || j == (i+1)) {
7506 						int j0, j1;
7507 						double w0, w1;
7508 						if (j == (i-1)) {
7509 							if (j <= 0)
7510 								j0 = j+3, j1 = j+4;
7511 							else if (j >= (m->nwav1-3))
7512 								j0 = j-2, j1 = j-1;
7513 							else
7514 								j0 = j-1, j1 = j+3;
7515 						} else if (j == i) {
7516 							if (j <= 1)
7517 								j0 = j+2, j1 = j+3;
7518 							else if (j >= (m->nwav1-2))
7519 								j0 = j-3, j1 = j-2;
7520 							else
7521 								j0 = j-2, j1 = j+2;
7522 						} else if (j == (i+1)) {
7523 							if (j <= 2)
7524 								j0 = j+1, j1 = j+2;
7525 							else if (j >= (m->nwav1-1))
7526 								j0 = j-4, j1 = j-3;
7527 							else
7528 								j0 = j-3, j1 = j+1;
7529 						}
7530 						w1 = (j - j0)/(j1 - j0);
7531 						w0 = 1.0 - w1;
7532 						slp[i][j] = w0 * m->straylight1[i][j0]
7533 						          + w1 * m->straylight1[i][j1];
7534 
7535 					}
7536 				}
7537 			}
7538 #else	/* !ONEDSTRAYLIGHTUS */
7539 			/* Then setup 2D stray light using rspl */
7540 			if ((trspl = new_rspl(RSPL_NOFLAGS, 2, 1)) == NULL) {
7541 				a1logd(p->log,3,"munki: creating rspl for high res conversion failed\n");
7542 				raw2wav->del(raw2wav);
7543 				return MUNKI_INT_NEW_RSPL_FAILED;
7544 			}
7545 
7546 			/* Set scattered points */
7547 			for (i = 0; i < m->nwav1; i++) {		/* Output wavelength */
7548 				for (j = 0; j < m->nwav1; j++) {	/* Input wavelength */
7549 					int ix = i * m->nwav1 + j;
7550 
7551 					sd[ix].p[0] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
7552 					sd[ix].p[1] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, j);
7553 					sd[ix].v[0] = m->straylight1[i][j];
7554 					sd[ix].w = 1.0;
7555 					if (j == (i-1) || j == i || j == (i+1))
7556 						sd[ix].w = 0.0;
7557 				}
7558 			}
7559 
7560 			glow[0] = m->wl_short2;
7561 			glow[1] = m->wl_short2;
7562 			ghigh[0] = m->wl_long2;
7563 			ghigh[1] = m->wl_long2;
7564 			gres[0] = m->nwav2;
7565 			gres[1] = m->nwav2;
7566 			avgdev[0] = 0.0;
7567 			avgdev[1] = 0.0;
7568 
7569 			trspl->fit_rspl_w(trspl, 0, sd, m->nwav1 * m->nwav1, glow, ghigh, gres, NULL, NULL, 0.5, avgdev, NULL);
7570 #endif	/* !ONEDSTRAYLIGHTUS */
7571 
7572 			m->straylight2 = dmatrixz(0, m->nwav2-1, 0, m->nwav2-1);
7573 
7574 			/* Create upsampled version */
7575 			for (i = 0; i < m->nwav2; i++) {		/* Output wavelength */
7576 				for (j = 0; j < m->nwav2; j++) {	/* Input wavelength */
7577 					double p0, p1;
7578 					p0 = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
7579 					if (p0 < min_wl)	/* Duplicate below this wl */
7580 						p0 = min_wl;
7581 					p1 = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, j);
7582 					if (p1 < min_wl)	/* Duplicate below this wl */
7583 						p1 = min_wl;
7584 #ifdef ONEDSTRAYLIGHTUS
7585 					/* Do linear interp with clipping at ends */
7586 					{
7587 						int x0, x1, y0, y1;
7588 						double xx, yy, w0, w1, v0, v1;
7589 
7590 						xx = (m->nwav1-1.0) * (p0 - m->wl_short1)/(m->wl_long1 - m->wl_short1);
7591 						x0 = (int)floor(xx);
7592 						if (x0 <= 0)
7593 							x0 = 0;
7594 						else if (x0 >= (m->nwav1-2))
7595 							x0 = m->nwav1-2;
7596 						x1 = x0 + 1;
7597 						w1 = xx - (double)x0;
7598 						w0 = 1.0 - w1;
7599 
7600 						yy = (m->nwav1-1.0) * (p1 - m->wl_short1)/(m->wl_long1 - m->wl_short1);
7601 						y0 = (int)floor(yy);
7602 						if (y0 <= 0)
7603 							y0 = 0;
7604 						else if (y0 >= (m->nwav1-2))
7605 							y0 = m->nwav1-2;
7606 						y1 = y0 + 1;
7607 						v1 = yy - (double)y0;
7608 						v0 = 1.0 - v1;
7609 
7610 						pp.v[0] = w0 * v0 * slp[x0][y0]
7611 						        + w0 * v1 * slp[x0][y1]
7612 						        + w1 * v0 * slp[x1][y0]
7613 						        + w1 * v1 * slp[x1][y1];
7614 					}
7615 #else /* !ONEDSTRAYLIGHTUS */
7616 					pp.p[0] = p0;
7617 					pp.p[1] = p1;
7618 					trspl->interp(trspl, &pp);
7619 #endif /* !ONEDSTRAYLIGHTUS */
7620 					m->straylight2[i][j] = pp.v[0] * HIGHRES_WIDTH/10.0;
7621 					if (m->straylight2[i][j] > 0.0)
7622 						m->straylight2[i][j] = 0.0;
7623 				}
7624 			}
7625 
7626 			/* Fix primary wavelength weight and neighbors */
7627 			for (i = 0; i < m->nwav2; i++) {		/* Output wavelength */
7628 				double sum;
7629 
7630 				if (i > 0)
7631 					m->straylight2[i][i-1] = 0.0;
7632 				m->straylight2[i][i] = 0.0;
7633 				if (i < (m->nwav2-1))
7634 					m->straylight2[i][i+1] = 0.0;
7635 
7636 				for (sum = 0.0, j = 0; j < m->nwav2; j++)
7637 					sum += m->straylight2[i][j];
7638 
7639 				m->straylight2[i][i] = 1.0 - sum;		/* Total sum should be 1.0 */
7640 			}
7641 
7642 #ifdef HIGH_RES_PLOT_STRAYL
7643 			/* Plot original and upsampled reference */
7644 			{
7645 				double *x1 = dvectorz(0, m->nwav2-1);
7646 				double *y1 = dvectorz(0, m->nwav2-1);
7647 				double *y2 = dvectorz(0, m->nwav2-1);
7648 
7649 				for (i = 0; i < m->nwav2; i++) {		/* Output wavelength */
7650 					double wli = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
7651 					int i1 = XSPECT_IX(m->wl_short1, m->wl_long1, m->nwav1, wli);
7652 
7653 					for (j = 0; j < m->nwav2; j++) {
7654 						double wl = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, j);
7655 						x1[j] = wl;
7656 						y1[j] = m->straylight2[i][j];
7657 						if (y1[j] == 0.0)
7658 							y1[j] = -8.0;
7659 						else
7660 							y1[j] = log10(fabs(y1[j]));
7661 						if (wli < m->wl_short1 || wli > m->wl_long1
7662 						 || wl < m->wl_short1 || wl > m->wl_long1) {
7663 							y2[j] = -8.0;
7664 						} else {
7665 							double x, wl1, wl2;
7666 							for (k = 0; k < (m->nwav1-1); k++) {
7667 								wl1 = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, k);
7668 								wl2 = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, k+1);
7669 								if (wl >= wl1 && wl <= wl2)
7670 									break;
7671 							}
7672 							x = (wl - wl1)/(wl2 - wl1);
7673 							y2[j] = m->straylight1[i1][k] + (m->straylight1[i1][k+1]
7674 							      - m->straylight1[i1][k]) * x;
7675 							if (y2[j] == 0.0)
7676 								y2[j] = -8.0;
7677 							else
7678 								y2[j] = log10(fabs(y2[j]));
7679 						}
7680 					}
7681 					do_plot(x1, y1, y2, NULL, m->nwav2);
7682 				}
7683 
7684 				free_dvector(x1, 0, m->nwav2-1);
7685 				free_dvector(y1, 0, m->nwav2-1);
7686 				free_dvector(y2, 0, m->nwav2-1);
7687 			}
7688 #endif /* HIGH_RES_PLOT */
7689 
7690 #ifdef ONEDSTRAYLIGHTUS
7691 			free_dmatrix(slp, 0, m->nwav1-1, 0, m->nwav1-1);
7692 #else /* !ONEDSTRAYLIGHTUS */
7693 			trspl->del(trspl);
7694 #endif /* !ONEDSTRAYLIGHTUS */
7695 
7696 			/* - - - - - - - - - - - - - - - - - - - - - - - - - */
7697 			/* Allocate space for per mode calibration reference */
7698 			/* and bring high res calibration factors into line */
7699 			/* with current standard res. ones */
7700 			for (i = 0; i < mk_no_modes; i++) {
7701 				munki_state *s = &m->ms[i];
7702 
7703 				s->cal_factor2 = dvectorz(0, m->nwav2-1);
7704 
7705 				switch(i) {
7706 					case mk_refl_spot:
7707 					case mk_refl_scan:
7708 						if (s->cal_valid) {
7709 							munki_absraw_to_abswav1(p, 1, &s->cal_factor1, &s->white_data);
7710 							munki_absraw_to_abswav2(p, 1, &s->cal_factor2, &s->white_data);
7711 							munki_compute_white_cal(p, s->cal_factor1, m->white_ref1, s->cal_factor1,
7712 							                           s->cal_factor2, m->white_ref2, s->cal_factor2);
7713 						}
7714 						break;
7715 
7716 					case mk_emiss_spot_na:
7717 					case mk_emiss_spot:
7718 					case mk_emiss_scan:
7719 						for (j = 0; j < m->nwav2; j++)
7720 							s->cal_factor2[j] = EMIS_SCALE_FACTOR * m->emis_coef2[j];
7721 						break;
7722 
7723 					case mk_tele_spot_na:
7724 					case mk_tele_spot:
7725 						for (j = 0; j < m->nwav2; j++)
7726 							s->cal_factor2[j] = EMIS_SCALE_FACTOR * m->proj_coef2[j];
7727 						break;
7728 
7729 					case mk_amb_spot:
7730 					case mk_amb_flash:
7731 						if (m->amb_coef1 != NULL) {
7732 							for (j = 0; j < m->nwav2; j++)
7733 								s->cal_factor2[j] = AMB_SCALE_FACTOR * m->amb_coef2[j];
7734 							s->cal_valid = 1;
7735 						}
7736 						break;
7737 					case mk_trans_spot:
7738 					case mk_trans_scan:
7739 						if (s->cal_valid) {
7740 							munki_absraw_to_abswav1(p, 1, &s->cal_factor1, &s->white_data);
7741 							munki_absraw_to_abswav2(p, 1, &s->cal_factor2, &s->white_data);
7742 							munki_compute_white_cal(p, s->cal_factor1, NULL, s->cal_factor1,
7743 				                                       s->cal_factor2, NULL, s->cal_factor2);
7744 						}
7745 						break;
7746 				}
7747 			}
7748 		}
7749 	}
7750 
7751 	raw2wav->del(raw2wav);
7752 
7753 	return ev;
7754 }
7755 
7756 #endif /* HIGH_RES */
7757 
7758 
7759 /* return nz if high res is supported */
munki_imp_highres(munki * p)7760 int munki_imp_highres(munki *p) {
7761 #ifdef HIGH_RES
7762 	return 1;
7763 #else
7764 	return 0;
7765 #endif /* HIGH_RES */
7766 }
7767 
7768 /* Set to high resolution mode */
munki_set_highres(munki * p)7769 munki_code munki_set_highres(munki *p) {
7770 	int i;
7771 	munkiimp *m = (munkiimp *)p->m;
7772 	munki_code ev = MUNKI_OK;
7773 
7774 #ifdef HIGH_RES
7775 	if (m->hr_inited == 0) {
7776 		if ((ev = munki_create_hr(p, 1)) != MUNKI_OK)	/* Reflective */
7777 			return ev;
7778 		if ((ev = munki_create_hr(p, 0)) != MUNKI_OK)	/* Emissive */
7779 			return ev;
7780 	}
7781 
7782 	m->nwav = m->nwav2;
7783 	m->wl_short = m->wl_short2;
7784 	m->wl_long = m->wl_long2;
7785 
7786 	m->rmtx_index = m->rmtx_index2;
7787 	m->rmtx_nocoef = m->rmtx_nocoef2;
7788 	m->rmtx_coef = m->rmtx_coef2;
7789 	m->emtx_index = m->emtx_index2;
7790 	m->emtx_nocoef = m->emtx_nocoef2;
7791 	m->emtx_coef = m->emtx_coef2;
7792 	m->white_ref = m->white_ref2;
7793 	m->emis_coef = m->emis_coef2;
7794 	m->amb_coef = m->amb_coef2;
7795 	m->proj_coef = m->proj_coef2;
7796 	m->straylight = m->straylight2;
7797 
7798 	for (i = 0; i < mk_no_modes; i++) {
7799 		munki_state *s = &m->ms[i];
7800 		s->cal_factor = s->cal_factor2;
7801 	}
7802 	m->highres = 1;
7803 #else
7804 	ev = MUNKI_UNSUPPORTED;
7805 #endif /* HIGH_RES */
7806 
7807 	return ev;
7808 }
7809 
7810 /* Set to standard resolution mode */
munki_set_stdres(munki * p)7811 munki_code munki_set_stdres(munki *p) {
7812 	int i;
7813 	munkiimp *m = (munkiimp *)p->m;
7814 	munki_code ev = MUNKI_OK;
7815 
7816 #ifdef HIGH_RES
7817 	m->nwav = m->nwav1;
7818 	m->wl_short = m->wl_short1;
7819 	m->wl_long = m->wl_long1;
7820 
7821 	m->rmtx_index = m->rmtx_index1;
7822 	m->rmtx_nocoef = m->rmtx_nocoef1;
7823 	m->rmtx_coef = m->rmtx_coef1;
7824 	m->emtx_index = m->emtx_index1;
7825 	m->emtx_nocoef = m->emtx_nocoef1;
7826 	m->emtx_coef = m->emtx_coef1;
7827 	m->white_ref = m->white_ref1;
7828 	m->emis_coef = m->emis_coef1;
7829 	m->amb_coef = m->amb_coef1;
7830 	m->proj_coef = m->proj_coef1;
7831 	m->straylight = m->straylight1;
7832 
7833 	for (i = 0; i < mk_no_modes; i++) {
7834 		munki_state *s = &m->ms[i];
7835 		s->cal_factor = s->cal_factor1;
7836 	}
7837 	m->highres = 0;
7838 
7839 #else
7840 	ev = MUNKI_UNSUPPORTED;
7841 #endif /* HIGH_RES */
7842 
7843 	return ev;
7844 }
7845 
7846 /* Modify the scan consistency tolerance */
munki_set_scan_toll(munki * p,double toll_ratio)7847 munki_code munki_set_scan_toll(munki *p, double toll_ratio) {
7848 	munkiimp *m = (munkiimp *)p->m;
7849 	munki_code ev = MUNKI_OK;
7850 
7851 	m->scan_toll_ratio = toll_ratio;
7852 
7853 	return MUNKI_OK;
7854 }
7855 
7856 /* Optical adjustment weights */
7857 static double opt_adj_weights[21] = {
7858 	1.4944496665144658e-282, 2.0036175483913455e-070, 1.2554893022685038e+232,
7859 	2.3898157055642966e+190, 1.5697625128432372e-076, 6.6912978722191457e+281,
7860 	1.2369092402930559e+277, 1.4430907501246712e-153, 3.0017439193018232e+238,
7861 	1.2978311824382444e+161, 5.5068703318775818e-311, 7.7791723264455314e-260,
7862 	6.4560484084110176e+170, 8.9481529920968425e+165, 1.3565405878488529e-153,
7863 	2.0835868791190880e-076, 5.4310198502711138e+241, 4.8689849775675438e+275,
7864 	9.2709981544886391e+122, 3.7958270103353899e-153, 7.1366083837501666e-154
7865 };
7866 
7867 /* Convert from spectral to XYZ, and transfer to the ipatch array */
munki_conv2XYZ(munki * p,ipatch * vals,int nvals,double ** specrd,instClamping clamp)7868 munki_code munki_conv2XYZ(
7869 	munki *p,
7870 	ipatch *vals,		/* Values to return */
7871 	int nvals,			/* Number of values */
7872 	double **specrd,	/* Spectral readings */
7873 	instClamping clamp	/* Clamp XYZ/Lab to be +ve */
7874 ) {
7875 	munkiimp *m = (munkiimp *)p->m;
7876 	munki_state *s = &m->ms[m->mmode];
7877 	xsp2cie *conv;	/* Spectral to XYZ conversion object */
7878 	int i, j, k;
7879 	int six = 0;		/* Starting index */
7880 	int nwl = m->nwav;	/* Number of wavelegths */
7881 	double wl_short = m->wl_short;	/* Starting wavelegth */
7882 	double sms;			/* Weighting */
7883 
7884 	if (s->emiss)
7885 		conv = new_xsp2cie(icxIT_none, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, (icxClamping)clamp);
7886 	else
7887 		conv = new_xsp2cie(icxIT_D50, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, (icxClamping)clamp);
7888 	if (conv == NULL)
7889 		return MUNKI_INT_CIECONVFAIL;
7890 
7891 	a1logd(p->log,3,"munki_conv2XYZ got wl_short %f, wl_long %f, nwav %d\n"
7892 	                "      after skip got wl_short %f, nwl = %d\n",
7893 	                m->wl_short, m->wl_long, m->nwav, wl_short, nwl);
7894 
7895 	for (sms = 0.0, i = 1; i < 21; i++)
7896 		sms += opt_adj_weights[i];
7897 	sms *= opt_adj_weights[0];
7898 
7899 	for (i = 0; i < nvals; i++) {
7900 
7901 		vals[i].loc[0] = '\000';
7902 		vals[i].mtype = inst_mrt_none;
7903 		vals[i].XYZ_v = 0;
7904 		vals[i].sp.spec_n = 0;
7905 		vals[i].duration = 0.0;
7906 
7907 		vals[i].sp.spec_n = nwl;
7908 		vals[i].sp.spec_wl_short = wl_short;
7909 		vals[i].sp.spec_wl_long = m->wl_long;
7910 
7911 		if (s->emiss) {
7912 			for (j = six, k = 0; j < m->nwav; j++, k++) {
7913 				vals[i].sp.spec[k] = specrd[i][j] * sms;
7914 			}
7915 			vals[i].sp.norm = 1.0;
7916 
7917 			/* Set the XYZ */
7918 			conv->convert(conv, vals[i].XYZ, &vals[i].sp);
7919 			vals[i].XYZ_v = 1;
7920 
7921 			if (s->ambient) {
7922 				if (s->flash)
7923 					vals[i].mtype = inst_mrt_ambient_flash;
7924 				else
7925 					vals[i].mtype = inst_mrt_ambient;
7926 			} else {
7927 				if (s->flash)
7928 					vals[i].mtype = inst_mrt_emission_flash;
7929 				else
7930 					vals[i].mtype = inst_mrt_emission;
7931 			}
7932 
7933 		} else {
7934 			for (j = six, k = 0; j < m->nwav; j++, k++) {
7935 				vals[i].sp.spec[k] = 100.0 * specrd[i][j] * sms;
7936 			}
7937 			vals[i].sp.norm = 100.0;
7938 
7939 			/* Set the XYZ */
7940 			conv->convert(conv, vals[i].XYZ, &vals[i].sp);
7941 			vals[i].XYZ_v = 1;
7942 			vals[i].XYZ[0] *= 100.0;
7943 			vals[i].XYZ[1] *= 100.0;
7944 			vals[i].XYZ[2] *= 100.0;
7945 
7946 			if (s->trans)
7947 				vals[i].mtype = inst_mrt_transmissive;
7948 			else
7949 				vals[i].mtype = inst_mrt_reflective;
7950 		}
7951 
7952 		/* Don't return spectral if not asked for */
7953 		if (!m->spec_en) {
7954 			vals[i].sp.spec_n = 0;
7955 		}
7956 	}
7957 
7958 	conv->del(conv);
7959 
7960 	/* Apply any XRGA conversion */
7961 	ipatch_convert_xrga(vals, nvals, xcalstd_nonpol, m->target_calstd, m->native_calstd, clamp);
7962 
7963 
7964 	return MUNKI_OK;
7965 }
7966 
7967 /* Compute a mode calibration factor given the reading of the white reference. */
7968 /* Return 1 if any of the transmission wavelengths are low. */
munki_compute_white_cal(munki * p,double * cal_factor1,double * white_ref1,double * white_read1,double * cal_factor2,double * white_ref2,double * white_read2)7969 int munki_compute_white_cal(
7970 	munki *p,
7971 	double *cal_factor1,	/* [nwav1] Calibration factor to compute */
7972 	double *white_ref1,		/* [nwav1] White reference to aim for, NULL for 1.0 */
7973 	double *white_read1,	/* [nwav1] The white that was read */
7974 	double *cal_factor2,	/* [nwav2] Calibration factor to compute */
7975 	double *white_ref2,		/* [nwav2] White reference to aim for, NULL for 1.0 */
7976 	double *white_read2		/* [nwav2] The white that was read */
7977 ) {
7978 	munkiimp *m = (munkiimp *)p->m;
7979 	munki_state *s = &m->ms[m->mmode];
7980 	int j, warn = 0;
7981 
7982 	a1logd(p->log,3,"munki_compute_white_cal called\n");
7983 
7984 	if (white_ref1 == NULL) {		/* transmission white reference */
7985 		double avgwh = 0.0;
7986 
7987 		/* Compute average white reference reading */
7988 		for (j = 0; j < m->nwav1; j++)
7989 			avgwh += white_read1[j];
7990 		avgwh /= (double)m->nwav1;
7991 
7992 		/* For each wavelength */
7993 		for (j = 0; j < m->nwav1; j++) {
7994 			/* If reference is < 0.4% of average */
7995 			if (white_read1[j]/avgwh < 0.004) {
7996 				cal_factor1[j] = 1.0/(0.004 * avgwh);
7997 				warn = 1;
7998 			} else {
7999 				cal_factor1[j] = 1.0/white_read1[j];
8000 			}
8001 		}
8002 
8003 	} else {					/* Reflection white reference */
8004 
8005 		/* For each wavelength */
8006 		for (j = 0; j < m->nwav1; j++) {
8007 			if (white_read1[j] < 1000.0)
8008 				cal_factor1[j] = white_ref1[j]/1000.0;
8009 			else
8010 				cal_factor1[j] = white_ref1[j]/white_read1[j];
8011 		}
8012 	}
8013 
8014 #ifdef HIGH_RES
8015 	if (m->hr_inited == 0)
8016 		return warn;
8017 
8018 	if (white_ref2 == NULL) {		/* transmission white reference */
8019 		double avgwh = 0.0;
8020 
8021 		/* Compute average white reference reading */
8022 		for (j = 0; j < m->nwav2; j++)
8023 			avgwh += white_read2[j];
8024 		avgwh /= (double)m->nwav2;
8025 
8026 		/* For each wavelength */
8027 		for (j = 0; j < m->nwav2; j++) {
8028 			/* If reference is < 0.4% of average */
8029 			if (white_read2[j]/avgwh < 0.004) {
8030 				cal_factor2[j] = 1.0/(0.004 * avgwh);
8031 				warn = 1;
8032 			} else {
8033 				cal_factor2[j] = 1.0/white_read2[j];
8034 			}
8035 		}
8036 
8037 	} else {					/* Reflection white reference */
8038 
8039 		/* For each wavelength */
8040 		for (j = 0; j < m->nwav2; j++) {
8041 			if (white_read2[j] < 1000.0)
8042 				cal_factor2[j] = white_ref2[j]/1000.0;
8043 			else
8044 				cal_factor2[j] = white_ref2[j]/white_read2[j];
8045 		}
8046 	}
8047 #endif /* HIGH_RES */
8048 	return warn;
8049 }
8050 
8051 /* For adaptive mode, compute a new integration time and gain mode */
8052 /* in order to optimise the sensor values. */
munki_optimise_sensor(munki * p,double * pnew_int_time,int * pnew_gain_mode,double cur_int_time,int cur_gain_mode,int permithg,int permitclip,double * targoscale,double scale,double deadtime)8053 munki_code munki_optimise_sensor(
8054 	munki *p,
8055 	double *pnew_int_time,
8056 	int    *pnew_gain_mode,
8057 	double cur_int_time,	/* Current intergration time */
8058 	int    cur_gain_mode,	/* nz if currently high gain */
8059 	int    permithg,		/* nz to permit switching to high gain mode */
8060 	int    permitclip,		/* nz to permit clipping out of range int_time, else error */
8061 	double *targoscale,		/* Optimising target scale ( <= 1.0) */
8062 							/* (May be altered if integration time isn't possible) */
8063 	double scale,			/* scale needed of current int time to reach optimum */
8064 	double deadtime			/* Dead integration time (if any) */
8065 ) {
8066 	munki_code ev = MUNKI_OK;
8067 	munkiimp *m = (munkiimp *)p->m;
8068 	munki_state *s = &m->ms[m->mmode];
8069 	double new_int_time;
8070 	double min_int_time;	/* Adjusted min_int_time */
8071 	int    new_gain_mode;
8072 
8073 	a1logd(p->log,3,"munki_optimise_sensor called, inttime %f, gain mode %d, scale %f\n",cur_int_time,cur_gain_mode, scale);
8074 
8075 	min_int_time = m->min_int_time - deadtime;
8076 	cur_int_time -= deadtime;
8077 
8078 	/* Compute new normal gain integration time */
8079 	if (cur_gain_mode)
8080 		new_int_time = cur_int_time * scale * m->highgain;
8081 	else
8082 		new_int_time = cur_int_time * scale;
8083 	new_gain_mode = 0;
8084 
8085 	a1logd(p->log,3,"target inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8086 
8087 	/* Adjust to low light situation by increasing the integration time. */
8088 	if (new_int_time > s->targmaxitime) {	/* Exceeding target integration time */
8089 		if (s->targmaxitime/new_int_time > s->targoscale2) {	/* But within range */
8090 			/* Compromise sensor target value to maintain targmaxitime */
8091 			new_int_time = s->targmaxitime;
8092 			a1logd(p->log,3,"Using targmaxitime with compromise sensor target\n");
8093 		} else {
8094 			/* Target reduced sensor value to give improved measurement time and continuity */
8095 			new_int_time *= s->targoscale2;
8096 			a1logd(p->log,3,"Using compromse sensor target\n");
8097 		}
8098 		/* Hmm. It seems not be a good idea to use high gain mode if it compromises */
8099 		/* the longer integration time which reduces noise. */
8100 		if (s->auto_gain) {
8101 			if (new_int_time > m->max_int_time && permithg) {
8102 				new_int_time /= m->highgain;
8103 				new_gain_mode = 1;
8104 				a1logd(p->log,3,"Switching to high gain mode\n");
8105 			}
8106 		}
8107 	}
8108 	a1logd(p->log,3,"after low light adjust, inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8109 
8110 	/* Deal with still low light */
8111 	if (new_int_time > m->max_int_time) {
8112 		if (permitclip)
8113 			new_int_time = m->max_int_time;
8114 		else
8115 			return MUNKI_RD_LIGHTTOOLOW;
8116 	}
8117 	a1logd(p->log,3,"after low light clip, inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8118 
8119 	/* Adjust to high light situation */
8120 	if (new_int_time < min_int_time && *targoscale < 1.0) {
8121 		*targoscale *= min_int_time/new_int_time;
8122 		new_int_time = min_int_time;
8123 	}
8124 	a1logd(p->log,3,"after high light adjust, targoscale %f, inttime %f, gain mode %d\n",*targoscale, new_int_time,new_gain_mode);
8125 
8126 	/* Deal with still high light */
8127 	if (new_int_time < min_int_time) {
8128 		if (permitclip)
8129 			new_int_time = min_int_time;
8130 		else
8131 			return MUNKI_RD_LIGHTTOOHIGH;
8132 	}
8133 	a1logd(p->log,3,"after high light clip, returning inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8134 
8135 	new_int_time += deadtime;
8136 
8137 	a1logd(p->log,3,"munki_optimise_sensor returning inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8138 	if (pnew_int_time != NULL)
8139 		*pnew_int_time = new_int_time;
8140 
8141 	if (pnew_gain_mode != NULL)
8142 		*pnew_gain_mode = new_gain_mode;
8143 
8144 	return MUNKI_OK;
8145 }
8146 
8147 /* Compute the number of measurements needed, given the target */
8148 /* time and integration time. Will return 0 if target time is 0 */
munki_comp_nummeas(munki * p,double meas_time,double int_time)8149 int munki_comp_nummeas(
8150 	munki *p,
8151 	double meas_time,
8152 	double int_time
8153 ) {
8154 	int nmeas;
8155 	if (meas_time <= 0.0)
8156 		return 0;
8157 	nmeas = (int)floor(meas_time/int_time + 0.5);
8158 	if (nmeas < 1)
8159 		nmeas = 1;
8160 	return nmeas;
8161 }
8162 
8163 /* Compute the rounded up number of measurements needed, */
8164 /* given the target time and integration time. */
8165 /* Will return 0 if target time is 0 */
munki_comp_ru_nummeas(munki * p,double meas_time,double int_time)8166 int munki_comp_ru_nummeas(
8167 	munki *p,
8168 	double meas_time,
8169 	double int_time
8170 ) {
8171 	int nmeas;
8172 	if (meas_time <= 0.0)
8173 		return 0;
8174 	nmeas = (int)ceil(meas_time/int_time);
8175 	return nmeas;
8176 }
8177 
8178 /* Convert the dark interpolation data to a useful state */
8179 /* (also allow for interpolating the shielded cell values) */
8180 void
munki_prepare_idark(munki * p)8181 munki_prepare_idark(
8182 	munki *p
8183 ) {
8184 	munkiimp *m = (munkiimp *)p->m;
8185 	munki_state *s = &m->ms[m->mmode];
8186 	int i, j;
8187 
8188 	/* For normal and high gain */
8189 	for (i = 0; i < 4; i+=2) {
8190 		for (j = -1; j < m->nraw; j++) {
8191 			double d01, d1;
8192 			d01 = s->idark_data[i+0][j];
8193 			d1 = s->idark_data[i+1][j];
8194 
8195 			/* Compute increment proportional to time */
8196 			s->idark_data[i+1][j] = (d1 - d01)/(s->idark_int_time[i+1] - s->idark_int_time[i+0]);
8197 
8198 			/* Compute base */
8199 			s->idark_data[i+0][j] = d01 - s->idark_data[i+1][j] * s->idark_int_time[i+0];;
8200 		}
8201 	}
8202 }
8203 
8204 /* Create the dark reference for the given integration time and gain */
8205 /* by interpolating from the 4 readings prepared earlier */
8206 munki_code
munki_interp_dark(munki * p,double * result,double inttime,int gainmode)8207 munki_interp_dark(
8208 	munki *p,
8209 	double *result,		/* Put result of interpolation here */
8210 	double inttime,
8211 	int gainmode
8212 ) {
8213 	munkiimp *m = (munkiimp *)p->m;
8214 	munki_state *s = &m->ms[m->mmode];
8215 	int i, j;
8216 
8217 	if (!s->idark_valid)
8218 		return MUNKI_INT_NOTCALIBRATED;
8219 
8220 	i = 0;
8221 	if (s->auto_gain && gainmode)
8222 		i = 2;
8223 
8224 	for (j = -1; j < m->nraw; j++) {
8225 		double tt;
8226 		tt = s->idark_data[i+0][j] + inttime * s->idark_data[i+1][j];
8227 		result[j] = tt;
8228 	}
8229 	return MUNKI_OK;
8230 }
8231 
8232 /* Set the noinitcalib mode */
munki_set_noinitcalib(munki * p,int v,int losecs)8233 void munki_set_noinitcalib(munki *p, int v, int losecs) {
8234 	munkiimp *m = (munkiimp *)p->m;
8235 	/* Ignore disabling init calib if more than losecs since instrument was open */
8236 a1logd(p->log,3,"set_noinitcalib v = %d, ->lo_secs %d, losecs %d secs\n",v, m->lo_secs,losecs);
8237 	if (v && losecs != 0 && m->lo_secs >= losecs) {
8238 		a1logd(p->log,3,"initcalib disable ignored because %d >= %d secs\n",m->lo_secs,losecs);
8239 		return;
8240 	}
8241 	m->noinitcalib = v;
8242 }
8243 
8244 /* Set the nocalibask mode */
8245 /* Don't ask user for confirmation of calibration */
8246 /* if the instrument is in the correct configuration for it. */
munki_set_nocalibask(munki * p,int v)8247 void munki_set_nocalibask(munki *p, int v) {
8248 	munkiimp *m = (munkiimp *)p->m;
8249 
8250 	m->nocalibask = v;
8251 }
8252 
8253 /* Set the trigger config */
munki_set_trig(munki * p,inst_opt_type trig)8254 void munki_set_trig(munki *p, inst_opt_type trig) {
8255 	munkiimp *m = (munkiimp *)p->m;
8256 	m->trig = trig;
8257 }
8258 
8259 /* Return the trigger config */
munki_get_trig(munki * p)8260 inst_opt_type munki_get_trig(munki *p) {
8261 	munkiimp *m = (munkiimp *)p->m;
8262 	return m->trig;
8263 }
8264 
8265 /* Switch thread handler */
munki_switch_thread(void * pp)8266 static int munki_switch_thread(void *pp) {
8267 	int nfailed = 0;
8268 	munki *p = (munki *)pp;
8269 	munkiimp *m = (munkiimp *)p->m;
8270 	munki_code rv = MUNKI_OK;
8271 	a1logd(p->log,3,"Switch thread started\n");
8272 //	for (nfailed = 0;nfailed < 5;)
8273 	/* Try indefinitely, in case instrument is put to sleep */
8274 	for (;;) {
8275 		mk_eve ecode;
8276 
8277 		rv = munki_waitfor_switch_th(p, &ecode, NULL, SW_THREAD_TIMEOUT);
8278 		if (m->th_term) {
8279 			m->th_termed = 1;
8280 			break;
8281 		}
8282 		if (rv == MUNKI_INT_BUTTONTIMEOUT) {
8283 			nfailed = 0;
8284 			continue;
8285 		}
8286 		if (rv != MUNKI_OK) {
8287 			nfailed++;
8288 			a1logd(p->log,3,"Switch thread failed with 0x%x\n",rv);
8289 			continue;
8290 		}
8291 		if (ecode == mk_eve_switch_press) {
8292 			m->switch_count++;
8293 			if (!m->hide_switch && p->eventcallback != NULL) {
8294 				p->eventcallback(p->event_cntx, inst_event_switch);
8295 			}
8296 		} else if (ecode == mk_eve_spos_change) {
8297 #ifdef FILTER_SPOS_EVENTS
8298 			/* Signal change to filer thread */
8299 			m->spos_msec = msec_time();
8300 			m->spos_change++;
8301 #else
8302 			if (m->eventcallback != NULL) {
8303 				m->eventcallback(p->event_cntx, inst_event_mconf);
8304 			}
8305 #endif
8306 		}
8307 	}
8308 	a1logd(p->log,3,"Switch thread returning\n");
8309 	return rv;
8310 }
8311 
8312 #ifdef FILTER_SPOS_EVENTS
munki_spos_thread(void * pp)8313 static int munki_spos_thread(void *pp) {
8314 	munki *p = (munki *)pp;
8315 	munkiimp *m = (munkiimp *)p->m;
8316 	int change = m->spos_change;		/* Current count */
8317 
8318 	a1logd(p->log,3,"spos thread started\n");
8319 
8320 	for (;;) {
8321 
8322 		if (m->spos_th_term) {
8323 			m->spos_th_termed = 1;
8324 			break;
8325 		}
8326 
8327 		/* Do callback if change has persisted for 1 second */
8328 		if (change != m->spos_change
8329 		 && (msec_time() - m->spos_msec) >= FILTER_TIME) {
8330 			change = m->spos_change;
8331 			if (p->eventcallback != NULL) {
8332 				p->eventcallback(p->event_cntx, inst_event_mconf);
8333 			}
8334 		}
8335 		msec_sleep(100);
8336 	}
8337 	return 0;
8338 }
8339 #endif
8340 
8341 
8342 /* ============================================================ */
8343 /* Low level commands */
8344 
8345 /* USB Instrument commands */
8346 
8347 /* Read from the EEProm */
8348 munki_code
munki_readEEProm(munki * p,unsigned char * buf,int addr,int size)8349 munki_readEEProm(
8350 	munki *p,
8351 	unsigned char *buf,		/* Where to read it to */
8352 	int addr,				/* Address in EEprom to read from */
8353 	int size				/* Number of bytes to read (max 65535) */
8354 ) {
8355 	munkiimp *m = (munkiimp *)p->m;
8356 	int rwbytes;			/* Data bytes read or written */
8357 	unsigned char pbuf[8];	/* Write EEprom parameters */
8358 	int se, rv = MUNKI_OK;
8359 
8360 	a1logd(p->log,2,"munki_readEEProm: address 0x%x size 0x%x\n",addr,size);
8361 
8362 	if (size < 0 || addr < 0 || (addr + size) > (m->noeeblocks * m->eeblocksize))
8363 		return MUNKI_INT_EEOUTOFRANGE;
8364 
8365 	int2buf(&pbuf[0], addr);
8366 	int2buf(&pbuf[4], size);
8367   	se = p->icom->usb_control(p->icom,
8368 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8369 	                   0x81, 0, 0, pbuf, 8, 2.0);
8370 
8371 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8372 		a1logd(p->log,1,"munki_readEEProm: read failed (1) with ICOM err 0x%x\n",se);
8373 		return rv;
8374 	}
8375 
8376 	/* Now read the bytes */
8377 	se = p->icom->usb_read(p->icom, NULL, 0x81, buf, size, &rwbytes, 6.0);
8378 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8379 		a1logd(p->log,1,"munki_readEEProm: read failed (2) with ICOM err 0x%x\n",se);
8380 		return rv;
8381 	}
8382 
8383 	if (rwbytes != size) {
8384 		a1logd(p->log,1,"munki_readEEProm: 0x%x bytes, short read error\n",rwbytes);
8385 		return MUNKI_HW_EE_SHORTREAD;
8386 	}
8387 
8388 	if (p->log->debug >= 5) {
8389 		int i;
8390 		char oline[100] = { '\000' }, *bp = oline;
8391 		for (i = 0; i < size; i++) {
8392 			if ((i % 16) == 0)
8393 				bp += sprintf(bp,"    %04x:",i);
8394 			bp += sprintf(bp," %02x",buf[i]);
8395 			if ((i+1) >= size || ((i+1) % 16) == 0) {
8396 				bp += sprintf(bp,"\n");
8397 				a1logd(p->log,5,oline);
8398 				bp = oline;
8399 			}
8400 		}
8401 	}
8402 
8403 	a1logd(p->log,2,"munki_readEEProm: got 0x%x bytes, ICOM err 0x%x\n",rwbytes, se);
8404 
8405 	return rv;
8406 }
8407 
8408 
8409 
8410 /* Get the firmware parameters */
8411 /* return pointers may be NULL if not needed. */
8412 munki_code
munki_getfirm(munki * p,int * fwrev,int * tickdur,int * minintcount,int * noeeblocks,int * eeblocksize)8413 munki_getfirm(
8414 	munki *p,
8415 	int *fwrev,			/* Return the formware version number as 8.8 */
8416 	int *tickdur,		/* Tick duration */
8417 	int *minintcount,	/* Minimum integration tick count */
8418 	int *noeeblocks,	/* Number of EEPROM blocks */
8419 	int *eeblocksize	/* Size of each block */
8420 ) {
8421 	unsigned char pbuf[24];	/* status bytes read */
8422 	int _fwrev_maj, _fwrev_min;
8423 	int _tickdur;
8424 	int _minintcount;
8425 	int _noeeblocks;
8426 	int _eeblocksize;
8427 	int se, rv = MUNKI_OK;
8428 
8429 	a1logd(p->log,2,"munki_getfirm:\n");
8430 
8431 	se = p->icom->usb_control(p->icom,
8432 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8433 	                   0x86, 0, 0, pbuf, 24, 2.0);
8434 
8435 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8436 		a1logd(p->log,1,"munki_getfirm: failed with ICOM err 0x%x\n",se);
8437 		return rv;
8438 	}
8439 
8440 	_fwrev_maj   = buf2int(&pbuf[0]);
8441 	_fwrev_min   = buf2int(&pbuf[4]);
8442 	_tickdur     = buf2int(&pbuf[8]);
8443 	_minintcount = buf2int(&pbuf[12]);
8444 	_noeeblocks  = buf2int(&pbuf[16]);
8445 	_eeblocksize = buf2int(&pbuf[20]);
8446 
8447 	a1logd(p->log,2,"munki_getfirm: returning fwrev %d.%d, tickdur %d, minint %d, eeblks %d, "
8448 	       "eeblksz %d ICOM err 0x%x\n", _fwrev_maj, _fwrev_min, _tickdur, _minintcount,
8449 	                                     _noeeblocks, _eeblocksize, se);
8450 
8451 	if (fwrev != NULL) *fwrev = _fwrev_maj * 256 + _fwrev_min ;
8452 	if (tickdur != NULL) *tickdur = _tickdur;
8453 	if (minintcount != NULL) *minintcount = _minintcount;
8454 	if (noeeblocks != NULL) *noeeblocks = _noeeblocks;
8455 	if (eeblocksize != NULL) *eeblocksize = _eeblocksize;
8456 
8457 	return rv;
8458 }
8459 
8460 /* Get the Chip ID */
8461 munki_code
munki_getchipid(munki * p,unsigned char chipid[8])8462 munki_getchipid(
8463 	munki *p,
8464 	unsigned char chipid[8]
8465 ) {
8466 	int se, rv = MUNKI_OK;
8467 
8468 	a1logd(p->log,2,"munki_getchipid: called\n");
8469 
8470 	se = p->icom->usb_control(p->icom,
8471 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8472 	                   0x8A, 0, 0, chipid, 8, 2.0);
8473 
8474 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8475 		a1logd(p->log,1,"munki_getchipid:  GetChipID failed with ICOM err 0x%x\n",se);
8476 		return rv;
8477 	}
8478 
8479 	a1logd(p->log,2," GetChipID returns %02X-%02X%02X%02X%02X%02X%02X%02X ICOM err 0x%x\n",
8480                            chipid[0], chipid[1], chipid[2], chipid[3],
8481                            chipid[4], chipid[5], chipid[6], chipid[7], se);
8482 	return rv;
8483 }
8484 
8485 /* Get the Version String */
8486 munki_code
munki_getversionstring(munki * p,char vstring[37])8487 munki_getversionstring(
8488 	munki *p,
8489 	char vstring[37]
8490 ) {
8491 	int se, rv = MUNKI_OK;
8492 
8493 	a1logd(p->log,2,"munki_getversionstring: called\n");
8494 
8495 	se = p->icom->usb_control(p->icom,
8496 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8497 	                   0x85, 0, 0, (unsigned char *)vstring, 36, 2.0);
8498 
8499 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8500 		a1logd(p->log,1,"munki_getversionstring: failed with ICOM err 0x%x\n",se);
8501 		return rv;
8502 	}
8503 
8504 	vstring[36] = '\000';
8505 
8506 	a1logd(p->log,2,"munki_getversionstring: returning '%s' ICOM err 0x%x\n", vstring, se);
8507 
8508 	return rv;
8509 }
8510 
8511 /* Get the measurement state */
8512 /* return pointers may be NULL if not needed. */
8513 munki_code
munki_getmeasstate(munki * p,int * ledtrange,int * ledtemp,int * dutycycle,int * ADfeedback)8514 munki_getmeasstate(
8515 	munki *p,
8516 	int *ledtrange,		/* LED temperature range */
8517 	int *ledtemp,		/* LED temperature */
8518 	int *dutycycle,		/* Duty Cycle */
8519 	int *ADfeedback		/* A/D converter feedback */
8520 ) {
8521 	unsigned char pbuf[16];	/* values read */
8522 	int _ledtrange;
8523 	int _ledtemp;
8524 	int _dutycycle;
8525 	int _ADfeedback;
8526 	int se, rv = MUNKI_OK;
8527 
8528 	a1logd(p->log,2,"munki_getmeasstate: called\n");
8529 
8530 	se = p->icom->usb_control(p->icom,
8531 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8532 	                   0x8F, 0, 0, pbuf, 16, 2.0);
8533 
8534 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8535 		a1logd(p->log,1,"munki_getmeasstate: failed with ICOM err 0x%x\n",se);
8536 		return rv;
8537 	}
8538 
8539 	_ledtrange   = buf2int(&pbuf[0]);
8540 	_ledtemp     = buf2int(&pbuf[4]);
8541 	_dutycycle   = buf2int(&pbuf[8]);
8542 	_ADfeedback  = buf2int(&pbuf[12]);
8543 
8544 	a1logd(p->log,2,"munki_getmeasstate: returning LED temp range %d, LED temp %d, "
8545 	                "Duty Cycle %d, ADFeefback %d, ICOM err 0x%x\n",
8546 	                _ledtrange, _ledtemp, _dutycycle, _ADfeedback, se);
8547 
8548 	if (ledtrange != NULL) *ledtrange = _ledtrange;
8549 	if (ledtemp != NULL) *ledtemp = _ledtemp;
8550 	if (dutycycle != NULL) *dutycycle = _dutycycle;
8551 	if (ADfeedback != NULL) *ADfeedback = _ADfeedback;
8552 
8553 	return rv;
8554 }
8555 
8556 /* Get the device status */
8557 /* return pointers may be NULL if not needed. */
8558 munki_code
munki_getstatus(munki * p,mk_spos * spos,mk_but * but)8559 munki_getstatus(
8560 	munki *p,
8561 	mk_spos *spos,		/* Return the sensor position */
8562 	mk_but *but			/* Return Button state */
8563 ) {
8564 	unsigned char pbuf[2];	/* status bytes read */
8565 	mk_spos _spos;
8566 	mk_but _but;
8567 	int se, rv = MUNKI_OK;
8568 
8569 	a1logd(p->log,2,"munki_getstatus: called\n");
8570 
8571 	se = p->icom->usb_control(p->icom,
8572 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8573 	                   0x87, 0, 0, pbuf, 2, 2.0);
8574 
8575 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8576 		a1logd(p->log,1,"munki_getstatus: failed with ICOM err 0x%x\n",se);
8577 		return rv;
8578 	}
8579 
8580 	_spos = (mk_spos)pbuf[0];
8581 	_but   = (mk_but)pbuf[1];
8582 
8583 	if (p->log->debug >= 3) {
8584 		char sb1[50], sb2[50];
8585 		if (_spos == mk_spos_proj)
8586 			strcpy(sb1, "Projector");
8587 		else if (_spos == mk_spos_surf)
8588 			strcpy(sb1, "Surface");
8589 		else if (_spos == mk_spos_calib)
8590 			strcpy(sb1, "Calibration");
8591 		else if (_spos == mk_spos_amb)
8592 			strcpy(sb1, "Ambient");
8593 		else
8594 			sprintf(sb1,"Unknown 0x%x",_spos);
8595 		if (_but == mk_but_switch_release)
8596 			strcpy(sb2, "Released");
8597 		else if (_but == mk_but_switch_press)
8598 			strcpy(sb2, "Pressed");
8599 		else
8600 			sprintf(sb2,"Unknown 0x%x",_but);
8601 
8602 		a1logd(p->log,3,"munki_getstatus: Sensor pos. %s, Button state %s, ICOM err 0x%x\n",
8603 	            sb1, sb2, se);
8604 	}
8605 
8606 	if (spos != NULL) *spos = _spos;
8607 	if (but != NULL) *but = _but;
8608 
8609 	return rv;
8610 }
8611 
8612 /* Set the indicator LED state (advanced) */
8613 /* NOTE that the instrument seems to turn it off */
8614 /* whenever any other sort of operation occurs. */
8615 munki_code
munki_setindled(munki * p,int p1,int p2,int p3,int p4,int p5)8616 munki_setindled(
8617     munki *p,
8618     int p1,			/* On time (msec) */
8619     int p2,			/* Off time (msec) */
8620     int p3,			/* Transition time (msec) */
8621     int p4,			/* Number of pulses, -1 = max */
8622     int p5			/* Ignored ? */
8623 ) {
8624 	unsigned char pbuf[20];	/* command bytes written */
8625 	int se, rv = MUNKI_OK;
8626 
8627 	a1logd(p->log,2,"munki_setindled: %d, %d, %d, %d, %d\n",
8628 	                   p1, p2, p3, p4, p5);
8629 
8630 	int2buf(&pbuf[0], p1);		/* On time (msec) */
8631 	int2buf(&pbuf[4], p2);		/* Off time (msec) */
8632 	int2buf(&pbuf[8], p3);		/* Transition time (msec) */
8633 	int2buf(&pbuf[12], p4);		/* Number of pulses, -1 = max */
8634 	int2buf(&pbuf[16], p5);		/* Unknown */
8635 
8636 	se = p->icom->usb_control(p->icom,
8637 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8638 	                   0x92, 0, 0, pbuf, 20, 2.0);
8639 
8640 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8641 		a1logd(p->log,1,"munki_setindled: failed with ICOM err 0x%x\n",se);
8642 		return rv;
8643 	}
8644 
8645 	a1logd(p->log,2,"munki_setindled: OK ICOM err 0x%x\n",se);
8646 
8647 	return rv;
8648 }
8649 
8650 /* Trigger a measurement with the given measurement parameters */
8651 munki_code
munki_triggermeasure(munki * p,int intclocks,int nummeas,int measmodeflags,int holdtempduty)8652 munki_triggermeasure(
8653 	munki *p,
8654 	int intclocks,		/* Number of integration clocks */
8655 	int nummeas,		/* Number of measurements to make */
8656 	int measmodeflags,	/* Measurement mode flags */
8657 	int holdtempduty	/* Hold temperature duty cycle */
8658 ) {
8659 	munkiimp *m = (munkiimp *)p->m;
8660 	unsigned char pbuf[12];	/* command bytes written */
8661 	int se, rv = MUNKI_OK;
8662 
8663 	a1logd(p->log,2,"munki_triggermeasure: lamp %d, scan %d, gain %d, intclks %d, nummeas %d\n",
8664 	          (measmodeflags & MUNKI_MMF_LAMP) ? 1 : 0,
8665 	          (measmodeflags & MUNKI_MMF_SCAN) ? 1 : 0,
8666 	          (measmodeflags & MUNKI_MMF_HIGHGAIN) ? 1 : 0,
8667               intclocks, nummeas);
8668 
8669 	pbuf[0] = (measmodeflags & MUNKI_MMF_LAMP) ? 1 : 0;
8670 	pbuf[1] = (measmodeflags & MUNKI_MMF_SCAN) ? 1 : 0;
8671 	pbuf[2] = (measmodeflags & MUNKI_MMF_HIGHGAIN) ? 1 : 0;
8672 	pbuf[3] = holdtempduty;
8673 	int2buf(&pbuf[4], intclocks);
8674 	int2buf(&pbuf[8], nummeas);
8675 
8676 	m->tr_t1 = m->tr_t2 = m->tr_t3 = m->tr_t4 = m->tr_t5 = m->tr_t6 = m->tr_t7 = 0;
8677 	m->tr_t1 = msec_time();     /* Diagnostic */
8678 
8679 	se = p->icom->usb_control(p->icom,
8680 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8681 	                   0x80, 0, 0, pbuf, 12, 2.0);
8682 	m->trigstamp = usec_time();
8683 
8684 	m->tr_t2 = msec_time();     /* Diagnostic */
8685 
8686 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8687 		a1logd(p->log,1,"munki_triggermeasure: failed with ICOM err 0x%x\n",se);
8688 		return rv;
8689 	}
8690 
8691 	a1logd(p->log,2,"munki_triggermeasure: OK ICOM err 0x%x\n",se);
8692 
8693 	return rv;
8694 }
8695 
8696 /* Read a measurements results. */
8697 /* A buffer full of bytes is returned. */
8698 static munki_code
munki_readmeasurement(munki * p,int inummeas,int scanflag,unsigned char * buf,int bsize,int * nummeas,int calib_measure,int dark_measure)8699 munki_readmeasurement(
8700 	munki *p,
8701 	int inummeas,			/* Initial number of measurements to expect */
8702 	int scanflag,			/* NZ if in scan mode to continue reading */
8703 	unsigned char *buf,		/* Where to read it to */
8704 	int bsize,				/* Bytes available in buffer */
8705 	int *nummeas,			/* Return number of readings measured */
8706 	int calib_measure,		/* flag - nz if this is a calibration measurement */
8707 	int dark_measure		/* flag - nz if this is a dark measurement */
8708 ) {
8709 	munkiimp *m = (munkiimp *)p->m;
8710 	unsigned char *ibuf = buf;	/* Incoming buffer */
8711 	int nmeas;					/* Number of measurements for this read */
8712 	double top, extra;			/* Time out period */
8713 	int rwbytes;				/* Data bytes read or written */
8714 	int se, rv = MUNKI_OK;
8715 	int treadings = 0;
8716 //	int gotshort = 0;			/* nz when got a previous short reading */
8717 
8718 	if ((bsize % (m->nsen * 2)) != 0) {
8719 		a1logd(p->log,1,"munki_readmeasurement: got %d bytes, nsen = %d\n",bsize,m->nsen);
8720 		return MUNKI_INT_ODDREADBUF;
8721 	}
8722 
8723 	extra = 1.0;		/* Extra timeout margin */
8724 
8725 #ifdef SINGLE_READ
8726 	if (scanflag == 0)
8727 		nmeas = inummeas;
8728 	else
8729 		nmeas = bsize / (m->nsen * 2);		/* Use a single large read */
8730 #else
8731 	nmeas = inummeas;				/* Smaller initial number of measurements */
8732 #endif
8733 
8734 	top = extra + m->c_inttime * nmeas;
8735 
8736 	a1logd(p->log,2,"munki_readmeasurement: inummeas %d, scanflag %d, address %p bsize 0x%x, timout %f\n",inummeas, scanflag, buf, bsize, top);
8737 
8738 	for (;;) {
8739 		int size;		/* number of bytes to read */
8740 
8741 		size = (m->nsen * 2) * nmeas;
8742 
8743 		if (size > bsize) {		/* oops, no room for read */
8744 			a1logd(p->log,1,"munki_readmeasurement: Buffer was too short for scan\n");
8745 			return MUNKI_INT_MEASBUFFTOOSMALL;
8746 		}
8747 
8748 		m->tr_t6 = msec_time();	/* Diagnostic, start of subsequent reads */
8749 		if (m->tr_t3 == 0) m->tr_t3 = m->tr_t6;		/* Diagnostic, start of first read */
8750 
8751 		a1logd(p->log,5,"about to call usb_read with %d bytes\n",size);
8752 		se = p->icom->usb_read(p->icom, NULL, 0x81, buf, size, &rwbytes, top);
8753 
8754 		m->tr_t5 = m->tr_t7;
8755 		m->tr_t7 = msec_time();	/* Diagnostic, end of subsequent reads */
8756 		if (m->tr_t4 == 0) {
8757 			m->tr_t5 = m->tr_t2;
8758 			m->tr_t4 = m->tr_t7;	/* Diagnostic, end of first read */
8759 		}
8760 
8761 #ifdef NEVER		/* Use short + timeout to terminate scan */
8762 		if (gotshort != 0 && se == ICOM_TO) {	/* We got a timeout after a short read. */
8763 			a1logd(p->log,1,"Read timed out in %f secs after getting short read\n"
8764 			                "(Trig & rd times %d %d %d %d)\n",
8765 			         top,
8766 			         m->tr_t2-m->tr_t1, m->tr_t3-m->tr_t2, m->tr_t4-m->tr_t3, m->tr_t6-m->tr_t5);
8767 			break;		/* We're done */
8768 		} else
8769 #endif
8770 		if (se == ICOM_SHORT) {		/* Expect this to terminate scan reading */
8771 			a1logd(p->log,5,"Short read, read %d bytes, asked for %d\n"
8772 			                "(Trig & rd times %d %d %d %d)\n",
8773 					 rwbytes,size,
8774 				     m->tr_t2-m->tr_t1, m->tr_t3-m->tr_t2, m->tr_t4-m->tr_t3, m->tr_t6-m->tr_t5);
8775 		} else if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8776 			if (m->trig_rv != MUNKI_OK) {
8777 				a1logd(p->log,1,"munki_readmeasurement: trigger failed, ICOM err 0x%x\n",m->trig_se);
8778 				return m->trig_rv;
8779 			}
8780 			if (se & ICOM_TO)
8781 				a1logd(p->log,1,"munki_readmeasurement: read timed out with top = %f\n",top);
8782 
8783 			a1logd(p->log,1,"munki_readmeasurement: read failed, bytes read 0x%x, ICOM err 0x%x\n",rwbytes, se);
8784 			return rv;
8785 		}
8786 
8787 		/* If we didn't read a multiple of m->nsen * 2, we've got problems */
8788 		if ((rwbytes % (m->nsen * 2)) != 0) {
8789 			a1logd(p->log,1,"munki_readmeasurement: read %d bytes, nsen %d, odd read error\n",rwbytes, m->nsen);
8790 			return MUNKI_HW_ME_ODDREAD;
8791 		}
8792 
8793 		/* Track where we're up to */
8794 		bsize -= rwbytes;
8795 		buf   += rwbytes;
8796 		treadings += rwbytes/(m->nsen * 2);
8797 
8798 		if (scanflag == 0) {	/* Not scanning */
8799 
8800 			/* Expect to read exactly what we asked for */
8801 			if (rwbytes != size) {
8802 				a1logd(p->log,1,"munki_readmeasurement: unexpected short read, got %d expected %d\n",rwbytes,size);
8803 				return MUNKI_HW_ME_SHORTREAD;
8804 			}
8805 			break;	/* And we're done */
8806 		}
8807 
8808 #ifdef NEVER		/* Use short + timeout to terminate scan */
8809 		/* We expect to get a short read at the end of a scan, */
8810 		/* or we might have the USB transfer truncated by somethinge else. */
8811 		/* Note the short read, and keep reading until we get a time out */
8812 		if (rwbytes != size) {
8813 			gotshort = 1;
8814 		} else {
8815 			gotshort = 0;
8816 		}
8817 #else				/* Use short to terminate scan */
8818 		/* We're scanning and expect to get a short read at the end of the scan. */
8819 		if (rwbytes != size) {
8820 			a1logd(p->log,5,"done because read %d bytes != %d\n",rwbytes,size);
8821 			break;
8822 		}
8823 #endif
8824 
8825 		if (bsize == 0) {		/* oops, no room for more scanning read */
8826 			unsigned char tbuf[NSEN_MAX * 2];
8827 
8828 			/* We need to clean up, so soak up all the data and throw it away */
8829 			while ((se = p->icom->usb_read(p->icom, NULL, 0x81, tbuf, m->nsen * 2, &rwbytes, top)) == ICOM_OK)
8830 				;
8831 			a1logd(p->log,1,"munki_readmeasurement: buffer was too short for scan\n");
8832 			return MUNKI_INT_MEASBUFFTOOSMALL;
8833 		}
8834 
8835 		/* Read a bunch more readings until the read is short or times out */
8836 		nmeas = bsize / (m->nsen * 2);
8837 		if (nmeas > 64)
8838 			nmeas = 64;
8839 		top = extra + m->c_inttime * nmeas;
8840 	}
8841 
8842 	/* Must have timed out in initial readings */
8843 	if (treadings < inummeas) {
8844 		a1logd(p->log,1,"munki_readmeasurement: read failed, bytes read 0x%x, ICOM err 0x%x\n",rwbytes, se);
8845 		return MUNKI_RD_SHORTMEAS;
8846 	}
8847 
8848 	if (p->log->debug >= 5) {
8849 		int i, size = treadings * m->nsen * 2;
8850 		char oline[100] = { '\000' }, *bp = oline;
8851 		for (i = 0; i < size; i++) {
8852 			if ((i % 16) == 0)
8853 				bp += sprintf(bp,"    %04x:",i);
8854 			bp += sprintf(bp," %02x",ibuf[i]);
8855 			if ((i+1) >= size || ((i+1) % 16) == 0) {
8856 				bp += sprintf(bp,"\n");
8857 				a1logd(p->log,5,oline);
8858 				bp = oline;
8859 			}
8860 		}
8861 	}
8862 
8863 	a1logd(p->log,2,"munki_readmeasurement: Read %d readings, ICOM err 0x%x\n"
8864 	                "(Trig & rd times %d %d %d %d)\n",
8865 	                 treadings, se,
8866 					 m->tr_t2-m->tr_t1, m->tr_t3-m->tr_t2, m->tr_t4-m->tr_t3, m->tr_t6-m->tr_t5);
8867 
8868 	if (nummeas != NULL) *nummeas = treadings;
8869 
8870 	return rv;
8871 }
8872 
8873 /* Simulating an event */
munki_simulate_event(munki * p,mk_eve ecode,int timestamp)8874 munki_code munki_simulate_event(munki *p, mk_eve ecode,  int timestamp) {
8875 	munkiimp *m = (munkiimp *)p->m;
8876 	unsigned char pbuf[8];	/* 8 bytes to write */
8877 	int se, rv = MUNKI_OK;
8878 
8879 	a1logd(p->log,2,"munki_simulate_event: 0x%x\n",ecode);
8880 
8881 	int2buf(&pbuf[0], ecode);
8882 	int2buf(&pbuf[4], timestamp);	/* msec since munki power up */
8883 
8884 	se = p->icom->usb_control(p->icom,
8885 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8886 	                   0x8E, 0, 0, pbuf, 8, 2.0);
8887 
8888 	if ((rv = icoms2munki_err(se)) != MUNKI_OK)
8889 		a1logd(p->log,1,"munki_simulate_event: event 0x%x failed with ICOM err 0x%x\n",ecode,se);
8890 	else
8891 		a1logd(p->log,2,"munki_simulate_event: 0x%x done, ICOM err 0x%x\n",ecode,se);
8892 
8893 	/* Cancel the I/O in case there is no response*/
8894 	msec_sleep(50);
8895 	if (m->th_termed == 0) {
8896 		a1logd(p->log,1,"munki_simulate_event: terminate switch thread failed, canceling I/O\n");
8897 		p->icom->usb_cancel_io(p->icom, &m->sw_cancel);
8898 	}
8899 
8900 	return rv;
8901 }
8902 
8903 /* Wait for a reply triggered by an event */
munki_waitfor_switch(munki * p,mk_eve * ecode,int * timest,double top)8904 munki_code munki_waitfor_switch(munki *p, mk_eve *ecode, int *timest, double top) {
8905 	int rwbytes;			/* Data bytes read */
8906 	unsigned char buf[8];	/* Result  */
8907 	int se, rv = MUNKI_OK;
8908 	mk_eve _ecode;
8909 	int _timest;
8910 
8911 	a1logd(p->log,2,"munki_waitfor_switch: Read 8 bytes from switch hit port\n");
8912 
8913 	/* Now read 8 bytes */
8914 	se = p->icom->usb_read(p->icom, NULL, 0x83, buf, 8, &rwbytes, top);
8915 
8916 	if (se & ICOM_TO) {
8917 		a1logd(p->log,1,"munki_waitfor_switch: read 0x%x bytes, timed out\n",rwbytes);
8918 		return MUNKI_INT_BUTTONTIMEOUT;
8919 	}
8920 
8921 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8922 		a1logd(p->log,2,"munki_waitfor_switch: read failed with ICOM err 0x%x\n",se);
8923 		return rv;
8924 	}
8925 
8926 	if (rwbytes != 8) {
8927 		a1logd(p->log,1,"munki_waitfor_switch: read %d bytes, short read error\n",rwbytes);
8928 		return MUNKI_HW_EE_SHORTREAD;
8929 	}
8930 
8931 	_ecode  = (mk_eve) buf2int(&buf[0]);
8932 	_timest = buf2int(&buf[4]);
8933 
8934 	if (p->log->debug >= 3) {
8935 		char sbuf[100];
8936 		if (_ecode == mk_eve_none)
8937 			strcpy(sbuf, "None");
8938 		else if (_ecode == mk_eve_switch_press)
8939 			strcpy(sbuf, "Button press");
8940 		else if (_ecode == mk_eve_switch_release)
8941 			strcpy(sbuf, "Button release");
8942 		else if (_ecode == mk_eve_spos_change)
8943 			strcpy(sbuf, "Sensor position change");
8944 		else
8945 			sprintf(sbuf,"Unknown 0x%x",_ecode);
8946 
8947 		a1logd(p->log,3,"munki_waitfor_switch: Event %s, timestamp %d ICOM err 0x%x\n", sbuf, _timest, se);
8948 	}
8949 
8950 	a1logd(p->log,2,"munki_waitfor_switch: read %d bytes OK\n",rwbytes);
8951 
8952 	if (ecode != NULL) *ecode = _ecode;
8953 	if (timest != NULL) *timest = _timest;
8954 
8955 	return rv;
8956 }
8957 
8958 /* Wait for a reply triggered by a key press or config change (thread version) */
8959 /* Returns MUNKI_OK if the switch has been pressed, */
8960 /* or MUNKI_INT_BUTTONTIMEOUT if */
8961 /* no switch was pressed befor the time expired, */
8962 /* or some other error. */
munki_waitfor_switch_th(munki * p,mk_eve * ecode,int * timest,double top)8963 munki_code munki_waitfor_switch_th(munki *p, mk_eve *ecode, int *timest, double top) {
8964 	munkiimp *m = (munkiimp *)p->m;
8965 	int rwbytes;			/* Data bytes read */
8966 	unsigned char buf[8];	/* Result  */
8967 	int se, rv = MUNKI_OK;
8968 	mk_eve _ecode;
8969 	int _timest;
8970 
8971 	a1logd(p->log,2,"munki_waitfor_switch_th: Read 8 bytes from switch hit port\n");
8972 
8973 	/* Now read 8 bytes */
8974 	se = p->icom->usb_read(p->icom, &m->sw_cancel, 0x83, buf, 8, &rwbytes, top);
8975 
8976 	if (se & ICOM_TO) {
8977 		a1logd(p->log,1,"munki_waitfor_switch_th: read 0x%x bytes, timed out\n",rwbytes);
8978 		return MUNKI_INT_BUTTONTIMEOUT;
8979 	}
8980 
8981 	if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8982 		a1logd(p->log,2,"munki_waitfor_switch_th: read failed with ICOM err 0x%x\n",se);
8983 		return rv;
8984 	}
8985 
8986 	if (rwbytes != 8) {
8987 		a1logd(p->log,1,"munki_waitfor_switch_th: read %d bytes, short read error\n",rwbytes);
8988 		return MUNKI_HW_EE_SHORTREAD;
8989 	}
8990 
8991 	_ecode  = (mk_eve) buf2int(&buf[0]);
8992 	_timest = buf2int(&buf[4]);			/* msec since munki power up */
8993 
8994 	if (p->log->debug >= 3) {
8995 		char sbuf[100];
8996 		if (_ecode == mk_eve_none)
8997 			strcpy(sbuf, "None");
8998 		else if (_ecode == mk_eve_switch_press)
8999 			strcpy(sbuf, "Button press");
9000 		else if (_ecode == mk_eve_switch_release)
9001 			strcpy(sbuf, "Button release");
9002 		else if (_ecode == mk_eve_spos_change)
9003 			strcpy(sbuf, "Sensor position change");
9004 		else
9005 			sprintf(sbuf,"Unknown 0x%x",_ecode);
9006 
9007 		a1logd(p->log,3,"munki_waitfor_switch_th: Event %s, timestamp %d ICOM err 0x%x\n", sbuf, _timest, se);
9008 	}
9009 
9010 	a1logd(p->log,2,"munki_waitfor_switch_th: read %d bytes OK\n",rwbytes);
9011 
9012 	if (ecode != NULL) *ecode = _ecode;
9013 	if (timest != NULL) *timest = _timest;
9014 
9015 	return rv;
9016 }
9017 
9018 
9019 /* ============================================================ */
9020 /* key/value dictionary support for EEProm contents */
9021 
9022 /* Fixup values for the window reference */
9023 
9024 /* Check values */
9025 static double proj_check[36] = {
9026 	0.0,
9027 	0.0,
9028 	0.0,
9029 	0.0,
9030 	0.0,
9031 	0.0,
9032 	0.827859997749328610,
9033 	0.849550008773803710,
9034 	0.855490028858184810,
9035 	0.858709990978240970,
9036 	0.861010015010833740,
9037 	0.862879991531372070,
9038 	0.864109992980957030,
9039 	0.864619970321655270,
9040 	0.865379989147186280,
9041 	0.865629971027374270,
9042 	0.865689992904663090,
9043 	0.865499973297119140,
9044 	0.865499973297119140,
9045 	0.865760028362274170,
9046 	0.866209983825683590,
9047 	0.866630017757415770,
9048 	0.867579996585845950,
9049 	0.868969976902008060,
9050 	0.870270013809204100,
9051 	0.871270000934600830,
9052 	0.872340023517608640,
9053 	0.873269975185394290,
9054 	0.873669981956481930,
9055 	0.873640000820159910,
9056 	0.874390006065368650,
9057 	0.873179972171783450,
9058 	0.872720003128051760,
9059 	0.872640013694763180,
9060 	0.873160004615783690,
9061 	0.873440027236938480
9062 };
9063 
9064 
9065 /* Correction values to emission ref. */
9066 static double proj_fix[36] = {
9067 	0.639684915542602540,
9068 	0.639684915542602540,
9069 	0.639684915542602540,
9070 	0.812916100025177000,
9071 	0.846581041812896730,
9072 	0.854855418205261230,
9073 	0.859299719333648680,
9074 	0.861804306507110600,
9075 	0.863713920116424560,
9076 	0.865424513816833500,
9077 	0.866307735443115230,
9078 	0.867028772830963130,
9079 	0.867631316184997560,
9080 	0.868214190006256100,
9081 	0.868206322193145750,
9082 	0.868299305438995360,
9083 	0.867988884449005130,
9084 	0.868103504180908200,
9085 	0.868657410144805910,
9086 	0.869595944881439210,
9087 	0.870542407035827640,
9088 	0.871895790100097660,
9089 	0.873195052146911620,
9090 	0.874702811241149900,
9091 	0.876054167747497560,
9092 	0.877129673957824710,
9093 	0.877931654453277590,
9094 	0.877546310424804690,
9095 	0.876341819763183590,
9096 	0.875181615352630620,
9097 	0.875020027160644530,
9098 	0.875684559345245360,
9099 	0.876559674739837650,
9100 	0.876724362373352050,
9101 	0.876553714275360110,
9102 	0.875786423683166500
9103 };
9104 
9105 /* Initialise the calibration from the EEProm contents. */
9106 /* (We're handed a buffer that's been rounded up to an even 32 bits by */
9107 /* padding with zero's) */
munki_parse_eeprom(munki * p,unsigned char * buf,unsigned int len)9108 munki_code munki_parse_eeprom(munki *p, unsigned char *buf, unsigned int len) {
9109 	munkiimp *m = (munkiimp *)p->m;
9110 	mkdata *d;
9111 	int rv = MUNKI_OK;
9112 	unsigned int chsum, sum;
9113 	int calver, compver;			/* Calibration version and compatiblity version */
9114 	unsigned char chipid[8];		/* Calibration chip id */
9115 	int tint, *tinta;				/* Temporary */
9116 	double tdouble;					/* Temporary */
9117 	int i, j;
9118 
9119 	a1logd(p->log,2,"munki_parse_eeprom: called with %d bytes\n",len);
9120 
9121 	/* Check the checksum */
9122 	chsum = buf2uint(buf+8);
9123 	int2buf(buf+8, 0);				/* Zero it out */
9124 
9125 	for (sum = 0, i = 0; i < (len-3); i += 4) {
9126 		sum += buf2uint(buf + i);
9127 	}
9128 
9129 
9130 
9131 	a1logd(p->log,3,"munki_parse_eeprom: cal chsum = 0x%x, should be 0x%x - %s\n",sum,chsum, sum == chsum ? "OK": "BAD");
9132 	if (sum != chsum)
9133 		return MUNKI_INT_CALBADCHSUM;
9134 
9135 
9136 	/* Create class to handle EEProm parsing */
9137 	if ((d = m->data = new_mkdata(p, buf, len)) == NULL)
9138 		return MUNKI_INT_CREATE_EEPROM_STORE;
9139 
9140 	/* Check out the version */
9141 	if (d->get_u16_ints(d, &calver, 0, 1) == NULL)
9142 		return MUNKI_DATA_RANGE;
9143 	if (d->get_u16_ints(d, &compver, 2, 1) == NULL)
9144 		return MUNKI_DATA_RANGE;
9145 	a1logd(p->log,4,"cal version = %d, compatible with %d\n",calver,compver);
9146 
9147 	/* We understand versions 3 to 6 */
9148 
9149 	if (calver < 3 || compver < 3 || compver > 6)
9150 		return MUNKI_HW_CALIBVERSION;
9151 
9152 	/* Choose the version we will treat it as */
9153 	if (calver > 6 && compver <= 6)
9154 		m->calver = 6;
9155 	else
9156 		m->calver = calver;
9157 	a1logd(p->log,4,"Treating as cal version = %d\n",m->calver);
9158 
9159 	/* Parse all the calibration info common for vers 3 - 6 */
9160 
9161 	/* Production number */
9162 	if (d->get_32_ints(d, &m->prodno, 12, 1) == NULL)
9163 		return MUNKI_DATA_RANGE;
9164 	a1logd(p->log,4,"Produnction no = %d\n",m->prodno);
9165 
9166 	/* Chip HW ID */
9167 	if (d->get_8_char(d, (unsigned char *)chipid, 16, 8) == NULL)
9168 		return MUNKI_DATA_RANGE;
9169 	a1logd(p->log,4,"HW Id = %02x-%02x%02x%02x%02x%02x%02x%02x\n",
9170 		                           chipid[0], chipid[1], chipid[2], chipid[3],
9171 		                           chipid[4], chipid[5], chipid[6], chipid[7]);
9172 
9173 	/* Check that the chipid matches the calibration */
9174 	for (i = 0; i < 8; i++) {
9175 		if (chipid[i] != m->chipid[i])
9176 			return MUNKI_HW_CALIBMATCH;
9177 	}
9178 
9179 	/* Serial number */
9180 	if (d->get_8_asciiz(d, m->serno, 24, 16) == NULL)
9181 		return MUNKI_DATA_RANGE;
9182 	a1logd(p->log,4,"serial number '%s'\n",m->serno);
9183 
9184 	/* Underlying calibration information */
9185 
9186 	m->nsen = 137;			/* Sensor bands stored */
9187 	m->nraw = 128;			/* Raw bands stored */
9188 	m->nwav1 = 36;			/* Standard res number of cooked spectrum band */
9189 	m->wl_short1 = 380.0;	/* Standard res short and long wavelengths */
9190 	m->wl_long1 = 730.0;
9191 
9192 	/* Fill this in here too */
9193 	m->wl_short2 = HIGHRES_SHORT;
9194 	m->wl_long2 = HIGHRES_LONG;
9195 	m->nwav2 = (int)((m->wl_long2-m->wl_short2)/HIGHRES_WIDTH + 0.5) + 1;
9196 
9197 	/* Reflection wavelength calibration information */
9198 	/* This is setup assuming 128 raw bands, starting */
9199 	/* at offset 6 from the values returned by the hardware. */
9200 	if ((m->rmtx_index1 = d->get_32_ints(d, NULL, 40, 36)) == NULL)
9201 		return MUNKI_DATA_RANGE;
9202 
9203 	/* Fake the number of matrix cooeficients for each out wavelength */
9204 	if ((m->rmtx_nocoef1 = (int *)malloc(sizeof(int) * 36)) == NULL)
9205 		return MUNKI_DATA_MEMORY;
9206 	for (i = 0; i < 36; i++)
9207 		m->rmtx_nocoef1[i] = 16;
9208 
9209 	if ((m->rmtx_coef1 = d->get_32_doubles(d, NULL, 184, 36 * 16)) == NULL)
9210 		return MUNKI_DATA_RANGE;
9211 
9212 	if (p->log->debug >= 7) {
9213 		a1logd(p->log,7,"Reflectance matrix:\n");
9214 		for(i = 0; i < 36; i++) {
9215 			a1logd(p->log,7," Wave %d, index %d\n",i, m->rmtx_index1[i]);
9216 			for (j = 0; j < 16; j++) {
9217 				if (m->rmtx_coef1[i * 16 + j] != 0.0)
9218 					a1logd(p->log,7,"  Wt %d =  %f\n",j, m->rmtx_coef1[i * 16 + j]);
9219 			}
9220 		}
9221 	}
9222 
9223 	/* Emission wavelength calibration information */
9224 	if ((m->emtx_index1 = d->get_32_ints(d, NULL, 2488, 36)) == NULL)
9225 		return MUNKI_DATA_RANGE;
9226 
9227 	/* Fake the number of matrix cooeficients for each out wavelength */
9228 	if ((m->emtx_nocoef1 = (int *)malloc(sizeof(int) * 36)) == NULL)
9229 		return MUNKI_DATA_MEMORY;
9230 	for (i = 0; i < 36; i++)
9231 		m->emtx_nocoef1[i] = 16;
9232 
9233 	if ((m->emtx_coef1 = d->get_32_doubles(d, NULL, 2632, 36 * 16)) == NULL)
9234 		return MUNKI_DATA_RANGE;
9235 
9236 	if (p->log->debug >= 7) {
9237 		a1logd(p->log,5,"Emmission matrix:\n");
9238 		for(i = 0; i < 36; i++) {
9239 			a1logd(p->log,7," Wave %d, index %d\n",i, m->emtx_index1[i]);
9240 			for (j = 0; j < 16; j++) {
9241 				if (m->emtx_coef1[i * 16 + j] != 0.0)
9242 					a1logd(p->log,7,"  Wt %d =  %f\n",j, m->emtx_coef1[i * 16 + j]);
9243 			}
9244 		}
9245 	}
9246 
9247 	/* Linearization */
9248 	if ((m->lin0 = d->rget_32_doubles(d, NULL, 4936, 4)) == NULL)
9249 		return MUNKI_DATA_RANGE;
9250 	m->nlin0 = 4;
9251 
9252 	if ((m->lin1 = d->rget_32_doubles(d, NULL, 4952, 4)) == NULL)
9253 		return MUNKI_DATA_RANGE;
9254 	m->nlin1 = 4;
9255 
9256 	if (p->log->debug >= 3) {
9257 		char oline[200] = { '\000' }, *bp = oline;
9258 
9259 		bp += sprintf(bp,"Normal non-lin    =");
9260 		for(i = 0; i < m->nlin0; i++)
9261 			bp += sprintf(bp," %1.10f",m->lin0[i]);
9262 		bp += sprintf(bp,"\n");
9263 		a1logd(p->log,2,oline);
9264 
9265 		bp = oline;
9266 		bp += sprintf(bp,"High Gain non-lin =");
9267 		for(i = 0; i < m->nlin1; i++)
9268 			bp += sprintf(bp," %1.10f",m->lin1[i]);
9269 		bp += sprintf(bp,"\n");
9270 		a1logd(p->log,2,oline);
9271 	}
9272 
9273 	/* Reflectance reference */
9274 	if ((m->white_ref1 = d->get_32_doubles(d, NULL, 4968, 36)) == NULL)
9275 		return MUNKI_DATA_RANGE;
9276 
9277 	/* Emission reference */
9278 	if ((m->emis_coef1 = d->get_32_doubles(d, NULL, 5112, 36)) == NULL)
9279 		return MUNKI_DATA_RANGE;
9280 
9281 	/* Ambient reference */
9282 	if ((m->amb_coef1 = d->get_32_doubles(d, NULL, 5256, 36)) == NULL)
9283 		return MUNKI_DATA_RANGE;
9284 
9285 	/* Sensor target values */
9286 	if (d->get_u16_ints(d, &tint, 5400, 1) == NULL)
9287 		return MUNKI_DATA_RANGE;
9288 	m->minsval = (double)tint;
9289 	if (d->get_u16_ints(d, &tint, 5402, 1) == NULL)
9290 		return MUNKI_DATA_RANGE;
9291 	m->optsval = (double)tint;
9292 	if (d->get_u16_ints(d, &tint, 5404, 1) == NULL)
9293 		return MUNKI_DATA_RANGE;
9294 	m->maxsval = (double)tint;
9295 	if (d->get_u16_ints(d, &tint, 5406, 1) == NULL)
9296 		return MUNKI_DATA_RANGE;
9297 	m->satlimit = (double)tint;
9298 
9299 	a1logd(p->log,4,"Sensor targmin %.0f, opt %.0f, max %.0f, sat %.0f\n",
9300 	                           m->minsval,m->optsval,m->maxsval,m->satlimit);
9301 
9302 	if (d->get_32_doubles(d, &m->cal_int_time, 5408, 1) == NULL)
9303 		return MUNKI_DATA_RANGE;
9304 	m->cal_int_time *= 1e-3;				/* Convert to seconds */
9305 
9306 	if (d->get_32_ints(d, &tint, 5412, 1) == NULL)
9307 		return MUNKI_DATA_RANGE;
9308 	m->ledpreheattime = tint * 1e-3;	/* Convert to seconds */
9309 
9310 	if (d->get_32_ints(d, &tint, 5416, 1) == NULL)
9311 		return MUNKI_DATA_RANGE;
9312 	m->ledwaittime = tint * 1e-3;		/* Convert to seconds */
9313 
9314 	if (d->get_u16_ints(d, &m->ledholdtempdc, 5420, 1) == NULL)
9315 		return MUNKI_DATA_RANGE;
9316 
9317 	a1logd(p->log,4,"Cal int time %f, LED pre-heat %f, Led wait %f, LED hold temp duty cycle %d\n", m->cal_int_time, m->ledpreheattime, m->ledwaittime, m->ledholdtempdc);
9318 
9319 	if (d->get_u16_ints(d, &tint, 5422, 1) == NULL)
9320 		return MUNKI_DATA_RANGE;
9321 	m->refinvalidsampt = tint * 1e-3;	/* Convert to seconds */
9322 
9323 	if (d->get_32_ints(d, &tint, 5424, 1) == NULL)
9324 		return MUNKI_DATA_RANGE;
9325 	m->calscantime = tint * 1e-3;	/* Convert to seconds */
9326 
9327 	a1logd(p->log,4,"Invalid sample time %f, Cal scan time %f\n",
9328 	                           m->refinvalidsampt, m->calscantime);
9329 
9330 	/* Stray light compensation. Note that 16 bit numbers are signed. */
9331 	if ((tinta = d->get_16_ints(d, NULL, 5428, 36 * 36)) == NULL)
9332 		return MUNKI_DATA_RANGE;
9333 	if (m->calver >= 4) {
9334 		if (d->get_32_doubles(d, &tdouble, 8020, 1) == NULL)
9335 			return MUNKI_DATA_RANGE;
9336 	} else {
9337 		tdouble = 0.001;		/* Hmm. this is quite different to EEProm value */
9338 	}
9339 	/* Convert from ints to floats */
9340 	m->straylight1 = dmatrixz(0, 35, 0, 35);
9341 	for (i = 0; i < 36; i++) {
9342 		for (j = 0; j < 36; j++) {
9343 			m->straylight1[i][j] = tdouble * tinta[i * 36 + j];
9344 			if (i == j)
9345 				m->straylight1[i][j] += 1.0;
9346 		}
9347 	}
9348 	free(tinta);
9349 
9350 	if (p->log->debug >= 7) {
9351 		a1logd(p->log,7,"Stray Light matrix:\n");
9352 		for(i = 0; i < 36; i++) {
9353 			double sum = 0.0;
9354 			a1logd(p->log,7," Wave %d, index %d\n",i, m->rmtx_index1[i]);
9355 			for (j = 0; j < 36; j++) {
9356 				sum += m->straylight1[i][j];
9357 				a1logd(p->log,7,"  Wt %d = %f\n",j, m->straylight1[i][j]);
9358 			}
9359 			a1logd(p->log,7,"  Sum = %f\n",sum);
9360 		}
9361 	}
9362 
9363 	if (m->calver >= 5) {
9364 		/* Projector reference */
9365 		if ((m->proj_coef1 = d->get_32_doubles(d, NULL, 8024, 36)) == NULL)
9366 			return MUNKI_DATA_RANGE;
9367 
9368 		/* Apparently this can be faulty though. Check if it is */
9369 		for (i = 0; i < 6; i++) {
9370 			if (m->proj_coef1[i] != m->proj_coef1[i])
9371 				break;					/* Not Nan */
9372 		}
9373 		if (i == 6) {				/* First 6 are Nan's */
9374 			for (; i < 36; i++) {
9375 				if ((m->emis_coef1[i]/m->proj_coef1[i] - proj_check[i]) > 0.001)
9376 					break;			/* Not less than 0.001 */
9377 			}
9378 		}
9379 		if (i == 36) {		/* It's faulty */
9380 			free(m->proj_coef1);
9381 			m->proj_coef1 = NULL;		/* Fall through to fakeup */
9382 		}
9383 	}
9384 
9385 	if (m->proj_coef1 == NULL) {		/* Fake up a projector reference */
9386 		if ((m->proj_coef1 = (double *)malloc(sizeof(double) * 36)) == NULL)
9387 			return MUNKI_DATA_MEMORY;
9388 		for (i = 0; i < 36; i++) {
9389 			m->proj_coef1[i] = m->emis_coef1[i]/proj_fix[i];
9390 		}
9391 		a1logd(p->log,4,"Faked up projector cal reference\n");
9392 	}
9393 
9394 	if (m->calver >= 6) {
9395 		if (d->get_8_ints(d, &m->adctype, 8168, 1) == NULL)
9396 			return MUNKI_DATA_RANGE;
9397 	} else {
9398 		m->adctype = 0;
9399 	}
9400 
9401 	if (p->log->debug >= 7) {
9402 		a1logd(p->log,4,"White ref, emission cal, ambient cal, proj cal:\n");
9403 		for(i = 0; i < 36; i++) {
9404 			a1logd(p->log,7," %d: %f, %f, %f, %f\n",i, m->white_ref1[i], m->emis_coef1[i],
9405 			                                          m->amb_coef1[i], m->proj_coef1[i]);
9406 		}
9407 	}
9408 
9409 #ifdef PLOT_RCALCURVE
9410 	/* Plot the reflection reference curve */
9411 	{
9412 		int i;
9413 		double xx[36];
9414 		double y1[36];
9415 
9416 		for (i = 0; i < m->nwav1; i++) {
9417 			xx[i] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
9418 			y1[i] = m->white_ref1[i];
9419 		}
9420 		printf("Reflection Reference (Black)\n");
9421 		do_plot(xx, y1, NULL, NULL, 36);
9422 	}
9423 #endif /* PLOT_RCALCURVE */
9424 
9425 #ifdef PLOT_ECALCURVES
9426 	/* Plot the emission reference curves */
9427 	{
9428 		int i;
9429 		double xx[36];
9430 		double y1[36], y2[36], y3[36];
9431 
9432 		printf("Emission Reference (Black), Ambient (Red), Projector (Green)\n");
9433 		for (i = 0; i < m->nwav1; i++) {
9434 			xx[i] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
9435 			y1[i] = m->emis_coef1[i];
9436 			y2[i] = m->amb_coef1[i];
9437 			y3[i] = m->proj_coef1[i];
9438 			if (y3[i] > 0.02)
9439 				y3[i] = 0.02;
9440 		}
9441 		do_plot(xx, y1, y2, y3, 36);
9442 	}
9443 #endif /* PLOT_ECALCURVES */
9444 
9445 	/* Default to standard resolution */
9446 	m->nwav = m->nwav1;
9447 	m->wl_short = m->wl_short1;
9448 	m->wl_long = m->wl_long1;
9449 
9450 	m->rmtx_index  = m->rmtx_index1;
9451 	m->rmtx_nocoef = m->rmtx_nocoef1;
9452 	m->rmtx_coef   = m->rmtx_coef1;
9453 	m->emtx_index  = m->emtx_index1;
9454 	m->emtx_nocoef = m->emtx_nocoef1;
9455 	m->emtx_coef   = m->emtx_coef1;
9456 
9457 	m->white_ref = m->white_ref1;
9458 	m->emis_coef = m->emis_coef1;
9459 	m->amb_coef  = m->amb_coef1;
9460 	m->proj_coef = m->proj_coef1;
9461 	m->straylight = m->straylight1;
9462 
9463 	m->highgain = 1.0/m->lin1[1];	/* Gain is encoded in linearity */
9464 	a1logd(p->log,3, "highgain = %f\n",m->highgain);
9465 
9466 	return rv;
9467 }
9468 
9469 
9470 /* Return a pointer to an array of chars containing data from 8 bits. */
9471 /* If rv is NULL, the returned value will have been allocated, othewise */
9472 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_8_char(struct _mkdata * d,unsigned char * rv,int off,int count)9473 static unsigned char *mkdata_get_8_char(struct _mkdata *d, unsigned char *rv, int off, int count) {
9474 	int i;
9475 
9476 	if (count <= 0
9477 	 || off < 0
9478 	 || (off + count * 1) > d->len)
9479 		return NULL;
9480 
9481 	if (rv == NULL) {
9482 		if ((rv = (unsigned char *)malloc(sizeof(int) * count)) == NULL)
9483 			return NULL;
9484 	}
9485 
9486 	for (i = 0; i < count; i++, off += 1) {
9487 		rv[i] = d->buf[off];
9488 	}
9489 	return rv;
9490 }
9491 
9492 /* Return a pointer to an nul terminated string containing data from 8 bits. */
9493 /* If rv is NULL, the returned value will have been allocated, othewise */
9494 /* the rv will be returned. Return NULL if out of range. */
9495 /* An extra space and a nul terminator will be added to the eeprom data */
mkdata_get_8_asciiz(struct _mkdata * d,char * rv,int off,int count)9496 static char *mkdata_get_8_asciiz(struct _mkdata *d, char *rv, int off, int count) {
9497 	int i;
9498 
9499 	if (count <= 0
9500 	 || off < 0
9501 	 || (off + count * 1) > d->len)
9502 		return NULL;
9503 
9504 	if (rv == NULL) {
9505 		if ((rv = (char *)malloc(sizeof(int) * (count + 1))) == NULL)
9506 			return NULL;
9507 	}
9508 
9509 	for (i = 0; i < count; i++, off += 1) {
9510 		rv[i] = (char)d->buf[off];
9511 	}
9512 	rv[i] = '\000';
9513 
9514 	return rv;
9515 }
9516 
9517 /* Return a pointer to an array of ints containing data from 8 bits. */
9518 /* If rv is NULL, the returned value will have been allocated, othewise */
9519 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_8_ints(struct _mkdata * d,int * rv,int off,int count)9520 static int *mkdata_get_8_ints(struct _mkdata *d, int *rv, int off, int count) {
9521 	int i;
9522 
9523 	if (count <= 0
9524 	 || off < 0
9525 	 || (off + count * 1) > d->len)
9526 		return NULL;
9527 
9528 	if (rv == NULL) {
9529 		if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9530 			return NULL;
9531 	}
9532 
9533 	for (i = 0; i < count; i++, off += 1) {
9534 		rv[i] = ((signed char *)d->buf)[off];
9535 	}
9536 	return rv;
9537 }
9538 
9539 /* Return a pointer to an array of ints containing data from unsigned 8 bits. */
9540 /* If rv is NULL, the returned value will have been allocated, othewise */
9541 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_u8_ints(struct _mkdata * d,int * rv,int off,int count)9542 static int *mkdata_get_u8_ints(struct _mkdata *d, int *rv, int off, int count) {
9543 	int i;
9544 
9545 	if (count <= 0
9546 	 || off < 0
9547 	 || (off + count * 1) > d->len)
9548 		return NULL;
9549 
9550 	if (rv == NULL) {
9551 		if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9552 			return NULL;
9553 	}
9554 
9555 	for (i = 0; i < count; i++, off += 1) {
9556 		rv[i] = d->buf[off];
9557 	}
9558 	return rv;
9559 }
9560 
9561 /* Return a pointer to an array of ints containing data from 16 bits. */
9562 /* If rv is NULL, the returned value will have been allocated, othewise */
9563 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_16_ints(struct _mkdata * d,int * rv,int off,int count)9564 static int *mkdata_get_16_ints(struct _mkdata *d, int *rv, int off, int count) {
9565 	int i;
9566 
9567 	if (count <= 0
9568 	 || off < 0
9569 	 || (off + count * 2) > d->len)
9570 		return NULL;
9571 
9572 	if (rv == NULL) {
9573 		if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9574 			return NULL;
9575 	}
9576 
9577 	for (i = 0; i < count; i++, off += 2) {
9578 		rv[i] = buf2short(d->buf + off);
9579 	}
9580 	return rv;
9581 }
9582 
9583 /* Return a pointer to an array of ints containing data from unsigned 16 bits. */
9584 /* If rv is NULL, the returned value will have been allocated, othewise */
9585 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_u16_ints(struct _mkdata * d,int * rv,int off,int count)9586 static int *mkdata_get_u16_ints(struct _mkdata *d, int *rv, int off, int count) {
9587 	int i;
9588 
9589 	if (count <= 0
9590 	 || off < 0
9591 	 || (off + count * 2) > d->len)
9592 		return NULL;
9593 
9594 	if (rv == NULL) {
9595 		if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9596 			return NULL;
9597 	}
9598 
9599 	for (i = 0; i < count; i++, off += 2) {
9600 		rv[i] = buf2ushort(d->buf + off);
9601 	}
9602 	return rv;
9603 }
9604 
9605 /* Return a pointer to an array of ints containing data from 32 bits. */
9606 /* If rv is NULL, the returned value will have been allocated, othewise */
9607 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_32_ints(struct _mkdata * d,int * rv,int off,int count)9608 static int *mkdata_get_32_ints(struct _mkdata *d, int *rv, int off, int count) {
9609 	int i;
9610 
9611 	if (count <= 0
9612 	 || off < 0
9613 	 || (off + count * 4) > d->len)
9614 		return NULL;
9615 
9616 	if (rv == NULL) {
9617 		if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9618 			return NULL;
9619 	}
9620 
9621 	for (i = 0; i < count; i++, off += 4) {
9622 		rv[i] = buf2int(d->buf + off);
9623 	}
9624 	return rv;
9625 }
9626 
9627 /* Return a pointer to an array of unsigned ints containing data from unsigned 32 bits. */
9628 /* If rv is NULL, the returned value will have been allocated, othewise */
9629 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_u32_uints(struct _mkdata * d,unsigned int * rv,int off,int count)9630 static unsigned int *mkdata_get_u32_uints(struct _mkdata *d, unsigned int *rv, int off, int count) {
9631 	int i;
9632 
9633 	if (count <= 0
9634 	 || off < 0
9635 	 || (off + count * 4) > d->len)
9636 		return NULL;
9637 
9638 	if (rv == NULL) {
9639 		if ((rv = (unsigned int *)malloc(sizeof(unsigned int) * count)) == NULL)
9640 			return NULL;
9641 	}
9642 
9643 	for (i = 0; i < count; i++, off += 4) {
9644 		rv[i] = buf2uint(d->buf + off);
9645 	}
9646 	return rv;
9647 }
9648 
9649 /* Return a pointer to an array of doubles containing data from 32 bits. */
9650 /* If rv is NULL, the returned value will have been allocated, othewise */
9651 /* the rv will be returned. Return NULL if out of range or malloc failure. */
mkdata_get_32_doubles(struct _mkdata * d,double * rv,int off,int count)9652 static double *mkdata_get_32_doubles(struct _mkdata *d, double *rv, int off, int count) {
9653 	int i;
9654 
9655 	if (count <= 0
9656 	 || off < 0
9657 	 || (off + count * 4) > d->len)
9658 		return NULL;
9659 
9660 	if (rv == NULL) {
9661 		if ((rv = (double *)malloc(sizeof(double) * count)) == NULL)
9662 			return NULL;
9663 	}
9664 
9665 	for (i = 0; i < count; i++, off += 4) {
9666 		unsigned int val;
9667 		val = buf2uint(d->buf + off);
9668 		rv[i] = IEEE754todouble(val);
9669 	}
9670 	return rv;
9671 }
9672 
9673 /* Return a pointer to an array of doubles containing data from 32 bits, */
9674 /* with the array filled in reverse order. */
9675 /* If rv is NULL, the returned value will have been allocated, othewise */
9676 /* the rv will be returned. Return NULL if out of range or malloc failure. */
mkdata_rget_32_doubles(struct _mkdata * d,double * rv,int off,int count)9677 static double *mkdata_rget_32_doubles(struct _mkdata *d, double *rv, int off, int count) {
9678 	int i;
9679 
9680 	if (count <= 0
9681 	 || off < 0
9682 	 || (off + count * 4) > d->len)
9683 		return NULL;
9684 
9685 	if (rv == NULL) {
9686 		if ((rv = (double *)malloc(sizeof(double) * count)) == NULL)
9687 			return NULL;
9688 	}
9689 
9690 	for (i = count-1; i >= 0; i--, off += 4) {
9691 		unsigned int val;
9692 		val = buf2uint(d->buf + off);
9693 		rv[i] = IEEE754todouble(val);
9694 	}
9695 	return rv;
9696 }
9697 
9698 
9699 /* Destroy ourselves */
mkdata_del(mkdata * d)9700 static void mkdata_del(mkdata *d) {
9701 	del_a1log(d->log);		/* Unref it */
9702 	free(d);
9703 }
9704 
9705 /* Constructor for mkdata */
new_mkdata(munki * p,unsigned char * buf,int len)9706 mkdata *new_mkdata(munki *p, unsigned char *buf, int len) {
9707 	mkdata *d;
9708 	if ((d = (mkdata *)calloc(1, sizeof(mkdata))) == NULL) {
9709 		a1loge(p->log, 1, "new_mkdata: malloc failed!\n");
9710 		return NULL;
9711 	}
9712 
9713 	d->p = p;
9714 
9715 	d->log = new_a1log_d(p->log);	/* Take reference */
9716 
9717 	d->buf = buf;
9718 	d->len = len;
9719 
9720 	d->get_8_char      = mkdata_get_8_char;
9721 	d->get_8_asciiz    = mkdata_get_8_asciiz;
9722 	d->get_8_ints      = mkdata_get_8_ints;
9723 	d->get_u8_ints     = mkdata_get_u8_ints;
9724 	d->get_16_ints     = mkdata_get_16_ints;
9725 	d->get_u16_ints    = mkdata_get_u16_ints;
9726 	d->get_32_ints     = mkdata_get_32_ints;
9727 	d->get_u32_uints   = mkdata_get_u32_uints;
9728 	d->get_32_doubles  = mkdata_get_32_doubles;
9729 	d->rget_32_doubles = mkdata_rget_32_doubles;
9730 
9731 	d->del           = mkdata_del;
9732 
9733 	return d;
9734 }
9735 
9736 /* ----------------------------------------------------------------- */
9737