1 
2 /*
3  * Argyll Color Correction System
4  *
5  * Gretag i1Pro implementation functions
6  */
7 
8 /*
9  * Author: Graeme W. Gill
10  * Date:   24/11/2006
11  *
12  * Copyright 2006 - 2014 Graeme W. Gill
13  * All rights reserved.
14  *
15  * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
16  * see the License2.txt file for licencing details.
17  */
18 
19 /*
20    If you make use of the instrument driver code here, please note
21    that it is the author(s) of the code who take responsibility
22    for its operation. Any problems or queries regarding driving
23    instruments with the Argyll drivers, should be directed to
24    the Argyll's author(s), and not to any other party.
25 
26    If there is some instrument feature or function that you
27    would like supported here, it is recommended that you
28    contact Argyll's author(s) first, rather than attempt to
29    modify the software yourself, if you don't have firm knowledge
30    of the instrument communicate protocols. There is a chance
31    that an instrument could be damaged by an incautious command
32    sequence, and the instrument companies generally cannot and
33    will not support developers that they have not qualified
34    and agreed to support.
35  */
36 
37 /* TTBD:
38 
39 	Would be nice to have option to save raw scan data to .ti3 file,
40 	and then have a utility/option to replay it through scan
41 	recognition, to be able to help remote diagnose scan problems.
42 
43 	Some things probably aren't quite correct:
44 	The way the sensor saturation and optimal target is
45 	computed probably doesn't account for the dark level
46 	correctly, since the targets are in sensor value,
47 	but the comparison is done after subtracting black ??
48 	See the Munki implementation for an approach to fix this ??
49 
50 	It should be possible to add a refresh-display calibration
51 	routine based on an emissive scan + the auto-correlation
52 	(see i1d3.c). Whether this will noticably improve repeatibility
53 	remains to be seen.
54 
55 */
56 
57 /*
58 	Notes:
59 
60 	Naming of spectral values:
61 
62 		sensor  - the 16 bit values from the sensor including any dummy/shielded values
63 		raw		- the floating point values of the spectral section
64 		absraw	- raw after scaling for integration time and gain settings.
65 
66 	The Rev D seems to die if it is ever given a GET_STATUS. This is why
67 	the WinUSB driver can't be used with it.
68 
69  */
70 
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <ctype.h>
74 #include <string.h>
75 #include <time.h>
76 #include <stdarg.h>
77 #include <math.h>
78 #if defined(UNIX)
79 # include <utime.h>
80 #else
81 # include <sys/utime.h>
82 #endif
83 #include <sys/stat.h>
84 #ifndef SALONEINSTLIB
85 #include "copyright.h"
86 #include "aconfig.h"
87 #include "numlib.h"
88 #include "rspl.h"
89 #else /* SALONEINSTLIB */
90 #include <fcntl.h>
91 #include "sa_config.h"
92 #include "numsup.h"
93 #include "rspl1.h"
94 #endif /* SALONEINSTLIB */
95 #include "xspect.h"
96 #include "insttypes.h"
97 #include "conv.h"
98 #include "icoms.h"
99 #include "sort.h"
100 
101 /* Configuration */
102 #define ENABLE_2		/* [Def] Enable i1pro2/Rev E driver code, else treat i1pro2 as i1pro */
103 #undef USE_HIGH_GAIN_MODE /* [Und] Make use of high gain mode in Rev A-D mode */
104 #define USE_THREAD		/* Need to use thread, or there are 1.5 second internal */
105 						/* instrument delays ! */
106 #undef WAIT_FOR_DELAY_TRIGGER	/* [Und] Hack to diagnose threading problems */
107 #undef ENABLE_WRITE		/* [Und] Enable writing of calibration and log data to the EEProm */
108 #define ENABLE_NONVCAL	/* [Def] Enable saving calibration state between program runs in a file */
109 #define ENABLE_NONLINCOR	/* [Def] Enable non-linear correction */
110 #define ENABLE_BKDRIFTC	/* [Def] Enable Emis. Black drift compensation using sheilded cell values */
111 #define HEURISTIC_BKDRIFTC	/* [Def] Enable heusristic black drift correction */
112 
113 #define WLCALTOUT (24 * 60 * 60) /* [24 Hrs] Wavelength calibration timeout in seconds */
114 #define DCALTOUT  (     30 * 60) /* [30 Minutes] Dark Calibration timeout in seconds */
115 #define DCALTOUT2 ( 1 * 60 * 60) /* [1 Hr] i1pro2 Dark Calibration timeout in seconds */
116 #define WCALTOUT  ( 1 * 60 * 60) /* [1 Hr] White Calibration timeout in seconds */
117 
118 #define MAXSCANTIME 30.0	/* [30] Maximum scan time in seconds */
119 #define SW_THREAD_TIMEOUT	(10 * 60.0) 	/* [10 Min] Switch read thread timeout */
120 
121 #define SINGLE_READ		/* [Def] Use a single USB read for scan to eliminate latency issues. */
122 #define HIGH_RES		/* [Def] Enable high resolution spectral mode code. Dissable */
123 						/* to break dependency on rspl library. */
124 # undef FAST_HIGH_RES_SETUP	/* Slightly better accuracy ? */
125 
126 /* Debug [Und] */
127 #undef DEBUG			/* Turn on debug printfs */
128 #undef PLOT_DEBUG		/* Use plot to show readings & processing */
129 #undef PLOT_REFRESH 	/* Plot refresh rate measurement info */
130 #undef PLOT_UPDELAY		/* Plot data used to determine display update delay */
131 #undef DUMP_SCANV		/* Dump scan readings to a file "i1pdump.txt" */
132 #undef DUMP_DARKM		/* Append raw dark readings to file "i1pddump.txt" */
133 #undef APPEND_MEAN_EMMIS_VAL /* Append averaged uncalibrated reading to file "i1pdump.txt" */
134 #undef TEST_DARK_INTERP    /* Test out the dark interpolation (need DEBUG for plot) */
135 #undef PATREC_DEBUG			/* Print & Plot patch/flash recognition information */
136 #undef PATREC_ALLBANDS		/* Plot all bands of scan */
137 #undef PATREC_SAVETRIMMED	/* Saved trimmed raw to file "i1pro_raw_trimed_N.csv */
138 #undef IGNORE_WHITE_INCONS	/* Ignore define reference reading inconsistency */
139 #undef HIGH_RES_DEBUG
140 #undef HIGH_RES_PLOT
141 #undef ANALIZE_EXISTING		/* Analize the manufacturers existing filter shape */
142 #undef PLOT_BLACK_SUBTRACT	/* Plot temperature corrected black subtraction */
143 #undef FAKE_AMBIENT		/* Fake the ambient mode for a Rev A */
144 
145 #undef USE_SPOT_OMD				/* [Und] Use Original Manufacturers Driver timing. Reduce */
146 								/* integration time and lamp turn on time. */
147 
148 #define DISP_INTT 2.0			/* Seconds per reading in display spot mode */
149 								/* More improves repeatability in dark colors, but limits */
150 								/* the maximum brightness level befor saturation. */
151 								/* A value of 2.0 seconds has a limit of about 110 cd/m^2 */
152 #define DISP_INTT2 0.8			/* High brightness display spot mode seconds per reading, */
153 								/* Should be good up to 275 cd/m^2 */
154 #define DISP_INTT3 0.3			/* High brightness display spot mode seconds per reading, */
155 								/* Should be good up to 700 cd/m^2 */
156 #define DISP_INTT4 0.1			/* Very high brightness display spot mode seconds per reading, */
157 								/* Should be good up to 2000 cd/m^2 ? */
158 
159 #define ADARKINT_MAX 2.0		/* Max cal time for adaptive dark cal */
160 #define ADARKINT_MAX2 4.0		/* Max cal time for adaptive dark cal Rev E or no high gain */
161 
162 #define EMIS_SCALE_FACTOR 1.0	/* Emission mode scale factor */
163 #define AMB_SCALE_FACTOR 1.0	/* Ambient mode scale factor for Lux */
164 //#define AMB_SCALE_FACTOR (1.0/3.141592654)	/* Ambient mode scale factor - convert */
165 //								/* from Lux to Lux/PI */
166 //								/* These factors get the same behaviour as the GMB drivers. */
167 
168 #define NSEN_MAX 140			/* Maximum nsen value we can cope with */
169 
170 /* High res mode settings */
171 #define HIGHRES_SHORT 370.0		/* i1pro2 uses more of the CCD, */
172 #define HIGHRES_LONG  730.0		/* leaving less scope for extenion */
173 #define HIGHRES_WIDTH  (10.0/3.0) /* (The 3.3333 spacing and lanczos2 seems a good combination) */
174 #define HIGHRES_REF_MIN 375.0	  /* Too much stray light below this in reflective mode */
175 
176 #include "i1pro.h"
177 #include "i1pro_imp.h"
178 #include "xrga.h"
179 
180 /* - - - - - - - - - - - - - - - - - - */
181 #define LAMP_OFF_TIME 1500		/* msec to make sure lamp is dark for dark measurement */
182 #define PATCH_CONS_THR 0.1		/* Dark measurement consistency threshold */
183 
184 #define USE_RD_SYNC				/* Use mutex syncronisation, else depend on TRIG_DELAY */
185 #define TRIG_DELAY 10			/* Measure trigger delay to allow pending read, msec */
186 
187 /* - - - - - - - - - - - - - - - - - - */
188 
189 #if defined(DEBUG) || defined(PLOT_DEBUG) || defined(PATREC_DEBUG)
190 # include <plot.h>
191 #endif
192 
193 
194 #if defined(DEBUG) || defined(PLOT_DEBUG) || defined(HIGH_RES_PLOT) || defined(PATREC_DEBUG)
195 static int disdebplot = 0;
196 
197 #define DISDPLOT disdebplot = 1;
198 #define ENDPLOT disdebplot = 0;
199 
200 #else
201 
202 #define DISDPLOT
203 #define ENDPLOT
204 
205 #endif	/* DEBUG */
206 
207 #if defined(DEBUG) || defined(PLOT_DEBUG) || defined(PATREC_DEBUG)
208 /* ============================================================ */
209 /* Debugging support */
210 
211 /* Plot a CCD spectra */
plot_raw(double * data)212 void plot_raw(double *data) {
213 	int i;
214 	double xx[NSEN_MAX];
215 	double yy[NSEN_MAX];
216 
217 	if (disdebplot)
218 		return;
219 
220 	for (i = 0; i < 128; i++) {
221 		xx[i] = (double)i;
222 		yy[i] = data[i];
223 	}
224 	do_plot(xx, yy, NULL, NULL, 128);
225 }
226 
227 /* Plot two CCD spectra */
plot_raw2(double * data1,double * data2)228 void plot_raw2(double *data1, double *data2) {
229 	int i;
230 	double xx[128];
231 	double y1[128];
232 	double y2[128];
233 
234 	if (disdebplot)
235 		return;
236 
237 	for (i = 0; i < 128; i++) {
238 		xx[i] = (double)i;
239 		y1[i] = data1[i];
240 		y2[i] = data2[i];
241 	}
242 	do_plot(xx, y1, y2, NULL, 128);
243 }
244 
245 /* Plot a converted spectra */
plot_wav(i1proimp * m,int hires,double * data)246 void plot_wav(i1proimp *m, int hires, double *data) {
247 	int i;
248 	double xx[128];
249 	double yy[128];
250 
251 	if (disdebplot)
252 		return;
253 
254 	for (i = 0; i < m->nwav[hires]; i++) {
255 		xx[i] = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], i);
256 		yy[i] = data[i];
257 	}
258 	do_plot(xx, yy, NULL, NULL, m->nwav[hires]);
259 }
260 
261 /* Plot two converted spectra for the current res. mode */
plot_wav_2(i1proimp * m,int hires,double * data1,double * data2)262 void plot_wav_2(i1proimp *m, int hires, double *data1, double *data2) {
263 	int i;
264 	double xx[128];
265 	double y1[128];
266 	double y2[128];
267 
268 	if (disdebplot)
269 		return;
270 
271 	for (i = 0; i < m->nwav[hires]; i++) {
272 		xx[i] = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], i);
273 		y1[i] = data1[i];
274 		y2[i] = data2[i];
275 	}
276 	do_plot(xx, y1, y2, NULL, m->nwav[hires]);
277 }
278 
279 #endif	/* PLOT_DEBUG */
280 
281 /* ============================================================ */
282 
283 /* Return a linear interpolated spectral value. Clip to ends */
wav_lerp(i1proimp * m,int hires,double * ary,double wl)284 static double wav_lerp(i1proimp *m, int hires, double *ary, double wl) {
285 	int jj;
286 	double wl0, wl1, bl;
287 	double rv;
288 
289 	jj = (int)floor(XSPECT_DIX(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], wl));
290 	if (jj < 0)
291 		jj = 0;
292 	else if (jj > (m->nwav[hires]-2))
293 		jj = m->nwav[hires]-2;
294 
295 	wl0 = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], jj);
296 	wl1 = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], jj+1);
297 
298 	bl = (wl - wl0)/(wl1 - wl0);
299 	if (bl < 0.0)
300 		bl = 0;
301 	else if (bl > 1.0)
302 		bl = 1.0;
303 
304 	rv = (1.0 - bl) * ary[jj] + bl * ary[jj+1];
305 
306 	return rv;
307 }
308 
309 /* Same as above, but return cv value on clip */
wav_lerp_cv(i1proimp * m,int hires,double * ary,double wl,double cv)310 static double wav_lerp_cv(i1proimp *m, int hires, double *ary, double wl, double cv) {
311 	int jj;
312 	double wl0, wl1, bl;
313 	double rv;
314 
315 	jj = (int)floor(XSPECT_DIX(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], wl));
316 	if (jj < 0)
317 		jj = 0;
318 	else if (jj > (m->nwav[hires]-2))
319 		jj = m->nwav[hires]-2;
320 
321 	wl0 = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], jj);
322 	wl1 = XSPECT_WL(m->wl_short[hires], m->wl_long[hires], m->nwav[hires], jj+1);
323 
324 	bl = (wl - wl0)/(wl1 - wl0);
325 	if (bl < 0.0 || bl > 1.0)
326 		return cv;
327 
328 	rv = (1.0 - bl) * ary[jj] + bl * ary[jj+1];
329 
330 	return rv;
331 }
332 
333 /* ============================================================ */
334 
335 /* Implementation struct */
336 
337 /* Add an implementation structure */
add_i1proimp(i1pro * p)338 i1pro_code add_i1proimp(i1pro *p) {
339 	i1proimp *m;
340 
341 	if ((m = (i1proimp *)calloc(1, sizeof(i1proimp))) == NULL) {
342 		a1logd(p->log,1,"add_i1proimp malloc %ld bytes failed (1)\n",sizeof(i1proimp));
343 		return I1PRO_INT_MALLOC;
344 	}
345 	m->p = p;
346 
347 	/* EEProm data store */
348 	if ((m->data = new_i1data(m)) == NULL)
349 		return I1PRO_INT_CREATE_EEPROM_STORE;
350 
351 	m->lo_secs = 2000000000;		/* A very long time */
352 	m->msec = msec_time();
353 
354 	p->m = (void *)m;
355 	return I1PRO_OK;
356 }
357 
358 /* Shutdown instrument, and then destroy */
359 /* implementation structure */
del_i1proimp(i1pro * p)360 void del_i1proimp(i1pro *p) {
361 
362 	a1logd(p->log,5,"i1pro_del called\n");
363 
364 #ifdef ENABLE_NONVCAL
365 	/* Touch it so that we know when the instrument was last open */
366 	i1pro_touch_calibration(p);
367 #endif /* ENABLE_NONVCAL */
368 
369 	if (p->m != NULL) {
370 		int i, j;
371 		i1proimp *m = (i1proimp *)p->m;
372 		i1pro_state *s;
373 		i1pro_code ev;
374 
375 		if (p->itype != instI1Pro2 && (ev = i1pro_update_log(p)) != I1PRO_OK) {
376 			a1logd(p->log,2,"i1pro_update_log: Updating the cal and log parameters to"
377 			                                              " EEProm failed failed\n");
378 		}
379 
380 		/* i1pro_terminate_switch() seems to fail on a rev A & Rev C ?? */
381 		if (m->th != NULL) {		/* Terminate switch monitor thread */
382 			m->th_term = 1;			/* Tell thread to exit on error */
383 			i1pro_terminate_switch(p);
384 
385 			for (i = 0; m->th_termed == 0 && i < 5; i++)
386 				msec_sleep(50);		/* Wait for thread to terminate */
387 			if (i >= 5) {
388 				a1logd(p->log,5,"i1pro switch thread termination failed\n");
389 			}
390 			m->th->del(m->th);
391 			usb_uninit_cancel(&m->sw_cancel);		/* Don't need cancel token now */
392 			usb_uninit_cancel(&m->rd_sync);			/* Don't need sync token now */
393 			a1logd(p->log,5,"i1pro switch thread terminated\n");
394 		}
395 
396 		if (m->trig_thread != NULL) {
397 			m->trig_thread->del(m->trig_thread);
398 			a1logd(p->log,5,"i1pro trigger thread terminated\n");
399 		}
400 
401 		/* Free any per mode data */
402 		for (i = 0; i < i1p_no_modes; i++) {
403 			s = &m->ms[i];
404 
405 			free_dvector(s->dark_data, -1, m->nraw-1);
406 			free_dvector(s->dark_data2, -1, m->nraw-1);
407 			free_dvector(s->dark_data3, -1, m->nraw-1);
408 			free_dvector(s->dark_data4, -1, m->nraw-1);
409 			free_dvector(s->white_data, -1, m->nraw-1);
410 			free_dmatrix(s->idark_data, 0, 3, -1, m->nraw-1);
411 
412 			free_dvector(s->cal_factor[0], 0, m->nwav[0]-1);
413 			free_dvector(s->cal_factor[1], 0, m->nwav[1]-1);
414 		}
415 
416 		/* Free EEProm key data */
417 		if (m->data != NULL)
418 			m->data->del(m->data);
419 
420 		/* Free all Rev E and high res raw2wav filters */
421 		for (i = 0; i < 2; i++) {
422 			for (j = 0; j < 2; j++) {
423 				if (m->mtx_c[i][j].index != NULL)
424 					free(m->mtx_c[i][j].index);
425 				if (m->mtx_c[i][j].nocoef != NULL)
426 					free(m->mtx_c[i][j].nocoef);
427 				if (m->mtx_c[i][j].coef != NULL)
428 					free(m->mtx_c[i][j].coef);
429 			}
430 		}
431 
432 		/* Free RevE straylight arrays */
433 		for (i = 0; i < 2; i++) {
434 			if (m->straylight[i] != NULL)
435 				free_dmatrix(m->straylight[i], 0, m->nwav[i]-1, 0, m->nwav[i]-1);
436 		}
437 
438 		/* RevA-D high res. recal support */
439 		if (m->raw2wav != NULL)
440 			m->raw2wav->del(m->raw2wav);
441 
442 		free(m);
443 		p->m = NULL;
444 	}
445 }
446 
447 /* ============================================================ */
448 /* High level functions */
449 
450 /* Initialise our software state from the hardware */
i1pro_imp_init(i1pro * p)451 i1pro_code i1pro_imp_init(i1pro *p) {
452 	i1proimp *m = (i1proimp *)p->m;
453 	i1pro_code ev = I1PRO_OK;
454 	unsigned char *eeprom;	/* EEProm contents, i1pro = half, i1pro2 = full */
455 	int len = 8192;
456 	char *envv;
457 
458 	a1logd(p->log,5,"i1pro_init:\n");
459 
460 	m->native_calstd = xcalstd_gmdi;		/* Rev A-D */
461 	if (p->itype == instI1Pro2) {
462 		m->native_calstd = xcalstd_xrga;	/* Rev E */
463 	}
464 	m->target_calstd = xcalstd_native;		/* Default to native calibration */
465 
466 	/* Honor Environment override */
467 	if ((envv = getenv("ARGYLL_XCALSTD")) != NULL) {
468 		if (strcmp(envv, "XRGA") == 0)
469 			m->target_calstd = xcalstd_xrga;
470 		else if (strcmp(envv, "XRDI") == 0)
471 			m->target_calstd = xcalstd_xrdi;
472 		else if (strcmp(envv, "GMDI") == 0)
473 			m->target_calstd = xcalstd_gmdi;
474 	}
475 
476 	/* Revert to i1pro if i1pro2 driver is not enabled */
477 	if (p->itype == instI1Pro2
478 #ifdef ENABLE_2
479 	 && getenv("ARGYLL_DISABLE_I1PRO2_DRIVER") != NULL	/* Disabled by environment */
480 #endif
481 	) {
482 		p->itype = instI1Pro;
483 	}
484 
485 	if (p->itype != instI1Monitor
486 	 && p->itype != instI1Pro
487 	 && p->itype != instI1Pro2)
488 		return I1PRO_UNKNOWN_MODEL;
489 
490 	m->trig = inst_opt_trig_user;
491 	m->scan_toll_ratio = 1.0;
492 
493 	/* Take conservative approach to when the light was last on. */
494 	/* Assume it might have been on right before init was called again. */
495 	m->slamponoff = msec_time();
496 	m->llampoffon = msec_time();
497 	m->llamponoff = msec_time();
498 
499 	if ((ev = i1pro_reset(p, 0x1f)) != I1PRO_OK)
500 		return ev;
501 
502 	usb_init_cancel(&m->sw_cancel);			/* Init switch cancel token */
503 	usb_init_cancel(&m->rd_sync);			/* Init reading sync token */
504 
505 #ifdef USE_THREAD
506 	/* Setup the switch monitoring thread */
507 	if ((m->th = new_athread(i1pro_switch_thread, (void *)p)) == NULL)
508 		return I1PRO_INT_THREADFAILED;
509 #endif
510 
511 	/* Get the current misc. status, fwrev etc */
512 	if ((ev = i1pro_getmisc(p, &m->fwrev, NULL, &m->maxpve, NULL, &m->powmode)) != I1PRO_OK)
513 		return ev;
514 	a1logd(p->log,2,"Firmware rev = %d, max +ve value = 0x%x\n",m->fwrev, m->maxpve);
515 
516 	if (p->itype == instI1Pro2 && m->fwrev < 600) {		/* Hmm */
517 		a1logd(p->log,2, "Strange, firmware isn't up to i1pro2 but has extra pipe..revert to i1pro driver\n",m->fwrev);
518 		p->itype = instI1Pro;
519 	}
520 
521 	/* Get the EEProm size */
522 	m->eesize = 8192;		/* Rev A..D */
523 	if (p->itype == instI1Pro2) {
524 #ifdef NEVER
525 // ~~99		Hmm. makes it worse. Why ???
526 //		/* Make sure LED sequence is finished, because it interferes with EEProm read! */
527 //		if ((ev = i1pro2_indLEDoff(p)) != I1PRO_OK)
528 //			return ev;
529 #endif
530 
531 		if ((ev = i1pro2_geteesize(p, &m->eesize)) != I1PRO_OK) {
532 			return ev;
533 		}
534 
535 	}
536 
537 	if (m->eesize < 8192) {
538 		a1logd(p->log,2,"Strange, EEProm size is < 8192!\n",m->fwrev);
539 		return I1PRO_HW_EE_SIZE;
540 	}
541 
542 	if ((eeprom = (unsigned char *)malloc(m->eesize)) == NULL) {
543 		a1logd(p->log,1,"Malloc %d bytes for eeprom failed\n",m->eesize);
544 		return I1PRO_INT_MALLOC;
545 	}
546 
547 	/* Read the EEProm */
548 	if ((ev = i1pro_readEEProm(p, eeprom, 0, m->eesize)) != I1PRO_OK) {
549 		free(eeprom);
550 		return ev;
551 	}
552 
553 	if (p->itype == instI1Pro2) {
554 		/* Get the Chip ID (This doesn't work until after reading the EEProm !) */
555 		if ((ev = i1pro2_getchipid(p, m->chipid)) != I1PRO_OK) {
556 			free(eeprom);
557 			return ev;
558 		}
559 	}
560 
561 	/* Parse the i1pro data */
562 	if ((ev = m->data->parse_eeprom(m->data, eeprom, m->eesize, 0)) != I1PRO_OK) {
563 		free(eeprom);
564 		return ev;
565 	}
566 
567 	/* Parse the i1pro2 extra data */
568 	if (p->itype == instI1Pro2) {
569 		if ((ev = m->data->parse_eeprom(m->data, eeprom, m->eesize, 1)) != I1PRO_OK) {
570 			free(eeprom);
571 			return ev;
572 		}
573 	}
574 	free(eeprom); eeprom = NULL;
575 
576 	/* Setup various calibration parameters from the EEprom */
577 	{
578 		int *ip, i, xcount;
579 		unsigned int count;
580 		double *dp;
581 
582 		/* Information about the instrument */
583 
584 		if ((ip = m->data->get_ints(m->data, &count, key_serno)) == NULL || count < 1)
585 			return I1PRO_HW_CALIBINFO;
586 		m->serno = ip[0];
587 		a1logd(p->log,2,"Serial number = %d\n",m->serno);
588 		sprintf(m->sserno,"%ud",m->serno);
589 
590 		if ((ip = m->data->get_ints(m->data, &count, key_dom)) == NULL || count < 1)
591 			return I1PRO_HW_CALIBINFO;
592 		m->dom = ip[0];
593 		a1logd(p->log,2, "Date of manufactur = %d-%d-%d\n",
594 		                          m->dom/1000000, (m->dom/10000) % 100, m->dom % 10000);
595 
596 		if ((ip = m->data->get_ints(m->data, &count, key_cpldrev)) == NULL || count < 1)
597 			return I1PRO_HW_CALIBINFO;
598 		m->cpldrev = ip[0];
599 		a1logd(p->log,2,"CPLD rev = %d\n",m->cpldrev);
600 
601 		if ((ip = m->data->get_ints(m->data, &count, key_capabilities)) == NULL || count < 1)
602 			return I1PRO_HW_CALIBINFO;
603 		m->capabilities = ip[0];
604 		if (m->capabilities & 0x6000)		/* Has ambient */
605 			m->capabilities2 |= I1PRO_CAP2_AMBIENT;	/* Mimic in capabilities2 */
606 		a1logd(p->log,2,"Capabilities flag = 0x%x\n",m->capabilities);
607 		if (m->capabilities & 0x6000)
608 			a1logd(p->log,2," Can read ambient\n");
609 
610 		if ((ip = m->data->get_ints(m->data, &count, key_physfilt)) == NULL || count < 1)
611 			return I1PRO_HW_CALIBINFO;
612 		m->physfilt = ip[0];
613 		if (m->physfilt == 0x82)
614 			m->capabilities2 |= I1PRO_CAP2_UV_FILT;	/* Mimic in cap 2 */
615 		a1logd(p->log,2,"Physical filter flag = 0x%x\n",m->physfilt);
616 		if (m->physfilt == 0x80)
617 			a1logd(p->log,2," No filter fitted\n");
618 		else if (m->physfilt == 0x81)
619 			a1logd(p->log,2," Emission only ??\n");
620 		else if (m->physfilt == 0x82)
621 			a1logd(p->log,2," Is fitted with Ultra Violet Filter\n");
622 
623 		/* Underlying calibration information */
624 
625 		m->nsen = 128;
626 		m->nraw = 128;
627 		if (p->itype == instI1Pro2) {
628 			int clkusec, subdiv, xraw, nraw;
629 			if ((ev = i1pro2_getmeaschar(p, &clkusec, &xraw, &nraw, &subdiv)) != I1PRO_OK)
630 				return ev;
631 			m->intclkp2 = clkusec * 1e-6;	/* Rev E integration clock period, ie. 36 usec */
632 			m->subclkdiv2 = subdiv;			/* Rev E sub clock divider, ie. 136 */
633 
634 			m->nsen = nraw + xraw;
635 			if (clkusec != 36 || xraw != 6 || nraw != 128 || subdiv != 136)
636 				return I1PRO_HW_UNEX_SPECPARMS;
637 
638 			if (m->nsen > NSEN_MAX)		/* Static allocation assumed */
639 				return I1PRO_HW_UNEX_SPECPARMS;
640 		}
641 		if (m->data->get_ints(m->data, &m->nwav[0], key_mtx_index) == 0)
642 			return I1PRO_HW_CALIBINFO;
643 		if (m->nwav[0] != 36)
644 			return I1PRO_HW_CALIBINFO;
645 		m->wl_short[0] = 380.0;		/* Normal res. range */
646 		m->wl_long[0] = 730.0;
647 
648 		/* Fill high res in too */
649 		m->wl_short[1] = HIGHRES_SHORT;
650 		m->wl_long[1] = HIGHRES_LONG;
651 		m->nwav[1] = (int)((m->wl_long[1]-m->wl_short[1])/HIGHRES_WIDTH + 0.5) + 1;
652 
653 		if ((dp = m->data->get_doubles(m->data, &count, key_hg_factor)) == NULL || count < 1)
654 			return I1PRO_HW_CALIBINFO;
655 		m->highgain = dp[0];
656 		a1logd(p->log,2,"High gain         = %.10f\n",m->highgain);
657 
658 		if ((m->lin0 = m->data->get_doubles(m->data, &m->nlin0, key_ng_lin)) == NULL
659 		                                                               || m->nlin0 < 1)
660 			return I1PRO_HW_CALIBINFO;
661 
662 		if ((m->lin1 = m->data->get_doubles(m->data, &m->nlin1, key_hg_lin)) == NULL
663 		                                                               || m->nlin1 < 1)
664 			return I1PRO_HW_CALIBINFO;
665 
666 		if (p->log->debug >= 2) {
667 			char oline[200] = { '\000' }, *bp = oline;
668 
669 			bp += sprintf(bp,"Normal non-lin    =");
670 			for(i = 0; i < m->nlin0; i++)
671 				bp += sprintf(bp," %1.10f",m->lin0[i]);
672 			bp += sprintf(bp,"\n");
673 			a1logd(p->log,2,oline);
674 
675 			bp = oline;
676 			bp += sprintf(bp,"High Gain non-lin =");
677 			for(i = 0; i < m->nlin1; i++)
678 				bp += sprintf(bp," %1.10f",m->lin1[i]);
679 			bp += sprintf(bp,"\n");
680 			a1logd(p->log,2,oline);
681 		}
682 
683 		if ((dp = m->data->get_doubles(m->data, &count, key_min_int_time)) == NULL || count < 1)
684 			return I1PRO_HW_CALIBINFO;
685 		m->min_int_time = dp[0];
686 
687 		/* And then override it */
688 		if (p->itype == instI1Pro2) {
689 			m->min_int_time = m->subclkdiv2 * m->intclkp2;		/* 0.004896 */
690 		} else {
691 			if (m->fwrev >= 301)
692 				m->min_int_time = 0.004716;
693 			else
694 				m->min_int_time = 0.00884;		/* == 1 sub clock */
695 		}
696 
697 		if ((dp = m->data->get_doubles(m->data, &count, key_max_int_time)) == NULL || count < 1)
698 			return I1PRO_HW_CALIBINFO;
699 		m->max_int_time = dp[0];
700 
701 
702 		if ((m->mtx_o.index = m->data->get_ints(m->data, &count, key_mtx_index)) == NULL
703 		                                                                   || count != m->nwav[0])
704 			return I1PRO_HW_CALIBINFO;
705 
706 		if ((m->mtx_o.nocoef = m->data->get_ints(m->data, &count, key_mtx_nocoef)) == NULL
707 		                                                                   || count != m->nwav[0])
708 			return I1PRO_HW_CALIBINFO;
709 
710 		for (xcount = i = 0; i < m->nwav[0]; i++)	/* Count number expected in matrix coeffs */
711 			xcount += m->mtx_o.nocoef[i];
712 
713 		if ((m->mtx_o.coef = m->data->get_doubles(m->data, &count, key_mtx_coef)) == NULL
714 		                                                                    || count != xcount)
715 			return I1PRO_HW_CALIBINFO;
716 
717 		if ((m->white_ref[0] = m->data->get_doubles(m->data, &count, key_white_ref)) == NULL
718 		                                                                   || count != m->nwav[0]) {
719 			if (p->itype != instI1Monitor)
720 				return I1PRO_HW_CALIBINFO;
721 			m->white_ref[0] = NULL;
722 		}
723 
724 		if ((m->emis_coef[0] = m->data->get_doubles(m->data, &count, key_emis_coef)) == NULL
725 		                                                                   || count != m->nwav[0])
726 			return I1PRO_HW_CALIBINFO;
727 
728 		if ((m->amb_coef[0] = m->data->get_doubles(m->data, &count, key_amb_coef)) == NULL
729 		                                                                   || count != m->nwav[0]) {
730 			if (p->itype != instI1Monitor
731 			 && m->capabilities & 0x6000)		/* Expect ambient calibration */
732 				return I1PRO_HW_CALIBINFO;
733 			m->amb_coef[0] = NULL;
734 		}
735 		/* Default to original EEProm raw to wav filters values*/
736 		m->mtx[0][0] = m->mtx_o;	/* Std res reflective */
737 		m->mtx[0][1] = m->mtx_o;	/* Std res emissive */
738 
739 		if ((ip = m->data->get_ints(m->data, &count, key_sens_target)) == NULL || count < 1)
740 			return I1PRO_HW_CALIBINFO;
741 		m->sens_target = ip[0];
742 
743 		if ((ip = m->data->get_ints(m->data, &count, key_sens_dark)) == NULL || count < 1)
744 			return I1PRO_HW_CALIBINFO;
745 		m->sens_dark = ip[0];
746 
747 		if ((ip = m->data->get_ints(m->data, &count, key_ng_sens_sat)) == NULL || count < 1)
748 			return I1PRO_HW_CALIBINFO;
749 		m->sens_sat0 = ip[0];
750 
751 		if ((ip = m->data->get_ints(m->data, &count, key_hg_sens_sat)) == NULL || count < 1)
752 			return I1PRO_HW_CALIBINFO;
753 		m->sens_sat1 = ip[0];
754 
755 		a1logd(p->log,2,"sens_target %d, sens_dark %d, sens_sat0 %d, sens_sat1 %d\n",
756 		                      m->sens_target, m->sens_dark, m->sens_sat0, m->sens_sat1);
757 
758 		/* Then read the log data. Don't fatal error if there is a problem with this. */
759 		for (;;) {
760 
761 			/* Total Measure (Emis/Remis/Ambient/Trans/Cal) count */
762 			if ((ip = m->data->get_ints(m->data, &count, key_meascount)) == NULL || count < 1)
763 				break;
764 			m->meascount = ip[0];
765 
766 			/* Remspotcal last calibration date */
767 			if ((ip = m->data->get_ints(m->data, &count, key_caldate)) == NULL || count < 1)
768 				break;
769 			m->caldate = ip[0];
770 
771 			/* Remission spot measure count at last Remspotcal. */
772 			if ((ip = m->data->get_ints(m->data, &count, key_calcount)) == NULL || count < 1)
773 				break;
774 			m->calcount = ip[0];
775 
776 			/* Last remision spot reading integration time */
777 			if ((dp = m->data->get_doubles(m->data, &count, key_rpinttime)) == NULL || count < 1)
778 				break;
779 			m->rpinttime = dp[0];
780 
781 			/* Remission spot measure count */
782 			if ((ip = m->data->get_ints(m->data, &count, key_rpcount)) == NULL || count < 1)
783 				break;
784 			m->rpcount = ip[0];
785 
786 			/* Remission scan measure count (??) */
787 			if ((ip = m->data->get_ints(m->data, &count, key_acount)) == NULL || count < 1)
788 				break;
789 			m->acount = ip[0];
790 
791 			/* Total lamp usage time in seconds (??) */
792 			if ((dp = m->data->get_doubles(m->data, &count, key_lampage)) == NULL || count < 1)
793 				break;
794 			m->lampage = dp[0];
795 			a1logd(p->log,3,"Read log information OK\n");
796 
797 			break;
798 		}
799 	}
800 
801 	/* Read Rev E specific keys */
802 	if (p->itype == instI1Pro2) {
803 		int i, j;
804 		double *dp;
805 		int *sip;
806 		unsigned int count;
807 		int *ip;
808 
809 		/* Capability bits */
810 		if ((ip = m->data->get_ints(m->data, &count, key2_capabilities)) == NULL
811 		                                                                  || count != 1)
812 			return I1PRO_HW_CALIBINFO;
813 		m->capabilities2 = *ip;
814 		if (p->log->debug >= 2) {
815 			a1logd(p->log,2,"Capabilities2 flag = 0x%x\n",m->capabilities2);
816 			if (m->capabilities2 & I1PRO_CAP2_AMBIENT)
817 				a1logd(p->log,2," Can read ambient\n");
818 			if (m->capabilities2 & I1PRO_CAP2_WL_LED)
819 				a1logd(p->log,2," Has Wavelength Calibration LED\n");
820 			if (m->capabilities2 & I1PRO_CAP2_UV_LED)
821 				a1logd(p->log,2," Has Ultra Violet LED\n");
822 			if (m->capabilities2 & I1PRO_CAP2_ZEB_RUL)
823 				a1logd(p->log,2," Has Zebra Ruler sensor\n");
824 			if (m->capabilities2 & I1PRO_CAP2_IND_LED)
825 				a1logd(p->log,2," Has user indicator LEDs\n");
826 			if (m->capabilities2 & I1PRO_CAP2_UV_FILT)
827 				a1logd(p->log,2," Is fitted with Ultra Violet Filter\n");
828 		}
829 
830 		if (m->capabilities2 & I1PRO_CAP2_WL_LED) {
831 			/* wavelength LED calibration integration time (0.56660) */
832 			if ((dp = m->data->get_doubles(m->data, &count, key2_wlcal_intt)) == NULL
833 			                                                                  || count != 1)
834 				return I1PRO_HW_CALIBINFO;
835 			m->wl_cal_inttime = *dp;
836 
837 			/* Wavelength calibration minimum level */
838 			if ((ip = m->data->get_ints(m->data, &count, key2_wlcal_minlev)) == NULL
839 			                                                                  || count != 1)
840 				return I1PRO_HW_CALIBINFO;
841 			/* Normalize it to 1.0 seconds (ie. 500/0.56660) */
842 			m->wl_cal_min_level = (double)(*ip) / m->wl_cal_inttime;
843 
844 			/* wavelength LED measurement expected FWHM in nm */
845 			if ((dp = m->data->get_doubles(m->data, &count, key2_wlcal_fwhm)) == NULL
846 			                                                                  || count != 1)
847 				return I1PRO_HW_CALIBINFO;
848 			m->wl_cal_fwhm = *dp;
849 
850 			/* wavelength LED measurement FWHM tollerance in nm */
851 			if ((dp = m->data->get_doubles(m->data, &count, key2_wlcal_fwhm_tol)) == NULL
852 			                                                                  || count != 1)
853 				return I1PRO_HW_CALIBINFO;
854 			m->wl_cal_fwhm_tol = *dp;
855 
856 			/* wavelength LED reference spectrum */
857 			if ((m->wl_led_spec = m->data->get_doubles(m->data, &m->wl_led_count, key2_wlcal_spec)) == NULL)
858 				return I1PRO_HW_CALIBINFO;
859 
860 			/* wavelength LED spectraum reference offset */
861 			if ((ip = m->data->get_ints(m->data, &count, key2_wlcal_ooff)) == NULL
862 			                                                                  || count != 1)
863 				return I1PRO_HW_CALIBINFO;
864 			m->wl_led_ref_off = *ip;
865 			/* Hmm. this is odd, but it doesn't work correctly otherwise... */
866 			m->wl_led_ref_off--;
867 
868 			/* wavelength calibration maximum error */
869 			if ((dp = m->data->get_doubles(m->data, &count, key2_wlcal_max)) == NULL
870 			                                                                  || count != 1)
871 				return I1PRO_HW_CALIBINFO;
872 			m->wl_err_max = *dp;
873 		}
874 
875 		/* CCD bin to wavelength polinomial */
876 		if ((m->wlpoly1 = m->data->get_doubles(m->data, &count, key2_wlpoly_1)) == NULL || count != 4)
877 			return I1PRO_HW_CALIBINFO;
878 
879 		if ((m->wlpoly2 = m->data->get_doubles(m->data, &count, key2_wlpoly_2)) == NULL || count != 4)
880 			return I1PRO_HW_CALIBINFO;
881 
882 		/* Stray light compensation. Note that 16 bit numbers are signed. */
883 		if ((sip = m->data->get_shorts(m->data, &count, key2_straylight)) == NULL
884 		                                                          || count != (36 * 36))
885 			return I1PRO_HW_CALIBINFO;
886 
887 		/* stray light scale factor */
888 		if ((dp = m->data->get_doubles(m->data, &count, key2_straylight_scale)) == NULL
889 		                                                                  || count != 1)
890 			return I1PRO_HW_CALIBINFO;
891 
892 		/* Convert from ints to floats */
893 		m->straylight[0] = dmatrixz(0, 35, 0, 35);
894 		for (i = 0; i < 36; i++) {
895 			for (j = 0; j < 36; j++) {
896 				m->straylight[0][i][j] = *dp * sip[i * 36 + j]/32767.0;
897 				if (i == j)
898 					m->straylight[0][i][j] += 1.0;
899 			}
900 
901 		}
902 
903 		if (p->log->debug >= 7) {
904 			a1logd(p->log,7,"Stray Light matrix:\n");
905 			for(i = 0; i < 36; i++) {
906 				double sum = 0.0;
907 				for (j = 0; j < 36; j++) {
908 					sum += m->straylight[0][i][j];
909 					a1logd(p->log,7,"  Wt %d = %f\n",j, m->straylight[0][i][j]);
910 				}
911 				a1logd(p->log,7,"  Sum = %f\n",sum);
912 			}
913 		}
914 
915 #ifdef NEVER
916 	/* Plot raw2wav polinomials for Rev E */
917 	{
918 		double *xx;
919 		double *y1, *y2;		/* Rev E poly1 and poly2 */
920 		double d1, d2;
921 		int i, k;
922 
923 		xx = dvector(0, m->nraw);		/* X index = raw bin */
924 		y1 = dvector(0, m->nraw);		/* Y = nm */
925 		y2 = dvector(0, m->nraw);		/* Y = nm */
926 
927 		d1 = d2 = 0.0;
928 		for (i = 0; i < m->nraw; i++) {
929 			double iv, v1, v2;
930 			xx[i] = i;
931 
932 			iv = (double)(128-i);
933 
934 			for (v1 = m->wlpoly1[4-1], k = 4-2; k >= 0; k--)
935 				v1 = v1 * iv + m->wlpoly1[k];
936 			y1[i] = v1;
937 			d1 += fabs(y2[i] - yy[i]);
938 
939 			for (v2 = m->wlpoly2[4-1], k = 4-2; k >= 0; k--)
940 				v2 = v2 * iv + m->wlpoly2[k];
941 			y2[i] = v2;
942 			d2 += fabs(y3[i] - yy[i]);
943 
944 //			printf("ix %d, poly1 %f, poly2 %f, del12 %f\n",i, y1[i], y2[i], y2[i] - y1[i]);
945 		}
946 		printf("Avge del of poly1 = %f, poly2 = %f\n",d1/128.0, d2/128.0);
947 
948 		printf("CCD bin to wavelength mapping of RevE polinomial:\n");
949 		do_plot6(xx, y1, y2, NULL, NULL, NULL, NULL, m->nraw);
950 		free_dvector(xx, 0, m->nraw);
951 		free_dvector(y1, 0, m->nraw);
952 		free_dvector(y2, 0, m->nraw);
953 	}
954 #endif
955 
956 	}
957 
958 	/* Set up the current state of each mode */
959 	{
960 		int i, j;
961 		i1pro_state *s;
962 
963 		/* First set state to basic configuration */
964 		/* (We assume it's been zero'd) */
965 		for (i = 0; i < i1p_no_modes; i++) {
966 			s = &m->ms[i];
967 
968 			s->mode = i;
969 
970 			/* Default to an emissive configuration */
971 			s->targoscale = 1.0;	/* Default full scale */
972 			s->targmaxitime = 2.0; /* Maximum integration time to aim for */
973 			s->targoscale2 = 0.15;	 /* Proportion of targoscale to meed etargmaxitime2 (!higain) */
974 			s->gainmode = 0;		/* Normal gain mode */
975 
976 			s->inttime = 0.5;		/* Integration time */
977 			s->lamptime = 0.50;		/* Lamp turn on time (up to 1.0 sec is better, */
978 
979 			s->wl_valid = 0;
980 			s->wl_led_off = m->wl_led_ref_off;
981 
982 			s->dark_valid = 0;		/* Dark cal invalid */
983 			s->dark_data = dvectorz(-1, m->nraw-1);
984 			s->dark_data2 = dvectorz(-1, m->nraw-1);
985 			s->dark_data3 = dvectorz(-1, m->nraw-1);
986 			s->dark_data4 = dvectorz(-1, m->nraw-1);
987 
988 			s->cal_valid = 0;		/* Scale cal invalid */
989 			s->cal_factor[0] = dvectorz(0, m->nwav[0]-1);
990 			s->cal_factor[1] = dvectorz(0, m->nwav[1]-1);
991 			s->white_data = dvectorz(-1, m->nraw-1);
992 
993 			s->idark_valid = 0;		/* Dark cal invalid */
994 			s->idark_data = dmatrixz(0, 3, -1, m->nraw-1);
995 
996 			s->min_wl = 0.0;		/* No minimum by default */
997 
998 			s->dark_int_time  = DISP_INTT;	/* 2.0 */
999 			s->dark_int_time2 = DISP_INTT2;	/* 0.8 */
1000 			s->dark_int_time3 = DISP_INTT3;	/* 0.3 */
1001 			s->dark_int_time4 = DISP_INTT4;	/* 0.1 */
1002 
1003 			s->idark_int_time[0] = s->idark_int_time[2] = m->min_int_time;
1004 			if (p->itype == instI1Pro2) {
1005 				s->idark_int_time[1] = s->idark_int_time[3] = ADARKINT_MAX2; /* 4.0 */
1006 			} else {
1007 #ifdef USE_HIGH_GAIN_MODE
1008 				s->idark_int_time[1] = s->idark_int_time[3] = ADARKINT_MAX; /* 2.0 */
1009 #else
1010 				s->idark_int_time[1] = s->idark_int_time[3] = ADARKINT_MAX2; /* 4.0 */
1011 #endif
1012 			}
1013 
1014 			s->want_calib = 1;		/* Do an initial calibration */
1015 			s->want_dcalib = 1;
1016 		}
1017 
1018 		/* Then add mode specific settings */
1019 		for (i = 0; i < i1p_no_modes; i++) {
1020 			s = &m->ms[i];
1021 			switch(i) {
1022 				case i1p_refl_spot:
1023 					s->targoscale = 1.0;		/* Optimised sensor scaling to full */
1024 					s->reflective = 1;
1025 					s->adaptive = 1;
1026 					s->inttime = 0.02366;		/* Should get this from the log ?? */
1027 					s->dark_int_time = s->inttime;
1028 
1029 					s->dadaptime = 0.10;
1030 					s->wadaptime = 0.10;
1031 #ifdef USE_SPOT_OMD
1032 					s->lamptime  = 0.20332;		/* (Lamp doesn't stabilize with this) */
1033 					s->dcaltime  = 0.02366;
1034 					s->wcaltime  = 0.02366;
1035 					s->dreadtime = 0.02366;
1036 					s->wreadtime = 0.02366;
1037 #else
1038 #ifndef NEVER
1039 					s->lamptime = 0.25;			/* This should give better accuracy */
1040 					s->dcaltime = 0.05;			/* without increasing lamp usage much. */
1041 					s->wcaltime = 0.05;			/* Make it too large (ie. 1.0 sec total) */
1042 					s->dreadtime = 0.05;		/* and it will dirty the i1pro2 lamp quickly */
1043 					s->wreadtime = 0.05;		/* though. */
1044 #else
1045 #  pragma message("######### i1pro_imp.c Dirty Lamp timing !!!!! ########")
1046 					s->lamptime = 0.5;			/* Dirty up the lamp. */
1047 					s->dcaltime = 2.0;
1048 					s->wcaltime = 2.0;
1049 					s->dreadtime = 2.0;
1050 					s->wreadtime = 2.0;
1051 #endif
1052 #endif
1053 					s->maxscantime = 0.0;
1054 					s->min_wl = HIGHRES_REF_MIN;/* Too much stray light below this */
1055 												/* given low illumination < 375nm */
1056 					break;
1057 				case i1p_refl_scan:
1058 					s->reflective = 1;
1059 					s->scan = 1;
1060 					s->adaptive = 1;
1061 					s->inttime = m->min_int_time;	/* Maximize scan rate */
1062 					s->dark_int_time = s->inttime;
1063 					if (m->fwrev >= 301)			/* (We're not using scan targoscale though) */
1064 						s->targoscale = 0.25;
1065 					else
1066 						s->targoscale = 0.5;
1067 					s->lamptime = 0.5;				/* Lamp turn on time - lots to match scan */
1068 					s->dadaptime = 0.10;
1069 					s->wadaptime = 0.10;
1070 					s->dcaltime = 0.5;
1071 					s->wcaltime = 2.5;				/* Lots to get lamp up to temp */
1072 					s->dreadtime = 0.10;			/* and to match OMD scan cal. on time */
1073 					s->wreadtime = 0.10;
1074 					s->maxscantime = MAXSCANTIME;
1075 					s->min_wl = HIGHRES_REF_MIN;	/* Too much stray light below this */
1076 					break;
1077 
1078 				case i1p_emiss_spot_na:			/* Emissive spot not adaptive */
1079 					s->targoscale = 0.90;		/* Allow extra 10% margine for drift */
1080 					for (j = 0; j < m->nwav[0]; j++)
1081 						s->cal_factor[0][j] = EMIS_SCALE_FACTOR * m->emis_coef[0][j];
1082 					s->cal_valid = 1;
1083 					s->emiss = 1;
1084 					s->adaptive = 0;
1085 
1086 					s->inttime = DISP_INTT;		/* Default disp integration time (ie. 2.0 sec) */
1087 					s->lamptime = 0.0;
1088 					s->dark_int_time = s->inttime;
1089 					s->dark_int_time2 = DISP_INTT2;	/* Alternate disp integration time (ie. 0.8) */
1090 					s->dark_int_time3 = DISP_INTT3;	/* Alternate disp integration time (ie. 0.3) */
1091 					s->dark_int_time4 = DISP_INTT4;	/* Alternate disp integration time (ie. 0.1) */
1092 
1093 					s->dadaptime = 0.0;
1094 					s->wadaptime = 0.10;
1095 					s->dcaltime = DISP_INTT;		/* ie. determines number of measurements */
1096 					s->dcaltime2 = DISP_INTT2 * 2;	/* Make it 1.6 seconds (ie, 2 x 0.8 seconds) */
1097 					s->dcaltime3 = DISP_INTT3 * 3;	/* Make it 0.9 seconds (ie, 3 x 0.3 seconds) */
1098 					s->dcaltime4 = DISP_INTT4 * 3;	/* Make it 0.3 seconds (ie, 3 x 0.1 seconds) */
1099 					s->wcaltime = 0.0;
1100 					s->dreadtime = 0.0;
1101 					s->wreadtime = DISP_INTT;
1102 					s->maxscantime = 0.0;
1103 					break;
1104 				case i1p_emiss_spot:
1105 					s->targoscale = 0.90;		/* Allow extra 10% margine for drift */
1106 					for (j = 0; j < m->nwav[0]; j++)
1107 						s->cal_factor[0][j] = EMIS_SCALE_FACTOR * m->emis_coef[0][j];
1108 					s->cal_valid = 1;
1109 					s->emiss = 1;
1110 					s->adaptive = 1;
1111 
1112 					s->lamptime = 0.0;
1113 					s->dadaptime = 0.0;
1114 					s->wadaptime = 0.10;
1115 					s->dcaltime = 1.0;
1116 					s->wcaltime = 0.0;
1117 					s->dreadtime = 0.0;
1118 					s->wreadtime = 1.0;
1119 					s->maxscantime = 0.0;
1120 					break;
1121 				case i1p_emiss_scan:
1122 					for (j = 0; j < m->nwav[0]; j++)
1123 						s->cal_factor[0][j] = EMIS_SCALE_FACTOR * m->emis_coef[0][j];
1124 					s->cal_valid = 1;
1125 					s->emiss = 1;
1126 					s->scan = 1;
1127 					s->adaptive = 1;			/* ???? */
1128 					s->inttime = m->min_int_time;	/* Maximize scan rate */
1129 					s->lamptime = 0.0;
1130 					s->dark_int_time = s->inttime;
1131 					if (m->fwrev >= 301)
1132 						s->targoscale = 0.25;		/* (We're not using scan targoscale though) */
1133 					else
1134 						s->targoscale = 0.5;
1135 
1136 					s->dadaptime = 0.0;
1137 					s->wadaptime = 0.10;
1138 					s->dcaltime = 1.0;
1139 					s->wcaltime = 0.0;
1140 					s->dreadtime = 0.0;
1141 					s->wreadtime = 0.10;
1142 					s->maxscantime = MAXSCANTIME;
1143 					break;
1144 
1145 				case i1p_amb_spot:
1146 #ifdef FAKE_AMBIENT
1147 					for (j = 0; j < m->nwav[0]; j++)
1148 						s->cal_factor[0][j] = EMIS_SCALE_FACTOR * m->emis_coef[0][j];
1149 					s->cal_valid = 1;
1150 #else
1151 					if (m->amb_coef[0] != NULL) {
1152 						for (j = 0; j < m->nwav[0]; j++)
1153 							s->cal_factor[0][j] = AMB_SCALE_FACTOR * m->emis_coef[0][j] * m->amb_coef[0][j];
1154 						s->cal_valid = 1;
1155 					}
1156 #endif
1157 					s->emiss = 1;
1158 					s->ambient = 1;
1159 					s->adaptive = 1;
1160 
1161 					s->lamptime = 0.0;
1162 					s->dadaptime = 0.0;
1163 					s->wadaptime = 0.10;
1164 					s->dcaltime = 1.0;
1165 					s->wcaltime = 0.0;
1166 					s->dreadtime = 0.0;
1167 					s->wreadtime = 1.0;
1168 					s->maxscantime = 0.0;
1169 					break;
1170 				case i1p_amb_flash:
1171 				/* This is intended for measuring flashes */
1172 #ifdef FAKE_AMBIENT
1173 					for (j = 0; j < m->nwav[0]; j++)
1174 						s->cal_factor[0][j] = EMIS_SCALE_FACTOR * m->emis_coef[0][j];
1175 					s->cal_valid = 1;
1176 #else
1177 					if (m->amb_coef[0] != NULL) {
1178 						for (j = 0; j < m->nwav[0]; j++)
1179 							s->cal_factor[0][j] = AMB_SCALE_FACTOR * m->emis_coef[0][j] * m->amb_coef[0][j];
1180 						s->cal_valid = 1;
1181 					}
1182 #endif
1183 					s->emiss = 1;
1184 					s->ambient = 1;
1185 					s->scan = 1;
1186 					s->adaptive = 0;
1187 					s->flash = 1;
1188 
1189 					s->inttime = m->min_int_time;	/* Maximize scan rate */
1190 					s->lamptime = 0.0;
1191 					s->dark_int_time = s->inttime;
1192 					if (m->fwrev >= 301)
1193 						s->targoscale = 0.25;		/* (We're not using scan targoscale though) */
1194 					else
1195 						s->targoscale = 0.5;
1196 
1197 					s->dadaptime = 0.0;
1198 					s->wadaptime = 0.10;
1199 					s->dcaltime = 1.0;
1200 					s->wcaltime = 0.0;
1201 					s->dreadtime = 0.0;
1202 					s->wreadtime = 0.12;
1203 					s->maxscantime = MAXSCANTIME;
1204 					break;
1205 
1206 				case i1p_trans_spot:
1207 					s->trans = 1;
1208 					s->adaptive = 1;
1209 
1210 					s->lamptime = 0.0;
1211 					s->dadaptime = 0.10;
1212 					s->wadaptime = 0.10;
1213 					s->dcaltime = 1.0;
1214 					s->wcaltime = 1.0;
1215 					s->dreadtime = 0.0;
1216 					s->wreadtime = 1.0;
1217 					s->maxscantime = 0.0;
1218 					s->min_wl = HIGHRES_REF_MIN;	/* Too much stray light below this */
1219 					break;
1220 				case i1p_trans_scan:
1221 					s->trans = 1;
1222 					s->scan = 1;
1223 					s->adaptive = 0;
1224 					s->inttime = m->min_int_time;	/* Maximize scan rate */
1225 					s->dark_int_time = s->inttime;
1226 					if (m->fwrev >= 301)			/* (We're not using scan targoscale though) */
1227 						s->targoscale = 0.25;
1228 					else
1229 						s->targoscale = 0.5;
1230 
1231 					s->lamptime = 0.0;
1232 					s->dadaptime = 0.10;
1233 					s->wadaptime = 0.10;
1234 					s->dcaltime = 1.0;
1235 					s->wcaltime = 1.0;
1236 					s->dreadtime = 0.00;
1237 					s->wreadtime = 0.10;
1238 					s->maxscantime = MAXSCANTIME;
1239 					s->min_wl = HIGHRES_REF_MIN;	/* Too much stray light below this */
1240 					break;
1241 			}
1242 		}
1243 	}
1244 
1245 	if (p->itype != instI1Monitor		/* Monitor doesn't have reflective cal */
1246 	 && p->itype != instI1Pro2) {		/* Rev E mode has different calibration */
1247 		/* Restore the previous reflective spot calibration from the EEProm */
1248 		/* Get ready to operate the instrument */
1249 		if ((ev = i1pro_restore_refspot_cal(p)) != I1PRO_OK)
1250 			return ev;
1251 	}
1252 
1253 #ifdef ENABLE_NONVCAL
1254 	/* Restore the all modes calibration from the local system */
1255 	i1pro_restore_calibration(p);
1256 	/* Touch it so that we know when the instrument was last opened */
1257 	i1pro_touch_calibration(p);
1258 #endif
1259 
1260 	/* Get ready to operate the instrument */
1261 	if ((ev = i1pro_establish_high_power(p)) != I1PRO_OK)
1262 		return ev;
1263 
1264 	/* Get the current measurement parameters (why ?) */
1265 	if ((ev = i1pro_getmeasparams(p, &m->r_intclocks, &m->r_lampclocks, &m->r_nummeas, &m->r_measmodeflags)) != I1PRO_OK)
1266 		return ev;
1267 
1268 	if (p->log->verb >= 1) {
1269 		a1logv(p->log,1,"Instrument Type:   %s\n",inst_name(p->itype));
1270 		a1logv(p->log,1,"Serial Number:     %d\n",m->serno);
1271 		a1logv(p->log,1,"Firmware version:  %d\n",m->fwrev);
1272 		a1logv(p->log,1,"CPLD version:      %d\n",m->cpldrev);
1273 		if (p->itype == instI1Pro2)
1274 			a1logv(p->log,1,"Chip ID:           %02x-%02x%02x%02x%02x%02x%02x%02x\n",
1275 			                           m->chipid[0], m->chipid[1], m->chipid[2], m->chipid[3],
1276 			                           m->chipid[4], m->chipid[5], m->chipid[6], m->chipid[7]);
1277 		a1logv(p->log,1,"Date manufactured: %d-%d-%d\n",
1278 		                                 m->dom/1000000, (m->dom/10000) % 100, m->dom % 10000);
1279 		// Hmm. physfilt == 0x81 for instI1Monitor ???
1280 		a1logv(p->log,1,"U.V. filter ?:     %s\n",m->physfilt == 0x82 ? "Yes" : "No");
1281 		a1logv(p->log,1,"Measure Ambient ?: %s\n",m->capabilities & 0x6000 ? "Yes" : "No");
1282 
1283 		a1logv(p->log,1,"Tot. Measurement Count:           %d\n",m->meascount);
1284 		a1logv(p->log,1,"Remission Spot Count:             %d\n",m->rpcount);
1285 		a1logv(p->log,1,"Remission Scan Count:             %d\n",m->acount);
1286 		a1logv(p->log,1,"Date of last Remission spot cal:  %s",ctime(&m->caldate));
1287 		a1logv(p->log,1,"Remission Spot Count at last cal: %d\n",m->calcount);
1288 		a1logv(p->log,1,"Total lamp usage:                 %f\n",m->lampage);
1289 	}
1290 
1291 #ifdef NEVER
1292 // ~~99 play with LED settings
1293 	if (p->itype == instI1Pro2) {
1294 
1295 		/* Makes it white */
1296 		unsigned char b2[] = {
1297 			0x00, 0x00, 0x00, 0x02,
1298 
1299 			0x00, 0x00, 0x00, 0x0a,
1300 			0x00, 0x00, 0x00, 0x01,
1301 			0x00, 0x36, 0x00,
1302 			0x00, 0x00, 0x01,
1303 
1304 			0x00, 0x00, 0x00, 0x0a,
1305 			0xff, 0xff, 0xff, 0xff,
1306 			0x3f, 0x36, 0x40,
1307 			0x00, 0x00, 0x01
1308 		};
1309 
1310 		printf("~1 send led sequence length %d\n",sizeof(b2));
1311 		if ((ev = i1pro2_indLEDseq(p, b2, sizeof(b2))) != I1PRO_OK)
1312 			return ev;
1313 	}
1314 	/* Make sure LED sequence is finished, because it interferes with EEProm read! */
1315 	if ((ev = i1pro2_indLEDoff(p)) != I1PRO_OK)
1316 		return ev;
1317 #endif
1318 
1319 	return ev;
1320 }
1321 
1322 /* Return a pointer to the serial number */
i1pro_imp_get_serial_no(i1pro * p)1323 char *i1pro_imp_get_serial_no(i1pro *p) {
1324 	i1proimp *m = (i1proimp *)p->m;
1325 
1326 	return m->sserno;
1327 }
1328 
1329 /* Return non-zero if capable of ambient mode */
i1pro_imp_ambient(i1pro * p)1330 int i1pro_imp_ambient(i1pro *p) {
1331 
1332 	if (p->inited) {
1333 		i1proimp *m = (i1proimp *)p->m;
1334 		if (m->capabilities & 0x6000)		/* Expect ambient calibration */
1335 			return 1;
1336 #ifdef FAKE_AMBIENT
1337 		return 1;
1338 #endif
1339 		return 0;
1340 
1341 	} else {
1342 		return 0;
1343 	}
1344 }
1345 
1346 /* Set the measurement mode. It may need calibrating */
i1pro_imp_set_mode(i1pro * p,i1p_mode mmode,inst_mode mode)1347 i1pro_code i1pro_imp_set_mode(
1348 	i1pro *p,
1349 	i1p_mode mmode,		/* Operating mode */
1350 	inst_mode mode		/* Full mode mask for options */
1351 ) {
1352 	i1proimp *m = (i1proimp *)p->m;
1353 
1354 	a1logd(p->log,2,"i1pro_imp_set_mode called with mode no %d and mask 0x%x\n",mmode,m);
1355 	switch(mmode) {
1356 		case i1p_refl_spot:
1357 		case i1p_refl_scan:
1358 			if (p->itype == instI1Monitor)
1359 				return I1PRO_INT_ILLEGALMODE;		/* i1Monitor can't do reflection */
1360 			break;
1361 		case i1p_emiss_spot_na:
1362 		case i1p_emiss_spot:
1363 		case i1p_emiss_scan:
1364 			break;
1365 		case i1p_amb_spot:
1366 		case i1p_amb_flash:
1367 			if (!i1pro_imp_ambient(p))
1368 				return I1PRO_INT_ILLEGALMODE;
1369 			break;
1370 		case i1p_trans_spot:
1371 		case i1p_trans_scan:
1372 			break;
1373 		default:
1374 			return I1PRO_INT_ILLEGALMODE;
1375 	}
1376 	m->mmode = mmode;
1377 	m->spec_en = (mode & inst_mode_spectral) != 0;
1378 
1379 	if ((mode & inst_mode_highres) != 0) {
1380 		i1pro_code rv;
1381 		if ((rv = i1pro_set_highres(p)) != I1PRO_OK)
1382 			return rv;
1383 	} else {
1384 		i1pro_set_stdres(p);	/* Ignore any error */
1385 	}
1386 
1387 	m->uv_en = 0;
1388 
1389 	if (mmode == i1p_refl_spot
1390 	 || mmode == i1p_refl_scan)
1391 		m->uv_en = (mode & inst_mode_ref_uv) != 0;
1392 
1393 	return I1PRO_OK;
1394 }
1395 
1396 /* Return needed and available inst_cal_type's */
i1pro_imp_get_n_a_cals(i1pro * p,inst_cal_type * pn_cals,inst_cal_type * pa_cals)1397 i1pro_code i1pro_imp_get_n_a_cals(i1pro *p, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
1398 	i1proimp *m = (i1proimp *)p->m;
1399 	i1pro_state *cs = &m->ms[m->mmode];
1400 	time_t curtime = time(NULL);
1401 	inst_cal_type n_cals = inst_calt_none;
1402 	inst_cal_type a_cals = inst_calt_none;
1403 	int wl_valid = cs->wl_valid;			/* Locally timed out versions of valid state */
1404 	int idark_valid = cs->idark_valid;
1405 	int dark_valid = cs->dark_valid;
1406 	int cal_valid = cs->cal_valid;
1407 
1408 	a1logd(p->log,2,"i1pro_imp_get_n_a_cals: checking mode %d\n",m->mmode);
1409 
1410 	/* Timout calibrations that are too old */
1411 	if (m->capabilities2 & I1PRO_CAP2_WL_LED) {
1412 		if ((curtime - cs->wldate) > WLCALTOUT) {
1413 			a1logd(p->log,2,"Invalidating wavelength cal as %d secs from last cal\n",curtime - cs->wldate);
1414 			wl_valid = 0;
1415 		}
1416 	}
1417 	if ((curtime - cs->iddate) > ((p->itype == instI1Pro2) ? DCALTOUT2 : DCALTOUT)) {
1418 		a1logd(p->log,2,"Invalidating adaptive dark cal as %d secs from last cal\n",curtime - cs->iddate);
1419 		idark_valid = 0;
1420 	}
1421 	if ((curtime - cs->ddate) > ((p->itype == instI1Pro2) ? DCALTOUT2 : DCALTOUT)) {
1422 		a1logd(p->log,2,"Invalidating dark cal as %d secs from last cal\n",curtime - cs->ddate);
1423 		dark_valid = 0;
1424 	}
1425 	if (!cs->emiss && (curtime - cs->cfdate) > WCALTOUT) {
1426 		a1logd(p->log,2,"Invalidating white cal as %d secs from last cal\n",curtime - cs->cfdate);
1427 		cal_valid = 0;
1428 	}
1429 
1430 #ifdef NEVER
1431 	printf("~1 reflective = %d, adaptive = %d, emiss = %d, trans = %d, scan = %d\n",
1432 	cs->reflective, cs->adaptive, cs->emiss, cs->trans, cs->scan);
1433 	printf("~1 idark_valid = %d, dark_valid = %d, cal_valid = %d\n",
1434 	idark_valid,dark_valid,cal_valid);
1435 	printf("~1 want_calib = %d, want_dcalib = %d, noinitcalib = %d\n",
1436 	cs->want_calib,cs->want_dcalib, m->noinitcalib);
1437 #endif /* NEVER */
1438 
1439 	if (m->capabilities2 & I1PRO_CAP2_WL_LED) {
1440 		if (!wl_valid
1441 		 || (cs->want_dcalib && !m->noinitcalib))	// ?? want_dcalib ??
1442 			n_cals |= inst_calt_wavelength;
1443 		a_cals |= inst_calt_wavelength;
1444 	}
1445 	if (cs->reflective) {
1446 		if (!dark_valid
1447 		 || (cs->want_dcalib && !m->noinitcalib))
1448 			n_cals |= inst_calt_ref_dark;
1449 		a_cals |= inst_calt_ref_dark;
1450 
1451 		if (!cal_valid
1452 		 || (cs->want_calib && !m->noinitcalib))
1453 			n_cals |= inst_calt_ref_white;
1454 		a_cals |= inst_calt_ref_white;
1455 	}
1456 	if (cs->emiss) {
1457 		if ((!cs->adaptive && !dark_valid)
1458 		 || (cs->adaptive && !idark_valid)
1459 		 || (cs->want_dcalib && !m->noinitcalib))
1460 			n_cals |= inst_calt_em_dark;
1461 		a_cals |= inst_calt_em_dark;
1462 	}
1463 	if (cs->trans) {
1464 		if ((!cs->adaptive && !dark_valid)
1465 		 || (cs->adaptive && !idark_valid)
1466 	     || (cs->want_dcalib && !m->noinitcalib))
1467 			n_cals |= inst_calt_trans_dark;
1468 		a_cals |= inst_calt_trans_dark;
1469 
1470 		if (!cal_valid
1471 	     || (cs->want_calib && !m->noinitcalib))
1472 			n_cals |= inst_calt_trans_vwhite;
1473 		a_cals |= inst_calt_trans_vwhite;
1474 	}
1475 	if (cs->emiss && !cs->adaptive && !cs->scan) {
1476 		if (!cs->done_dintsel)
1477 			n_cals |= inst_calt_emis_int_time;
1478 		a_cals |= inst_calt_emis_int_time;
1479 	}
1480 
1481 	/* Special case high res. emissive cal fine calibration, */
1482 	/* needs reflective cal. */
1483 	/* Hmmm. Should we do this every time for emission, in case */
1484 	/* we switch to hires mode ??? */
1485 	if ((cs->emiss || cs->trans)			/* We're in an emissive mode */
1486 	 && m->hr_inited						/* and hi-res has been setup */
1487 	 && (!m->emis_hr_cal || (n_cals & inst_calt_em_dark)) /* and the emis cal hasn't been */
1488 											/* fine tuned or we will be doing a dark cal */
1489 	 && p->itype != instI1Monitor) {		/* i1Monitor doesn't have reflective cal capability */
1490 		n_cals |= inst_calt_ref_white;		/* Need a reflective white calibration */
1491 		a_cals |= inst_calt_ref_white;
1492 	}
1493 
1494 	if (pn_cals != NULL)
1495 		*pn_cals = n_cals;
1496 
1497 	if (pa_cals != NULL)
1498 		*pa_cals = a_cals;
1499 
1500 	a1logd(p->log,3,"i1pro_imp_get_n_a_cals: returning n_cals 0x%x, a_cals 0x%x\n",n_cals, a_cals);
1501 
1502 	return I1PRO_OK;
1503 }
1504 
1505 /* - - - - - - - - - - - - - - - - */
1506 /* Calibrate for the current mode. */
1507 /* Request an instrument calibration of the current mode. */
i1pro_imp_calibrate(i1pro * p,inst_cal_type * calt,inst_cal_cond * calc,inst_calc_id_type * idtype,char id[CALIDLEN])1508 i1pro_code i1pro_imp_calibrate(
1509 	i1pro *p,
1510 	inst_cal_type *calt,	/* Calibration type to do/remaining */
1511 	inst_cal_cond *calc,	/* Current condition/desired condition */
1512 	inst_calc_id_type *idtype,	/* Condition identifier type */
1513 	char id[CALIDLEN]		/* Condition identifier (ie. white reference ID) */
1514 ) {
1515 	i1pro_code ev = I1PRO_OK;
1516 	i1proimp *m = (i1proimp *)p->m;
1517 	int mmode = m->mmode;
1518 	i1pro_state *cs = &m->ms[m->mmode];
1519 	int sx1, sx2, sx3, sx;
1520 	time_t cdate = time(NULL);
1521 	int nummeas = 0;
1522 	int ltocmode = 0;			/* 1 = Lamp turn on compensation mode */
1523 	int i, k;
1524     inst_cal_type needed, available;
1525 
1526 	a1logd(p->log,2,"i1pro_imp_calibrate called with calt 0x%x, calc 0x%x\n",*calt, *calc);
1527 
1528 	if ((ev = i1pro_imp_get_n_a_cals(p, &needed, &available)) != I1PRO_OK)
1529 		return ev;
1530 
1531 	/* Translate inst_calt_all/needed into something specific */
1532 	if (*calt == inst_calt_all
1533 	 || *calt == inst_calt_needed
1534 	 || *calt == inst_calt_available) {
1535 		if (*calt == inst_calt_all)
1536 			*calt = (needed & inst_calt_n_dfrble_mask) | inst_calt_ap_flag;
1537 		else if (*calt == inst_calt_needed)
1538 			*calt = needed & inst_calt_n_dfrble_mask;
1539 		else if (*calt == inst_calt_available)
1540 			*calt = available & inst_calt_n_dfrble_mask;
1541 
1542 		a1logd(p->log,4,"i1pro_imp_calibrate: doing calt 0x%x\n",*calt);
1543 
1544 		if ((*calt & inst_calt_n_dfrble_mask) == 0)		/* Nothing todo */
1545 			return I1PRO_OK;
1546 	}
1547 
1548 	/* See if it's a calibration we understand */
1549 	if (*calt & ~available & inst_calt_all_mask) {
1550 		a1logd(p->log,4,"i1pro_imp_calibrate: unsupported, calt 0x%x, available 0x%x\n",*calt,available);
1551 		return I1PRO_UNSUPPORTED;
1552 	}
1553 
1554 	if (*calt & inst_calt_ap_flag) {
1555 		sx1 = 0; sx2 = sx3 = i1p_no_modes;		/* Go through all the modes */
1556 	} else {
1557 		/* Special case - doing reflective cal. to fix emiss hires */
1558 		if ((cs->emiss || cs->trans)			/* We're in an emissive mode */
1559 		 && (*calt & inst_calt_ref_white)) {		/* but we're asked for a ref white cal */
1560 			sx1 = m->mmode; sx2 = sx1 + 1;		/* Just current mode */
1561 			sx3 = i1p_refl_spot;				/* no extra mode */
1562 		} else {
1563 			sx1 = m->mmode; sx2 = sx1 + 1;		/* Just current mode */
1564 			sx3 = i1p_no_modes;					/* no extra mode */
1565 		}
1566 	}
1567 
1568 	/* Go through the modes we are going to cover */
1569 	for (sx = sx1; sx < sx2; (++sx >= sx2 && sx3 != i1p_no_modes) ? sx = sx3, sx2 = sx+1, sx3 = i1p_no_modes : 0) {
1570 		i1pro_state *s = &m->ms[sx];
1571 		m->mmode = sx;				/* A lot of functions we call rely on this */
1572 
1573 		a1logd(p->log,2,"\nCalibrating mode %d\n", s->mode);
1574 
1575 		/* Sanity check scan mode settings, in case something strange */
1576 		/* has been restored from the persistence file. */
1577 		if (s->scan && s->inttime > (2.1 * m->min_int_time)) {
1578 			s->inttime = m->min_int_time;	/* Maximize scan rate */
1579 		}
1580 
1581 		/* Wavelength calibration: */
1582 		if ((m->capabilities2 & I1PRO_CAP2_WL_LED)
1583 		 && (*calt & (inst_calt_wavelength | inst_calt_ap_flag))
1584 		 && ((*calc & inst_calc_cond_mask) == inst_calc_man_ref_white
1585 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_am_dark)) {
1586 			double *wlraw;
1587 			double optscale;
1588 			double *abswav;
1589 
1590 			a1logd(p->log,2,"\nDoing wavelength calibration\n");
1591 
1592 			wlraw = dvectorz(-1, m->nraw-1);
1593 
1594 			if ((ev = i1pro2_wl_measure(p, wlraw, &optscale, &m->wl_cal_inttime, 1.0)) != I1PRO_OK) {
1595 				a1logd(p->log,2,"i1pro2_wl_measure() failed\n");
1596 				return ev;
1597 			}
1598 
1599 			/* Find the best fit of the measured values to the reference spectrum */
1600 			if ((ev = i1pro2_match_wl_meas(p, &cs->wl_led_off, wlraw)) != I1PRO_OK) {
1601 				a1logd(p->log,2,"i1pro2_match_wl_meas() failed\n");
1602 				return ev;
1603 			}
1604 
1605 			free_dvector(wlraw, -1, m->nraw-1);
1606 
1607 			/* Compute normal res. emissive/transmissive wavelength corrected filters */
1608 			if ((ev = i1pro_compute_wav_filters(p, 0, 0)) != I1PRO_OK) {
1609 				a1logd(p->log,2,"i1pro_compute_wav_filters() failed\n");
1610 				return ev;
1611 			}
1612 
1613 			/* Compute normal res. reflective wavelength corrected filters */
1614 			if ((ev = i1pro_compute_wav_filters(p, 0, 1)) != I1PRO_OK) {
1615 				a1logd(p->log,2,"i1pro_compute_wav_filters() failed\n");
1616 				return ev;
1617 			}
1618 
1619 			/* Re-compute high res. wavelength corrected filters */
1620 			if (m->hr_inited && (ev = i1pro_create_hr(p)) != I1PRO_OK) {
1621 				a1logd(p->log,2,"i1pro_create_hr() failed\n");
1622 				return ev;
1623 			}
1624 
1625 			cs->wl_valid = 1;
1626 			cs->wldate = cdate;
1627 			*calt &= ~inst_calt_wavelength;
1628 
1629 			/* Save the calib to all modes */
1630 			a1logd(p->log,5,"Saving wavelength calib to similar modes\n");
1631 			for (i = 0; i < i1p_no_modes; i++) {
1632 				i1pro_state *ss = &m->ms[i];
1633 				if (ss == cs)
1634 					continue;
1635 				ss->wl_valid = cs->wl_valid;
1636 				ss->wldate = cs->wldate;
1637 				ss->wl_led_off = cs->wl_led_off;
1638 			}
1639 		}
1640 
1641 		/* Fixed int. time black calibration: */
1642 		/* Reflective uses on the fly black, even for adaptive. */
1643 		/* Emiss and trans can use single black ref only for non-adaptive */
1644 		/* using the current inttime & gainmode, while display mode */
1645 		/* does an extra fallback black cal for bright displays. */
1646 		if ((*calt & (inst_calt_ref_dark
1647 		            | inst_calt_em_dark
1648 		            | inst_calt_trans_dark | inst_calt_ap_flag))
1649 		 && ((*calc & inst_calc_cond_mask) == inst_calc_man_ref_white		/* Any condition conducive to dark calib */
1650 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_em_dark
1651 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_am_dark
1652 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_trans_dark)
1653 		 && ( s->reflective
1654 		  || (s->emiss && !s->adaptive && !s->scan)
1655 		  || (s->trans && !s->adaptive))) {
1656 			int stm;
1657 			int usesdct234 = 0;			/* Is a mode that uses dcaltime2, 3 & 4 */
1658 
1659 			if (s->emiss && !s->adaptive && !s->scan)
1660 				usesdct234 = 1;
1661 
1662 			nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->inttime);
1663 
1664 			a1logd(p->log,2,"\nDoing initial black calibration with dcaltime %f, int_time %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode);
1665 			stm = msec_time();
1666 			if ((ev = i1pro_dark_measure(p, s->dark_data,
1667 				                               nummeas, &s->inttime, s->gainmode)) != I1PRO_OK) {
1668 				m->mmode = mmode;			/* Restore actual mode */
1669 				return ev;
1670 			}
1671 			a1logd(p->log,2,"Execution time of dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1672 
1673 			/* Special display mode alternate integration time black measurement */
1674 			if (usesdct234) {
1675 				nummeas = i1pro_comp_nummeas(p, s->dcaltime2, s->dark_int_time2);
1676 				a1logd(p->log,2,"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);
1677 				stm = msec_time();
1678 				if ((ev = i1pro_dark_measure(p, s->dark_data2,
1679 					                       nummeas, &s->dark_int_time2, s->gainmode)) != I1PRO_OK) {
1680 					m->mmode = mmode;			/* Restore actual mode */
1681 					return ev;
1682 				}
1683 				a1logd(p->log,2,"Execution time of 2nd dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1684 
1685 				nummeas = i1pro_comp_nummeas(p, s->dcaltime3, s->dark_int_time3);
1686 				a1logd(p->log,2,"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);
1687 				nummeas = i1pro_comp_nummeas(p, s->dcaltime3, s->dark_int_time3);
1688 				stm = msec_time();
1689 				if ((ev = i1pro_dark_measure(p, s->dark_data3,
1690 					                       nummeas, &s->dark_int_time3, s->gainmode)) != I1PRO_OK) {
1691 					m->mmode = mmode;			/* Restore actual mode */
1692 					return ev;
1693 				}
1694 				a1logd(p->log,2,"Execution time of 3rd dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1695 
1696 				nummeas = i1pro_comp_nummeas(p, s->dcaltime4, s->dark_int_time4);
1697 				a1logd(p->log,2,"Doing 4th initial black calibration with dcaltime4 %f, dark_int_time4 %f, nummeas %d, gainmode %d\n", s->dcaltime4, s->dark_int_time4, nummeas, s->gainmode);
1698 				nummeas = i1pro_comp_nummeas(p, s->dcaltime4, s->dark_int_time4);
1699 				stm = msec_time();
1700 				if ((ev = i1pro_dark_measure(p, s->dark_data4,
1701 					                       nummeas, &s->dark_int_time4, s->gainmode)) != I1PRO_OK) {
1702 					m->mmode = mmode;			/* Restore actual mode */
1703 					return ev;
1704 				}
1705 				a1logd(p->log,2,"Execution time of 4rd dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1706 			}
1707 			s->dark_valid = 1;
1708 			s->want_dcalib = 0;
1709 			s->ddate = cdate;
1710 			s->dark_int_time = s->inttime;
1711 			s->dark_gain_mode = s->gainmode;
1712 			*calt &= ~(inst_calt_ref_dark
1713 		             | inst_calt_em_dark
1714 		             | inst_calt_trans_dark);
1715 
1716 			/* Save the calib to all similar modes */
1717 			for (i = 0; i < i1p_no_modes; i++) {
1718 				i1pro_state *ss = &m->ms[i];
1719 				if (ss == s || ss->ddate == cdate)
1720 					continue;
1721 				if (( s->reflective
1722 				  || (ss->emiss && !ss->adaptive && !ss->scan)
1723 				  || (ss->trans && !ss->adaptive))
1724 				 && ss->dark_int_time == s->dark_int_time
1725 				 && ss->dark_gain_mode == s->dark_gain_mode) {
1726 
1727 					ss->dark_valid = s->dark_valid;
1728 					ss->want_dcalib = s->want_dcalib;
1729 					ss->ddate = s->ddate;
1730 					ss->dark_int_time = s->dark_int_time;
1731 					ss->dark_gain_mode = s->dark_gain_mode;
1732 					for (k = -1; k < m->nraw; k++)
1733 						ss->dark_data[k] = s->dark_data[k];
1734 					/* If this is a mode with dark_data2/3/4, tranfer it too */
1735 					if (usesdct234 && ss->emiss && !ss->adaptive && !ss->scan) {
1736 						ss->dark_int_time2 = s->dark_int_time2;
1737 						ss->dark_int_time3 = s->dark_int_time3;
1738 						ss->dark_int_time4 = s->dark_int_time4;
1739 						for (k = -1; k < m->nraw; k++) {
1740 							ss->dark_data2[k] = s->dark_data2[k];
1741 							ss->dark_data3[k] = s->dark_data3[k];
1742 							ss->dark_data4[k] = s->dark_data4[k];
1743 						}
1744 					}
1745 				}
1746 			}
1747 		}
1748 
1749 		/* Emissive scan black calibration: */
1750 		/* Emsissive scan (flash) uses the fastest possible scan rate (??) */
1751 		if ((*calt & (inst_calt_em_dark | inst_calt_ap_flag))
1752 		 && ((*calc & inst_calc_cond_mask) == inst_calc_man_ref_white		/* Any condition conducive to dark calib */
1753 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_em_dark
1754 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_am_dark
1755 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_trans_dark)
1756 		 && (s->emiss && !s->adaptive && s->scan)) {
1757 			int stm;
1758 
1759 			nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->inttime);
1760 
1761 			a1logd(p->log,2,"\nDoing emissive (flash) black calibration with dcaltime %f, int_time %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode);
1762 			stm = msec_time();
1763 			if ((ev = i1pro_dark_measure(p, s->dark_data,
1764 				                                 nummeas, &s->inttime, s->gainmode)) != I1PRO_OK) {
1765 				m->mmode = mmode;			/* Restore actual mode */
1766 				return ev;
1767 			}
1768 			a1logd(p->log,2,"Execution time of dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1769 
1770 			s->dark_valid = 1;
1771 			s->want_dcalib = 0;
1772 			s->ddate = cdate;
1773 			s->dark_int_time = s->inttime;
1774 			s->dark_gain_mode = s->gainmode;
1775 			*calt &= ~inst_calt_em_dark;
1776 
1777 			/* Save the calib to all similar modes */
1778 			/* We're assuming they have the same int times */
1779 			a1logd(p->log,5,"Saving emissive scan black calib to similar modes\n");
1780 			for (i = 0; i < i1p_no_modes; i++) {
1781 				i1pro_state *ss = &m->ms[i];
1782 				if (ss == s || ss->ddate == s->ddate)
1783 					continue;
1784 				if (ss->emiss && !ss->adaptive && ss->scan) {
1785 					ss->dark_valid = s->dark_valid;
1786 					ss->want_dcalib = s->want_dcalib;
1787 					ss->ddate = s->ddate;
1788 					ss->dark_int_time = s->dark_int_time;
1789 					ss->dark_gain_mode = s->dark_gain_mode;
1790 					for (k = -1; k < m->nraw; k++)
1791 						ss->dark_data[k] = s->dark_data[k];
1792 				}
1793 			}
1794 		}
1795 
1796 		/* Adaptive black calibration: */
1797 		/* Deal with an emmissive/transmissive black reference */
1798 		/* in non-scan mode, where the integration time and gain may vary. */
1799 		/* The black is interpolated from readings with two extreme integration times */
1800 		if ((*calt & (inst_calt_ref_dark
1801 		            | inst_calt_em_dark
1802 		            | inst_calt_trans_dark | inst_calt_ap_flag))
1803 		 /* Any condition conducive to dark calib */
1804 		 && ((*calc & inst_calc_cond_mask) == inst_calc_man_ref_white
1805 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_em_dark
1806 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_am_dark
1807 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_trans_dark)
1808 		 && ((s->emiss && s->adaptive && !s->scan)
1809 		  || (s->trans && s->adaptive && !s->scan))) {
1810 			int i, j, k;
1811 
1812 			a1logd(p->log,2,"\nDoing emis/trans adapative black calibration\n");
1813 
1814 			/* Adaptive where we can't measure the black reference on the fly, */
1815 			/* so bracket it and interpolate. */
1816 			/* The black reference is probably temperature dependent, but */
1817 			/* there's not much we can do about this. */
1818 
1819 			nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->idark_int_time[0]);
1820 			a1logd(p->log,2,"\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);
1821 			if ((ev = i1pro_dark_measure(p, s->idark_data[0],
1822 				                          nummeas, &s->idark_int_time[0], 0)) != I1PRO_OK) {
1823 				m->mmode = mmode;			/* Restore actual mode */
1824 				return ev;
1825 			}
1826 
1827 			nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->idark_int_time[1]);
1828 			a1logd(p->log,2,"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);
1829 			if ((ev = i1pro_dark_measure(p, s->idark_data[1],
1830 				                          nummeas, &s->idark_int_time[1], 0)) != I1PRO_OK) {
1831 				m->mmode = mmode;			/* Restore actual mode */
1832 				return ev;
1833 			}
1834 
1835 #ifdef USE_HIGH_GAIN_MODE
1836 			if (p->itype != instI1Pro2) {	/* Rev E doesn't have high gain mode */
1837 				nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]);
1838 				a1logd(p->log,2,"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);
1839 				if ((ev = i1pro_dark_measure(p, s->idark_data[2],
1840 					                       nummeas, &s->idark_int_time[2], 1)) != I1PRO_OK) {
1841 					m->mmode = mmode;			/* Restore actual mode */
1842 					return ev;
1843 				}
1844 
1845 				a1logd(p->log,2,"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);
1846 				nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->idark_int_time[3]);
1847 				if ((ev = i1pro_dark_measure(p, s->idark_data[3],
1848 					                        nummeas, &s->idark_int_time[3], 1)) != I1PRO_OK) {
1849 					m->mmode = mmode;			/* Restore actual mode */
1850 					return ev;
1851 				}
1852 			}
1853 #endif /* USE_HIGH_GAIN_MODE */
1854 
1855 			i1pro_prepare_idark(p);
1856 			s->idark_valid = 1;
1857 			s->iddate = cdate;
1858 
1859 			if ((ev = i1pro_interp_dark(p, s->dark_data, s->inttime, s->gainmode)) != I1PRO_OK) {
1860 				m->mmode = mmode;			/* Restore actual mode */
1861 				return ev;
1862 			}
1863 			s->dark_valid = 1;
1864 			s->want_dcalib = 0;
1865 			s->ddate = s->iddate;
1866 			s->dark_int_time = s->inttime;
1867 			s->dark_gain_mode = s->gainmode;
1868 			*calt &= ~(inst_calt_ref_dark
1869 		            | inst_calt_em_dark
1870 		            | inst_calt_trans_dark);
1871 
1872 			/* Save the calib to all similar modes */
1873 			/* We're assuming they have the same int times */
1874 			a1logd(p->log,5,"Saving adaptive black calib to similar modes\n");
1875 			for (i = 0; i < i1p_no_modes; i++) {
1876 				i1pro_state *ss = &m->ms[i];
1877 				if (ss == s || ss->iddate == s->iddate)
1878 					continue;
1879 				if ((ss->emiss || ss->trans) && ss->adaptive && !ss->scan) {
1880 					ss->idark_valid = s->idark_valid;
1881 					ss->want_dcalib = s->want_dcalib;
1882 					ss->iddate = s->iddate;
1883 					ss->dark_int_time = s->dark_int_time;
1884 					ss->dark_gain_mode = s->dark_gain_mode;
1885 #ifdef USE_HIGH_GAIN_MODE
1886 					for (j = 0; j < (p->itype != instI1Pro2) ? 4 : 2; j++)
1887 #else
1888 					for (j = 0; j < 2; j++)
1889 #endif
1890 					{
1891 						ss->idark_int_time[j] = s->idark_int_time[j];
1892 						for (k = -1; k < m->nraw; k++)
1893 							ss->idark_data[j][k] = s->idark_data[j][k];
1894 					}
1895 				}
1896 			}
1897 
1898 			a1logd(p->log,5,"Done adaptive interpolated black calibration\n");
1899 
1900 			/* Test accuracy of dark level interpolation */
1901 #ifdef TEST_DARK_INTERP
1902 			{
1903 				double tinttime;
1904 				double ref[128], interp[128];
1905 
1906 	//			fprintf(stderr,"Normal gain offsets:\n");
1907 	//			plot_raw(s->idark_data[0]);
1908 	//			fprintf(stderr,"Normal gain multiplier:\n");
1909 	//			plot_raw(s->idark_data[1]);
1910 
1911 #ifdef DUMP_DARKM
1912 				extern int ddumpdarkm;
1913 				ddumpdarkm = 1;
1914 #endif
1915 				for (tinttime = m->min_int_time; ; tinttime *= 2.0) {
1916 					if (tinttime >= m->max_int_time)
1917 						tinttime = m->max_int_time;
1918 					nummeas = i1pro_comp_nummeas(p, s->dcaltime, tinttime);
1919 					if ((ev = i1pro_dark_measure(p, ref, nummeas, &tinttime, 0)) != I1PRO_OK) {
1920 						m->mmode = mmode;			/* Restore actual mode */
1921 						return ev;
1922 					}
1923 					i1pro_interp_dark(p, interp, tinttime, 0);
1924 #ifdef DEBUG
1925 					fprintf(stderr,"Low gain ref vs. interp dark offset for inttime %f:\n",tinttime);
1926 					plot_raw2(ref, interp);
1927 #endif
1928 					if ((tinttime * 1.1) > m->max_int_time)
1929 						break;
1930 				}
1931 #ifdef DUMP_DARKM
1932 				ddumpdarkm = 0;
1933 #endif
1934 
1935 #ifdef USE_HIGH_GAIN_MODE
1936 				if (p->itype != instI1Pro2) {	/* Rev E doesn't have high gain mode */
1937 	//				fprintf(stderr,"High gain offsets:\n");
1938 	//				plot_raw(s->idark_data[2]);
1939 	//				fprintf(stderr,"High gain multiplier:\n");
1940 	//				plot_raw(s->idark_data[3]);
1941 
1942 					for (tinttime = m->min_int_time; ; tinttime *= 2.0) {
1943 						if (tinttime >= m->max_int_time)
1944 							tinttime = m->max_int_time;
1945 						nummeas = i1pro_comp_nummeas(p, s->dcaltime, tinttime);
1946 						if ((ev = i1pro_dark_measure(p, ref, nummeas, &tinttime, 1)) != I1PRO_OK) {
1947 							m->mmode = mmode;			/* Restore actual mode */
1948 							return ev;
1949 						}
1950 						i1pro_interp_dark(p, interp, tinttime, 1);
1951 #ifdef DEBUG
1952 						fprintf(stderr,"High gain ref vs. interp dark offset for inttime %f:\n",tinttime);
1953 						plot_raw2(ref, interp);
1954 #endif
1955 						if ((tinttime * 1.1) > m->max_int_time)
1956 							break;
1957 					}
1958 				}
1959 #endif /* USE_HIGH_GAIN_MODE */
1960 			}
1961 #endif	/* NEVER */
1962 
1963 		}
1964 
1965 		/* Deal with an emissive/transmisive adaptive black reference */
1966 		/* when in scan mode. */
1967 		if ((*calt & (inst_calt_ref_dark
1968 		            | inst_calt_em_dark
1969 		            | inst_calt_trans_dark | inst_calt_ap_flag))
1970 		 /* Any condition conducive to dark calib */
1971 		 && ((*calc & inst_calc_cond_mask) == inst_calc_man_ref_white
1972 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_em_dark
1973 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_am_dark
1974 		  || (*calc & inst_calc_cond_mask) == inst_calc_man_trans_dark)
1975 		 && ((s->emiss && s->adaptive && s->scan)
1976 		  || (s->trans && s->adaptive && s->scan))) {
1977 			int j;
1978 
1979 			a1logd(p->log,2,"\nDoing emis/trans adapative scan mode black calibration\n");
1980 
1981 			/* We know scan is locked to the minimum integration time, */
1982 			/* so we can measure the dark data at that integration time, */
1983 			/* but we don't know what gain mode will be used, so measure both, */
1984 			/* and choose the appropriate one on the fly. */
1985 
1986 			s->idark_int_time[0] = s->inttime;
1987 			nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->idark_int_time[0]);
1988 			a1logd(p->log,2,"\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);
1989 			if ((ev = i1pro_dark_measure(p, s->idark_data[0],
1990 				                                  nummeas, &s->idark_int_time[0], 0)) != I1PRO_OK) {
1991 				m->mmode = mmode;			/* Restore actual mode */
1992 				return ev;
1993 			}
1994 
1995 #ifdef USE_HIGH_GAIN_MODE
1996 			if (p->itype != instI1Pro2) {	/* Rev E doesn't have high gain mode */
1997 				s->idark_int_time[2] = s->inttime;
1998 				nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]);
1999 				a1logd(p->log,2,"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);
2000 				if ((ev = i1pro_dark_measure(p, s->idark_data[2],
2001 					                           nummeas, &s->idark_int_time[2], 1)) != I1PRO_OK) {
2002 					m->mmode = mmode;			/* Restore actual mode */
2003 					return ev;
2004 				}
2005 			}
2006 #endif
2007 
2008 			s->idark_valid = 1;
2009 			s->iddate = cdate;
2010 
2011 #ifdef USE_HIGH_GAIN_MODE
2012 		   	if (s->gainmode) {
2013 				for (j = -1; j < m->nraw; j++)
2014 					s->dark_data[j] = s->idark_data[2][j];
2015 			} else
2016 #endif
2017 			{
2018 				for (j = -1; j < m->nraw; j++)
2019 					s->dark_data[j] = s->idark_data[0][j];
2020 			}
2021 			s->dark_valid = 1;
2022 			s->want_dcalib = 0;
2023 			s->ddate = s->iddate;
2024 			s->dark_int_time = s->inttime;
2025 			s->dark_gain_mode = s->gainmode;
2026 			*calt &= ~(inst_calt_ref_dark
2027 		            | inst_calt_em_dark
2028 		            | inst_calt_trans_dark);
2029 
2030 			a1logd(p->log,2,"Done adaptive scan black calibration\n");
2031 
2032 			/* Save the calib to all similar modes */
2033 			/* We're assuming they have the same int times */
2034 			a1logd(p->log,5,"Saving adaptive scan black calib to similar modes\n");
2035 			for (i = 0; i < i1p_no_modes; i++) {
2036 				i1pro_state *ss = &m->ms[i];
2037 				if (ss == s || ss->iddate == s->iddate)
2038 					continue;
2039 				if ((ss->emiss || ss->trans) && ss->adaptive && s->scan) {
2040 					ss->idark_valid = s->idark_valid;
2041 					ss->want_dcalib = s->want_dcalib;
2042 					ss->iddate = s->iddate;
2043 					ss->dark_int_time = s->dark_int_time;
2044 					ss->dark_gain_mode = s->dark_gain_mode;
2045 #ifdef USE_HIGH_GAIN_MODE
2046 					for (j = 0; j < (p->itype != instI1Pro2) ? 4 : 2; j += 2)
2047 #else
2048 					for (j = 0; j < 2; j += 2)
2049 #endif
2050 					{
2051 						ss->idark_int_time[j] = s->idark_int_time[j];
2052 						for (k = -1; k < m->nraw; k++)
2053 							ss->idark_data[j][k] = s->idark_data[j][k];
2054 					}
2055 				}
2056 			}
2057 		}
2058 
2059 		/* If we are doing a white reference calibrate */
2060 		if ((*calt & (inst_calt_ref_white
2061 		            | inst_calt_trans_vwhite | inst_calt_ap_flag))
2062 		 && (((*calc & inst_calc_cond_mask) == inst_calc_man_ref_white && s->reflective)
2063 		  || ((*calc & inst_calc_cond_mask) == inst_calc_man_trans_white && s->trans))) {
2064 			double scale;
2065 
2066 			a1logd(p->log,2,"\nDoing initial white calibration with current inttime %f, gainmode %d\n",
2067 			                                                               s->inttime, s->gainmode);
2068 			nummeas = i1pro_comp_nummeas(p, s->wcaltime, s->inttime);
2069 			ev = i1pro_whitemeasure(p, s->cal_factor[0], s->cal_factor[1], s->white_data, &scale, nummeas,
2070 			           &s->inttime, s->gainmode, s->scan ? 1.0 : s->targoscale, 0);
2071 			if (ev == I1PRO_RD_SENSORSATURATED) {
2072 				scale = 0.0;			/* Signal it this way */
2073 				ev = I1PRO_OK;
2074 			}
2075 			if (ev != I1PRO_OK) {
2076 				m->mmode = mmode;			/* Restore actual mode */
2077 				return ev;
2078 			}
2079 
2080 			/* For non-scan modes, we adjust the integration time to avoid saturation, */
2081 			/* and to try and match the target optimal sensor value */
2082 			if (!s->scan) {
2083 				/* If it's adaptive and not good, or if it's not adaptive and even worse, */
2084 				/* or if we're using lamp dynamic compensation for reflective scan, */
2085 				/* change the parameters until the white is optimal. */
2086 				if ((s->adaptive && (scale < 0.95 || scale > 1.05))
2087 				 || (scale < 0.3 || scale > 2.0)) {
2088 
2089 					/* Need to have done adaptive black measure to change inttime/gain params */
2090 					if ((*calc & inst_calc_cond_mask) != inst_calc_man_ref_white
2091 					  && !s->idark_valid) {
2092 						m->mmode = mmode;			/* Restore actual mode */
2093 						return I1PRO_RD_TRANSWHITERANGE;
2094 					}
2095 
2096 					if (scale == 0.0) {		/* If sensor was saturated */
2097 						s->inttime = m->min_int_time;
2098 						s->gainmode = 0;
2099 						s->dark_valid = 0;
2100 						if (!s->emiss)
2101 							s->cal_valid = 0;
2102 
2103 						if ((*calc & inst_calc_cond_mask) == inst_calc_man_ref_white) {
2104 							nummeas = i1pro_comp_nummeas(p, s->dadaptime, s->inttime);
2105 							a1logd(p->log,2,"Doing another black calibration with dadaptime %f, min inttime %f, nummeas %d, gainmode %d\n", s->dadaptime, s->inttime, nummeas, s->gainmode);
2106 							if ((ev = i1pro_dark_measure(p, s->dark_data,
2107 								             nummeas, &s->inttime, s->gainmode)) != I1PRO_OK) {
2108 								m->mmode = mmode;			/* Restore actual mode */
2109 								return ev;
2110 							}
2111 
2112 						} else if (s->idark_valid) {
2113 							/* compute interpolated dark refence for chosen inttime & gainmode */
2114 							a1logd(p->log,2,"Interpolate dark calibration reference\n");
2115 							if ((ev = i1pro_interp_dark(p, s->dark_data,
2116 								                       s->inttime, s->gainmode)) != I1PRO_OK) {
2117 								m->mmode = mmode;			/* Restore actual mode */
2118 								return ev;
2119 							}
2120 							s->dark_valid = 1;
2121 							s->ddate = s->iddate;
2122 							s->dark_int_time = s->inttime;
2123 							s->dark_gain_mode = s->gainmode;
2124 						} else {
2125 							m->mmode = mmode;			/* Restore actual mode */
2126 							return I1PRO_INT_NOINTERPDARK;
2127 						}
2128 						a1logd(p->log,2,"Doing another white calibration with min inttime %f, gainmode %d\n",
2129 						                                                  s->inttime,s->gainmode);
2130 						nummeas = i1pro_comp_nummeas(p, s->wadaptime, s->inttime);
2131 						if ((ev = i1pro_whitemeasure(p, s->cal_factor[0], s->cal_factor[1], s->white_data,
2132 						   &scale, nummeas, &s->inttime, s->gainmode, s->targoscale, 0))
2133 							                                                != I1PRO_OK) {
2134 							m->mmode = mmode;			/* Restore actual mode */
2135 							return ev;
2136 						}
2137 					}
2138 
2139 					/* Compute a new integration time and gain mode */
2140 					/* in order to optimise the sensor values. Error if can't get */
2141 					/* scale we want. */
2142 					if ((ev = i1pro_optimise_sensor(p, &s->inttime, &s->gainmode,
2143 					         s->inttime, s->gainmode, s->trans, 0, s->targoscale, scale)) != I1PRO_OK) {
2144 						m->mmode = mmode;			/* Restore actual mode */
2145 						return ev;
2146 					}
2147 					a1logd(p->log,2,"Computed optimal white inttime %f and gainmode %d\n",
2148 					                                                      s->inttime,s->gainmode);
2149 
2150 					if ((*calc & inst_calc_cond_mask) == inst_calc_man_ref_white) {
2151 						nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->inttime);
2152 						a1logd(p->log,2,"Doing final black calibration with dcaltime %f, opt inttime %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode);
2153 						if ((ev = i1pro_dark_measure(p, s->dark_data,
2154 								                   nummeas, &s->inttime, s->gainmode)) != I1PRO_OK) {
2155 							m->mmode = mmode;			/* Restore actual mode */
2156 							return ev;
2157 						}
2158 						s->dark_valid = 1;
2159 						s->ddate = cdate;
2160 						s->dark_int_time = s->inttime;
2161 						s->dark_gain_mode = s->gainmode;
2162 
2163 					} else if (s->idark_valid) {
2164 						/* compute interpolated dark refence for chosen inttime & gainmode */
2165 						a1logd(p->log,2,"Interpolate dark calibration reference\n");
2166 						if ((ev = i1pro_interp_dark(p, s->dark_data,
2167 							                             s->inttime, s->gainmode)) != I1PRO_OK) {
2168 							m->mmode = mmode;			/* Restore actual mode */
2169 							return ev;
2170 						}
2171 						s->dark_valid = 1;
2172 						s->ddate = s->iddate;
2173 						s->dark_int_time = s->inttime;
2174 						s->dark_gain_mode = s->gainmode;
2175 					} else {
2176 						m->mmode = mmode;			/* Restore actual mode */
2177 						return I1PRO_INT_NOINTERPDARK;
2178 					}
2179 
2180 					a1logd(p->log,2,"Doing final white calibration with opt int_time %f, gainmode %d\n",
2181 					                                                 s->inttime,s->gainmode);
2182 					nummeas = i1pro_comp_nummeas(p, s->wcaltime, s->inttime);
2183 					if ((ev = i1pro_whitemeasure(p, s->cal_factor[0], s->cal_factor[1], s->white_data,
2184 					  &scale, nummeas, &s->inttime, s->gainmode, s->targoscale, ltocmode)) != I1PRO_OK) {
2185 						m->mmode = mmode;			/* Restore actual mode */
2186 						return ev;
2187 					}
2188 				}
2189 
2190 			/* For scan we take a different approach. We try and use the minimum possible */
2191 			/* integration time so as to maximize sampling rate, and adjust the gain */
2192 			/* if necessary. */
2193 			} else if (s->adaptive) {
2194 				int j;
2195 				if (scale == 0.0) {		/* If sensor was saturated */
2196 					a1logd(p->log,3,"Scan illuminant is saturating sensor\n");
2197 					if (s->gainmode == 0) {
2198 						m->mmode = mmode;			/* Restore actual mode */
2199 						return I1PRO_RD_SENSORSATURATED;		/* Nothing we can do */
2200 					}
2201 					a1logd(p->log,3,"Switching to low gain mode\n");
2202 					s->gainmode = 0;
2203 					/* Measure white again with low gain */
2204 					nummeas = i1pro_comp_nummeas(p, s->wcaltime, s->inttime);
2205 					if ((ev = i1pro_whitemeasure(p, s->cal_factor[0], s->cal_factor[1], s->white_data,
2206 					   &scale, nummeas, &s->inttime, s->gainmode, 1.0, 0)) != I1PRO_OK) {
2207 						m->mmode = mmode;			/* Restore actual mode */
2208 						return ev;
2209 					}
2210 				} else if (p->itype != instI1Pro2 && s->gainmode == 0 && scale > m->highgain) {
2211 #ifdef USE_HIGH_GAIN_MODE
2212 					a1logd(p->log,3,"Scan signal is so low we're switching to high gain mode\n");
2213 					s->gainmode = 1;
2214 					/* Measure white again with high gain */
2215 					nummeas = i1pro_comp_nummeas(p, s->wcaltime, s->inttime);
2216 					if ((ev = i1pro_whitemeasure(p, s->cal_factor[0], s->cal_factor[1], s->white_data,
2217 					   &scale, nummeas, &s->inttime, s->gainmode, 1.0, 0)) != I1PRO_OK) {
2218 						m->mmode = mmode;			/* Restore actual mode */
2219 						return ev;
2220 					}
2221 #endif /* USE_HIGH_GAIN_MODE */
2222 				}
2223 
2224 				a1logd(p->log,2,"After scan gain adaption scale = %f\n",scale);
2225 				if (scale > 6.0) {
2226 					m->transwarn |= 2;
2227 					a1logd(p->log,2, "scan white reference is not bright enough by %f\n",scale);
2228 				}
2229 
2230 				if ((*calc & inst_calc_cond_mask) == inst_calc_man_ref_white) {
2231 					nummeas = i1pro_comp_nummeas(p, s->dcaltime, s->inttime);
2232 					a1logd(p->log,2,"Doing final black calibration with dcaltime %f, opt inttime %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode);
2233 					if ((ev = i1pro_dark_measure(p, s->dark_data,
2234 						              nummeas, &s->inttime, s->gainmode)) != I1PRO_OK) {
2235 						m->mmode = mmode;			/* Restore actual mode */
2236 						return ev;
2237 					}
2238 					s->dark_valid = 1;
2239 					s->ddate = cdate;
2240 					s->dark_int_time = s->inttime;
2241 					s->dark_gain_mode = s->gainmode;
2242 
2243 				} else if (s->idark_valid) {
2244 					/* compute interpolated dark refence for chosen inttime & gainmode */
2245 					a1logd(p->log,2,"Interpolate dark calibration reference\n");
2246 					if (s->gainmode) {
2247 						for (j = -1; j < m->nraw; j++)
2248 							s->dark_data[j] = s->idark_data[2][j];
2249 					} else {
2250 						for (j = -1; j < m->nraw; j++)
2251 							s->dark_data[j] = s->idark_data[0][j];
2252 					}
2253 					s->dark_valid = 1;
2254 					s->ddate = s->iddate;
2255 					s->dark_int_time = s->inttime;
2256 					s->dark_gain_mode = s->gainmode;
2257 				} else {
2258 					m->mmode = mmode;			/* Restore actual mode */
2259 					return I1PRO_INT_NOINTERPDARK;
2260 				}
2261 				a1logd(p->log,2,"Doing final white calibration with opt int_time %f, gainmode %d\n",
2262 				                                                 s->inttime,s->gainmode);
2263 			}
2264 
2265 			/* We've settled on the inttime and gain mode to get a good white reference. */
2266 			if (s->reflective) {	/* We read the white reference - check it */
2267 				/* Check a reflective white measurement, and check that */
2268 				/* it seems reasonable. Return I1PRO_OK if it is, error if not. */
2269 				/* (Using cal_factor[] as temp.) */
2270 				a1logd(p->log,2,"Checking white reference\n");
2271 				if ((ev = i1pro_check_white_reference1(p, s->cal_factor[0])) != I1PRO_OK) {
2272 					m->mmode = mmode;			/* Restore actual mode */
2273 					return ev;
2274 				}
2275 				/* Compute a calibration factor given the reading of the white reference. */
2276 				ev = i1pro_compute_white_cal(p,
2277 				                s->cal_factor[0], m->white_ref[0], s->cal_factor[0],
2278 				                s->cal_factor[1], m->white_ref[1], s->cal_factor[1],
2279 								!s->scan);		/* Use this for emis hires fine tune if not scan */
2280 
2281 				/* Print white lamp magnitude to track lamp darkening */
2282 				if (p->log != NULL && p->log->debug >= 1) {
2283 					double sum = 0.0;
2284 					for (i = 0; i < m->nwav[0]; i++)
2285 						sum += 1.0/s->cal_factor[0][i];
2286 
2287 					a1logd(p->log,1,"Refl. lamp magnitude = %e\n",sum);
2288 				}
2289 
2290 				if (ev == I1PRO_RD_TRANSWHITEWARN)		/* Shouldn't happen ? */
2291 					ev = I1PRO_OK;
2292 				if (ev != I1PRO_OK) {
2293 					m->mmode = mmode;			/* Restore actual mode */
2294 					return ev;
2295 				}
2296 
2297 			} else {	/* transmissive */
2298 				/* Compute a calibration factor given the reading of the white reference. */
2299 				ev = i1pro_compute_white_cal(p, s->cal_factor[0], NULL, s->cal_factor[0],
2300 				                                s->cal_factor[1], NULL, s->cal_factor[1], 0);
2301 				if (ev == I1PRO_RD_TRANSWHITEWARN) {
2302 					m->transwarn |= 1;
2303 					ev = I1PRO_OK;
2304 				}
2305 				if (ev != I1PRO_OK) {
2306 					m->mmode = mmode;			/* Restore actual mode */
2307 					return ev;
2308 				}
2309 			}
2310 			s->cal_valid = 1;
2311 			s->cfdate = cdate;
2312 			s->want_calib = 0;
2313 			*calt &= ~(inst_calt_ref_white
2314 			         | inst_calt_trans_vwhite);
2315 		}
2316 
2317 		/* Deal with a display integration time selection */
2318 		if ((*calt & (inst_calt_emis_int_time | inst_calt_ap_flag))
2319 		 && (*calc & inst_calc_cond_mask) == inst_calc_emis_white
2320 		 && (s->emiss && !s->adaptive && !s->scan)) {
2321 			double scale;
2322 			double *data;
2323 			double *tt, tv;
2324 
2325 			data = dvectorz(-1, m->nraw-1);
2326 
2327 			a1logd(p->log,2,"\nDoing display integration time calibration\n");
2328 
2329 			/* Undo any previous swaps */
2330 			if (s->dispswap == 1) {
2331 				tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
2332 				tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
2333 			} else if (s->dispswap == 2) {
2334 				tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
2335 				tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
2336 			} else if (s->dispswap == 3) {
2337 				tv = s->inttime; s->inttime = s->dark_int_time4; s->dark_int_time4 = tv;
2338 				tt = s->dark_data; s->dark_data = s->dark_data4; s->dark_data4 = tt;
2339 			}
2340 			s->dispswap = 0;
2341 
2342 			/* Simply measure the full display white, and if it's close to */
2343 			/* saturation, switch to the alternate display integration time */
2344 			nummeas = i1pro_comp_nummeas(p, s->wreadtime, s->inttime);
2345 			ev = i1pro_whitemeasure(p, NULL, NULL, data , &scale, nummeas,
2346 			           &s->inttime, s->gainmode, s->targoscale, 0);
2347 			if (ev == I1PRO_RD_SENSORSATURATED || scale < 1.0) {
2348 				a1logd(p->log,2,"Switching to 2nd display integration time %f seconds\n",s->dark_int_time2);
2349 				/* swap in 2nd display integration time */
2350 				tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
2351 				tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
2352 				s->dispswap = 1;
2353 
2354 				/* Do another measurement of the full display white, and if it's close to */
2355 				/* saturation, switch to the 3rd display integration time */
2356 				nummeas = i1pro_comp_nummeas(p, s->wreadtime, s->inttime);
2357 				ev = i1pro_whitemeasure(p, NULL, NULL, data , &scale, nummeas,
2358 				           &s->inttime, s->gainmode, s->targoscale, 0);
2359 				if (ev == I1PRO_RD_SENSORSATURATED || scale < 1.0) {
2360 					a1logd(p->log,2,"Switching to 3rd display integration time %f seconds\n",s->dark_int_time3);
2361 					/* Undo previous swap */
2362 					tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
2363 					tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
2364 					/* swap in 3rd time */
2365 					tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
2366 					tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
2367 					s->dispswap = 2;
2368 
2369 					/* Do another measurement of the full display white, and if it's close to */
2370 					/* saturation, switch to the 4th display integration time */
2371 					nummeas = i1pro_comp_nummeas(p, s->wreadtime, s->inttime);
2372 					ev = i1pro_whitemeasure(p, NULL, NULL, data , &scale, nummeas,
2373 					           &s->inttime, s->gainmode, s->targoscale, 0);
2374 					if (ev == I1PRO_RD_SENSORSATURATED || scale < 1.0) {
2375 						a1logd(p->log,2,"Switching to 4th display integration time %f seconds\n",s->dark_int_time3);
2376 						/* Undo previous swap */
2377 						tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
2378 						tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
2379 						/* swap in 4th time */
2380 						tv = s->inttime; s->inttime = s->dark_int_time4; s->dark_int_time4 = tv;
2381 						tt = s->dark_data; s->dark_data = s->dark_data4; s->dark_data4 = tt;
2382 						s->dispswap = 3;
2383 					}
2384 				}
2385 			}
2386 			free_dvector(data, -1, m->nraw-1);
2387 			if (ev != I1PRO_OK) {
2388 				m->mmode = mmode;			/* Restore actual mode */
2389 				return ev;
2390 			}
2391 			s->done_dintsel = 1;
2392 			s->diseldate = cdate;
2393 			*calt &= ~inst_calt_emis_int_time;
2394 
2395 			a1logd(p->log,5,"Done display integration time calibration\n");
2396 		}
2397 
2398 	}	/* Look at next mode */
2399 	m->mmode = mmode;			/* Restore actual mode */
2400 
2401 	/* Make sure there's the right condition for any remaining calibrations. */
2402 	/* Do ref_white first in case we are doing a high res fine tune. */
2403 
2404 	if (*calt & (inst_calt_ref_dark | inst_calt_ref_white)) {
2405 		*idtype = inst_calc_id_ref_sn;
2406 		sprintf(id, "%d",m->serno);
2407 		if ((*calc & inst_calc_cond_mask) != inst_calc_man_ref_white) {
2408 			/* Calibrate using white tile */
2409 			*calc = inst_calc_man_ref_white;
2410 			return I1PRO_CAL_SETUP;
2411 		}
2412 	} else if (*calt & inst_calt_wavelength) {		/* Wavelength calibration */
2413 		if (cs->emiss && cs->ambient) {
2414 			*idtype = inst_calc_id_none;
2415 			id[0] = '\000';
2416 			if ((*calc & inst_calc_cond_mask) != inst_calc_man_am_dark) {
2417 				/* Calibrate using ambient adapter */
2418 				*calc = inst_calc_man_am_dark;
2419 				return I1PRO_CAL_SETUP;
2420 			}
2421 		} else {
2422 			*idtype = inst_calc_id_ref_sn;
2423 			sprintf(id, "%d",m->serno);
2424 			if ((*calc & inst_calc_cond_mask) != inst_calc_man_ref_white) {
2425 				/* Calibrate using white tile */
2426 				*calc = inst_calc_man_ref_white;
2427 				return I1PRO_CAL_SETUP;
2428 			}
2429 		}
2430 	} else if (*calt & inst_calt_em_dark) {		/* Emissive Dark calib */
2431 		*idtype = inst_calc_id_none;
2432 		id[0] = '\000';
2433 		if ((*calc & inst_calc_cond_mask) != inst_calc_man_em_dark) {
2434 			/* Any sort of dark reference */
2435 			*calc = inst_calc_man_em_dark;
2436 			return I1PRO_CAL_SETUP;
2437 		}
2438 	} else if (*calt & inst_calt_trans_dark) {	/* Transmissvice dark */
2439 		*idtype = inst_calc_id_none;
2440 		id[0] = '\000';
2441 		if ((*calc & inst_calc_cond_mask) != inst_calc_man_trans_dark) {
2442 			*calc = inst_calc_man_trans_dark;
2443 			return I1PRO_CAL_SETUP;
2444 		}
2445 	} else if (*calt & inst_calt_trans_vwhite) {/* Transmissvice white for emulated transmission */
2446 		*idtype = inst_calc_id_none;
2447 		id[0] = '\000';
2448 		if ((*calc & inst_calc_cond_mask) != inst_calc_man_trans_white) {
2449 			*calc = inst_calc_man_trans_white;
2450 			return I1PRO_CAL_SETUP;
2451 		}
2452 	} else if (*calt & inst_calt_emis_int_time) {
2453 		*idtype = inst_calc_id_none;
2454 		id[0] = '\000';
2455 		if ((*calc & inst_calc_cond_mask) != inst_calc_emis_white) {
2456 			*calc = inst_calc_emis_white;
2457 			return I1PRO_CAL_SETUP;
2458 		}
2459 	}
2460 
2461 	/* Go around again if we've still got calibrations to do */
2462 	if (*calt & inst_calt_all_mask) {
2463 		return I1PRO_CAL_SETUP;
2464 	}
2465 
2466 	/* We must be done */
2467 
2468 	/* Update and write the EEProm log if the is a refspot calibration */
2469 	if (cs->reflective && !cs->scan && cs->dark_valid && cs->cal_valid) {
2470 		m->calcount = m->rpcount;
2471 		m->caldate = cdate;
2472 		if ((ev = i1pro_update_log(p)) != I1PRO_OK) {
2473 			a1logd(p->log,2,"i1pro_update_log: Updating the cal and log parameters"
2474 			                                               " to EEProm failed\n");
2475 		}
2476 	}
2477 
2478 #ifdef ENABLE_NONVCAL
2479 	/* Save the calibration to a file */
2480 	i1pro_save_calibration(p);
2481 #endif
2482 
2483 	if (m->transwarn) {
2484 		*calc = inst_calc_message;
2485 		if (m->transwarn & 2) {
2486 			*idtype = inst_calc_id_trans_low;
2487 			strcpy(id, "Warning: Transmission light source is too low for accuracy!");
2488 		} else {
2489 			*idtype = inst_calc_id_trans_wl;
2490 			strcpy(id, "Warning: Transmission light source is low at some wavelengths!");
2491 		}
2492 		m->transwarn = 0;
2493 	}
2494 
2495 	a1logd(p->log,2,"Finished cal with dark_valid = %d, cal_valid = %d\n",cs->dark_valid, cs->cal_valid);
2496 
2497 	return I1PRO_OK;
2498 }
2499 
2500 /* Interpret an icoms error into a I1PRO error */
icoms2i1pro_err(int se)2501 int icoms2i1pro_err(int se) {
2502 	if (se != ICOM_OK)
2503 		return I1PRO_COMS_FAIL;
2504 	return I1PRO_OK;
2505 }
2506 
2507 /* - - - - - - - - - - - - - - - - */
2508 /* Do a dummy reflective read, to fix Lamp Drift. */
2509 
i1pro_imp_lamp_fix(i1pro * p,double seconds)2510 i1pro_code i1pro_imp_lamp_fix(
2511 i1pro *p,
2512 double seconds) {	/* Number of seconds to turn lamp on for */
2513 	i1pro_code ev = I1PRO_OK;
2514 	i1proimp *m = (i1proimp *)p->m;
2515 	int nummeas;
2516 	double inttime;
2517 	unsigned char *buf;		/* Raw USB reading buffer */
2518 	unsigned int bsize;
2519 	i1p_mode cmode = m->mmode;		/* Remember current mode */
2520 
2521 	if (seconds > (5 * 60.0)) {
2522 		a1loge(p->log, inst_internal_error, "i1pro_imp_lamp_fix %f sec is too long\n",seconds);
2523 		return I1PRO_INT_ASSERT;
2524 	}
2525 
2526 	m->mmode = i1p_refl_spot;		/* Override current mode */
2527 	inttime = 0.2;					/* Constrain buffer size */
2528 	nummeas = (int)(seconds/inttime + 0.5);
2529 	bsize = m->nsen * 2 * nummeas;	/* 16 bit raw values */
2530 
2531 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
2532 		m->mmode = cmode;
2533 		a1logd(p->log,1,"i1pro_read_patches malloc %d bytes failed (11)\n",bsize);
2534 		return I1PRO_INT_MALLOC;
2535 	}
2536 
2537 	/* Trigger measure and gather raw readings */
2538 	a1logd(p->log, 1, "i1pro_imp_lamp_fix %f seconds\n",seconds);
2539 	if ((ev = i1pro_read_patches_1(p, nummeas, nummeas, &inttime, 0,
2540 	                                       NULL, buf, bsize)) != I1PRO_OK) {
2541 		m->mmode = cmode;
2542 		free(buf);
2543 		return ev;
2544 	}
2545 
2546 	m->mmode = cmode;
2547 	free(buf);
2548 
2549 	return I1PRO_OK;
2550 }
2551 
2552 /* - - - - - - - - - - - - - - - - */
2553 /* Measure a display update delay. It is assumed that */
2554 /* white_stamp(init) has been called, and then a */
2555 /* white to black change has been made to the displayed color, */
2556 /* and this will measure the time it took for the update to */
2557 /* be noticed by the instrument, up to 2.0 seconds. */
2558 /* (It is assumed that white_change() will be called at the time the patch */
2559 /* changes color.) */
2560 /* inst_misread will be returned on failure to find a transition to black. */
2561 #define NDMXTIME 2.0		/* Maximum time to take */
2562 #define NDSAMPS 500			/* Debug samples */
2563 
2564 typedef struct {
2565 	double sec;
2566 	double rgb[3];
2567 	double tot;
2568 } i1rgbdsamp;
2569 
i1pro_imp_meas_delay(i1pro * p,int * pdispmsec,int * pinstmsec)2570 i1pro_code i1pro_imp_meas_delay(
2571 i1pro *p,
2572 int *pdispmsec,		/* Return display update delay in msec */
2573 int *pinstmsec) {	/* Return instrument latency in msec */
2574 	i1pro_code ev = I1PRO_OK;
2575 	i1proimp *m = (i1proimp *)p->m;
2576 	i1pro_state *s = &m->ms[m->mmode];
2577 	int i, j, k, mm;
2578 	double **multimeas;			/* Spectral measurements */
2579 	int nummeas;
2580 	double rgbw[3] = { 610.0, 520.0, 460.0 };
2581 	double inttime;
2582 	double rstart;
2583 	i1rgbdsamp *samp;
2584 	double stot, etot, del, thr;
2585 	double stime, etime;
2586 	int dispmsec, instmsec;
2587 
2588 	if (pinstmsec != NULL)
2589 		*pinstmsec = 0;
2590 
2591 	if ((rstart = usec_time()) < 0.0) {
2592 		a1loge(p->log, inst_internal_error, "i1pro_imp_meas_delay: No high resolution timers\n");
2593 		return inst_internal_error;
2594 	}
2595 
2596 	/* Read the samples */
2597 	inttime = m->min_int_time;
2598 	nummeas = (int)(NDMXTIME/inttime + 0.5);
2599 	multimeas = dmatrix(0, nummeas-1, -1, m->nwav[m->highres]-1);
2600 	if ((samp = (i1rgbdsamp *)calloc(sizeof(i1rgbdsamp), nummeas)) == NULL) {
2601 		a1logd(p->log, 1, "i1pro_meas_delay: malloc failed\n");
2602 		return I1PRO_INT_MALLOC;
2603 	}
2604 
2605 	/* We rely on the measurement code setting m->trigstamp when the */
2606 	/* trigger packet is sent to the instrument */
2607 	if ((ev = i1pro_read_patches_all(p, multimeas, nummeas, &inttime, 0)) != inst_ok) {
2608 		free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav[m->highres]-1);
2609 		free(samp);
2610 		return ev;
2611 	}
2612 
2613 	if (m->whitestamp < 0.0) {
2614 		a1logd(p->log, 1, "i1pro_meas_delay: White transition wasn't timestamped\n");
2615 		return inst_internal_error;
2616 	}
2617 
2618 	/* Convert the samples to RGB */
2619 	/* Add 10 msec fudge factor */
2620 	for (i = 0; i < nummeas; i++) {
2621 		samp[i].sec = i * inttime + (m->trigstamp - m->whitestamp)/1000000.0 + 0.01;
2622 		samp[i].rgb[0] = samp[i].rgb[1] = samp[i].rgb[2] = 0.0;
2623 		for (j = 0; j < m->nwav[m->highres]; j++) {
2624 			double wl = XSPECT_WL(m->wl_short[m->highres], m->wl_long[m->highres], m->nwav[m->highres], j);
2625 
2626 //printf("~1 multimeas %d %d = %f\n",i, j, multimeas[i][j]);
2627 			for (k = 0; k < 3; k++) {
2628 				double tt = (double)(wl - rgbw[k]);
2629 				tt = (50.0 - fabs(tt))/50.0;
2630 				if (tt < 0.0)
2631 					tt = 0.0;
2632 				samp[i].rgb[k] += sqrt(tt) * multimeas[i][j];
2633 			}
2634 		}
2635 		samp[i].tot = samp[i].rgb[0] + samp[i].rgb[1] + samp[i].rgb[2];
2636 	}
2637 	free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav[m->highres]-1);
2638 
2639 	a1logd(p->log, 3, "i1pro_meas_delay: Read %d samples for refresh calibration\n",nummeas);
2640 
2641 	/* Over the first 100msec, locate the maximum value */
2642 	stime = samp[0].sec;
2643 	stot = -1e9;
2644 	for (i = 0; i < nummeas; i++) {
2645 		if (samp[i].tot > stot)
2646 			stot = samp[i].tot;
2647 		if ((samp[i].sec - stime) > 0.1)
2648 			break;
2649 	}
2650 
2651 	/* Over the last 100msec, locate the maximum value */
2652 	etime = samp[nummeas-1].sec;
2653 	etot = -1e9;
2654 	for (i = nummeas-1; i >= 0; i--) {
2655 		if (samp[i].tot > etot)
2656 			etot = samp[i].tot;
2657 		if ((etime - samp[i].sec) > 0.1)
2658 			break;
2659 	}
2660 
2661 	del = etot - stot;
2662 	thr = stot + 0.30 * del;		/* 30% of transition threshold */
2663 
2664 #ifdef PLOT_UPDELAY
2665 	a1logd(p->log, 0, "i1pro_meas_delay: start tot %f end tot %f del %f, thr %f\n", stot, etot, del, thr);
2666 #endif
2667 
2668 #ifdef PLOT_UPDELAY
2669 	/* Plot the raw sensor values */
2670 	{
2671 		double xx[NDSAMPS];
2672 		double y1[NDSAMPS];
2673 		double y2[NDSAMPS];
2674 		double y3[NDSAMPS];
2675 		double y4[NDSAMPS];
2676 
2677 		for (i = 0; i < nummeas && i < NDSAMPS; i++) {
2678 			xx[i] = samp[i].sec;
2679 			y1[i] = samp[i].rgb[0];
2680 			y2[i] = samp[i].rgb[1];
2681 			y3[i] = samp[i].rgb[2];
2682 			y4[i] = samp[i].tot;
2683 //printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].tot);
2684 		}
2685 		printf("Display update delay measure sensor values and time (sec)\n");
2686 		do_plot6(xx, y1, y2, y3, y4, NULL, NULL, nummeas);
2687 	}
2688 #endif
2689 
2690 	/* Check that there has been a transition */
2691 	if (del < 5.0) {
2692 		free(samp);
2693 		a1logd(p->log, 1, "i1pro_meas_delay: can't detect change from black to white\n");
2694 		return I1PRO_RD_NOTRANS_FOUND;
2695 	}
2696 
2697 	/* Working from the start, locate the time at which the level was above the threshold */
2698 	for (i = 0; i < (nummeas-1); i++) {
2699 		if (samp[i].tot > thr)
2700 			break;
2701 	}
2702 
2703 	a1logd(p->log, 2, "i1pro_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec);
2704 
2705 	/* Compute overall delay */
2706 	dispmsec = (int)(samp[i].sec * 1000.0 + 0.5);				/* Display update time */
2707 	instmsec = (int)((m->trigstamp - rstart)/1000.0 + 0.5);		/* Reaction time */
2708 
2709 #ifdef PLOT_UPDELAY
2710 	a1logd(p->log, 0, "i1pro_meas_delay: disp %d, trig %d msec\n",dispmsec,instmsec);
2711 #else
2712 	a1logd(p->log, 2, "i1pro_meas_delay: disp %d, trig %d msec\n",dispmsec,instmsec);
2713 #endif
2714 
2715 	if (dispmsec < 0) 		/* This can happen if the patch generator delays it's return */
2716 		dispmsec = 0;
2717 
2718 	if (pdispmsec != NULL)
2719 		*pdispmsec = dispmsec;
2720 
2721 	if (pinstmsec != NULL)
2722 		*pinstmsec = instmsec;
2723 
2724 #ifdef PLOT_UPDELAY
2725 	a1logd(p->log, 0, "i1pro_meas_delay: returning %d & %d msec\n",dispmsec,instmsec);
2726 #else
2727 	a1logd(p->log, 2, "i1pro_meas_delay: returning %d & %d msec\n",dispmsec,instmsec);
2728 #endif
2729 	free(samp);
2730 
2731 	return I1PRO_OK;
2732 }
2733 #undef NDSAMPS
2734 #undef NDMXTIME
2735 
2736 /* Timestamp the white patch change during meas_delay() */
i1pro_imp_white_change(i1pro * p,int init)2737 inst_code i1pro_imp_white_change(i1pro *p, int init) {
2738 	i1proimp *m = (i1proimp *)p->m;
2739 
2740 	if (init)
2741 		m->whitestamp = -1.0;
2742 	else {
2743 		if ((m->whitestamp = usec_time()) < 0.0) {
2744 			a1loge(p->log, inst_internal_error, "i1pro_imp_wite_change: No high resolution timers\n");
2745 			return inst_internal_error;
2746 		}
2747 	}
2748 
2749 	return inst_ok;
2750 }
2751 
2752 /* - - - - - - - - - - - - - - - - */
2753 /* Measure a patch or strip in the current mode. */
2754 /* To try and speed up the reaction time between */
2755 /* triggering a scan measurement and being able to */
2756 /* start moving the instrument, we pre-allocate */
2757 /* all the buffers and arrays, and pospone processing */
2758 /* until after the scan is complete. */
i1pro_imp_measure(i1pro * p,ipatch * vals,int nvals,instClamping clamp)2759 i1pro_code i1pro_imp_measure(
2760 	i1pro *p,
2761 	ipatch *vals,		/* Pointer to array of instrument patch value */
2762 	int nvals,			/* Number of values */
2763 	instClamping clamp	/* Clamp XYZ/Lab to be +ve */
2764 ) {
2765 	i1pro_code ev = I1PRO_OK;
2766 	i1proimp *m = (i1proimp *)p->m;
2767 	i1pro_state *s = &m->ms[m->mmode];
2768 	unsigned char *buf = NULL;	/* Raw USB reading buffer for reflection dark cal */
2769 	unsigned int bsize;
2770 	unsigned char *mbuf = NULL;	/* Raw USB reading buffer for measurement */
2771 	unsigned int mbsize;
2772 	int nummeas = 0, maxnummeas = 0;
2773 	int nmeasuered = 0;			/* Number actually measured */
2774 	double **specrd = NULL;		/* Cooked spectral patch values */
2775 	double duration = 0.0;		/* Possible flash duration value */
2776 	int user_trig = 0;
2777 
2778 	a1logd(p->log,2,"i1pro_imp_measure: Taking %d measurments in %s%s%s%s%s%s mode called\n", nvals,
2779 		        s->emiss ? "Emission" : s->trans ? "Trans" : "Refl",
2780 		        s->emiss && s->ambient ? " Ambient" : "",
2781 		        s->scan ? " Scan" : "",
2782 		        s->flash ? " Flash" : "",
2783 		        s->adaptive ? " Adaptive" : "",
2784 		        m->uv_en ? " UV" : "");
2785 
2786 
2787 	if ((s->emiss && s->adaptive && !s->idark_valid)
2788 	 || ((!s->emiss || !s->adaptive) && !s->dark_valid)
2789 	 || !s->cal_valid) {
2790 		a1logd(p->log,3,"emis %d, adaptive %d, idark_valid %d\n",s->emiss,s->adaptive,s->idark_valid);
2791 		a1logd(p->log,3,"dark_valid %d, cal_valid %d\n",s->dark_valid,s->cal_valid);
2792 		a1logd(p->log,3,"i1pro_imp_measure need calibration\n");
2793 		return I1PRO_RD_NEEDS_CAL;
2794 	}
2795 
2796 	if (nvals <= 0
2797 	 || (!s->scan && nvals > 1)) {
2798 		a1logd(p->log,2,"i1pro_imp_measure wrong number of patches\n");
2799 		return I1PRO_INT_WRONGPATCHES;
2800 	}
2801 
2802 	/* Notional number of measurements, before adaptive and not counting scan */
2803 	nummeas = i1pro_comp_nummeas(p, s->wreadtime, s->inttime);
2804 
2805 	/* Allocate buf for pre-measurement dark calibration */
2806 	if (s->reflective) {
2807 		bsize = m->nsen * 2 * nummeas;
2808 		if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
2809 			a1logd(p->log,1,"i1pro_imp_measure malloc %d bytes failed (5)\n",bsize);
2810 			return I1PRO_INT_MALLOC;
2811 		}
2812 	}
2813 
2814 	/* Allocate buffer for measurement */
2815 	maxnummeas = i1pro_comp_nummeas(p, s->maxscantime, s->inttime);
2816 	if (maxnummeas < nummeas)
2817 		maxnummeas = nummeas;
2818 	mbsize = m->nsen * 2 * maxnummeas;
2819 	if ((mbuf = (unsigned char *)malloc(sizeof(unsigned char) * mbsize)) == NULL) {
2820 		if (buf != NULL)
2821 			free(buf);
2822 		a1logd(p->log,1,"i1pro_imp_measure malloc %d bytes failed (6)\n",mbsize);
2823 		return I1PRO_INT_MALLOC;
2824 	}
2825 	specrd = dmatrix(0, nvals-1, 0, m->nwav[m->highres]-1);
2826 
2827 	if (m->trig == inst_opt_trig_user_switch) {
2828 		m->hide_switch = 1;						/* Supress switch events */
2829 
2830 #ifdef USE_THREAD
2831 		{
2832 			int currcount = m->switch_count;		/* Variable set by thread */
2833 			while (currcount == m->switch_count) {
2834 				inst_code rc;
2835 				int cerr;
2836 
2837 				/* Don't trigger on user key if scan, only trigger */
2838 				/* on instrument switch */
2839 				if (p->uicallback != NULL
2840 				 && (rc = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
2841 					if (rc == inst_user_abort) {
2842 						ev = I1PRO_USER_ABORT;
2843 						break;						/* Abort */
2844 					}
2845 					if (!s->scan && rc == inst_user_trig) {
2846 						ev = I1PRO_USER_TRIG;
2847 						user_trig = 1;
2848 						break;						/* Trigger */
2849 					}
2850 				}
2851 				msec_sleep(100);
2852 			}
2853 		}
2854 #else
2855 		/* Throw one away in case the switch was pressed prematurely */
2856 		i1pro_waitfor_switch_th(p, 0.01);
2857 
2858 		for (;;) {
2859 			inst_code rc;
2860 			int cerr;
2861 
2862 			if ((ev = i1pro_waitfor_switch_th(p, 0.1)) != I1PRO_OK
2863 			 && ev != I1PRO_INT_BUTTONTIMEOUT)
2864 				break;			/* Error */
2865 
2866 			if (ev == I1PRO_OK)
2867 				break;			/* switch triggered */
2868 
2869 			/* Don't trigger on user key if scan, only trigger */
2870 			/* on instrument switch */
2871 			if (p->uicallback != NULL
2872 			 && (rc = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
2873 				if (rc == inst_user_abort) {
2874 					ev = I1PRO_USER_ABORT;
2875 					break;						/* Abort */
2876 				}
2877 				if (!s->scan && rc == inst_user_trig) {
2878 					ev = I1PRO_USER_TRIG;
2879 					user_trig = 1;
2880 					break;						/* Trigger */
2881 				}
2882 			}
2883 		}
2884 #endif
2885 		a1logd(p->log,3,"############# triggered ##############\n");
2886 		if (p->uicallback)	/* Notify of trigger */
2887 			p->uicallback(p->uic_cntx, inst_triggered);
2888 
2889 		m->hide_switch = 0;						/* Enable switch events again */
2890 
2891 	} else if (m->trig == inst_opt_trig_user) {
2892 		if (p->uicallback == NULL) {
2893 			a1logd(p->log, 1, "hcfr: inst_opt_trig_user but no uicallback function set!\n");
2894 			ev = I1PRO_UNSUPPORTED;
2895 
2896 		} else {
2897 
2898 			for (;;) {
2899 				inst_code rc;
2900 				if ((rc = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
2901 					if (rc == inst_user_abort) {
2902 						ev = I1PRO_USER_ABORT;	/* Abort */
2903 						break;
2904 					}
2905 					if (rc == inst_user_trig) {
2906 						ev = I1PRO_USER_TRIG;
2907 						user_trig = 1;
2908 						break;						/* Trigger */
2909 					}
2910 				}
2911 				msec_sleep(200);
2912 			}
2913 		}
2914 		a1logd(p->log,3,"############# triggered ##############\n");
2915 		if (p->uicallback)	/* Notify of trigger */
2916 			p->uicallback(p->uic_cntx, inst_triggered);
2917 
2918 	/* Progromatic Trigger */
2919 	} else {
2920 		/* Check for abort */
2921 		if (p->uicallback != NULL
2922 		 && (ev = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort)
2923 			ev = I1PRO_USER_ABORT;	/* Abort */
2924 	}
2925 
2926 	if (ev != I1PRO_OK && ev != I1PRO_USER_TRIG) {
2927 		free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
2928 		free(mbuf);
2929 		if (buf != NULL)
2930 			free(buf);
2931 		a1logd(p->log,2,"i1pro_imp_measure user aborted, terminated, command, or failure\n");
2932 		return ev;		/* User abort, term, command or failure */
2933 	}
2934 
2935 	if (s->emiss && !s->scan && s->adaptive) {
2936 		int saturated = 0;
2937 		double optscale = 1.0;
2938 		s->inttime = 0.25;
2939 		s->gainmode = 0;
2940 		s->dark_valid = 0;
2941 
2942 		a1logd(p->log,2,"Trial measure emission with inttime %f, gainmode %d\n",s->inttime,s->gainmode);
2943 
2944 		/* Take a trial measurement reading using the current mode. */
2945 		/* Used to determine if sensor is saturated, or not optimal */
2946 //		nummeas = i1pro_comp_nummeas(p, s->wreadtime, s->inttime);
2947 		nummeas = 1;
2948 		if ((ev = i1pro_trialmeasure(p, &saturated, &optscale, nummeas, &s->inttime, s->gainmode,
2949 		                                                            s->targoscale)) != I1PRO_OK) {
2950 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
2951 			free(mbuf);
2952 			a1logd(p->log,2,"i1pro_imp_measure trial measure failed\n");
2953 			return ev;
2954 		}
2955 
2956 		if (saturated) {
2957 			s->inttime = m->min_int_time;
2958 
2959 			a1logd(p->log,2,"2nd trial measure emission with inttime %f, gainmode %d\n",
2960 			                                         s->inttime,s->gainmode);
2961 			/* Take a trial measurement reading using the current mode. */
2962 			/* Used to determine if sensor is saturated, or not optimal */
2963 			nummeas = i1pro_comp_nummeas(p, 0.25, s->inttime);
2964 			if ((ev = i1pro_trialmeasure(p, &saturated, &optscale, nummeas, &s->inttime,
2965 			                                          s->gainmode, s->targoscale)) != I1PRO_OK) {
2966 				free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
2967 				free(mbuf);
2968 				a1logd(p->log,2,"i1pro_imp_measure trial measure failed\n");
2969 				return ev;
2970 			}
2971 		}
2972 
2973 		a1logd(p->log,2,"Compute optimal integration time\n");
2974 		/* For adaptive mode, compute a new integration time and gain mode */
2975 		/* in order to optimise the sensor values. */
2976 		if ((ev = i1pro_optimise_sensor(p, &s->inttime, &s->gainmode,
2977 		         s->inttime, s->gainmode, 1, 1, s->targoscale, optscale)) != I1PRO_OK) {
2978 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
2979 			free(mbuf);
2980 			a1logd(p->log,2,"i1pro_imp_measure optimise sensor failed\n");
2981 			return ev;
2982 		}
2983 		a1logd(p->log,2,"Computed optimal emiss inttime %f and gainmode %d\n",s->inttime,s->gainmode);
2984 
2985 		a1logd(p->log,2,"Interpolate dark calibration reference\n");
2986 		if ((ev = i1pro_interp_dark(p, s->dark_data, s->inttime, s->gainmode)) != I1PRO_OK) {
2987 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
2988 			free(mbuf);
2989 			a1logd(p->log,2,"i1pro_imp_measure interplate dark ref failed\n");
2990 			return ev;
2991 		}
2992 		s->dark_valid = 1;
2993 		s->dark_int_time = s->inttime;
2994 		s->dark_gain_mode = s->gainmode;
2995 
2996 		/* Recompute number of measurements and realloc measurement buffer */
2997 		free(mbuf);
2998 		nummeas = i1pro_comp_nummeas(p, s->wreadtime, s->inttime);
2999 		maxnummeas = i1pro_comp_nummeas(p, s->maxscantime, s->inttime);
3000 		if (maxnummeas < nummeas)
3001 			maxnummeas = nummeas;
3002 		mbsize = m->nsen * 2 * maxnummeas;
3003 		if ((mbuf = (unsigned char *)malloc(sizeof(unsigned char) * mbsize)) == NULL) {
3004 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
3005 			a1logd(p->log,1,"i1pro_imp_measure malloc %d bytes failed (7)\n",mbsize);
3006 			return I1PRO_INT_MALLOC;
3007 		}
3008 
3009 	} else if (s->reflective) {
3010 
3011 		DISDPLOT
3012 
3013 		a1logd(p->log,2,"Doing on the fly black calibration_1 with nummeas %d int_time %f, gainmode %d\n",
3014 		                                                   nummeas, s->inttime, s->gainmode);
3015 
3016 		if ((ev = i1pro_dark_measure_1(p, nummeas, &s->inttime, s->gainmode, buf, bsize))
3017 		                                                                           != I1PRO_OK) {
3018 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
3019 			free(buf);
3020 			free(mbuf);
3021 			a1logd(p->log,2,"i1pro_imp_measure dak measure 1 failed\n");
3022 			return ev;
3023 		}
3024 
3025 		ENDPLOT
3026 	}
3027 	/* Take a measurement reading using the current mode. */
3028 	/* Converts to completely processed output readings. */
3029 
3030 	a1logd(p->log,2,"Do main measurement reading\n");
3031 
3032 	/* Indicate to the user that they can now scan the instrument, */
3033 	/* after a little delay that allows for the instrument reaction time. */
3034 	if (s->scan) {
3035 		int delay = 200 + (int)(s->lamptime * 1000.0 + 0.5);
3036 		if (p->eventcallback != NULL) {
3037 			issue_scan_ready((inst *)p, delay);
3038 		} else {
3039 			/* delay then 1KHz for 200 msec */
3040 			msec_beep(delay, 1000, 200);
3041 		}
3042 	}
3043 
3044 	/* Retry loop for certaing cases */
3045 	for (;;) {
3046 
3047 		/* Trigger measure and gather raw readings */
3048 		if ((ev = i1pro_read_patches_1(p, nummeas, maxnummeas, &s->inttime, s->gainmode,
3049 		                                       &nmeasuered, mbuf, mbsize)) != I1PRO_OK) {
3050 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
3051 			if (buf != NULL)
3052 				free(buf);
3053 			free(mbuf);
3054 			a1logd(p->log,2,"i1pro_imp_measure failed at i1pro_read_patches_1\n");
3055 			return ev;
3056 		}
3057 
3058 		/* Complete reflective black reference measurement */
3059 		if (s->reflective) {
3060 			a1logd(p->log,3,"Calling black calibration_2 calc with nummeas %d, inttime %f, gainmode %d\n", nummeas, s->inttime,s->gainmode);
3061 			if ((ev = i1pro_dark_measure_2(p, s->dark_data,
3062 				            nummeas, s->inttime, s->gainmode, buf, bsize)) != I1PRO_OK) {
3063 				free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
3064 				free(buf);
3065 				free(mbuf);
3066 				a1logd(p->log,2,"i1pro_imp_measure failed at i1pro_dark_measure_2\n");
3067 				return ev;
3068 			}
3069 			s->dark_valid = 1;
3070 			s->dark_int_time = s->inttime;
3071 			s->dark_gain_mode = s->gainmode;
3072 			free(buf);
3073 		}
3074 
3075 		/* Process the raw measurement readings into final spectral readings */
3076 		ev = i1pro_read_patches_2(p, &duration, specrd, nvals, s->inttime, s->gainmode,
3077 		                                              nmeasuered, mbuf, mbsize);
3078 		/* Special case display mode read. If the sensor is saturated, and */
3079 		/* we haven't already done so, switch to the alternate integration time */
3080 		/* and try again. */
3081 		if (s->emiss && !s->scan && !s->adaptive
3082 		 && ev == I1PRO_RD_SENSORSATURATED
3083 		 && s->dispswap < 3) {
3084 			double *tt, tv;
3085 
3086 			if (s->dispswap == 0) {
3087 				a1logd(p->log,2,"Switching to 2nd display integration time %f seconds\n",s->dark_int_time2);
3088 				tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
3089 				tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
3090 				s->dispswap = 1;
3091 			} else if (s->dispswap == 1) {
3092 				a1logd(p->log,2,"Switching to 3rd display integration time %f seconds\n",s->dark_int_time3);
3093 				/* Undo first swap */
3094 				tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
3095 				tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
3096 				/* Do 2nd swap */
3097 				tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
3098 				tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
3099 				s->dispswap = 2;
3100 			} else if (s->dispswap == 2) {
3101 				a1logd(p->log,2,"Switching to 4th display integration time %f seconds\n",s->dark_int_time4);
3102 				/* Undo 2nd swap */
3103 				tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
3104 				tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
3105 				/* Do 3rd swap */
3106 				tv = s->inttime; s->inttime = s->dark_int_time4; s->dark_int_time4 = tv;
3107 				tt = s->dark_data; s->dark_data = s->dark_data4; s->dark_data4 = tt;
3108 				s->dispswap = 3;
3109 			}
3110 			/* Recompute number of measurements and realloc measurement buffer */
3111 			free(mbuf);
3112 			nummeas = i1pro_comp_nummeas(p, s->wreadtime, s->inttime);
3113 			maxnummeas = i1pro_comp_nummeas(p, s->maxscantime, s->inttime);
3114 			if (maxnummeas < nummeas)
3115 				maxnummeas = nummeas;
3116 			mbsize = m->nsen * 2 * maxnummeas;
3117 			if ((mbuf = (unsigned char *)malloc(sizeof(unsigned char) * mbsize)) == NULL) {
3118 				free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
3119 				a1logd(p->log,1,"i1pro_imp_measure malloc %d bytes failed (7)\n",mbsize);
3120 				return I1PRO_INT_MALLOC;
3121 			}
3122 			continue;			/* Do the measurement again */
3123 		}
3124 
3125 		if (ev != I1PRO_OK) {
3126 			free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
3127 			free(mbuf);
3128 			a1logd(p->log,2,"i1pro_imp_measure failed at i1pro_read_patches_2\n");
3129 			return ev;
3130 		}
3131 		break;		/* Don't repeat */
3132 	}
3133 	free(mbuf);
3134 
3135 	/* Transfer spectral and convert to XYZ */
3136 	if ((ev = i1pro_conv2XYZ(p, vals, nvals, specrd, clamp)) != I1PRO_OK) {
3137 		free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
3138 		a1logd(p->log,2,"i1pro_imp_measure failed at i1pro_conv2XYZ\n");
3139 		return ev;
3140 	}
3141 	free_dmatrix(specrd, 0, nvals-1, 0, m->nwav[m->highres]-1);
3142 
3143 	if (nvals > 0)
3144 		vals[0].duration = duration;	/* Possible flash duration */
3145 
3146 	/* Update log counters */
3147 	if (s->reflective) {
3148 		if (s->scan)
3149 			m->acount++;
3150 		else {
3151 			m->rpinttime = s->inttime;
3152 			m->rpcount++;
3153 		}
3154 	}
3155 
3156 	a1logd(p->log,3,"i1pro_imp_measure sucessful return\n");
3157 	if (user_trig)
3158 		return I1PRO_USER_TRIG;
3159 	return ev;
3160 }
3161 
3162 /* - - - - - - - - - - - - - - - - */
3163 /*
3164 
3165 	Determining the refresh rate for a refresh type display.
3166 
3167 	This is easy when the max sample rate of the i1 is above
3168 	the nyquist of the display, and will always be the case
3169 	for the range we are prepared to measure (up to 100Hz)
3170 	when using an Rev B, D or E, but is a problem for the
3171 	rev A and ColorMunki, which can only sample at 113Hz.
3172 
3173 	We work around this problem by detecting when
3174 	we are measuring an alias of the refresh rate, and
3175 	average the aliasing corrected measurements.
3176 
3177 	If there is no aparent refresh, or the refresh rate is not determinable,
3178 	return a period of 0.0 and inst_ok;
3179 */
3180 
3181 i1pro_code i1pro_measure_rgb(i1pro *p, double *inttime, double *rgb);
3182 
3183 #ifndef PSRAND32L
3184 # define PSRAND32L(S) ((S) * 1664525L + 1013904223L)
3185 #endif
3186 #undef FREQ_SLOW_PRECISE	/* [und] Interpolate then autocorrelate, else autc & filter */
3187 #define NFSAMPS 80		/* Number of samples to read */
3188 #define NFMXTIME 6.0	/* Maximum time to take (2000 == 6) */
3189 #define PBPMS 20		/* bins per msec */
3190 #define PERMIN ((1000 * PBPMS)/40)	/* 40 Hz */
3191 #define PERMAX ((1000 * PBPMS)/4)	/* 4 Hz*/
3192 #define NPER (PERMAX - PERMIN + 1)
3193 #define PWIDTH (8 * PBPMS)			/* 8 msec bin spread to look for peak in */
3194 #define MAXPKS 20					/* Number of peaks to find */
3195 #define TRIES 8	 					/* Number of different sample rates to try */
3196 
i1pro_imp_meas_refrate(i1pro * p,double * ref_rate)3197 i1pro_code i1pro_imp_meas_refrate(
3198 	i1pro *p,
3199 	double *ref_rate
3200 ) {
3201 	i1pro_code ev = I1PRO_OK;
3202 	i1proimp *m = (i1proimp *)p->m;
3203 	i1pro_state *s = &m->ms[m->mmode];
3204 	int i, j, k, mm;
3205 	double **multimeas;			/* Spectral measurements */
3206 	int nummeas;
3207 	double rgbw[3] = { 610.0, 520.0, 460.0 };
3208 	double inttime;
3209 	static unsigned int randn = 0x12345678;
3210 	struct {
3211 		double sec;
3212 		double rgb[3];
3213 	} samp[NFSAMPS * 2];
3214 	int nfsamps;			/* Actual samples read */
3215 	double minv[3];			/* Minimum reading */
3216 	double maxv[3];			/* Maximum reading */
3217 	double maxt;			/* Time range */
3218 #ifdef FREQ_SLOW_PRECISE
3219 	int nbins;
3220 	double *bins[3];		/* PBPMS sample bins */
3221 #else
3222 	double tcorr[NPER];		/* Temp for initial autocorrelation */
3223 	int ntcorr[NPER];		/* Number accumulated */
3224 #endif
3225 	double corr[NPER];		/* Filtered correlation for each period value */
3226 	double mincv, maxcv;	/* Max and min correlation values */
3227 	double crange;			/* Correlation range */
3228 	double peaks[MAXPKS];	/* Peak wavelength */
3229 	double peakh[MAXPKS];	/* Peak heighheight */
3230 	int npeaks;				/* Number of peaks */
3231 	double pval;			/* Period value */
3232 	double rfreq[TRIES];	/* Computed refresh frequency for each try */
3233 	double rsamp[TRIES];	/* Sampling rate used to measure frequency */
3234 	int tix = 0;			/* try index */
3235 
3236 	a1logd(p->log,2,"i1pro_imp_meas_refrate called\n");
3237 
3238 	if (ref_rate != NULL)
3239 		*ref_rate = 0.0;
3240 
3241 	if (!s->emiss) {
3242 		a1logd(p->log,2,"i1pro_imp_meas_refrate not in emissive mode\n");
3243 		return I1PRO_UNSUPPORTED;
3244 	}
3245 
3246 	for (mm = 0; mm < TRIES; mm++) {
3247 		rfreq[mm] = 0.0;
3248 		npeaks = 0;			/* Number of peaks */
3249 		nummeas = NFSAMPS;
3250 		multimeas = dmatrix(0, nummeas-1, -1, m->nwav[m->highres]-1);
3251 
3252 		if (mm == 0)
3253 			inttime = m->min_int_time;
3254 		else {
3255 			double rval, dmm;
3256 			randn = PSRAND32L(randn);
3257 			rval = (double)randn/4294967295.0;
3258 			dmm = ((double)mm + rval - 0.5)/(TRIES - 0.5);
3259 			inttime = m->min_int_time * (1.0 + dmm * 0.80);
3260 		}
3261 
3262 		if ((ev = i1pro_read_patches_all(p, multimeas, nummeas, &inttime, 0)) != inst_ok) {
3263 			free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav[m->highres]-1);
3264 			return ev;
3265 		}
3266 
3267 		rsamp[tix] = 1.0/inttime;
3268 
3269 		/* Convert the samples to RGB */
3270 		for (i = 0; i < nummeas && i < NFSAMPS; i++) {
3271 			samp[i].sec = i * inttime;
3272 			samp[i].rgb[0] = samp[i].rgb[1] = samp[i].rgb[2] = 0.0;
3273 			for (j = 0; j < m->nwav[m->highres]; j++) {
3274 				double wl = XSPECT_WL(m->wl_short[m->highres], m->wl_long[m->highres], m->nwav[m->highres], j);
3275 
3276 //printf("~1 multimeas %d %d = %f\n",i, j, multimeas[i][j]);
3277 				for (k = 0; k < 3; k++) {
3278 					double tt = (double)(wl - rgbw[k]);
3279 					tt = (40.0 - fabs(tt))/40.0;
3280 					if (tt < 0.0)
3281 						tt = 0.0;
3282 					samp[i].rgb[k] += sqrt(tt) * multimeas[i][j];
3283 				}
3284 			}
3285 		}
3286 		free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav[m->highres]-1);
3287 		nfsamps = i;
3288 
3289 		a1logd(p->log, 3, "i1pro_meas_refrate: Read %d samples for refresh calibration\n",nfsamps);
3290 
3291 #ifdef NEVER
3292 		/* Plot the raw sensor values */
3293 		{
3294 			double xx[NFSAMPS];
3295 			double y1[NFSAMPS];
3296 			double y2[NFSAMPS];
3297 			double y3[NFSAMPS];
3298 
3299 			for (i = 0; i < nfsamps; i++) {
3300 				xx[i] = samp[i].sec;
3301 				y1[i] = samp[i].rgb[0];
3302 				y2[i] = samp[i].rgb[1];
3303 				y3[i] = samp[i].rgb[2];
3304 //			printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].rgb[0]);
3305 			}
3306 			printf("Fast scan sensor values and time (sec)\n");
3307 			do_plot6(xx, y1, y2, y3, NULL, NULL, NULL, nfsamps);
3308 		}
3309 #endif
3310 
3311 		/* Locate the smallest values and maximum time */
3312 		maxt = -1e6;
3313 		minv[0] = minv[1] = minv[2] = 1e20;
3314 		maxv[0] = maxv[1] = maxv[2] = -11e20;
3315 		for (i = nfsamps-1; i >= 0; i--) {
3316 			if (samp[i].sec > maxt)
3317 				maxt = samp[i].sec;
3318 			for (j = 0; j < 3; j++) {
3319 				if (samp[i].rgb[j] < minv[j])
3320 					minv[j] = samp[i].rgb[j];
3321 				if (samp[i].rgb[j] > maxv[j])
3322 					maxv[j] = samp[i].rgb[j];
3323 			}
3324 		}
3325 		/* Re-zero the sample times, and normalise the readings */
3326 		for (i = nfsamps-1; i >= 0; i--) {
3327 			samp[i].sec -= samp[0].sec;
3328 			if (samp[i].sec > maxt)
3329 				maxt = samp[i].sec;
3330 			for (j = 0; j < 3; j++) {
3331 				samp[i].rgb[j] -= minv[j];
3332 			}
3333 		}
3334 
3335 #ifdef FREQ_SLOW_PRECISE	/* Interp then autocorrelate */
3336 
3337 		/* Create PBPMS bins and interpolate readings into them */
3338 		nbins = 1 + (int)(maxt * 1000.0 * PBPMS + 0.5);
3339 		for (j = 0; j < 3; j++) {
3340 			if ((bins[j] = (double *)calloc(sizeof(double), nbins)) == NULL) {
3341 				a1loge(p->log, inst_internal_error, "i1pro_meas_refrate: malloc failed\n");
3342 				return I1PRO_INT_MALLOC;
3343 			}
3344 		}
3345 
3346 		/* Do the interpolation */
3347 		for (k = 0; k < (nfsamps-1); k++) {
3348 			int sbin, ebin;
3349 			sbin = (int)(samp[k].sec * 1000.0 * PBPMS + 0.5);
3350 			ebin = (int)(samp[k+1].sec * 1000.0 * PBPMS + 0.5);
3351 			for (i = sbin; i <= ebin; i++) {
3352 				double bl;
3353 #if defined(__APPLE__) && defined(__POWERPC__)
3354 				gcc_bug_fix(i);
3355 #endif
3356 				bl = (i - sbin)/(double)(ebin - sbin);	/* 0.0 to 1.0 */
3357 				for (j = 0; j < 3; j++) {
3358 					bins[j][i] = (1.0 - bl) * samp[k].rgb[j] + bl * samp[k+1].rgb[j];
3359 				}
3360 			}
3361 		}
3362 
3363 #ifdef NEVER
3364 
3365 		/* Plot interpolated values */
3366 		{
3367 			double *xx;
3368 			double *y1;
3369 			double *y2;
3370 			double *y3;
3371 
3372 			xx = malloc(sizeof(double) * nbins);
3373 			y1 = malloc(sizeof(double) * nbins);
3374 			y2 = malloc(sizeof(double) * nbins);
3375 			y3 = malloc(sizeof(double) * nbins);
3376 
3377 			if (xx == NULL || y1 == NULL || y2 == NULL || y3 == NULL) {
3378 				a1loge(p->log, inst_internal_error, "i1pro_meas_refrate: malloc failed\n");
3379 				for (j = 0; j < 3; j++)
3380 					free(bins[j]);
3381 				return I1PRO_INT_MALLOC;
3382 			}
3383 			for (i = 0; i < nbins; i++) {
3384 				xx[i] = i / (double)PBPMS;			/* msec */
3385 				y1[i] = bins[0][i];
3386 				y2[i] = bins[1][i];
3387 				y3[i] = bins[2][i];
3388 			}
3389 			printf("Interpolated fast scan sensor values and time (msec) for inttime %f\n",inttime);
3390 			do_plot6(xx, y1, y2, y3, NULL, NULL, NULL, nbins);
3391 
3392 			free(xx);
3393 			free(y1);
3394 			free(y2);
3395 			free(y3);
3396 		}
3397 #endif /* PLOT_REFRESH */
3398 
3399 		/* Compute auto-correlation at 1/PBPMS msec intervals */
3400 		/* from 25 msec (40Hz) to 100msec (10 Hz) */
3401 		mincv = 1e48, maxcv = -1e48;
3402 		for (i = 0; i < NPER; i++) {
3403 			int poff = PERMIN + i;		/* Offset to corresponding sample */
3404 
3405 			corr[i] = 0;
3406 			for (k = 0; (k + poff) < nbins; k++) {
3407 				corr[i] += bins[0][k] * bins[0][k + poff]
3408 				        +  bins[1][k] * bins[1][k + poff]
3409 				        +  bins[2][k] * bins[2][k + poff];
3410 			}
3411 			corr[i] /= (double)k;		/* Normalize */
3412 
3413 			if (corr[i] > maxcv)
3414 				maxcv = corr[i];
3415 			if (corr[i] < mincv)
3416 				mincv = corr[i];
3417 		}
3418 		/* Free the bins */
3419 		for (j = 0; j < 3; j++)
3420 			free(bins[j]);
3421 
3422 #else /* !FREQ_SLOW_PRECISE  Fast - autocorrellate then filter */
3423 
3424 	/* Upsample by a factor of 2 */
3425 	for (i = nfsamps-1; i >= 0; i--) {
3426 		j = 2 * i;
3427 		samp[j].sec = samp[i].sec;
3428 		samp[j].rgb[0] = samp[i].rgb[0];
3429 		samp[j].rgb[1] = samp[i].rgb[1];
3430 		samp[j].rgb[2] = samp[i].rgb[2];
3431 		if (i > 0) {
3432 			j--;
3433 			samp[j].sec = 0.5 * (samp[i].sec + samp[i-1].sec);
3434 			samp[j].rgb[0] = 0.5 * (samp[i].rgb[0] + samp[i-1].rgb[0]);
3435 			samp[j].rgb[1] = 0.5 * (samp[i].rgb[1] + samp[i-1].rgb[1]);
3436 			samp[j].rgb[2] = 0.5 * (samp[i].rgb[2] + samp[i-1].rgb[2]);
3437 		}
3438 	}
3439 	nfsamps = 2 * nfsamps - 1;
3440 
3441 	/* Do point by point correllation of samples */
3442 	for (i = 0; i < NPER; i++) {
3443 		tcorr[i] = 0.0;
3444 		ntcorr[i] = 0;
3445 	}
3446 
3447 	for (j = 0; j < (nfsamps-1); j++) {
3448 
3449 		for (k = j+1; k < nfsamps; k++) {
3450 			double del, cor;
3451 			int bix;
3452 
3453 			del = samp[k].sec - samp[j].sec;
3454 			bix = (int)(del * 1000.0 * PBPMS + 0.5);
3455 			if (bix < PERMIN)
3456 				continue;
3457 			if (bix > PERMAX)
3458 				break;
3459 			bix -= PERMIN;
3460 
3461 			cor = samp[j].rgb[0] * samp[k].rgb[0]
3462 	            + samp[j].rgb[1] * samp[k].rgb[1]
3463 	            + samp[j].rgb[2] * samp[k].rgb[2];
3464 
3465 //printf("~1 j %d k %d, del %f bix %d cor %f\n",j,k,del,bix,cor);
3466 			tcorr[bix] += cor;
3467 			ntcorr[bix]++;
3468 		}
3469 	}
3470 	/* Divide out count and linearly interpolate */
3471 	j = 0;
3472 	for (i = 0; i < NPER; i++) {
3473 		if (ntcorr[i] > 0) {
3474 			tcorr[i] /= ntcorr[i];
3475 			if ((i - j) > 1) {
3476 				if (j == 0) {
3477 					for (k = j; k < i; k++)
3478 						tcorr[k] = tcorr[i];
3479 
3480 				} else {		/* Linearly interpolate from last value */
3481 					double ww = (double)i-j;
3482 					for (k = j+1; k < i; k++) {
3483 						double bl = (k-j)/ww;
3484 						tcorr[k] = (1.0 - bl) * tcorr[j] + bl * tcorr[i];
3485 					}
3486 				}
3487 			}
3488 			j = i;
3489 		}
3490 	}
3491 	if (j < (NPER-1)) {
3492 		for (k = j+1; k < NPER; k++) {
3493 			tcorr[k] = tcorr[j];
3494 		}
3495 	}
3496 
3497 #ifdef PLOT_REFRESH
3498 	/* Plot unfiltered auto correlation */
3499 	{
3500 		double xx[NPER];
3501 		double y1[NPER];
3502 
3503 		for (i = 0; i < NPER; i++) {
3504 			xx[i] = (i + PERMIN) / (double)PBPMS;			/* msec */
3505 			y1[i] = tcorr[i];
3506 		}
3507 		printf("Unfiltered auto correlation (msec)\n");
3508 		do_plot6(xx, y1, NULL, NULL, NULL, NULL, NULL, NPER);
3509 	}
3510 #endif /* PLOT_REFRESH */
3511 
3512 	/* Apply a gausian filter */
3513 #define FWIDTH 100
3514 	{
3515 		double gaus_[2 * FWIDTH * PBPMS + 1];
3516 		double *gaus = &gaus_[FWIDTH * PBPMS];
3517 		double bb = 1.0/pow(2, 5.0);
3518 		double fw = inttime * 1000.0;
3519 		int ifw;
3520 
3521 //printf("~1 sc = %f = %f msec\n",1.0/inttime, fw);
3522 //printf("~1 fw = %f, ifw = %d\n",fw,ifw);
3523 
3524 		fw *= 0.9;
3525 		ifw = (int)ceil(fw * PBPMS);
3526 		if (ifw > FWIDTH * PBPMS)
3527 			error("i1pro: Not enough space for lanczos 2 filter");
3528 		for (j = -ifw; j <= ifw; j++) {
3529 			double x, y;
3530 			x = j/(PBPMS * fw);
3531 			if (fabs(x) > 1.0)
3532 				y = 0.0;
3533 			else
3534 				y = 1.0/pow(2, 5.0 * x * x) - bb;
3535 			gaus[j] = y;
3536 //printf("~1 gaus[%d] = %f\n",j,y);
3537 		}
3538 
3539 		for (i = 0; i < NPER; i++) {
3540 			double sum = 0.0;
3541 			double wght = 0.0;
3542 
3543 			for (j = -ifw; j <= ifw; j++) {
3544 				double w;
3545 				int ix = i + j;
3546 				if (ix < 0)
3547 					ix = -ix;
3548 				if (ix > (NPER-1))
3549 					ix = 2 * NPER-1 - ix;
3550 				w = gaus[j];
3551 				sum += w * tcorr[ix];
3552 				wght += w;
3553 			}
3554 //printf("~1 corr[%d] wgt = %f\n",i,wght);
3555 			corr[i] = sum / wght;
3556 		}
3557 	}
3558 
3559 	/* Compute min & max */
3560 	mincv = 1e48, maxcv = -1e48;
3561 	for (i = 0; i < NPER; i++) {
3562 		if (corr[i] > maxcv)
3563 			maxcv = corr[i];
3564 		if (corr[i] < mincv)
3565 			mincv = corr[i];
3566 	}
3567 
3568 #endif /* !FREQ_SLOW_PRECISE  Fast - autocorrellate then filter */
3569 
3570 		crange = maxcv - mincv;
3571 		a1logd(p->log,3,"Correlation value range %f - %f = %f = %f%%\n",mincv, maxcv,crange, 100.0 * (maxcv-mincv)/maxcv);
3572 
3573 #ifdef PLOT_REFRESH
3574 		/* Plot this measuremnts auto correlation */
3575 		{
3576 			double xx[NPER];
3577 			double y1[NPER];
3578 
3579 			for (i = 0; i < NPER; i++) {
3580 				xx[i] = (i + PERMIN) / (double)PBPMS;			/* msec */
3581 				y1[i] = corr[i];
3582 			}
3583 			printf("Auto correlation (msec)\n");
3584 			do_plot6(xx, y1, NULL, NULL, NULL, NULL, NULL, NPER);
3585 		}
3586 #endif /* PLOT_REFRESH */
3587 
3588 #define PFDB 4	// normally 4
3589 		/* If there is sufficient level and distict correlations */
3590 		if (crange/maxcv >= 0.1) {
3591 
3592 			a1logd(p->log,PFDB,"Searching for peaks\n");
3593 
3594 			/* Locate all the peaks starting at the longest correllation */
3595 			for (i = (NPER-1-PWIDTH); i >= 0 && npeaks < MAXPKS; i--) {
3596 				double v1, v2, v3;
3597 				v1 = corr[i];
3598 				v2 = corr[i + PWIDTH/2];	/* Peak */
3599 				v3 = corr[i + PWIDTH];
3600 
3601 				if (fabs(v3 - v1)/crange < 0.05
3602 				 && (v2 - v1)/crange > 0.025
3603 				 && (v2 - v3)/crange > 0.025
3604 				 && (v2 - mincv)/crange > 0.5) {
3605 					double pkv;			/* Peak value */
3606 					int pki;			/* Peak index */
3607 					double ii, bl;
3608 
3609 #ifdef PLOT_REFRESH
3610 					a1logd(p->log,PFDB,"Max between %f and %f msec\n",
3611 					       (i + PERMIN)/(double)PBPMS,(i + PWIDTH + PERMIN)/(double)PBPMS);
3612 #endif
3613 
3614 					/* Locate the actual peak */
3615 					pkv = -1.0;
3616 					pki = 0;
3617 					for (j = i; j < (i + PWIDTH); j++) {
3618 						if (corr[j] > pkv) {
3619 							pkv = corr[j];
3620 							pki = j;
3621 						}
3622 					}
3623 #ifdef PLOT_REFRESH
3624 					a1logd(p->log,PFDB,"Peak is at %f msec, %f corr\n", (pki + PERMIN)/(double)PBPMS, pkv);
3625 #endif
3626 
3627 					/* Interpolate the peak value for higher precision */
3628 					/* j = bigest */
3629 					if (corr[pki-1] > corr[pki+1])  {
3630 						j = pki-1;
3631 						k = pki+1;
3632 					} else {
3633 						j = pki+1;
3634 						k = pki-1;
3635 					}
3636 					bl = (corr[pki] - corr[j])/(corr[pki] - corr[k]);
3637 					bl = (bl + 1.0)/2.0;
3638 					ii = bl * pki + (1.0 - bl) * j;
3639 					pval = (ii + PERMIN)/(double)PBPMS;
3640 #ifdef PLOT_REFRESH
3641 					a1logd(p->log,PFDB,"Interpolated peak is at %f msec\n", pval);
3642 #endif
3643 					peaks[npeaks] = pval;
3644 					peakh[npeaks] = corr[pki];
3645 					npeaks++;
3646 
3647 					i -= PWIDTH;
3648 				}
3649 #ifdef NEVER
3650 				if (v2 > v1 && v2 > v3) {
3651 					printf("Peak rehjected:\n");
3652 					printf("(v3 - v1)/crange = %f < 0.05 ?\n",fabs(v3 - v1)/crange);
3653 					printf("(v2 - v1)/crange = %f > 0.025 ?\n",(v2 - v1)/crange);
3654 					printf("(v2 - v3)/crange = %f > 0.025 ?\n",(v2 - v3)/crange);
3655 					printf("(v2 - mincv)/crange = %f > 0.5 ?\n",(v2 - mincv)/crange);
3656 				}
3657 #endif
3658 			}
3659 			a1logd(p->log,3,"Number of peaks located = %d\n",npeaks);
3660 
3661 		} else {
3662 			a1logd(p->log,3,"All rejected, crange/maxcv = %f < 0.06\n",crange/maxcv);
3663 		}
3664 #undef PFDB
3665 
3666 		a1logd(p->log,3,"Number of peaks located = %d\n",npeaks);
3667 
3668 		if (npeaks > 1) {		/* Compute aparent refresh rate */
3669 			int nfails;
3670 			double div, avg, ano;
3671 			/* Try and locate a common divisor amongst all the peaks. */
3672 			/* This is likely to be the underlying refresh rate. */
3673 			for (k = 0; k < npeaks; k++) {
3674 				for (j = 1; j < 25; j++) {
3675 					avg = ano = 0.0;
3676 					div = peaks[k]/(double)j;
3677 					if (div < 5.0)
3678 						continue;		/* Skip anything higher than 200Hz */
3679 //printf("~1 trying %f Hz\n",1000.0/div);
3680 					for (nfails = i = 0; i < npeaks; i++) {
3681 						double rem, cnt;
3682 
3683 						rem = peaks[i]/div;
3684 						cnt = floor(rem + 0.5);
3685 						rem = fabs(rem - cnt);
3686 
3687 #ifdef PLOT_REFRESH
3688 						a1logd(p->log, 3, "remainder for peak %d = %f\n",i,rem);
3689 #endif
3690 						if (rem > 0.06) {
3691 							if (++nfails > 2)
3692 								break;				/* Fail this divisor */
3693 						} else {
3694 							avg += peaks[i];		/* Already weighted by cnt */
3695 							ano += cnt;
3696 						}
3697 					}
3698 
3699 					if (nfails == 0 || (nfails <= 2 && npeaks >= 6))
3700 						break;		/* Sucess */
3701 					/* else go and try a different divisor */
3702 				}
3703 				if (j < 25)
3704 					break;			/* Success - found common divisor */
3705 			}
3706 			if (k >= npeaks) {
3707 				a1logd(p->log,3,"Failed to locate common divisor\n");
3708 
3709 			} else {
3710 				pval = 0.001 * avg/ano;
3711 				if (pval < inttime) {
3712 					a1logd(p->log,3,"Discarding frequency %f > sample rate %f\n",1.0/pval, 1.0/inttime);
3713 				} else {
3714 					pval = 1.0/pval;		/* Convert to frequency */
3715 					rfreq[tix++] = pval;
3716 					a1logd(p->log,3,"Located frequency %f sum %f dif %f\n",pval, pval + 1.0/inttime, fabs(pval - 1.0/inttime));
3717 				}
3718 			}
3719 		}
3720 	}
3721 
3722 	if (tix >= 3) {
3723 
3724 		for (mm = 0; mm < tix; mm++) {
3725 			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]));
3726 		}
3727 
3728 		/* Decide if we are above the nyquist, or whether */
3729 		/* we have aliases of the fundamental */
3730 		{
3731 			double brange = 1e38;
3732 			double brate = 0.0;
3733 			int bsplit = -1;
3734 			double min, max, avg, range;
3735 			int split, mul, niia;
3736 
3737 			/* Compute fundamental and sub aliases at all possible splits. */
3738 			/* Skip the reading at the split. */
3739 			for (split = tix; split >= -1; split--) {
3740 				min = 1e38; max = -1e38; avg = 0.0; niia = 0;
3741 				for (mm = 0; mm < tix; mm++) {
3742 					double alias;
3743 
3744 					if (mm == split)
3745 						continue;
3746 					if (mm < split)
3747 						alias = rfreq[mm];
3748 					else
3749 						alias = fabs(rsamp[mm] - rfreq[mm]);
3750 
3751 					avg += alias;
3752 					niia++;
3753 
3754 					if (alias < min)
3755 						min = alias;
3756 					if (alias > max)
3757 						max = alias;
3758 				}
3759 				avg /= (double)niia;
3760 				range = (max - min)/(max + min);
3761 //printf("~1 split %d avg = %f, range = %f\n",split,avg,range);
3762 				if (range < brange) {
3763 					brange = range;
3764 					brate = avg;
3765 					bsplit = split;
3766 				}
3767 			}
3768 
3769 			/* Compute sub and add aliases at all possible splits */
3770 			/* Skip the reading at the split. */
3771 			for (split = tix; split >= -1; split--) {
3772 				min = 1e38; max = -1e38; avg = 0.0; niia = 0;
3773 				for (mm = 0; mm < tix; mm++) {
3774 					double alias;
3775 
3776 					if (mm == split)
3777 						continue;
3778 					if (mm < split)
3779 						alias = fabs(rsamp[mm] - rfreq[mm]);
3780 					else
3781 						alias = rsamp[mm] + rfreq[mm];
3782 
3783 					avg += alias;
3784 					niia++;
3785 
3786 					if (alias < min)
3787 						min = alias;
3788 					if (alias > max)
3789 						max = alias;
3790 				}
3791 				avg /= (double)niia;
3792 				range = (max - min)/(max + min);
3793 //printf("~1 split %d avg = %f, range = %f\n",100 + split,avg,range);
3794 				if (range < brange) {
3795 					brange = range;
3796 					brate = avg;
3797 					bsplit = 100 + split;
3798 				}
3799 			}
3800 
3801 			a1logd(p->log, 3, "Selected split %d range %f\n",bsplit,brange);
3802 
3803 			/* Hmm. Could reject result and re-try if brange is too large ? ( > 0.005 ?) */
3804 
3805 			if (brange > 0.05) {
3806 				a1logd(p->log, 3, "Readings are too inconsistent (brange %.1f%%) - should retry ?\n",brange * 100.0);
3807 			} else {
3808 
3809 				if (ref_rate != NULL)
3810 					*ref_rate = brate;
3811 
3812 				/* Error against my 85Hz CRT - GWG */
3813 //				a1logd(p->log, 1, "Refresh rate %f Hz, error = %.4f%%\n",brate,100.0 * fabs(brate - 85.0)/(85.0));
3814 				return I1PRO_OK;
3815 			}
3816 		}
3817 	} else {
3818 		a1logd(p->log, 3, "Not enough tries suceeded to determine refresh rate\n");
3819 	}
3820 
3821 	return I1PRO_RD_NOREFR_FOUND;
3822 }
3823 #undef NFSAMPS
3824 #undef PBPMS
3825 #undef PERMIN
3826 #undef PERMAX
3827 #undef NPER
3828 #undef PWIDTH
3829 
3830 /* - - - - - - - - - - - - - - - - - - - - - - */
3831 /* i1 refspot calibration/log stored on instrument */
3832 /* RevA..D only! */
3833 
3834 /* Restore the reflective spot calibration information from the EEPRom */
3835 /* Always returns success, even if the restore fails, */
3836 /* which may happen for an instrument that's never been used or had calibration */
3837 /* written to its EEProm */
3838 /* RevA..D only! */
i1pro_restore_refspot_cal(i1pro * p)3839 i1pro_code i1pro_restore_refspot_cal(i1pro *p) {
3840 	int chsum1, *chsum2;
3841 	int *ip, i;
3842 	unsigned int count;
3843 	double *dp;
3844 	unsigned char buf[256];
3845 	i1proimp *m = (i1proimp *)p->m;
3846 	i1pro_state *s = &m->ms[i1p_refl_spot];		/* NOT current mode, refspot mode */
3847 	i1key offst = 0;							/* Offset to copy to use */
3848 	i1pro_code ev = I1PRO_OK;
3849 	int o_nsen;									/* Actual nsen data */
3850 
3851 	a1logd(p->log,2,"Doing Restoring reflective spot calibration information from the EEProm\n");
3852 
3853 	chsum1 = m->data->checksum(m->data, 0);
3854 	if ((chsum2 = m->data->get_int(m->data, key_checksum, 0)) == NULL || chsum1 != *chsum2) {
3855 		offst = key_2logoff;
3856 		chsum1 = m->data->checksum(m->data, key_2logoff);
3857 		if ((chsum2 = m->data->get_int(m->data, key_checksum + key_2logoff, 0)) == NULL
3858 		     || chsum1 != *chsum2) {
3859 			a1logd(p->log,2,"Neither EEPRom checksum was valid\n");
3860 			return I1PRO_OK;
3861 		}
3862 	}
3863 
3864 	/* Get the calibration gain mode */
3865 	if ((ip = m->data->get_ints(m->data, &count, key_gainmode + offst)) == NULL || count < 1) {
3866 		a1logd(p->log,2,"Failed to read calibration gain mode from EEPRom\n");
3867 		return I1PRO_OK;
3868 	}
3869 	if (ip[0] == 0) {
3870 #ifdef USE_HIGH_GAIN_MODE
3871 		s->gainmode = 1;
3872 #else
3873 		s->gainmode = 0;
3874 		a1logd(p->log,2,"Calibration gain mode was high, and high gain not compiled in\n");
3875 		return I1PRO_OK;
3876 #endif /* !USE_HIGH_GAIN_MODE */
3877 	} else
3878 		s->gainmode = 0;
3879 
3880 	/* Get the calibration integration time */
3881 	if ((dp = m->data->get_doubles(m->data, &count, key_inttime + offst)) == NULL || count < 1) {
3882 		a1logd(p->log,2,"Failed to read calibration integration time from EEPRom\n");
3883 		return I1PRO_OK;
3884 	}
3885 	s->inttime = dp[0];
3886 	if (s->inttime < m->min_int_time)	/* Hmm. EEprom is occasionaly screwed up */
3887 		s->inttime = m->min_int_time;
3888 
3889 	/* Get the dark data */
3890 	if ((ip = m->data->get_ints(m->data, &count, key_darkreading + offst)) == NULL
3891 	                                                              || count != 128) {
3892 		a1logv(p->log,1,"Failed to read calibration dark data from EEPRom\n");
3893 		return I1PRO_OK;
3894 	}
3895 
3896 	/* Convert back to a single raw big endian instrument readings */
3897 	for (i = 0; i < 128; i++) {
3898 		buf[i * 2 + 0] = (ip[i] >> 8) & 0xff;
3899 		buf[i * 2 + 1] = ip[i] & 0xff;
3900 	}
3901 
3902 	/* Convert to calibration data */
3903 	a1logd(p->log,3,"Calling black calibration_2 calc with nummeas %d, inttime %f, gainmode %d\n", 1, s->inttime,s->gainmode);
3904 	o_nsen = m->nsen;
3905 	m->nsen = 128;		/* Assume EEprom cal data is <= Rev D format */
3906 	if ((ev = i1pro_dark_measure_2(p, s->dark_data, 1, s->inttime, s->gainmode,
3907 		                                                 buf, 256)) != I1PRO_OK) {
3908 		a1logd(p->log,2,"Failed to convert EEProm dark data to calibration\n");
3909 		m->nsen = o_nsen;
3910 		return I1PRO_OK;
3911 	}
3912 
3913 	/* We've sucessfully restored the dark calibration */
3914 	s->dark_valid = 1;
3915 	s->ddate = m->caldate;
3916 
3917 	/* Get the white calibration data */
3918 	if ((ip = m->data->get_ints(m->data, &count, key_whitereading + offst)) == NULL
3919 	                                                               || count != 128) {
3920 		a1logd(p->log,2,"Failed to read calibration white data from EEPRom\n");
3921 		m->nsen = o_nsen;
3922 		return I1PRO_OK;
3923 	}
3924 
3925 	/* Convert back to a single raw big endian instrument readings */
3926 	for (i = 0; i < 128; i++) {
3927 		buf[i * 2 + 0] = (ip[i] >> 8) & 0xff;
3928 		buf[i * 2 + 1] = ip[i] & 0xff;
3929 	}
3930 
3931 	/* Convert to calibration data */
3932 	m->nsen = 128;		/* Assume EEprom cal data is <= Rev D format */
3933 	if ((ev = i1pro_whitemeasure_buf(p, s->cal_factor[0], s->cal_factor[1], s->white_data,
3934 	                s->inttime, s->gainmode, buf)) != I1PRO_OK) {
3935 		/* This may happen for an instrument that's never been used */
3936 		a1logd(p->log,2,"Failed to convert EEProm white data to calibration\n");
3937 		m->nsen = o_nsen;
3938 		return I1PRO_OK;
3939 	}
3940 	m->nsen = o_nsen;
3941 
3942 	/* Check a reflective white measurement, and check that */
3943 	/* it seems reasonable. Return I1PRO_OK if it is, error if not. */
3944 	/* (Using cal_factor[] as temp.) */
3945 	if ((ev = i1pro_check_white_reference1(p, s->cal_factor[0])) != I1PRO_OK) {
3946 		/* This may happen for an instrument that's never been used */
3947 		a1logd(p->log,2,"Failed to convert EEProm white data to calibration\n");
3948 		return I1PRO_OK;
3949 	}
3950 	/* Compute a calibration factor given the reading of the white reference. */
3951 	ev = i1pro_compute_white_cal(p, s->cal_factor[0], m->white_ref[0], s->cal_factor[0],
3952 	                               s->cal_factor[1], m->white_ref[1], s->cal_factor[1], 1);
3953 	if (ev != I1PRO_RD_TRANSWHITEWARN && ev != I1PRO_OK) {
3954 		a1logd(p->log,2,"i1pro_compute_white_cal failed to convert EEProm data to calibration\n");
3955 		return I1PRO_OK;
3956 	}
3957 
3958 	/* We've sucessfully restored the calibration */
3959 	s->cal_valid = 1;
3960 	s->cfdate = m->caldate;
3961 
3962 	return I1PRO_OK;
3963 }
3964 
3965 /* Save the reflective spot calibration information to the EEPRom data object. */
3966 /* Note we don't actually write to the EEProm here! */
3967 /* For RevA..D only! */
i1pro_set_log_data(i1pro * p)3968 static i1pro_code i1pro_set_log_data(i1pro *p) {
3969 	int *ip, i;
3970 	unsigned int count;
3971 	double *dp;
3972 	double absmeas[128];
3973 	i1proimp *m = (i1proimp *)p->m;
3974 	i1pro_state *s = &m->ms[i1p_refl_spot];		/* NOT current mode, refspot mode */
3975 	i1key offst = 0;							/* Offset to copy to use */
3976 	i1pro_code ev = I1PRO_OK;
3977 
3978 	a1logd(p->log,3,"i1pro_set_log_data called\n");
3979 
3980 	if (s->dark_valid == 0 || s->cal_valid == 0)
3981 		return I1PRO_INT_NO_CAL_TO_SAVE;
3982 
3983 	/* Set the calibration gain mode */
3984 	if ((ip = m->data->get_ints(m->data, &count, key_gainmode + offst)) == NULL || count < 1) {
3985 		a1logd(p->log,2,"Failed to access calibration gain mode from EEPRom\n");
3986 		return I1PRO_INT_EEPROM_DATA_MISSING;
3987 	}
3988 	if (s->gainmode == 0)
3989 		ip[0] = 1;
3990 	else
3991 		ip[0] = 0;
3992 
3993 	/* Set the calibration integration time */
3994 	if ((dp = m->data->get_doubles(m->data, &count, key_inttime + offst)) == NULL || count < 1) {
3995 		a1logd(p->log,2,"Failed to read calibration integration time from EEPRom\n");
3996 		return I1PRO_INT_EEPROM_DATA_MISSING;
3997 	}
3998 	dp[0] = s->inttime;
3999 
4000 	/* Set the dark data */
4001 	if ((ip = m->data->get_ints(m->data, &count, key_darkreading + offst)) == NULL
4002 	                                                              || count != 128) {
4003 		a1logd(p->log,2,"Failed to access calibration dark data from EEPRom\n");
4004 		return I1PRO_INT_EEPROM_DATA_MISSING;
4005 	}
4006 
4007 	/* Convert abs dark_data to raw data */
4008 	if ((ev = i1pro_absraw_to_meas(p, ip, s->dark_data, s->inttime, s->gainmode)) != I1PRO_OK)
4009 		return ev;
4010 
4011 	/* Add back black level to white data */
4012 	for (i = 0; i < 128; i++)
4013 		absmeas[i] = s->white_data[i] + s->dark_data[i];
4014 
4015 	/* Get the white data */
4016 	if ((ip = m->data->get_ints(m->data, &count, key_whitereading + offst)) == NULL
4017 	                                                               || count != 128) {
4018 		a1logd(p->log,2,"Failed to access calibration white data from EEPRom\n");
4019 		return I1PRO_INT_EEPROM_DATA_MISSING;
4020 	}
4021 
4022 	/* Convert abs white_data to raw data */
4023 	if ((ev = i1pro_absraw_to_meas(p, ip, absmeas, s->inttime, s->gainmode)) != I1PRO_OK)
4024 		return ev;
4025 
4026 	/* Set all the log counters */
4027 
4028 	/* Total Measure (Emis/Remis/Ambient/Trans/Cal) count */
4029 	if ((ip = m->data->get_ints(m->data, &count, key_meascount)) == NULL || count < 1) {
4030 		a1logd(p->log,2,"Failed to access meascount log counter from EEPRom\n");
4031 		return I1PRO_INT_EEPROM_DATA_MISSING;
4032 	}
4033 	ip[0] = m->meascount;
4034 
4035 	/* Remspotcal last calibration date */
4036 	if ((ip = m->data->get_ints(m->data, &count, key_caldate)) == NULL || count < 1) {
4037 		a1logd(p->log,2,"Failed to access caldate log counter from EEPRom\n");
4038 		return I1PRO_INT_EEPROM_DATA_MISSING;
4039 	}
4040 	ip[0] = m->caldate;
4041 
4042 	/* Remission spot measure count at last Remspotcal. */
4043 	if ((ip = m->data->get_ints(m->data, &count, key_calcount)) == NULL || count < 1) {
4044 		a1logd(p->log,2,"Failed to access calcount log counter from EEPRom\n");
4045 		return I1PRO_INT_EEPROM_DATA_MISSING;
4046 	}
4047 	ip[0] = m->calcount;
4048 
4049 	/* Last remision spot reading integration time */
4050 	if ((dp = m->data->get_doubles(m->data, &count, key_rpinttime)) == NULL || count < 1) {
4051 		a1logd(p->log,2,"Failed to access rpinttime log counter from EEPRom\n");
4052 		return I1PRO_INT_EEPROM_DATA_MISSING;
4053 	}
4054 	dp[0] = m->rpinttime;
4055 
4056 	/* Remission spot measure count */
4057 	if ((ip = m->data->get_ints(m->data, &count, key_rpcount)) == NULL || count < 1) {
4058 		a1logd(p->log,2,"Failed to access rpcount log counter from EEPRom\n");
4059 		return I1PRO_INT_EEPROM_DATA_MISSING;
4060 	}
4061 	ip[0] = m->rpcount;
4062 
4063 	/* Remission scan measure count (??) */
4064 	if ((ip = m->data->get_ints(m->data, &count, key_acount)) == NULL || count < 1) {
4065 		a1logd(p->log,2,"Failed to access acount log counter from EEPRom\n");
4066 		return I1PRO_INT_EEPROM_DATA_MISSING;
4067 	}
4068 	ip[0] = m->acount;
4069 
4070 	/* Total lamp usage time in seconds (??) */
4071 	if ((dp = m->data->get_doubles(m->data, &count, key_lampage)) == NULL || count < 1) {
4072 		a1logd(p->log,2,"Failed to access lampage log counter from EEPRom\n");
4073 		return I1PRO_INT_EEPROM_DATA_MISSING;
4074 	}
4075 	dp[0] = m->lampage;
4076 
4077 	a1logd(p->log,5,"i1pro_set_log_data done\n");
4078 
4079 	return I1PRO_OK;
4080 }
4081 
4082 /* Update the single remission calibration and instrument usage log */
4083 /* For RevA..D only! */
i1pro_update_log(i1pro * p)4084 i1pro_code i1pro_update_log(i1pro *p) {
4085 	i1pro_code ev = I1PRO_OK;
4086 #ifdef ENABLE_WRITE
4087 	i1proimp *m = (i1proimp *)p->m;
4088 	unsigned char *buf;		/* Buffer to write to EEProm */
4089 	unsigned int len;
4090 
4091 	a1logd(p->log,5,"i1pro_update_log:\n");
4092 
4093 	/* Copy refspot calibration and log data to EEProm data store */
4094 	if ((ev = i1pro_set_log_data(p)) != I1PRO_OK) {
4095 		a1logd(p->log,2,"i1pro_update_log i1pro_set_log_data failed\n");
4096 		return ev;
4097 	}
4098 
4099 	/* Compute checksum and serialise into buffer ready to write */
4100 	if ((ev = m->data->prep_section1(m->data, &buf, &len)) != I1PRO_OK) {
4101 		a1logd(p->log,2,"i1pro_update_log prep_section1 failed\n");
4102 		return ev;
4103 	}
4104 
4105 	/* First copy of log */
4106 	if ((ev = i1pro_writeEEProm(p, buf, 0x0000, len)) != I1PRO_OK) {
4107 		a1logd(p->log,2,"i1pro_update_log i1pro_writeEEProm 0x0000 failed\n");
4108 		return ev;
4109 	}
4110 	/* Second copy of log */
4111 	if ((ev = i1pro_writeEEProm(p, buf, 0x0800, len)) != I1PRO_OK) {
4112 		a1logd(p->log,2,"i1pro_update_log i1pro_writeEEProm 0x0800 failed\n");
4113 		return ev;
4114 	}
4115 	free(buf);
4116 
4117 	a1logd(p->log,5,"i1pro_update_log done\n");
4118 #else
4119 	a1logd(p->log,5,"i1pro_update_log: skipped as EPRom write is disabled\n");
4120 #endif
4121 
4122 	return ev;
4123 }
4124 
4125 /* - - - - - - - - - - - - - - - - - - - - - - - - - - */
4126 /* Save the calibration for all modes, stored on local system */
4127 
4128 #ifdef ENABLE_NONVCAL
4129 
4130 /* non-volatile save/restor state */
4131 typedef struct {
4132 	int ef;					/* Error flag, 1 = write failed, 2 = close failed */
4133 	unsigned int chsum;		/* Checksum */
4134 	int nbytes;				/* Number of bytes checksummed */
4135 } i1pnonv;
4136 
update_chsum(i1pnonv * x,unsigned char * p,int nn)4137 static void update_chsum(i1pnonv *x, unsigned char *p, int nn) {
4138 	int i;
4139 	for (i = 0; i < nn; i++, p++)
4140 		x->chsum = ((x->chsum << 13) | (x->chsum >> (32-13))) + *p;
4141 	x->nbytes += nn;
4142 }
4143 
4144 /* Write an array of ints to the file. Set the error flag to nz on error */
write_ints(i1pnonv * x,FILE * fp,int * dp,int n)4145 static void write_ints(i1pnonv *x, FILE *fp, int *dp, int n) {
4146 
4147 	if (fwrite((void *)dp, sizeof(int), n, fp) != n) {
4148 		x->ef = 1;
4149 	} else {
4150 		update_chsum(x, (unsigned char *)dp, n * sizeof(int));
4151 	}
4152 }
4153 
4154 /* Write an array of doubles to the file. Set the error flag to nz on error */
write_doubles(i1pnonv * x,FILE * fp,double * dp,int n)4155 static void write_doubles(i1pnonv *x, FILE *fp, double *dp, int n) {
4156 
4157 	if (fwrite((void *)dp, sizeof(double), n, fp) != n) {
4158 		x->ef = 1;
4159 	} else {
4160 		update_chsum(x, (unsigned char *)dp, n * sizeof(double));
4161 	}
4162 }
4163 
4164 /* Write an array of time_t's to the file. Set the error flag to nz on error */
4165 /* (This will cause file checksum fail if different executables on the same */
4166 /*  system have different time_t values) */
write_time_ts(i1pnonv * x,FILE * fp,time_t * dp,int n)4167 static void write_time_ts(i1pnonv *x, FILE *fp, time_t *dp, int n) {
4168 
4169 	if (fwrite((void *)dp, sizeof(time_t), n, fp) != n) {
4170 		x->ef = 1;
4171 	} else {
4172 		update_chsum(x, (unsigned char *)dp, n * sizeof(time_t));
4173 	}
4174 }
4175 
4176 /* Read an array of ints from the file. Set the error flag to nz on error */
read_ints(i1pnonv * x,FILE * fp,int * dp,int n)4177 static void read_ints(i1pnonv *x, FILE *fp, int *dp, int n) {
4178 
4179 	if (fread((void *)dp, sizeof(int), n, fp) != n) {
4180 		x->ef = 1;
4181 	} else {
4182 		update_chsum(x, (unsigned char *)dp, n * sizeof(int));
4183 	}
4184 }
4185 
4186 /* Read an array of doubles from the file. Set the error flag to nz on error */
read_doubles(i1pnonv * x,FILE * fp,double * dp,int n)4187 static void read_doubles(i1pnonv *x, FILE *fp, double *dp, int n) {
4188 
4189 	if (fread((void *)dp, sizeof(double), n, fp) != n) {
4190 		x->ef = 1;
4191 	} else {
4192 		update_chsum(x, (unsigned char *)dp, n * sizeof(double));
4193 	}
4194 }
4195 
4196 /* Read an array of time_t's from the file. Set the error flag to nz on error */
4197 /* (This will cause file checksum fail if different executables on the same */
4198 /*  system have different time_t values) */
read_time_ts(i1pnonv * x,FILE * fp,time_t * dp,int n)4199 static void read_time_ts(i1pnonv *x, FILE *fp, time_t *dp, int n) {
4200 
4201 	if (fread((void *)dp, sizeof(time_t), n, fp) != n) {
4202 		x->ef = 1;
4203 	} else {
4204 		update_chsum(x, (unsigned char *)dp, n * sizeof(time_t));
4205 	}
4206 }
4207 
i1pro_save_calibration(i1pro * p)4208 i1pro_code i1pro_save_calibration(i1pro *p) {
4209 	i1proimp *m = (i1proimp *)p->m;
4210 	i1pro_code ev = I1PRO_OK;
4211 	i1pro_state *s;
4212 	int i;
4213 	char nmode[10];
4214 	char cal_name[100];		/* Name */
4215 	char **cal_paths = NULL;
4216 	int no_paths = 0;
4217 	FILE *fp;
4218 	i1pnonv x;
4219 	int ss;
4220 	int argyllversion = ARGYLL_VERSION;
4221 	int isRevE = p->itype == instI1Pro2 ? 1 : 0;
4222 
4223 	strcpy(nmode, "w");
4224 #if !defined(O_CREAT) && !defined(_O_CREAT)
4225 # error "Need to #include fcntl.h!"
4226 #endif
4227 #if defined(O_BINARY) || defined(_O_BINARY)
4228 	strcat(nmode, "b");
4229 #endif
4230 
4231 	/* Create the file name */
4232 	sprintf(cal_name, "ArgyllCMS/.i1p_%d.cal", m->serno);
4233 	if ((no_paths = xdg_bds(NULL, &cal_paths, xdg_cache, xdg_write, xdg_user, cal_name)) < 1) {
4234 		a1logd(p->log,1,"i1pro_save_calibration xdg_bds returned no paths\n");
4235 		return I1PRO_INT_CAL_SAVE;
4236 	}
4237 
4238 	a1logd(p->log,2,"i1pro_save_calibration saving to file '%s'\n",cal_paths[0]);
4239 
4240 	if (create_parent_directories(cal_paths[0])
4241 	 || (fp = fopen(cal_paths[0], nmode)) == NULL) {
4242 		a1logd(p->log,2,"i1pro_save_calibration failed to open file for writing\n");
4243 		xdg_free(cal_paths, no_paths);
4244 		return I1PRO_INT_CAL_SAVE;
4245 	}
4246 
4247 	x.ef = 0;
4248 	x.chsum = 0;
4249 	x.nbytes = 0;
4250 
4251 	/* A crude structure signature */
4252 	ss = sizeof(i1pro_state) + sizeof(i1proimp);
4253 
4254 	/* Some file identification */
4255 	write_ints(&x, fp, &argyllversion, 1);
4256 	write_ints(&x, fp, &ss, 1);
4257 	write_ints(&x, fp, &m->serno, 1);
4258 	write_ints(&x, fp, &isRevE, 1);
4259 	write_ints(&x, fp, (int *)&m->nraw, 1);
4260 	write_ints(&x, fp, (int *)&m->nwav[0], 1);
4261 	write_ints(&x, fp, (int *)&m->nwav[1], 1);
4262 
4263 	/* For each mode, save the calibration if it's valid */
4264 	for (i = 0; i < i1p_no_modes; i++) {
4265 		s = &m->ms[i];
4266 
4267 		/* Mode identification */
4268 		write_ints(&x, fp, &s->emiss, 1);
4269 		write_ints(&x, fp, &s->trans, 1);
4270 		write_ints(&x, fp, &s->reflective, 1);
4271 		write_ints(&x, fp, &s->scan, 1);
4272 		write_ints(&x, fp, &s->flash, 1);
4273 		write_ints(&x, fp, &s->ambient, 1);
4274 		write_ints(&x, fp, &s->adaptive, 1);
4275 
4276 		/* Configuration calibration is valid for */
4277 		write_ints(&x, fp, &s->gainmode, 1);
4278 		write_doubles(&x, fp, &s->inttime, 1);
4279 
4280 		/* Calibration information */
4281 		write_ints(&x, fp, &s->wl_valid, 1);
4282 		write_time_ts(&x, fp, &s->wldate, 1);
4283 		write_doubles(&x, fp, &s->wl_led_off, 1);
4284 		write_ints(&x, fp, &s->dark_valid, 1);
4285 		write_time_ts(&x, fp, &s->ddate, 1);
4286 		write_doubles(&x, fp, &s->dark_int_time, 1);
4287 		write_doubles(&x, fp, s->dark_data-1, m->nraw+1);
4288 		write_doubles(&x, fp, &s->dark_int_time2, 1);
4289 		write_doubles(&x, fp, s->dark_data2-1, m->nraw+1);
4290 		write_doubles(&x, fp, &s->dark_int_time3, 1);
4291 		write_doubles(&x, fp, s->dark_data3-1, m->nraw+1);
4292 		write_doubles(&x, fp, &s->dark_int_time4, 1);
4293 		write_doubles(&x, fp, s->dark_data4-1, m->nraw+1);
4294 		write_ints(&x, fp, &s->dark_gain_mode, 1);
4295 
4296 		if (!s->emiss) {
4297 			write_ints(&x, fp, &s->cal_valid, 1);
4298 			write_time_ts(&x, fp, &s->cfdate, 1);
4299 			write_doubles(&x, fp, s->cal_factor[0], m->nwav[0]);
4300 			write_doubles(&x, fp, s->cal_factor[1], m->nwav[1]);
4301 			write_doubles(&x, fp, s->white_data-1, m->nraw+1);
4302 		}
4303 
4304 		write_ints(&x, fp, &s->idark_valid, 1);
4305 		write_time_ts(&x, fp, &s->iddate, 1);
4306 		write_doubles(&x, fp, s->idark_int_time, 4);
4307 		write_doubles(&x, fp, s->idark_data[0]-1, m->nraw+1);
4308 		write_doubles(&x, fp, s->idark_data[1]-1, m->nraw+1);
4309 		write_doubles(&x, fp, s->idark_data[2]-1, m->nraw+1);
4310 		write_doubles(&x, fp, s->idark_data[3]-1, m->nraw+1);
4311 	}
4312 
4313 	a1logd(p->log,3,"nbytes = %d, Checkum = 0x%x\n",x.nbytes,x.chsum);
4314 	write_ints(&x, fp, (int *)&x.chsum, 1);
4315 
4316 	if (fclose(fp) != 0)
4317 		x.ef = 2;
4318 
4319 	if (x.ef != 0) {
4320 		a1logd(p->log,2,"Writing calibration file failed with %d\n",x.ef);
4321 		delete_file(cal_paths[0]);
4322 		return I1PRO_INT_CAL_SAVE;
4323 	} else {
4324 		a1logd(p->log,2,"Writing calibration file succeeded\n");
4325 	}
4326 	xdg_free(cal_paths, no_paths);
4327 
4328 	return ev;
4329 }
4330 
4331 /* Restore the all modes calibration from the local system */
i1pro_restore_calibration(i1pro * p)4332 i1pro_code i1pro_restore_calibration(i1pro *p) {
4333 	i1proimp *m = (i1proimp *)p->m;
4334 	i1pro_code ev = I1PRO_OK;
4335 	i1pro_state *s, ts;
4336 	int i, j;
4337 	char nmode[10];
4338 	char cal_name[100];		/* Name */
4339 	char **cal_paths = NULL;
4340 	int no_paths = 0;
4341 	FILE *fp;
4342 	i1pnonv x;
4343 	int argyllversion;
4344 	int isRevE;
4345 	int ss, serno, nraw, nwav0, nwav1, nbytes, chsum1, chsum2;
4346 
4347 	strcpy(nmode, "r");
4348 #if !defined(O_CREAT) && !defined(_O_CREAT)
4349 # error "Need to #include fcntl.h!"
4350 #endif
4351 #if defined(O_BINARY) || defined(_O_BINARY)
4352 	strcat(nmode, "b");
4353 #endif
4354 	/* Create the file name */
4355 	sprintf(cal_name, "ArgyllCMS/.i1p_%d.cal" SSEPS "color/.i1p_%d.cal", m->serno, m->serno);
4356 	if ((no_paths = xdg_bds(NULL, &cal_paths, xdg_cache, xdg_read, xdg_user, cal_name)) < 1) {
4357 		a1logd(p->log,2,"i1pro_restore_calibration xdg_bds failed to locate file'\n");
4358 		return I1PRO_INT_CAL_RESTORE;
4359 	}
4360 
4361 	a1logd(p->log,2,"i1pro_restore_calibration restoring from file '%s'\n",cal_paths[0]);
4362 
4363 	/* Check the last modification time */
4364 	{
4365 		struct sys_stat sbuf;
4366 
4367 		if (sys_stat(cal_paths[0], &sbuf) == 0) {
4368 			m->lo_secs = time(NULL) - sbuf.st_mtime;
4369 			a1logd(p->log,2,"i1pro_restore_calibration: %d secs from instrument last open\n",m->lo_secs);
4370 		} else {
4371 			a1logd(p->log,2,"i1pro_restore_calibration: stat on file failed\n");
4372 		}
4373 	}
4374 
4375 	if ((fp = fopen(cal_paths[0], nmode)) == NULL) {
4376 		a1logd(p->log,2,"i1pro_restore_calibration failed to open file for reading\n");
4377 		xdg_free(cal_paths, no_paths);
4378 		return I1PRO_INT_CAL_RESTORE;
4379 	}
4380 
4381 	x.ef = 0;
4382 	x.chsum = 0;
4383 	x.nbytes = 0;
4384 
4385 	/* Check the file identification */
4386 	read_ints(&x, fp, &argyllversion, 1);
4387 	read_ints(&x, fp, &ss, 1);
4388 	read_ints(&x, fp, &serno, 1);
4389 	read_ints(&x, fp, &isRevE, 1);
4390 	read_ints(&x, fp, &nraw, 1);
4391 	read_ints(&x, fp, &nwav0, 1);
4392 	read_ints(&x, fp, &nwav1, 1);
4393 	if (x.ef != 0
4394 	 || argyllversion != ARGYLL_VERSION
4395 	 || ss != (sizeof(i1pro_state) + sizeof(i1proimp))
4396 	 || serno != m->serno
4397 	 || isRevE != (p->itype == instI1Pro2 ? 1 : 0)
4398 	 || nraw != m->nraw
4399 	 || nwav0 != m->nwav[0]
4400 	 || nwav1 != m->nwav[1]) {
4401 		a1logd(p->log,2,"Identification didn't verify\n");
4402 		goto reserr;
4403 	}
4404 
4405 	/* Do a dummy read to check the checksum */
4406 	for (i = 0; i < i1p_no_modes; i++) {
4407 		int di;
4408 		double dd;
4409 		time_t dt;
4410 		int emiss, trans, reflective, ambient, scan, flash, adaptive;
4411 
4412 		s = &m->ms[i];
4413 
4414 		/* Mode identification */
4415 		read_ints(&x, fp, &emiss, 1);
4416 		read_ints(&x, fp, &trans, 1);
4417 		read_ints(&x, fp, &reflective, 1);
4418 		read_ints(&x, fp, &scan, 1);
4419 		read_ints(&x, fp, &flash, 1);
4420 		read_ints(&x, fp, &ambient, 1);
4421 		read_ints(&x, fp, &adaptive, 1);
4422 
4423 		/* Configuration calibration is valid for */
4424 		read_ints(&x, fp, &di, 1);
4425 		read_doubles(&x, fp, &dd, 1);
4426 
4427 		/* Calibration information */
4428 		read_ints(&x, fp, &di, 1);
4429 		read_time_ts(&x, fp, &dt, 1);
4430 		read_doubles(&x, fp, &dd, 1);
4431 
4432 		read_ints(&x, fp, &di, 1);
4433 		read_time_ts(&x, fp, &dt, 1);
4434 		read_doubles(&x, fp, &dd, 1);
4435 		for (j = -1; j < m->nraw; j++)
4436 			read_doubles(&x, fp, &dd, 1);
4437 		read_doubles(&x, fp, &dd, 1);
4438 		for (j = -1; j < m->nraw; j++)
4439 			read_doubles(&x, fp, &dd, 1);
4440 		read_doubles(&x, fp, &dd, 1);			/* dark_data3 */
4441 		for (j = -1; j < m->nraw; j++)
4442 			read_doubles(&x, fp, &dd, 1);
4443 		read_doubles(&x, fp, &dd, 1);			/* dark_data4 */
4444 		for (j = -1; j < m->nraw; j++)
4445 			read_doubles(&x, fp, &dd, 1);
4446 		read_ints(&x, fp, &di, 1);
4447 
4448 		if (!s->emiss) {
4449 			read_ints(&x, fp, &di, 1);
4450 			read_time_ts(&x, fp, &dt, 1);
4451 			for (j = 0; j < m->nwav[0]; j++)
4452 				read_doubles(&x, fp, &dd, 1);
4453 			for (j = 0; j < m->nwav[1]; j++)
4454 				read_doubles(&x, fp, &dd, 1);
4455 			for (j = -1; j < m->nraw; j++)
4456 				read_doubles(&x, fp, &dd, 1);
4457 		}
4458 
4459 		read_ints(&x, fp, &di, 1);
4460 		read_time_ts(&x, fp, &dt, 1);
4461 		for (j = 0; j < 4; j++)
4462 			read_doubles(&x, fp, &dd, 1);
4463 		for (j = -1; j < m->nraw; j++)
4464 			read_doubles(&x, fp, &dd, 1);
4465 		for (j = -1; j < m->nraw; j++)
4466 			read_doubles(&x, fp, &dd, 1);
4467 		for (j = -1; j < m->nraw; j++)
4468 			read_doubles(&x, fp, &dd, 1);
4469 		for (j = -1; j < m->nraw; j++)
4470 			read_doubles(&x, fp, &dd, 1);
4471 	}
4472 
4473 	chsum1 = x.chsum;
4474 	nbytes = x.nbytes;
4475 	read_ints(&x, fp, &chsum2, 1);
4476 
4477 	if (x.ef != 0
4478 	 || chsum1 != chsum2) {
4479 		a1logd(p->log,2,"Checksum didn't verify, bytes %d, got 0x%x, expected 0x%x\n",nbytes,chsum1, chsum2);
4480 		goto reserr;
4481 	}
4482 
4483 	rewind(fp);
4484 	x.ef = 0;
4485 	x.chsum = 0;
4486 	x.nbytes = 0;
4487 
4488 	/* Allocate space in temp structure */
4489 
4490 	ts.dark_data = dvectorz(-1, m->nraw-1);
4491 	ts.dark_data2 = dvectorz(-1, m->nraw-1);
4492 	ts.dark_data3 = dvectorz(-1, m->nraw-1);
4493 	ts.dark_data4 = dvectorz(-1, m->nraw-1);
4494 	ts.cal_factor[0] = dvectorz(0, m->nwav[0]-1);
4495 	ts.cal_factor[1] = dvectorz(0, m->nwav[1]-1);
4496 	ts.white_data = dvectorz(-1, m->nraw-1);
4497 	ts.idark_data = dmatrixz(0, 3, -1, m->nraw-1);
4498 
4499 	/* Read the identification */
4500 	read_ints(&x, fp, &argyllversion, 1);
4501 	read_ints(&x, fp, &ss, 1);
4502 	read_ints(&x, fp, &m->serno, 1);
4503 	read_ints(&x, fp, &isRevE, 1);
4504 	read_ints(&x, fp, (int *)&m->nraw, 1);
4505 	read_ints(&x, fp, (int *)&m->nwav[0], 1);
4506 	read_ints(&x, fp, (int *)&m->nwav[1], 1);
4507 
4508 	/* For each mode, restore the calibration if it's valid */
4509 	for (i = 0; i < i1p_no_modes; i++) {
4510 		s = &m->ms[i];
4511 
4512 		/* Mode identification */
4513 		read_ints(&x, fp, &ts.emiss, 1);
4514 		read_ints(&x, fp, &ts.trans, 1);
4515 		read_ints(&x, fp, &ts.reflective, 1);
4516 		read_ints(&x, fp, &ts.scan, 1);
4517 		read_ints(&x, fp, &ts.flash, 1);
4518 		read_ints(&x, fp, &ts.ambient, 1);
4519 		read_ints(&x, fp, &ts.adaptive, 1);
4520 
4521 		/* Configuration calibration is valid for */
4522 		read_ints(&x, fp, &ts.gainmode, 1);
4523 		read_doubles(&x, fp, &ts.inttime, 1);
4524 
4525 		/* Calibration information: */
4526 
4527 		/* Wavelength */
4528 		read_ints(&x, fp, &ts.wl_valid, 1);
4529 		read_time_ts(&x, fp, &ts.wldate, 1);
4530 		read_doubles(&x, fp, &ts.wl_led_off, 1);
4531 
4532 		/* Static Dark */
4533 		read_ints(&x, fp, &ts.dark_valid, 1);
4534 		read_time_ts(&x, fp, &ts.ddate, 1);
4535 		read_doubles(&x, fp, &ts.dark_int_time, 1);
4536 		read_doubles(&x, fp, ts.dark_data-1, m->nraw+1);
4537 		read_doubles(&x, fp, &ts.dark_int_time2, 1);
4538 		read_doubles(&x, fp, ts.dark_data2-1, m->nraw+1);
4539 		read_doubles(&x, fp, &ts.dark_int_time3, 1);
4540 		read_doubles(&x, fp, ts.dark_data3-1, m->nraw+1);
4541 		read_doubles(&x, fp, &ts.dark_int_time4, 1);
4542 		read_doubles(&x, fp, ts.dark_data4-1, m->nraw+1);
4543 		read_ints(&x, fp, &ts.dark_gain_mode, 1);
4544 
4545 		if (!ts.emiss) {
4546 			/* Reflective */
4547 			read_ints(&x, fp, &ts.cal_valid, 1);
4548 			read_time_ts(&x, fp, &ts.cfdate, 1);
4549 			read_doubles(&x, fp, ts.cal_factor[0], m->nwav[0]);
4550 			read_doubles(&x, fp, ts.cal_factor[1], m->nwav[1]);
4551 			read_doubles(&x, fp, ts.white_data-1, m->nraw+1);
4552 		}
4553 
4554 		/* Adaptive Dark */
4555 		read_ints(&x, fp, &ts.idark_valid, 1);
4556 		read_time_ts(&x, fp, &ts.iddate, 1);
4557 		read_doubles(&x, fp, ts.idark_int_time, 4);
4558 		read_doubles(&x, fp, ts.idark_data[0]-1, m->nraw+1);
4559 		read_doubles(&x, fp, ts.idark_data[1]-1, m->nraw+1);
4560 		read_doubles(&x, fp, ts.idark_data[2]-1, m->nraw+1);
4561 		read_doubles(&x, fp, ts.idark_data[3]-1, m->nraw+1);
4562 
4563 		/* If the configuration for this mode matches */
4564 		/* that of the calibration, restore the calibration */
4565 		/* for this mode. */
4566 		if (x.ef == 0				/* No read error */
4567 		 &&	s->emiss == ts.emiss
4568 		 && s->trans == ts.trans
4569 		 && s->reflective == ts.reflective
4570 		 && s->scan == ts.scan
4571 		 && s->flash == ts.flash
4572 		 && s->ambient == ts.ambient
4573 		 && s->adaptive == ts.adaptive
4574 		 && (s->adaptive || fabs(s->inttime - ts.inttime) < 0.01)
4575 		 && (s->adaptive || fabs(s->dark_int_time - ts.dark_int_time) < 0.01)
4576 		 && (s->adaptive || fabs(s->dark_int_time2 - ts.dark_int_time2) < 0.01)
4577 		 && (s->adaptive || fabs(s->dark_int_time3 - ts.dark_int_time3) < 0.01)
4578 		 && (s->adaptive || fabs(s->dark_int_time4 - ts.dark_int_time4) < 0.01)
4579 		 && (!s->adaptive || fabs(s->idark_int_time[0] - ts.idark_int_time[0]) < 0.01)
4580 		 && (!s->adaptive || fabs(s->idark_int_time[1] - ts.idark_int_time[1]) < 0.01)
4581 		 && (!s->adaptive || fabs(s->idark_int_time[2] - ts.idark_int_time[2]) < 0.01)
4582 		 && (!s->adaptive || fabs(s->idark_int_time[3] - ts.idark_int_time[3]) < 0.01)
4583 		) {
4584 			/* Copy all the fields read above */
4585 			s->emiss = ts.emiss;
4586 			s->trans = ts.trans;
4587 			s->reflective = ts.reflective;
4588 			s->scan = ts.scan;
4589 			s->flash = ts.flash;
4590 			s->ambient = ts.ambient;
4591 			s->adaptive = ts.adaptive;
4592 
4593 			s->gainmode = ts.gainmode;
4594 			s->inttime = ts.inttime;
4595 
4596 			s->wl_valid = ts.wl_valid;
4597 			s->wldate = ts.wldate;
4598 			s->wl_led_off = ts.wl_led_off;
4599 
4600 			s->dark_valid = ts.dark_valid;
4601 			s->ddate = ts.ddate;
4602 			s->dark_int_time = ts.dark_int_time;
4603 			for (j = -1; j < m->nraw; j++)
4604 				s->dark_data[j] = ts.dark_data[j];
4605 			s->dark_int_time2 = ts.dark_int_time2;
4606 			for (j = -1; j < m->nraw; j++)
4607 				s->dark_data2[j] = ts.dark_data2[j];
4608 			s->dark_int_time3 = ts.dark_int_time3;
4609 			for (j = -1; j < m->nraw; j++)
4610 				s->dark_data3[j] = ts.dark_data3[j];
4611 			s->dark_int_time4 = ts.dark_int_time4;
4612 			for (j = -1; j < m->nraw; j++)
4613 				s->dark_data4[j] = ts.dark_data4[j];
4614 			s->dark_gain_mode = ts.dark_gain_mode;
4615 			if (!ts.emiss) {
4616 				s->cal_valid = ts.cal_valid;
4617 				s->cfdate = ts.cfdate;
4618 				for (j = 0; j < m->nwav[0]; j++)
4619 					s->cal_factor[0][j] = ts.cal_factor[0][j];
4620 				for (j = 0; j < m->nwav[1]; j++)
4621 					s->cal_factor[1][j] = ts.cal_factor[1][j];
4622 				for (j = -1; j < m->nraw; j++)
4623 					s->white_data[j] = ts.white_data[j];
4624 			}
4625 			s->idark_valid = ts.idark_valid;
4626 			s->iddate = ts.iddate;
4627 			for (j = 0; j < 4; j++)
4628 				s->idark_int_time[j] = ts.idark_int_time[j];
4629 			for (j = -1; j < m->nraw; j++)
4630 				s->idark_data[0][j] = ts.idark_data[0][j];
4631 			for (j = -1; j < m->nraw; j++)
4632 				s->idark_data[1][j] = ts.idark_data[1][j];
4633 			for (j = -1; j < m->nraw; j++)
4634 				s->idark_data[2][j] = ts.idark_data[2][j];
4635 			for (j = -1; j < m->nraw; j++)
4636 				s->idark_data[3][j] = ts.idark_data[3][j];
4637 
4638 		} else {
4639 			a1logd(p->log,2,"Not restoring cal for mode %d since params don't match:\n",i);
4640 			a1logd(p->log,2,"emis = %d : %d, trans = %d : %d, ref = %d : %d\n",s->emiss,ts.emiss,s->trans,ts.trans,s->reflective,ts.reflective);
4641 			a1logd(p->log,2,"scan = %d : %d, flash = %d : %d, ambi = %d : %d, adapt = %d : %d\n",s->scan,ts.scan,s->flash,ts.flash,s->ambient,ts.ambient,s->adaptive,ts.adaptive);
4642 			a1logd(p->log,2,"inttime = %f : %f\n",s->inttime,ts.inttime);
4643 			a1logd(p->log,2,"darkit1 = %f : %f, 2 = %f : %f, 3 = %f : %f, 4 = %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,s->dark_int_time4,ts.dark_int_time4);
4644 			a1logd(p->log,2,"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]);
4645 		}
4646 	}
4647 
4648 	/* Free up temporary space */
4649 	free_dvector(ts.dark_data, -1, m->nraw-1);
4650 	free_dvector(ts.dark_data2, -1, m->nraw-1);
4651 	free_dvector(ts.dark_data3, -1, m->nraw-1);
4652 	free_dvector(ts.dark_data4, -1, m->nraw-1);
4653 	free_dvector(ts.white_data, -1, m->nraw-1);
4654 	free_dmatrix(ts.idark_data, 0, 3, -1, m->nraw-1);
4655 
4656 	free_dvector(ts.cal_factor[0], 0, m->nwav[0]-1);
4657 	free_dvector(ts.cal_factor[1], 0, m->nwav[1]-1);
4658 
4659 	a1logd(p->log,5,"i1pro_restore_calibration done\n");
4660  reserr:;
4661 
4662 	fclose(fp);
4663 	xdg_free(cal_paths, no_paths);
4664 
4665 	return ev;
4666 }
4667 
i1pro_touch_calibration(i1pro * p)4668 i1pro_code i1pro_touch_calibration(i1pro *p) {
4669 	i1proimp *m = (i1proimp *)p->m;
4670 	i1pro_code ev = I1PRO_OK;
4671 	char cal_name[100];		/* Name */
4672 	char **cal_paths = NULL;
4673 	int no_paths = 0;
4674 	int rv;
4675 
4676 	/* Locate the file name */
4677 	sprintf(cal_name, "ArgyllCMS/.i1p_%d.cal" SSEPS "color/.i1p_%d.cal", m->serno, m->serno);
4678 	if ((no_paths = xdg_bds(NULL, &cal_paths, xdg_cache, xdg_read, xdg_user, cal_name)) < 1) {
4679 		a1logd(p->log,2,"i1pro_restore_calibration xdg_bds failed to locate file'\n");
4680 		return I1PRO_INT_CAL_TOUCH;
4681 	}
4682 
4683 	a1logd(p->log,2,"i1pro_touch_calibration touching file '%s'\n",cal_paths[0]);
4684 
4685 	if ((rv = sys_utime(cal_paths[0], NULL)) != 0) {
4686 		a1logd(p->log,2,"i1pro_touch_calibration failed with %d\n",rv);
4687 		xdg_free(cal_paths, no_paths);
4688 		return I1PRO_INT_CAL_TOUCH;
4689 	}
4690 	xdg_free(cal_paths, no_paths);
4691 
4692 	return ev;
4693 }
4694 
4695 #endif /* ENABLE_NONVCAL */
4696 
4697 /* ============================================================ */
4698 /* Intermediate routines  - composite commands/processing */
4699 
4700 /* Some sort of configuration needed get instrument ready. */
4701 /* Does it have a sleep mode that we need to deal with ?? */
4702 /* Note this always does a reset. */
4703 i1pro_code
i1pro_establish_high_power(i1pro * p)4704 i1pro_establish_high_power(i1pro *p) {
4705 	i1pro_code ev = I1PRO_OK;
4706 	i1proimp *m = (i1proimp *)p->m;
4707 	int i;
4708 
4709 	/* Get the current misc. status */
4710 	if ((ev = i1pro_getmisc(p, &m->fwrev, NULL, &m->maxpve, NULL, &m->powmode)) != I1PRO_OK)
4711 		return ev;
4712 
4713 	a1logd(p->log,2,"CPLD rev = %d\n",m->cpldrev);
4714 
4715 	if (m->powmode != 8) {		/* In high power mode */
4716 		if ((ev = i1pro_reset(p, 0x1f)) != I1PRO_OK)
4717 			return ev;
4718 
4719 		return I1PRO_OK;
4720 	}
4721 
4722 	a1logd(p->log,4,"Switching to high power mode\n");
4723 
4724 	/* Switch to high power mode */
4725 	if ((ev = i1pro_reset(p, 1)) != I1PRO_OK)
4726 		return ev;
4727 
4728 	/* Wait up to 1.5 seconds for it return high power indication */
4729 	for (i = 0; i < 15; i++) {
4730 
4731 		/* Get the current misc. status */
4732 		if ((ev = i1pro_getmisc(p, &m->fwrev, NULL, &m->maxpve, NULL, &m->powmode)) != I1PRO_OK)
4733 			return ev;
4734 
4735 		if (m->powmode != 8) {		/* In high power mode */
4736 			if ((ev = i1pro_reset(p, 0x1f)) != I1PRO_OK)
4737 				return ev;
4738 
4739 			return I1PRO_OK;
4740 		}
4741 
4742 		msec_sleep(100);
4743 	}
4744 
4745 	/* Failed to switch into high power mode */
4746 	return I1PRO_HW_HIGHPOWERFAIL;
4747 }
4748 
4749 /* Take a dark reference measurement - part 1 */
i1pro_dark_measure_1(i1pro * p,int nummeas,double * inttime,int gainmode,unsigned char * buf,unsigned int bsize)4750 i1pro_code i1pro_dark_measure_1(
4751 	i1pro *p,
4752 	int nummeas,			/* Number of readings to take */
4753 	double *inttime, 		/* Integration time to use/used */
4754 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
4755 	unsigned char *buf,		/* USB reading buffer to use */
4756 	unsigned int bsize		/* Size of buffer */
4757 ) {
4758 	i1pro_code ev = I1PRO_OK;
4759 
4760 	if (nummeas <= 0)
4761 		return I1PRO_INT_ZEROMEASURES;
4762 
4763 	if ((ev = i1pro_trigger_one_measure(p, nummeas, inttime, gainmode, i1p_dark_cal)) != I1PRO_OK)
4764 		return ev;
4765 
4766 	if ((ev = i1pro_readmeasurement(p, nummeas, 0, buf, bsize, NULL, i1p_dark_cal)) != I1PRO_OK)
4767 		return ev;
4768 
4769 	return ev;
4770 }
4771 
4772 /* Take a dark reference measurement - part 2 */
i1pro_dark_measure_2(i1pro * p,double * absraw,int nummeas,double inttime,int gainmode,unsigned char * buf,unsigned int bsize)4773 i1pro_code i1pro_dark_measure_2(
4774 	i1pro *p,
4775 	double *absraw,			/* Return array [-1 nraw] of absraw values */
4776 	int nummeas,			/* Number of readings to take */
4777 	double inttime, 		/* Integration time to use/used */
4778 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
4779 	unsigned char *buf,		/* raw USB reading buffer to process */
4780 	unsigned int bsize		/* Buffer size to process */
4781 ) {
4782 	i1pro_code ev = I1PRO_OK;
4783 	i1proimp *m = (i1proimp *)p->m;
4784 	i1pro_state *s = &m->ms[m->mmode];
4785 	double **multimes;		/* Multiple measurement results */
4786 	double sensavg;			/* Overall average of sensor readings */
4787 	double satthresh;		/* Saturation threshold */
4788 	double darkthresh;		/* Dark threshold */
4789 	int rv;
4790 
4791 	multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);	/* -1 is RevE shielded cells values */
4792 
4793 	if (gainmode == 0)
4794 		satthresh = m->sens_sat0;
4795 	else
4796 		satthresh = m->sens_sat1;
4797 
4798 	darkthresh = m->sens_dark + inttime * 900.0;
4799 	if (gainmode)
4800 		darkthresh *= m->highgain;
4801 
4802 	/* Take a buffer full of raw readings, and convert them to */
4803 	/* absolute linearised sensor values. */
4804 	if ((ev = i1pro_sens_to_absraw(p, multimes, buf, nummeas, inttime, gainmode, &darkthresh))
4805 		                                                                          != I1PRO_OK) {
4806 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4807 		return ev;
4808 	}
4809 
4810 	satthresh = i1pro_raw_to_absraw(p, satthresh, inttime, gainmode);
4811 	darkthresh = i1pro_raw_to_absraw(p, darkthresh, inttime, gainmode);
4812 
4813 	/* Average a set of measurements into one. */
4814 	/* Return zero if readings are consistent and not saturated. */
4815 	/* Return nz with bit 1 set if the readings are not consistent */
4816 	/* Return nz with bit 2 set if the readings are saturated */
4817 	/* Return the highest individual element. */
4818 	/* Return the overall average. */
4819 	rv = i1pro_average_multimeas(p, absraw, multimes, nummeas, NULL, &sensavg,
4820 	                                                       satthresh, darkthresh);
4821 	free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4822 
4823 #ifdef PLOT_DEBUG
4824 	printf("Average absolute sensor readings, average = %f, satthresh %f:\n",sensavg, satthresh);
4825 	plot_raw(absraw);
4826 #endif
4827 
4828 	if (rv & 1)
4829 		return I1PRO_RD_DARKREADINCONS;
4830 
4831 	if (rv & 2)
4832 		return I1PRO_RD_SENSORSATURATED;
4833 
4834 	a1logd(p->log,3,"Dark threshold = %f\n",darkthresh);
4835 
4836 	if (sensavg > (2.0 * darkthresh))
4837 		return I1PRO_RD_DARKNOTVALID;
4838 
4839 	return ev;
4840 }
4841 
4842 #ifdef DUMP_DARKM
4843 int ddumpdarkm = 0;
4844 #endif
4845 
4846 /* Take a dark reference measurement (combined parts 1 & 2) */
i1pro_dark_measure(i1pro * p,double * absraw,int nummeas,double * inttime,int gainmode)4847 i1pro_code i1pro_dark_measure(
4848 	i1pro *p,
4849 	double *absraw,			/* Return array [-1 nraw] of absraw values */
4850 	int nummeas,			/* Number of readings to take */
4851 	double *inttime, 		/* Integration time to use/used */
4852 	int gainmode			/* Gain mode to use, 0 = normal, 1 = high */
4853 ) {
4854 	i1proimp *m = (i1proimp *)p->m;
4855 	i1pro_code ev = I1PRO_OK;
4856 	unsigned char *buf;		/* Raw USB reading buffer */
4857 	unsigned int bsize;
4858 
4859 	bsize = m->nsen * 2  * nummeas;
4860 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
4861 		a1logd(p->log,1,"i1pro_dark_measure malloc %d bytes failed (8)\n",bsize);
4862 		return I1PRO_INT_MALLOC;
4863 	}
4864 
4865 	if ((ev = i1pro_dark_measure_1(p, nummeas, inttime, gainmode, buf, bsize)) != I1PRO_OK) {
4866 		free(buf);
4867 		return ev;
4868 	}
4869 
4870 	if ((ev = i1pro_dark_measure_2(p, absraw,
4871 		                 nummeas, *inttime, gainmode, buf, bsize)) != I1PRO_OK) {
4872 		free(buf);
4873 		return ev;
4874 	}
4875 	free(buf);
4876 
4877 #ifdef DUMP_DARKM
4878 	/* Dump raw dark readings to a file "i1pddump.txt" */
4879 	if (ddumpdarkm) {
4880 		int j;
4881 		FILE *fp;
4882 
4883 		if ((fp = fopen("i1pddump.txt", "a")) == NULL)
4884 			a1logw(p->log,"Unable to open debug file i1pddump.txt\n");
4885 		else {
4886 
4887 			fprintf(fp, "\nDark measure: nummeas %d, inttime %f, gainmode %d, darkcells %f\n",nummeas,*inttime,gainmode, absraw[-1]);
4888 			fprintf(fp,"\t\t\t{ ");
4889 			for (j = 0; j < (m->nraw-1); j++)
4890 				fprintf(fp, "%f, ",absraw[j]);
4891 			fprintf(fp, "%f },\n",absraw[j]);
4892 			fclose(fp);
4893 		}
4894 	}
4895 #endif
4896 
4897 	return ev;
4898 }
4899 
4900 
4901 /* Take a white reference measurement */
4902 /* (Subtracts black and processes into wavelenths) */
i1pro_whitemeasure(i1pro * p,double * abswav0,double * abswav1,double * absraw,double * optscale,int nummeas,double * inttime,int gainmode,double targoscale,int ltocmode)4903 i1pro_code i1pro_whitemeasure(
4904 	i1pro *p,
4905 	double *abswav0,		/* Return array [nwav[0]] of abswav values (may be NULL) */
4906 	double *abswav1,		/* Return array [nwav[1]] of abswav values (if hr_init, may be NULL) */
4907 	double *absraw,			/* Return array [-1 nraw] of absraw values */
4908 	double *optscale,		/* Factor to scale gain/int time by to make optimal (may be NULL) */
4909 	int nummeas,			/* Number of readings to take */
4910 	double *inttime, 		/* Integration time to use/used */
4911 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
4912 	double targoscale,		/* Optimal reading scale factor */
4913 	int ltocmode			/* 1 = Lamp turn on compensation mode */
4914 ) {
4915 	i1pro_code ev = I1PRO_OK;
4916 	i1proimp *m = (i1proimp *)p->m;
4917 	i1pro_state *s = &m->ms[m->mmode];
4918 	unsigned char *buf;		/* Raw USB reading buffer */
4919 	unsigned int bsize;
4920 	double **multimes;		/* Multiple measurement results */
4921 	double darkthresh;		/* Consitency threshold scale limit */
4922 	int rv;
4923 
4924 	a1logd(p->log,3,"i1pro_whitemeasure called \n");
4925 
4926 	darkthresh = m->sens_dark + *inttime * 900.0;		/* Default */
4927 	if (gainmode)
4928 		darkthresh *= m->highgain;
4929 
4930 	if (nummeas <= 0)
4931 		return I1PRO_INT_ZEROMEASURES;
4932 
4933 	/* Allocate temporaries up front to avoid delay between trigger and read */
4934 	bsize = m->nsen * 2 * nummeas;
4935 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
4936 		a1logd(p->log,1,"i1pro_whitemeasure malloc %d bytes failed (10)\n",bsize);
4937 		return I1PRO_INT_MALLOC;
4938 	}
4939 	multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
4940 
4941 	a1logd(p->log,3,"Triggering measurement cycle, nummeas %d, inttime %f, gainmode %d\n",
4942 	                                              nummeas, *inttime, gainmode);
4943 
4944 	if ((ev = i1pro_trigger_one_measure(p, nummeas, inttime, gainmode, i1p_cal)) != I1PRO_OK) {
4945 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4946 		free(buf);
4947 		return ev;
4948 	}
4949 
4950 	a1logd(p->log,4,"Gathering readings\n");
4951 
4952 	if ((ev = i1pro_readmeasurement(p, nummeas, 0, buf, bsize, NULL, i1p_cal)) != I1PRO_OK) {
4953 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4954 		free(buf);
4955 		return ev;
4956 	}
4957 
4958 	/* Take a buffer full of raw readings, and convert them to */
4959 	/* absolute linearised sensor values. */
4960 	if ((ev = i1pro_sens_to_absraw(p, multimes, buf, nummeas, *inttime, gainmode, &darkthresh))
4961 		                                                                          != I1PRO_OK) {
4962 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4963 		free(buf);
4964 		return ev;
4965 	}
4966 
4967 #ifdef PLOT_DEBUG
4968 	printf("Dark data:\n");
4969 	plot_raw(s->dark_data);
4970 #endif
4971 
4972 	/* Subtract the black level */
4973 	i1pro_sub_absraw(p, nummeas, *inttime, gainmode, multimes, s->dark_data);
4974 
4975 	/* Convert linearised white value into output wavelength white reference */
4976 	ev = i1pro_whitemeasure_3(p, abswav0, abswav1, absraw, optscale, nummeas,
4977 	                                      *inttime, gainmode, targoscale, multimes, darkthresh);
4978 
4979 	free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4980 	free(buf);
4981 
4982 	return ev;
4983 }
4984 
4985 /* Process a single raw white reference measurement */
4986 /* (Subtracts black and processes into wavelenths) */
4987 /* Used for restoring calibration from the EEProm */
i1pro_whitemeasure_buf(i1pro * p,double * abswav0,double * abswav1,double * absraw,double inttime,int gainmode,unsigned char * buf)4988 i1pro_code i1pro_whitemeasure_buf(
4989 	i1pro *p,
4990 	double *abswav0,		/* Return array [nwav[0]] of abswav values (may be NULL) */
4991 	double *abswav1,		/* Return array [nwav[1]] of abswav values (if hr_init, may be NULL) */
4992 	double *absraw,			/* Return array [-1 nraw] of absraw values */
4993 	double inttime, 		/* Integration time to used */
4994 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
4995 	unsigned char *buf		/* Raw buffer */
4996 ) {
4997 	i1pro_code ev = I1PRO_OK;
4998 	i1proimp *m = (i1proimp *)p->m;
4999 	i1pro_state *s = &m->ms[m->mmode];
5000 	double *meas;			/* Multiple measurement results */
5001 	double darkthresh;		/* Consitency threshold scale limit */
5002 
5003 	a1logd(p->log,3,"i1pro_whitemeasure_buf called \n");
5004 
5005 	meas = dvector(-1, m->nraw-1);
5006 
5007 	darkthresh = m->sens_dark + inttime * 900.0;	/* Default */
5008 	if (gainmode)
5009 		darkthresh *= m->highgain;
5010 
5011 	/* Take a buffer full of raw readings, and convert them to */
5012 	/* absolute linearised sensor values. */
5013 	if ((ev = i1pro_sens_to_absraw(p, &meas, buf, 1, inttime, gainmode, &darkthresh))
5014 		                                                                != I1PRO_OK) {
5015 		return ev;
5016 	}
5017 
5018 	/* Subtract the black level */
5019 	i1pro_sub_absraw(p, 1, inttime, gainmode, &meas, s->dark_data);
5020 
5021 	/* Convert linearised white value into output wavelength white reference */
5022 	ev = i1pro_whitemeasure_3(p, abswav0, abswav1, absraw, NULL, 1, inttime, gainmode,
5023 	                                                           0.0, &meas, darkthresh);
5024 
5025 	free_dvector(meas, -1, m->nraw-1);
5026 
5027 	return ev;
5028 }
5029 
5030 /* Take a white reference measurement - part 3 */
5031 /* Average, check, and convert to output wavelengths */
i1pro_whitemeasure_3(i1pro * p,double * abswav0,double * abswav1,double * absraw,double * optscale,int nummeas,double inttime,int gainmode,double targoscale,double ** multimes,double darkthresh)5032 i1pro_code i1pro_whitemeasure_3(
5033 	i1pro *p,
5034 	double *abswav0,		/* Return array [nwav[0]] of abswav values (may be NULL) */
5035 	double *abswav1,		/* Return array [nwav[1]] of abswav values (if hr_init, may be NULL) */
5036 	double *absraw,			/* Return array [-1 nraw] of absraw values */
5037 	double *optscale,		/* Factor to scale gain/int time by to make optimal (may be NULL) */
5038 	int nummeas,			/* Number of readings to take */
5039 	double inttime, 		/* Integration time to use/used */
5040 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
5041 	double targoscale,		/* Optimal reading scale factor */
5042 	double **multimes,		/* Multiple measurement results */
5043 	double darkthresh		/* Raw dark threshold */
5044 ) {
5045 	i1pro_code ev = I1PRO_OK;
5046 	i1proimp *m = (i1proimp *)p->m;
5047 	i1pro_state *s = &m->ms[m->mmode];
5048 	double highest;			/* Highest of sensor readings */
5049 	double sensavg;			/* Overall average of sensor readings */
5050 	double satthresh;		/* Saturation threshold */
5051 	double opttarget;		/* Optimal sensor target */
5052 	int rv;
5053 
5054 	a1logd(p->log,3,"i1pro_whitemeasure_3 called \n");
5055 
5056 	if (gainmode == 0)
5057 		satthresh = m->sens_sat0;
5058 	else
5059 		satthresh = m->sens_sat1;
5060 	satthresh = i1pro_raw_to_absraw(p, satthresh, inttime, gainmode);
5061 
5062 	darkthresh = i1pro_raw_to_absraw(p, darkthresh, inttime, gainmode);
5063 
5064 	/* Average a set of measurements into one. */
5065 	/* Return zero if readings are consistent and not saturated. */
5066 	/* Return nz with bit 1 set if the readings are not consistent */
5067 	/* Return nz with bit 2 set if the readings are saturated */
5068 	/* Return the highest individual element. */
5069 	/* Return the overall average. */
5070 	rv = i1pro_average_multimeas(p, absraw, multimes, nummeas, &highest, &sensavg,
5071 	                                                           satthresh, darkthresh);
5072 #ifdef PLOT_DEBUG
5073 	printf("Average absolute sensor readings, average = %f, satthresh %f:\n",sensavg, satthresh);
5074 	plot_raw(absraw);
5075 #endif
5076 
5077 #ifndef IGNORE_WHITE_INCONS
5078 	if (rv & 1) {
5079 		return I1PRO_RD_WHITEREADINCONS;
5080 	}
5081 #endif /* IGNORE_WHITE_INCONS */
5082 
5083 	if (rv & 2) {
5084 		return I1PRO_RD_SENSORSATURATED;
5085 	}
5086 
5087 	/* Convert an absraw array from raw wavelengths to output wavelenths */
5088 	if (abswav0 != NULL) {
5089 		i1pro_absraw_to_abswav(p, 0, s->reflective, 1, &abswav0, &absraw);
5090 
5091 #ifdef PLOT_DEBUG
5092 		printf("Converted to wavelengths std res:\n");
5093 		plot_wav(m, 0, abswav0);
5094 #endif
5095 	}
5096 
5097 #ifdef HIGH_RES
5098 	if (abswav1 != NULL && m->hr_inited) {
5099 		i1pro_absraw_to_abswav(p, 1, s->reflective, 1, &abswav1, &absraw);
5100 
5101 #ifdef PLOT_DEBUG
5102 		printf("Converted to wavelengths high res:\n");
5103 		plot_wav(m, 1, abswav1);
5104 #endif
5105 	}
5106 #endif /* HIGH_RES */
5107 
5108 	if (optscale != NULL) {
5109 		double lhighest = highest;
5110 
5111 		if (lhighest < 1.0)
5112 			lhighest = 1.0;
5113 
5114 		/* Compute correction factor to make sensor optimal */
5115 		opttarget = i1pro_raw_to_absraw(p, (double)m->sens_target, inttime, gainmode);
5116 		opttarget *= targoscale;
5117 
5118 
5119 		a1logd(p->log,3,"Optimal target = %f, amount to scale = %f\n",opttarget, opttarget/lhighest);
5120 
5121 		*optscale = opttarget/lhighest;
5122 	}
5123 
5124 	return ev;
5125 }
5126 
5127 /* Take a wavelength reference measurement */
5128 /* (Measure and subtracts black and convert to absraw) */
i1pro2_wl_measure(i1pro * p,double * absraw,double * optscale,double * inttime,double targoscale)5129 i1pro_code i1pro2_wl_measure(
5130 	i1pro *p,
5131 	double *absraw,			/* Return array [-1 nraw] of absraw values */
5132 	double *optscale,		/* Factor to scale gain/int time by to make optimal (may be NULL) */
5133 	double *inttime, 		/* Integration time to use/used */
5134 	double targoscale		/* Optimal reading scale factor */
5135 ) {
5136 	i1pro_code ev = I1PRO_OK;
5137 	i1proimp *m = (i1proimp *)p->m;
5138 	i1pro_state *s = &m->ms[m->mmode];
5139 	int nummeas = 1;		/* Number of measurements to take */
5140 	int gainmode = 0;		/* Gain mode to use, 0 = normal, 1 = high */
5141 	unsigned char *buf;		/* Raw USB reading buffer */
5142 	unsigned int bsize;
5143 	double *dark;			/* Dark reading */
5144 	double **multimes;		/* Measurement results */
5145 	double darkthresh;		/* Consitency threshold scale limit/reading dark cell values */
5146 	double highest;			/* Highest of sensor readings */
5147 	double sensavg;			/* Overall average of sensor readings */
5148 	double satthresh;		/* Saturation threshold */
5149 	double opttarget;		/* Optimal sensor target */
5150 	int rv;
5151 
5152 	a1logd(p->log,3,"i1pro2_wl_measure called \n");
5153 
5154 	/* Allocate temporaries up front to avoid delay between trigger and read */
5155 	bsize = m->nsen * 2;
5156 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
5157 		a1logd(p->log,1,"i1pro2_wl_measure malloc %d bytes failed (10)\n",bsize);
5158 		return I1PRO_INT_MALLOC;
5159 	}
5160 
5161 	/* Do a dark reading at our integration time */
5162 	dark = dvector(-1, m->nraw-1);
5163 	multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
5164 
5165 	if ((ev = i1pro_dark_measure(p, dark, nummeas, inttime, gainmode)) != I1PRO_OK) {
5166 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5167 		free_dvector(dark, -1, m->nraw-1);
5168 		free(buf);
5169 		return ev;
5170 	}
5171 
5172 #ifdef PLOT_DEBUG
5173 	printf("Absraw dark data:\n");
5174 	plot_raw(dark);
5175 #endif
5176 
5177 	a1logd(p->log,3,"Triggering wl measurement cycle, inttime %f\n", *inttime);
5178 
5179 	if ((ev = i1pro_trigger_one_measure(p, nummeas, inttime, gainmode, i1p2_wl_cal)) != I1PRO_OK) {
5180 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5181 		free_dvector(dark, -1, m->nraw-1);
5182 		free(buf);
5183 		return ev;
5184 	}
5185 
5186 	a1logd(p->log,4,"Gathering readings\n");
5187 
5188 	if ((ev = i1pro_readmeasurement(p, nummeas, 0, buf, bsize, NULL, i1p2_wl_cal)) != I1PRO_OK) {
5189 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5190 		free_dvector(dark, -1, m->nraw-1);
5191 		free(buf);
5192 		return ev;
5193 	}
5194 
5195 	/* Take a buffer full of raw readings, and convert them to */
5196 	/* absolute linearised sensor values. */
5197 	if ((ev = i1pro_sens_to_absraw(p, multimes, buf, nummeas, *inttime, gainmode, &darkthresh))
5198 		                                                                           != I1PRO_OK) {
5199 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5200 		free_dvector(dark, -1, m->nraw-1);
5201 		free(buf);
5202 		return ev;
5203 	}
5204 
5205 	/* Convert satthresh and darkthresh/dark_cell values to abs */
5206 	if (gainmode == 0)
5207 		satthresh = m->sens_sat0;
5208 	else
5209 		satthresh = m->sens_sat1;
5210 	satthresh = i1pro_raw_to_absraw(p, satthresh, *inttime, gainmode);
5211 	darkthresh = i1pro_raw_to_absraw(p, darkthresh, *inttime, gainmode);
5212 
5213 #ifdef PLOT_DEBUG
5214 	printf("Absraw WL data:\n");
5215 	plot_raw(multimes[0]);
5216 #endif
5217 
5218 	/* Subtract the black level */
5219 	i1pro_sub_absraw(p, nummeas, *inttime, gainmode, multimes, dark);
5220 
5221 #ifdef PLOT_DEBUG
5222 	printf("Absraw WL - black data:\n");
5223 	plot_raw(multimes[0]);
5224 #endif
5225 
5226 	/* Average a set of measurements into one. */
5227 	/* Return zero if readings are consistent and not saturated. */
5228 	/* Return nz with bit 1 set if the readings are not consistent */
5229 	/* Return nz with bit 2 set if the readings are saturated */
5230 	/* Return the highest individual element. */
5231 	/* Return the overall average. */
5232 	rv = i1pro_average_multimeas(p, absraw, multimes, 1, &highest, &sensavg,
5233 	                                                           satthresh, darkthresh);
5234 #ifdef PLOT_DEBUG
5235 	printf("Average absolute sensor readings, average = %f, satthresh %f, absraw WL result:\n",sensavg, satthresh);
5236 	plot_raw(absraw);
5237 #endif
5238 
5239 #ifndef IGNORE_WHITE_INCONS
5240 	if (rv & 1) {
5241 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5242 		free_dvector(dark, -1, m->nraw-1);
5243 		free(buf);
5244 		return I1PRO_RD_WHITEREADINCONS;
5245 	}
5246 #endif /* IGNORE_WHITE_INCONS */
5247 
5248 	if (rv & 2) {
5249 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5250 		free_dvector(dark, -1, m->nraw-1);
5251 		free(buf);
5252 		return I1PRO_RD_SENSORSATURATED;
5253 	}
5254 
5255 	if (optscale != NULL) {
5256 		double lhighest = highest;
5257 
5258 		if (lhighest < 1.0)
5259 			lhighest = 1.0;
5260 
5261 		/* Compute correction factor to make sensor optimal */
5262 		opttarget = i1pro_raw_to_absraw(p, (double)m->sens_target, *inttime, gainmode);
5263 		opttarget *= targoscale;
5264 
5265 
5266 		a1logd(p->log,3,"Optimal target = %f, amount to scale = %f\n",opttarget, opttarget/lhighest);
5267 
5268 		*optscale = opttarget/lhighest;
5269 	}
5270 
5271 	free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5272 	free_dvector(dark, -1, m->nraw-1);
5273 	free(buf);
5274 
5275 	return ev;
5276 }
5277 
5278 /* Take a measurement reading using the current mode, part 1 */
5279 /* Converts to completely processed output readings. */
5280 /* (NOTE:- this can't be used for calibration, as it implements uv mode) */
i1pro_read_patches_1(i1pro * p,int minnummeas,int maxnummeas,double * inttime,int gainmode,int * nmeasuered,unsigned char * buf,unsigned int bsize)5281 i1pro_code i1pro_read_patches_1(
5282 	i1pro *p,
5283 	int minnummeas,			/* Minimum number of measurements to take */
5284 	int maxnummeas,			/* Maximum number of measurements to allow for */
5285 	double *inttime, 		/* Integration time to use/used */
5286 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
5287 	int *nmeasuered,		/* Number actually measured */
5288 	unsigned char *buf,		/* Raw USB reading buffer */
5289 	unsigned int bsize
5290 ) {
5291 	i1pro_code ev = I1PRO_OK;
5292 	i1proimp *m = (i1proimp *)p->m;
5293 	i1pro_state *s = &m->ms[m->mmode];
5294 	i1p_mmodif mmod = i1p_norm;
5295 	int rv = 0;
5296 
5297 	if (minnummeas <= 0)
5298 		return I1PRO_INT_ZEROMEASURES;
5299 	if (minnummeas > maxnummeas)
5300 		maxnummeas = minnummeas;
5301 
5302 	if (m->uv_en)
5303 		mmod = i1p2_UV;
5304 
5305 	a1logd(p->log,3,"Triggering & gathering cycle, minnummeas %d, inttime %f, gainmode %d\n",
5306 	                                              minnummeas, *inttime, gainmode);
5307 
5308 	if ((ev = i1pro_trigger_one_measure(p, minnummeas, inttime, gainmode, mmod)) != I1PRO_OK) {
5309 		return ev;
5310 	}
5311 
5312 	if ((ev = i1pro_readmeasurement(p, minnummeas, m->c_measmodeflags & I1PRO_MMF_SCAN,
5313 	                                             buf, bsize, nmeasuered, mmod)) != I1PRO_OK) {
5314 		return ev;
5315 	}
5316 
5317 	return ev;
5318 }
5319 
5320 /* Take a measurement reading using the current mode, part 2 */
5321 /* Converts to completely processed output readings. */
i1pro_read_patches_2(i1pro * p,double * duration,double ** specrd,int numpatches,double inttime,int gainmode,int nmeasuered,unsigned char * buf,unsigned int bsize)5322 i1pro_code i1pro_read_patches_2(
5323 	i1pro *p,
5324 	double *duration,		/* Return flash duration */
5325 	double **specrd,		/* Return array [numpatches][nwav] of spectral reading values */
5326 	int numpatches,			/* Number of patches to return */
5327 	double inttime, 		/* Integration time to used */
5328 	int gainmode,			/* Gain mode useed, 0 = normal, 1 = high */
5329 	int nmeasuered,			/* Number actually measured */
5330 	unsigned char *buf,		/* Raw USB reading buffer */
5331 	unsigned int bsize
5332 ) {
5333 	i1pro_code ev = I1PRO_OK;
5334 	i1proimp *m = (i1proimp *)p->m;
5335 	i1pro_state *s = &m->ms[m->mmode];
5336 	double **multimes;		/* Multiple measurement results [maxnummeas|nmeasuered][-1 nraw]*/
5337 	double **absraw;		/* Linearsised absolute sensor raw values [numpatches][-1 nraw]*/
5338 	double satthresh;		/* Saturation threshold */
5339 	double darkthresh;		/* Dark threshold for consistency scaling limit */
5340 	int rv = 0;
5341 
5342 	if (duration != NULL)
5343 		*duration = 0.0;	/* default value */
5344 
5345 	darkthresh = m->sens_dark + inttime * 900.0;		/* Default */
5346 	if (gainmode)
5347 		darkthresh *= m->highgain;
5348 
5349 	/* Allocate temporaries */
5350 	multimes = dmatrix(0, nmeasuered-1, -1, m->nraw-1);
5351 	absraw = dmatrix(0, numpatches-1, -1, m->nraw-1);
5352 
5353 	/* Take a buffer full of raw readings, and convert them to */
5354 	/* absolute linearised sensor values. */
5355 	if ((ev = i1pro_sens_to_absraw(p, multimes, buf, nmeasuered, inttime, gainmode, &darkthresh))
5356 		                                                                             != I1PRO_OK) {
5357 		free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5358 		free_dmatrix(multimes, 0, nmeasuered-1, -1, m->nraw-1);
5359 		return ev;
5360 	}
5361 
5362 	/* Subtract the black level */
5363 	i1pro_sub_absraw(p, nmeasuered, inttime, gainmode, multimes, s->dark_data);
5364 
5365 #ifdef DUMP_SCANV
5366 	/* Dump raw scan readings to a file "i1pdump.txt" */
5367 	{
5368 		int i, j;
5369 		FILE *fp;
5370 
5371 		if ((fp = fopen("i1pdump.txt", "w")) == NULL)
5372 			a1logw(p->log,"Unable to open debug file i1pdump.txt\n");
5373 		else {
5374 			for (i = 0; i < nmeasuered; i++) {
5375 				fprintf(fp, "%d	",i);
5376 				for (j = 0; j < m->nraw; j++) {
5377 					fprintf(fp, "%f	",multimes[i][j]);
5378 				}
5379 				fprintf(fp,"\n");
5380 			}
5381 			fclose(fp);
5382 		}
5383 	}
5384 #endif
5385 	if (gainmode == 0)
5386 		satthresh = m->sens_sat0;
5387 	else
5388 		satthresh = m->sens_sat1;
5389 	satthresh = i1pro_raw_to_absraw(p, satthresh, inttime, gainmode);
5390 
5391 	darkthresh = i1pro_raw_to_absraw(p, darkthresh, inttime, gainmode);
5392 
5393 	if (!s->scan) {
5394 		if (numpatches != 1) {
5395 			free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5396 			free_dmatrix(multimes, 0, nmeasuered-1, -1, m->nraw-1);
5397 			a1logd(p->log,2,"i1pro_read_patches_2 spot read failed because numpatches != 1\n");
5398 			return I1PRO_INT_WRONGPATCHES;
5399 		}
5400 
5401 		/* Average a set of measurements into one. */
5402 		/* Return zero if readings are consistent and not saturated. */
5403 		/* Return nz with bit 1 set if the readings are not consistent */
5404 		/* Return nz with bit 2 set if the readings are saturated */
5405 		/* Return the highest individual element. */
5406 		/* Return the overall average. */
5407 		rv = i1pro_average_multimeas(p, absraw[0], multimes, nmeasuered, NULL, NULL,
5408 	                                                            satthresh, darkthresh);
5409 	} else {
5410 		if (s->flash) {
5411 
5412 			if (numpatches != 1) {
5413 				free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5414 				free_dmatrix(multimes, 0, nmeasuered-1, -1, m->nraw-1);
5415 				a1logd(p->log,2,"i1pro_read_patches_2 spot read failed because numpatches != 1\n");
5416 				return I1PRO_INT_WRONGPATCHES;
5417 			}
5418 			if ((ev = i1pro_extract_patches_flash(p, &rv, duration, absraw[0], multimes,
5419 			                                                 nmeasuered, inttime)) != I1PRO_OK) {
5420 				free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5421 				free_dmatrix(multimes, 0, nmeasuered-1, -1, m->nraw-1);
5422 				a1logd(p->log,2,"i1pro_read_patches_2 spot read failed at i1pro_extract_patches_flash\n");
5423 				return ev;
5424 			}
5425 
5426 		} else {
5427 			a1logd(p->log,3,"Number of patches measured = %d\n",nmeasuered);
5428 
5429 {
5430 			/* Recognise the required number of ref/trans patch locations, */
5431 			/* and average the measurements within each patch. */
5432 			if ((ev = i1pro_extract_patches_multimeas(p, &rv, absraw, numpatches, multimes,
5433 			                            nmeasuered, NULL, satthresh, inttime)) != I1PRO_OK) {
5434 				free_dmatrix(multimes, 0, nmeasuered-1, -1, m->nraw-1);
5435 				free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5436 				a1logd(p->log,2,"i1pro_read_patches_2 spot read failed at i1pro_extract_patches_multimeas\n");
5437 				return ev;
5438 			}
5439 }
5440 		}
5441 	}
5442 	free_dmatrix(multimes, 0, nmeasuered-1, -1, m->nraw-1);
5443 
5444 	if (rv & 1) {
5445 		free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5446 		a1logd(p->log,3,"i1pro_read_patches_2 spot read failed with inconsistent readings\n");
5447 		return I1PRO_RD_READINCONS;
5448 	}
5449 
5450 	if (rv & 2) {
5451 		free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5452 		a1logd(p->log,3,"i1pro_read_patches_2 spot read failed with sensor saturated\n");
5453 		return I1PRO_RD_SENSORSATURATED;
5454 	}
5455 
5456 	/* Convert an absraw array from raw wavelengths to output wavelenths */
5457 	i1pro_absraw_to_abswav(p, m->highres, s->reflective, numpatches, specrd, absraw);
5458 	free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5459 
5460 #ifdef APPEND_MEAN_EMMIS_VAL
5461 	/* Append averaged emission reading to file "i1pdump.txt" */
5462 	{
5463 		int i, j;
5464 		FILE *fp;
5465 
5466 		/* Create wavelegth label */
5467 		if ((fp = fopen("i1pdump.txt", "r")) == NULL) {
5468 			if ((fp = fopen("i1pdump.txt", "w")) == NULL)
5469 				a1logw(p->log,"Unable to reate debug file i1pdump.txt\n");
5470 			else {
5471 				for (j = 0; j < m->nwav[m->highres]; j++)
5472 					fprintf(fp,"%f ",XSPECT_WL(m->wl_short[m->highres], m->wl_long[m->highres], m->nwav[m->highres], j));
5473 				fprintf(fp,"\n");
5474 				fclose(fp);
5475 			}
5476 		}
5477 		if ((fp = fopen("i1pdump.txt", "a")) == NULL) {
5478 			a1logw(p->log,"Unable to open debug file i1pdump.txt\n");
5479 		else {
5480 			for (j = 0; j < m->nwav[m->highres]; j++)
5481 				fprintf(fp, "%f	",specrd[0][j] * m->emis_coef[j]);
5482 			fprintf(fp,"\n");
5483 			fclose(fp);
5484 		}
5485 	}
5486 #endif
5487 
5488 #ifdef PLOT_DEBUG
5489 	printf("Converted to wavelengths:\n");
5490 	plot_wav(m, m->highres, specrd[0]);
5491 #endif
5492 
5493 	/* Scale to the calibrated output values */
5494 	i1pro_scale_specrd(p, specrd, numpatches, specrd);
5495 
5496 #ifdef PLOT_DEBUG
5497 	printf("Calibrated measuerment spectra:\n");
5498 	plot_wav(m, m->highres, specrd[0]);
5499 #endif
5500 
5501 	return ev;
5502 }
5503 
5504 /* Take a measurement reading using the current mode, part 2a */
5505 /* Converts to completely processed output readings, */
5506 /* but don't average together or extract patches or flash. */
5507 /* (! Note that we aren't currently detecting saturation here!) */
5508 i1pro_code i1pro_read_patches_2a(
5509 	i1pro *p,
5510 	double **specrd,		/* Return array [numpatches][nwav] of spectral reading values */
5511 	int numpatches,			/* Number of patches measured and to return */
5512 	double inttime, 		/* Integration time to used */
5513 	int gainmode,			/* Gain mode useed, 0 = normal, 1 = high */
5514 	unsigned char *buf,		/* Raw USB reading buffer */
5515 	unsigned int bsize
5516 ) {
5517 	i1pro_code ev = I1PRO_OK;
5518 	i1proimp *m = (i1proimp *)p->m;
5519 	i1pro_state *s = &m->ms[m->mmode];
5520 	double **absraw;		/* Linearsised absolute sensor raw values [numpatches][-1 nraw]*/
5521 	double satthresh;		/* Saturation threshold */
5522 	double darkthresh;		/* Dark threshold for consistency scaling limit */
5523 
5524 	darkthresh = m->sens_dark + inttime * 900.0;		/* Default */
5525 	if (gainmode)
5526 		darkthresh *= m->highgain;
5527 
5528 	/* Allocate temporaries */
5529 	absraw = dmatrix(0, numpatches-1, -1, m->nraw-1);
5530 
5531 	/* Take a buffer full of raw readings, and convert them to */
5532 	/* absolute linearised sensor values. */
5533 	if ((ev = i1pro_sens_to_absraw(p, absraw, buf, numpatches, inttime, gainmode, &darkthresh))
5534 		                                                                           != I1PRO_OK) {
5535 		free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5536 		return ev;
5537 	}
5538 
5539 	/* Subtract the black level */
5540 	i1pro_sub_absraw(p, numpatches, inttime, gainmode, absraw, s->dark_data);
5541 
5542 	if (gainmode == 0)
5543 		satthresh = m->sens_sat0;
5544 	else
5545 		satthresh = m->sens_sat1;
5546 	satthresh = i1pro_raw_to_absraw(p, satthresh, inttime, gainmode);
5547 
5548 	darkthresh = i1pro_raw_to_absraw(p, darkthresh, inttime, gainmode);
5549 
5550 	a1logd(p->log,3,"Number of patches measured = %d\n",numpatches);
5551 
5552 	/* Convert an absraw array from raw wavelengths to output wavelenths */
5553 	i1pro_absraw_to_abswav(p, m->highres, s->reflective, numpatches, specrd, absraw);
5554 	free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
5555 
5556 #ifdef PLOT_DEBUG
5557 	printf("Converted to wavelengths:\n");
5558 	plot_wav(m, m->highres, specrd[0]);
5559 #endif
5560 
5561 	/* Scale to the calibrated output values */
5562 	i1pro_scale_specrd(p, specrd, numpatches, specrd);
5563 
5564 #ifdef PLOT_DEBUG
5565 	printf("Calibrated measuerment spectra:\n");
5566 	plot_wav(m, m->highres, specrd[0]);
5567 #endif
5568 
5569 	return ev;
5570 }
5571 
5572 /* Take a measurement reading using the current mode (combined parts 1 & 2) */
5573 /* Converts to completely processed output readings. */
5574 /* (NOTE:- this can't be used for calibration, as it implements uv mode) */
5575 i1pro_code i1pro_read_patches(
5576 	i1pro *p,
5577 	double *duration,		/* Return flash duration */
5578 	double **specrd,		/* Return array [numpatches][nwav] of spectral reading values */
5579 	int numpatches,			/* Number of patches to return */
5580 	int minnummeas,			/* Minimum number of measurements to take */
5581 	int maxnummeas,			/* Maximum number of measurements to allow for */
5582 	double *inttime, 		/* Integration time to use/used */
5583 	int gainmode			/* Gain mode to use, 0 = normal, 1 = high */
5584 ) {
5585 	i1pro_code ev = I1PRO_OK;
5586 	i1proimp *m = (i1proimp *)p->m;
5587 	i1pro_state *s = &m->ms[m->mmode];
5588 	unsigned char *buf;		/* Raw USB reading buffer */
5589 	unsigned int bsize;
5590 	int nmeasuered;			/* Number actually measured */
5591 	int rv = 0;
5592 
5593 	if (minnummeas <= 0)
5594 		return I1PRO_INT_ZEROMEASURES;
5595 	if (minnummeas > maxnummeas)
5596 		maxnummeas = minnummeas;
5597 
5598 	/* Allocate temporaries */
5599 	bsize = m->nsen * 2 * maxnummeas;
5600 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
5601 		a1logd(p->log,1,"i1pro_read_patches malloc %d bytes failed (11)\n",bsize);
5602 		return I1PRO_INT_MALLOC;
5603 	}
5604 
5605 	/* Trigger measure and gather raw readings */
5606 	if ((ev = i1pro_read_patches_1(p, minnummeas, maxnummeas, inttime, gainmode,
5607 	                                       &nmeasuered, buf, bsize)) != I1PRO_OK) {
5608 		free(buf);
5609 		return ev;
5610 	}
5611 
5612 	/* Process the raw readings */
5613 	if ((ev = i1pro_read_patches_2(p, duration, specrd, numpatches, *inttime, gainmode,
5614 	                                              nmeasuered, buf, bsize)) != I1PRO_OK) {
5615 		free(buf);
5616 		return ev;
5617 	}
5618 	free(buf);
5619 	return ev;
5620 }
5621 
5622 /* Take a measurement reading using the current mode (combined parts 1 & 2a) */
5623 /* Converts to completely processed output readings, without averaging or extracting */
5624 /* sample patches. */
5625 /* (NOTE:- this can't be used for calibration, as it implements uv mode) */
5626 i1pro_code i1pro_read_patches_all(
5627 	i1pro *p,
5628 	double **specrd,		/* Return array [numpatches][nwav] of spectral reading values */
5629 	int numpatches,			/* Number of sample to measure */
5630 	double *inttime, 		/* Integration time to use/used */
5631 	int gainmode			/* Gain mode to use, 0 = normal, 1 = high */
5632 ) {
5633 	i1pro_code ev = I1PRO_OK;
5634 	i1proimp *m = (i1proimp *)p->m;
5635 	i1pro_state *s = &m->ms[m->mmode];
5636 	unsigned char *buf;		/* Raw USB reading buffer */
5637 	unsigned int bsize;
5638 	int rv = 0;
5639 
5640 	bsize = m->nsen * 2 * numpatches;	/* 16 bit raw values */
5641 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
5642 		a1logd(p->log,1,"i1pro_read_patches malloc %d bytes failed (11)\n",bsize);
5643 		return I1PRO_INT_MALLOC;
5644 	}
5645 
5646 	/* Trigger measure and gather raw readings */
5647 	if ((ev = i1pro_read_patches_1(p, numpatches, numpatches, inttime, gainmode,
5648 	                                       NULL, buf, bsize)) != I1PRO_OK) {
5649 		free(buf);
5650 		return ev;
5651 	}
5652 
5653 	/* Process the raw readings without averaging or extraction */
5654 	if ((ev = i1pro_read_patches_2a(p, specrd, numpatches, *inttime, gainmode,
5655 	                                                 buf, bsize)) != I1PRO_OK) {
5656 		free(buf);
5657 		return ev;
5658 	}
5659 	free(buf);
5660 	return ev;
5661 }
5662 
5663 /* Take a trial measurement reading using the current mode. */
5664 /* Used to determine if sensor is saturated, or not optimal */
5665 /* in adaptive emission mode. */
5666 i1pro_code i1pro_trialmeasure(
5667 	i1pro *p,
5668 	int *saturated,			/* Return nz if sensor is saturated */
5669 	double *optscale,		/* Factor to scale gain/int time by to make optimal (may be NULL) */
5670 	int nummeas,			/* Number of readings to take */
5671 	double *inttime, 		/* Integration time to use/used */
5672 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
5673 	double targoscale		/* Optimal reading scale factor */
5674 ) {
5675 	i1pro_code ev = I1PRO_OK;
5676 	i1proimp *m = (i1proimp *)p->m;
5677 	i1pro_state *s = &m->ms[m->mmode];
5678 	unsigned char *buf;		/* Raw USB reading buffer */
5679 	unsigned int bsize;
5680 	double **multimes;		/* Multiple measurement results */
5681 	double *absraw;		/* Linearsised absolute sensor raw values */
5682 	int nmeasuered;			/* Number actually measured */
5683 	double highest;			/* Highest of sensor readings */
5684 	double sensavg;			/* Overall average of sensor readings */
5685 	double satthresh;		/* Saturation threshold */
5686 	double darkthresh;		/* Dark threshold */
5687 	double opttarget;		/* Optimal sensor target */
5688 	int rv;
5689 
5690 	if (nummeas <= 0)
5691 		return I1PRO_INT_ZEROMEASURES;
5692 
5693 	darkthresh = m->sens_dark + *inttime * 900.0;
5694 	if (gainmode)
5695 		darkthresh *= m->highgain;
5696 
5697 	/* Allocate up front to avoid delay between trigger and read */
5698 	bsize = m->nsen * 2 * nummeas;
5699 	if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
5700 		a1logd(p->log,1,"i1pro_trialmeasure malloc %d bytes failed (12)\n",bsize);
5701 		return I1PRO_INT_MALLOC;
5702 	}
5703 	multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
5704 	absraw = dvector(-1, m->nraw-1);
5705 
5706 	a1logd(p->log,3,"Triggering measurement cycle, nummeas %d, inttime %f, gainmode %d\n",
5707 	                                              nummeas, *inttime, gainmode);
5708 
5709 	if ((ev = i1pro_trigger_one_measure(p, nummeas, inttime, gainmode, i1p_cal)) != I1PRO_OK) {
5710 		free_dvector(absraw, -1, m->nraw-1);
5711 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5712 		free(buf);
5713 		return ev;
5714 	}
5715 
5716 	a1logd(p->log,4,"Gathering readings\n");
5717 	if ((ev = i1pro_readmeasurement(p, nummeas, m->c_measmodeflags & I1PRO_MMF_SCAN,
5718 	                                             buf, bsize, &nmeasuered, i1p_cal)) != I1PRO_OK) {
5719 		free_dvector(absraw, -1, m->nraw-1);
5720 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5721 		free(buf);
5722 		return ev;
5723 	}
5724 
5725 	/* Take a buffer full of raw readings, and convert them to */
5726 	/* absolute linearised sensor values. */
5727 	if ((ev = i1pro_sens_to_absraw(p, multimes, buf, nmeasuered, *inttime, gainmode, &darkthresh))
5728 		                                                                            != I1PRO_OK) {
5729 		free_dvector(absraw, -1, m->nraw-1);
5730 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5731 		free(buf);
5732 		return ev;
5733 	}
5734 
5735 	/* Compute dark subtraction for this trial's parameters */
5736 	if ((ev = i1pro_interp_dark(p, s->dark_data,
5737 		                                  s->inttime, s->gainmode)) != I1PRO_OK) {
5738 		free_dvector(absraw, -1, m->nraw-1);
5739 		free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5740 		free(buf);
5741 		a1logd(p->log,2,"i1pro_trialmeasure interplate dark ref failed\n");
5742 		return ev;
5743 	}
5744 
5745 	/* Subtract the black level */
5746 	i1pro_sub_absraw(p, nummeas, *inttime, gainmode, multimes, s->dark_data);
5747 
5748 	if (gainmode == 0)
5749 		satthresh = m->sens_sat0;
5750 	else
5751 		satthresh = m->sens_sat1;
5752 	satthresh = i1pro_raw_to_absraw(p, satthresh, *inttime, gainmode);
5753 
5754 	darkthresh = i1pro_raw_to_absraw(p, darkthresh, *inttime, gainmode);
5755 
5756 	/* Average a set of measurements into one. */
5757 	/* Return zero if readings are consistent and not saturated. */
5758 	/* Return nz with bit 1 set if the readings are not consistent */
5759 	/* Return nz with bit 2 set if the readings are saturated */
5760 	/* Return the highest individual element. */
5761 	/* Return the overall average. */
5762 	rv = i1pro_average_multimeas(p, absraw, multimes, nmeasuered, &highest, &sensavg,
5763 	                                                            satthresh, darkthresh);
5764 #ifdef PLOT_DEBUG
5765 	printf("Average absolute sensor readings, average = %f, satthresh %f:\n",sensavg, satthresh);
5766 	plot_raw(absraw);
5767 #endif
5768 
5769 	if (saturated != NULL) {
5770 		*saturated = 0;
5771 		if (rv & 2)
5772 			*saturated = 1;
5773 	}
5774 
5775 	/* Compute correction factor to make sensor optimal */
5776 	opttarget = (double)m->sens_target * targoscale;
5777 	opttarget = i1pro_raw_to_absraw(p, opttarget, *inttime, gainmode);
5778 
5779 	if (optscale != NULL) {
5780 		double lhighest = highest;
5781 
5782 		if (lhighest < 1.0)
5783 			lhighest = 1.0;
5784 
5785 		*optscale = opttarget/lhighest;
5786 	}
5787 
5788 	free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
5789 	free_dvector(absraw, -1, m->nraw-1);
5790 	free(buf);
5791 
5792 	return ev;
5793 }
5794 
5795 /* Trigger a single measurement cycle. This could be a dark calibration, */
5796 /* a calibration, or a real measurement. This is used to create the */
5797 /* higher level "calibrate" and "take reading" functions. */
5798 /* The setup for the operation is in the current mode state. */
5799 /* Call i1pro_readmeasurement() to collect the results */
5800 i1pro_code
5801 i1pro_trigger_one_measure(
5802 	i1pro *p,
5803 	int nummeas,			/* Minimum number of measurements to make */
5804 	double *inttime, 		/* Integration time to use/used */
5805 	int gainmode,			/* Gain mode to use, 0 = normal, 1 = high */
5806 	i1p_mmodif mmodif		/* Measurement modifier enum */
5807 ) {
5808 	i1pro_code ev = I1PRO_OK;
5809 	i1proimp *m = (i1proimp *)p->m;
5810 	i1pro_state *s = &m->ms[m->mmode];
5811 	unsigned int timssinceoff;	/* time in msec since lamp turned off */
5812 	double dintclocks;
5813 	int intclocks;		/* Number of integration clocks */
5814 	double dlampclocks;
5815 	int lampclocks;		/* Number of lamp turn on sub-clocks */
5816 	int measmodeflags;	/* Measurement mode command flags */
5817 	int measmodeflags2;	/* Rev E Measurement mode command flags */
5818 
5819 	/* Sanity check in case bad value was restored, due to switch */
5820 	/* from Rev A-D to Rev E mode. */
5821 	if (*inttime < m->min_int_time)
5822 		*inttime = m->min_int_time;
5823 
5824 	/* The Rev E measure mode has it's own settings */
5825 	if (p->itype == instI1Pro2) {
5826 		m->intclkp = m->intclkp2;		/* From i1pro2_getmeaschar() ? */
5827 		m->subclkdiv = m->subclkdiv2;
5828 		m->subtmode = 0;
5829 
5830 	} else {
5831 		/* Set any special hardware up for this sort of read */
5832 		if (*inttime != m->c_inttime) {		/* integration time is different */
5833 			int mcmode, maxmcmode;
5834 			int intclkusec;
5835 			int subtmodeflags;
5836 
5837 			/* Setting for fwrev < 301 */
5838 			/* (This is what getmcmode() returns for mcmode = 1 on fwrev >= 301) */
5839 			m->intclkp = 68.0e-6;
5840 			m->subclkdiv = 130;
5841 			m->subtmode = 0;
5842 
5843 			if (m->fwrev >= 301) {	/* Special hardware in latter versions of instrument */
5844 
5845 #ifdef DEBUG
5846 				/* Show all the available clock modes */
5847 				for (mcmode = 1;; mcmode++) {
5848 					int rmcmode, subclkdiv;
5849 
5850 					if ((ev = i1pro_setmcmode(p, mcmode)) != I1PRO_OK)
5851 						break;
5852 
5853 					if ((ev = i1pro_getmcmode(p, &maxmcmode, &rmcmode, &subclkdiv,
5854 					                             &intclkusec, &subtmodeflags) ) != I1PRO_OK)
5855 						break;
5856 
5857 					fprintf(stderr,"getcmode %d: maxcmode %d, mcmode %d, subclkdif %d, intclkusec %d, subtmodeflags 0x%x\n",mcmode,maxmcmode,rmcmode,subclkdiv,intclkusec,subtmodeflags);
5858 					if (mcmode >= maxmcmode)
5859 						break;
5860 				}
5861 #endif
5862 				/* Configure a clock mode that gives us an optimal integration time ? */
5863 				for (mcmode = 1;; mcmode++) {
5864 					if ((ev = i1pro_setmcmode(p, mcmode)) != I1PRO_OK)
5865 						return ev;
5866 
5867 					if ((ev = i1pro_getmcmode(p, &maxmcmode, &mcmode, &m->subclkdiv,
5868 					                             &intclkusec, &subtmodeflags) ) != I1PRO_OK)
5869 						return ev;
5870 
5871 					if ((*inttime/(intclkusec * 1e-6)) > 65535.0) {
5872 						return I1PRO_INT_INTTOOBIG;
5873 					}
5874 
5875 					if (*inttime >= (intclkusec * m->subclkdiv * 1e-6 * 0.99))
5876 						break;			/* Setting is optimal */
5877 
5878 					/* We need to go around again */
5879 					if (mcmode >= maxmcmode) {
5880 						return I1PRO_INT_INTTOOSMALL;
5881 					}
5882 				}
5883 				m->c_mcmode = mcmode;
5884 				m->intclkp = intclkusec * 1e-6;
5885 				a1logd(p->log,3,"Switched to perfect mode, subtmode flag = 0x%x, intclk = %f Mhz\n",subtmodeflags & 0x01, 1.0/intclkusec);
5886 				if (subtmodeflags & 0x01)
5887 					m->subtmode = 1;	/* Last reading subtract mode */
5888 			}
5889 		}
5890 	}
5891 	a1logd(p->log,3,"Integration clock period = %f ussec\n",m->intclkp * 1e6);
5892 
5893 	/* Compute integration clocks */
5894 	dintclocks = floor(*inttime/m->intclkp + 0.5);
5895 	if (p->itype == instI1Pro2) {
5896 		if (dintclocks > 4294967296.0)		/* This is probably not the actual limit */
5897 			return I1PRO_INT_INTTOOBIG;
5898 	} else {
5899 		if (dintclocks > 65535.0)
5900 			return I1PRO_INT_INTTOOBIG;
5901 	}
5902 	intclocks = (int)dintclocks;
5903 	*inttime = (double)intclocks * m->intclkp;		/* Quantized integration time */
5904 
5905 	if (s->reflective && (mmodif & 0x10)) {
5906 		dlampclocks = floor(s->lamptime/(m->subclkdiv * m->intclkp) + 0.5);
5907 		if (dlampclocks > 256.0)		/* Clip - not sure why. Silly value anyway */
5908 			dlampclocks = 256.0;
5909 		lampclocks = (int)dlampclocks;
5910 		s->lamptime = dlampclocks * m->subclkdiv * m->intclkp;	/* Quantized lamp time */
5911 	} else {
5912 		dlampclocks = 0.0;
5913 		lampclocks = 0;
5914 	}
5915 
5916 	if (nummeas > 65535)
5917 		nummeas = 65535;		/* Or should we error ? */
5918 
5919 	/* Create measurement mode flag values for this operation for both */
5920 	/* legacy and Rev E mode. Other code will examine legacy mode flags */
5921 	measmodeflags = 0;
5922 	if (s->scan && !(mmodif & 0x20)) 			/* Never scan on a calibration */
5923 		measmodeflags |= I1PRO_MMF_SCAN;
5924 	if (!s->reflective || !(mmodif & 0x10))
5925 		measmodeflags |= I1PRO_MMF_NOLAMP;		/* No lamp if not reflective or dark measure */
5926 	if (gainmode == 0)
5927 		measmodeflags |= I1PRO_MMF_LOWGAIN;		/* Normal gain mode */
5928 
5929 	if (p->itype == instI1Pro2) {
5930 		measmodeflags2 = 0;
5931 		if (s->scan && !(mmodif & 0x20))			/* Never scan on a calibration */
5932 			measmodeflags2 |= I1PRO2_MMF_SCAN;
5933 
5934 		if (mmodif == i1p2_UV)
5935 			measmodeflags2 |= I1PRO2_MMF_UV_LED;	/* UV LED illumination measurement */
5936 		else if (mmodif == i1p2_wl_cal)
5937 			measmodeflags2 |= I1PRO2_MMF_WL_LED;	/* Wavelegth illumination cal */
5938 		else if (s->reflective && (mmodif & 0x10))
5939 			measmodeflags2 |= I1PRO2_MMF_LAMP;		/* lamp if reflective and mmodif possible */
5940 
5941 		if (gainmode != 0)
5942 			return I1PRO_INT_NO_HIGH_GAIN;
5943 	}
5944 
5945 	a1logd(p->log,2,"i1pro: Int time %f msec, delay %f msec, no readings %d, expect %f msec\n",
5946 		*inttime * 1000.0,
5947 		((measmodeflags & I1PRO_MMF_NOLAMP) ? 0.0 : s->lamptime) * 1000.0,
5948 		nummeas,
5949 		(nummeas * *inttime + ((measmodeflags & I1PRO_MMF_NOLAMP) ? 0.0 : s->lamptime)) * 1000.0);
5950 
5951 	/* Do a setmeasparams */
5952 #ifdef NEVER
5953 	if (intclocks != m->c_intclocks				/* If any parameters have changed */
5954 	 || lampclocks != m->c_lampclocks
5955 	 || nummeas != m->c_nummeas
5956 	 || measmodeflags != m->c_measmodeflags
5957 	 || measmodeflags2 != m->c_measmodeflags2)
5958 #endif /* NEVER */
5959 	{
5960 
5961 		if (p->itype != instI1Pro2) {			/* Rev E sets the params in the measure command */
5962 			/* Set the hardware for measurement */
5963 			if ((ev = i1pro_setmeasparams(p, intclocks, lampclocks, nummeas, measmodeflags)) != I1PRO_OK)
5964 				return ev;
5965 		} else {
5966 			a1logd(p->log,2,"\ni1pro: SetMeasureParam2 %d, %d, %d, 0x%04x @ %d msec\n",
5967 	                   intclocks, lampclocks, nummeas, measmodeflags2,
5968 	                   msec_time() - m->msec);
5969 		}
5970 
5971 		m->c_intclocks = intclocks;
5972 		m->c_lampclocks = lampclocks;
5973 		m->c_nummeas = nummeas;
5974 		m->c_measmodeflags = measmodeflags;
5975 		m->c_measmodeflags2 = measmodeflags2;
5976 
5977 		m->c_inttime = *inttime;		/* Special harware is configured */
5978 		m->c_lamptime = s->lamptime;
5979 	}
5980 
5981 	/* If the lamp needs to be off, make sure at least 1.5 seconds */
5982 	/* have elapsed since it was last on, to make sure it's dark. */
5983 	if ((measmodeflags & I1PRO_MMF_NOLAMP)
5984 	 && (timssinceoff = (msec_time() - m->llamponoff)) < LAMP_OFF_TIME) {
5985 		a1logd(p->log,3,"Sleep %d msec for lamp cooldown\n",LAMP_OFF_TIME - timssinceoff);
5986 		msec_sleep(LAMP_OFF_TIME - timssinceoff);	/* Make sure time adds up to 1.5 seconds */
5987 	}
5988 
5989 	/* Trigger a measurement */
5990 	usb_reinit_cancel(&m->rd_sync);			/* Prepare to sync rd and trigger */
5991 	if (p->itype != instI1Pro2) {
5992 		if ((ev = i1pro_triggermeasure(p, TRIG_DELAY)) != I1PRO_OK)
5993 			return ev;
5994 	} else {
5995 		if ((ev = i1pro2_triggermeasure(p, TRIG_DELAY)) != I1PRO_OK)
5996 			return ev;
5997 	}
5998 
5999 	return ev;
6000 }
6001 
6002 /* ============================================================ */
6003 /* Big endian wire format conversion routines */
6004 
6005 /* Take an int, and convert it into a byte buffer big endian */
6006 static void int2buf(unsigned char *buf, int inv) {
6007 	buf[0] = (inv >> 24) & 0xff;
6008 	buf[1] = (inv >> 16) & 0xff;
6009 	buf[2] = (inv >> 8) & 0xff;
6010 	buf[3] = (inv >> 0) & 0xff;
6011 }
6012 
6013 /* Take a short, and convert it into a byte buffer big endian */
6014 static void short2buf(unsigned char *buf, int inv) {
6015 	buf[0] = (inv >> 8) & 0xff;
6016 	buf[1] = (inv >> 0) & 0xff;
6017 }
6018 
6019 /* Take a word sized buffer, and convert it to an int */
6020 static int buf2int(unsigned char *buf) {
6021 	int val;
6022 	val = buf[0];	/* Hmm. should this be sign extended ?? */
6023 	val = ((val << 8) + (0xff & buf[1]));
6024 	val = ((val << 8) + (0xff & buf[2]));
6025 	val = ((val << 8) + (0xff & buf[3]));
6026 	return val;
6027 }
6028 
6029 /* Take a short sized buffer, and convert it to an int */
6030 static int buf2short(unsigned char *buf) {
6031 	int val;
6032 	val = *((signed char *)buf);		/* Sign extend */
6033 	val = ((val << 8) + (0xff & buf[1]));
6034 	return val;
6035 }
6036 
6037 /* Take a unsigned short sized buffer, and convert it to an int */
6038 static int buf2ushort(unsigned char *buf) {
6039 	int val;
6040 	val = (0xff & buf[0]);
6041 	val = ((val << 8) + (0xff & buf[1]));
6042 	return val;
6043 }
6044 
6045 /* ============================================================ */
6046 /* lower level reading processing and computation */
6047 
6048 /* Take a buffer full of sensor readings, and convert them to */
6049 /* absolute raw values. Linearise if Rev A..D */
6050 /* If RevE, fill in the [-1] value with the shielded cell values */
6051 /* Note the rev E darkthresh returned has NOT been converted to an absolute raw value */
6052 i1pro_code i1pro_sens_to_absraw(
6053 	i1pro *p,
6054 	double **absraw,		/* Array of [nummeas][-1 nraw] value to return */
6055 	unsigned char *buf,		/* Raw measurement data must be nsen * nummeas */
6056 	int nummeas,			/* Return number of readings measured */
6057 	double inttime, 		/* Integration time used */
6058 	int gainmode,			/* Gain mode, 0 = normal, 1 = high */
6059 	double *pdarkthresh     /* Return a raw dark threshold value (Rev E) */
6060 ) {
6061 	i1proimp *m = (i1proimp *)p->m;
6062 	int i, j, k;
6063 	unsigned char *bp;
6064 	unsigned int maxpve = m->maxpve;	/* maximum +ve sensor value + 1 */
6065 	double avlastv = 0.0;
6066 	double darkthresh = 0.0;			/* Rev E calculated values */
6067 	double ndarkthresh = 0.0;
6068 	double gain;
6069 	int npoly;			/* Number of linearisation coefficients */
6070 	double *polys;		/* the coeficients */
6071 	double scale;		/* Absolute scale value */
6072 	int sskip = 0;		/* Bytes to skip at start */
6073 	int eskip = 0;		/* Bytes to skip at end */
6074 
6075 	if (gainmode) {
6076 		gain = m->highgain;
6077 		npoly = m->nlin1;
6078 		polys = m->lin1;
6079 	} else {
6080 		gain = 1.0;
6081 		npoly = m->nlin0;
6082 		polys = m->lin0;
6083 	}
6084 	scale = 1.0/(inttime * gain);
6085 
6086 	/* Now process the buffer values */
6087 	if (m->nsen > m->nraw) {	/* It's a Rev E, so we have extra values, */
6088 								/* and don't linearize here. */
6089 		sskip = 6 * 2;		/* 6 dark reading values */
6090 		eskip = 0 * 2;		/* none to skip at end */
6091 
6092 		if ((sskip + m->nraw * 2 + eskip) != (m->nsen * 2)) {
6093 			a1loge(p->log,1,"i1pro Rev E - sskip %d + nraw %d + eskip %d != nsen %d\n"
6094 			                                 ,sskip, m->nraw * 2, eskip, m->nsen * 2);
6095 			return I1PRO_INT_ASSERT;
6096 		}
6097 
6098 		for (bp = buf, i = 0; i < nummeas; i++, bp += eskip) {
6099 			unsigned int rval;
6100 			double fval;
6101 
6102 			/* The first 6 readings (xraw from i1pro2_getmeaschar()) are shielded cells, */
6103 			/* and we use them as an estimate of the dark reading consistency, as well as for */
6104 			/* compensating the dark level calibration for any temperature changes. */
6105 
6106 			/* raw average of all measurement shielded cell values */
6107 			for (k = 0; k < 6; k++) {
6108 				darkthresh += (double)buf2ushort(bp + k * 2);
6109 				ndarkthresh++;
6110 			}
6111 
6112 			/* absraw of shielded cells per reading */
6113 			absraw[i][-1] = 0.0;
6114 			for (k = 0; k < 6; k++) {
6115 				rval = buf2ushort(bp + k * 2);
6116 				fval = (double)(int)rval;
6117 
6118 				/* And scale to be an absolute sensor reading */
6119 				absraw[i][-1] += fval * scale;
6120 			}
6121 			absraw[i][-1] /= 6.0;
6122 
6123 			for (bp += sskip, j = 0; j < m->nraw; j++, bp += 2) {
6124 				rval = buf2ushort(bp);
6125 				a1logd(p->log,9,"% 3d:rval 0x%x, ",j, rval);
6126 				a1logd(p->log,9,"srval 0x%x, ",rval);
6127 				fval = (double)(int)rval;
6128 				a1logd(p->log,9,"fval %.0f, ",fval);
6129 
6130 				/* And scale to be an absolute sensor reading */
6131 				absraw[i][j] = fval * scale;
6132 				a1logd(p->log,9,"absval %.1f\n",fval * scale);
6133 			}
6134 		}
6135 		darkthresh /= ndarkthresh;
6136 		if (pdarkthresh != NULL)
6137 			*pdarkthresh = darkthresh;
6138 		a1logd(p->log,3,"i1pro_sens_to_absraw: Dark threshold = %f\n",darkthresh);
6139 
6140 	} else {
6141 		/* if subtmode is set, compute the average last reading raw value. */
6142 		/* Could this be some sort of temperature compensation offset ??? */
6143 		/* (Rev A produces a value that is quite different to a sensor value, */
6144 		/* ie. 1285 = 0x0505, while RevD and RevE in legacy mode have a value of 0 */
6145 		/* I've not seen anything actually use subtmode - maybe this is Rev B only ?) */
6146 		/* The 0 band seens to contain values similar to band 1, so it's not clear */
6147 		/* why the manufacturers driver appears to be discarding it ? */
6148 
6149 		/* (Not sure if it's reasonable to extend the sign and then do this */
6150 		/* computation, or whether it makes any difference, since I've never */
6151 		/* seen this mode triggered. */
6152 		if (m->subtmode) {
6153 			for (bp = buf + 254, i = 0; i < nummeas; i++, bp += (m->nsen * 2)) {
6154 				unsigned int lastv;
6155 				lastv = buf2ushort(bp);
6156 				if (lastv >= maxpve) {
6157 					lastv -= 0x00010000;	/* Convert to -ve */
6158 				}
6159 				avlastv += (double)lastv;
6160 			}
6161 			avlastv /= (double)nummeas;
6162 			a1logd(p->log,3,"subtmode got avlastv = %f\n",avlastv);
6163 		}
6164 
6165 		for (bp = buf, i = 0; i < nummeas; i++) {
6166 			absraw[i][-1] = 1.0;		/* Not used in RevA-D */
6167 
6168 			for (j = 0; j < 128; j++, bp += 2)  {
6169 				unsigned int rval;
6170 				double fval, lval;
6171 
6172 				rval = buf2ushort(bp);
6173 				a1logd(p->log,9,"% 3d:rval 0x%x, ",j, rval);
6174 				if (rval >= maxpve)
6175 					rval -= 0x00010000;	/* Convert to -ve */
6176 				a1logd(p->log,9,"srval 0x%x, ",rval);
6177 				fval = (double)(int)rval;
6178 				a1logd(p->log,9,"fval %.0f, ",fval);
6179 				fval -= avlastv;
6180 				a1logd(p->log,9,"fval-av %.0f, ",fval);
6181 
6182 #ifdef ENABLE_NONLINCOR
6183 				/* Linearise */
6184 				for (lval = polys[npoly-1], k = npoly-2; k >= 0; k--)
6185 					lval = lval * fval + polys[k];
6186 #else
6187 				lval = fval;
6188 #endif
6189 				a1logd(p->log,9,"lval %.1f, ",lval);
6190 
6191 				/* And scale to be an absolute sensor reading */
6192 				absraw[i][j] = lval * scale;
6193 				a1logd(p->log,9,"absval %.1f\n",lval * scale);
6194 				// a1logd(p->log,3,"Meas %d band %d raw = %f\n",i,j,fval);
6195 			}
6196 
6197 			/* Duplicate last values in buffer to make up to 128 */
6198 			absraw[i][0] = absraw[i][1];
6199 			absraw[i][127] = absraw[i][126];
6200 		}
6201 	}
6202 
6203 	return I1PRO_OK;
6204 }
6205 
6206 /* Take a raw value, and convert it into an absolute raw value. */
6207 /* Note that linearisation is ignored, since it is assumed to be insignificant */
6208 /* to the black threshold and saturation values. */
6209 double i1pro_raw_to_absraw(
6210 	i1pro *p,
6211 	double raw,				/* Input value */
6212 	double inttime, 		/* Integration time used */
6213 	int gainmode			/* Gain mode, 0 = normal, 1 = high */
6214 ) {
6215 	i1proimp *m = (i1proimp *)p->m;
6216 	int i, j, k;
6217 	double gain;
6218 	double scale;		/* Absolute scale value */
6219 	double fval;
6220 
6221 	if (gainmode) {
6222 		gain = m->highgain;
6223 	} else {
6224 		gain = 1.0;
6225 	}
6226 	scale = 1.0/(inttime * gain);
6227 
6228 	return raw * scale;
6229 }
6230 
6231 
6232 /* Invert a polinomial equation. */
6233 /* Since the linearisation is nearly a straight line, */
6234 /* a simple Newton inversion will suffice. */
6235 static double inv_poly(double *polys, int npoly, double inv) {
6236 	double outv = inv, lval, del = 100.0;
6237 	int i, k;
6238 
6239 	for (i = 0; i < 200 && fabs(del) > 1e-7; i++) {
6240 		for (lval = polys[npoly-1], k = npoly-2; k >= 0; k--) {
6241 			lval = lval * outv + polys[k];
6242 		}
6243 		del = (inv - lval);
6244 		outv += 0.99 * del;
6245 	}
6246 
6247 	return outv;
6248 }
6249 
6250 /* Take a single set of absolute linearised sensor values and */
6251 /* convert them back into Rev A..D raw reading values. */
6252 /* This is used for saving a calibration to the EEProm */
6253 i1pro_code i1pro_absraw_to_meas(
6254 	i1pro *p,
6255 	int *meas,				/* Return raw measurement data */
6256 	double *absraw,			/* Array of [-1 nraw] value to process */
6257 	double inttime, 		/* Integration time used */
6258 	int gainmode			/* Gain mode, 0 = normal, 1 = high */
6259 ) {
6260 	i1proimp *m = (i1proimp *)p->m;
6261 	unsigned int maxpve = m->maxpve;	/* maximum +ve sensor value + 1 */
6262 	int i, j, k;
6263 	double avlastv = 0.0;
6264 	double gain;
6265 	int npoly;			/* Number of linearisation coefficients */
6266 	double *polys;		/* the coeficients */
6267 	double scale;		/* Absolute scale value */
6268 
6269 	if (m->subtmode) {
6270 		a1logd(p->log,1,"i1pro_absraw_to_meas subtmode set\n");
6271 		return I1PRO_INT_MALLOC;
6272 	}
6273 
6274 	if (gainmode) {
6275 		gain = m->highgain;
6276 		npoly = m->nlin1;
6277 		polys = m->lin1;
6278 	} else {
6279 		gain = 1.0;
6280 		npoly = m->nlin0;
6281 		polys = m->lin0;
6282 	}
6283 	scale = 1.0/(inttime * gain);
6284 
6285 	for (j = 0; j < 128; j++)  {
6286 		double fval, lval;
6287 		unsigned int rval;
6288 
6289 		/* Unscale from absolute sensor reading */
6290 		lval = absraw[j] / scale;
6291 
6292 #ifdef ENABLE_NONLINCOR
6293 		/* Un-linearise */
6294 		fval = inv_poly(polys, npoly, lval);
6295 #else
6296 		fval = lval;
6297 #endif
6298 
6299 		if (fval < (double)((int)maxpve-65536))
6300 			fval = (double)((int)maxpve-65536);
6301 		else if (fval > (double)(maxpve-1))
6302 			fval = (double)(maxpve-1);
6303 
6304 		rval = (unsigned int)(int)floor(fval + 0.5);
6305 		meas[j] = rval;
6306 	}
6307 	return I1PRO_OK;
6308 }
6309 
6310 /* Average a set of measurements into one. */
6311 /* Return zero if readings are consistent and not saturated. */
6312 /* Return nz with bit 1 set if the readings are not consistent */
6313 /* Return nz with bit 2 set if the readings are saturated */
6314 /* Return the highest individual element. */
6315 /* Return the overall average. */
6316 int i1pro_average_multimeas(
6317 	i1pro *p,
6318 	double *avg,			/* return average [-1 nraw] */
6319 	double **multimeas,		/* Array of [nummeas][-1 nraw] value to average */
6320 	int nummeas,			/* number of readings to be averaged */
6321 	double *phighest,		/* If not NULL, return highest value from all bands and msrmts. */
6322 	double *poallavg,		/* If not NULL, return overall average of bands and measurements */
6323 	double satthresh,		/* Sauration threshold, 0 for none */
6324 	double darkthresh		/* Dark threshold (used for consistency check scaling) */
6325 ) {
6326 	i1proimp *m = (i1proimp *)p->m;
6327 	int i, j;
6328 	double highest = -1e6;
6329 	double oallavg = 0.0;
6330 	double avgoverth = 0.0;		/* Average over threshold */
6331 	double maxavg = -1e38;		/* Track min and max averages of readings */
6332 	double minavg = 1e38;
6333 	double norm;
6334 	int rv = 0;
6335 
6336 	a1logd(p->log,3,"i1pro_average_multimeas %d readings\n",nummeas);
6337 
6338 	for (j = -1; j < 128; j++)
6339 		avg[j] = 0.0;
6340 
6341 	/* Now process the buffer values */
6342 	for (i = 0; i < nummeas; i++) {
6343 		double measavg = 0.0;
6344 		int k;
6345 
6346 		for (j = k = 0; j < m->nraw; j++) {
6347 			double val;
6348 
6349 			val = multimeas[i][j];
6350 
6351 			avg[j] += val;		/* Per value average */
6352 
6353 			/* Skip 0 and 127 cell values for RevA-D */
6354 			if (m->nsen == m->nraw && (j == 0 || j == 127))
6355 				continue;
6356 
6357 			if (val > highest)
6358 				highest = val;
6359 			if (val > satthresh)
6360 				avgoverth++;
6361 			measavg += val;
6362 			k++;
6363 		}
6364 		measavg /= (double)k;
6365 		oallavg += measavg;
6366 		if (measavg < minavg)
6367 			minavg = measavg;
6368 		if (measavg > maxavg)
6369 			maxavg = measavg;
6370 
6371 		/* and shielded values */
6372 		avg[-1] += multimeas[i][-1];
6373 	}
6374 
6375 	for (j = -1; j < 128; j++)
6376 		avg[j] /= (double)nummeas;
6377 	oallavg /= (double)nummeas;
6378 	avgoverth /= (double)nummeas;
6379 
6380 	if (phighest != NULL)
6381 		*phighest = highest;
6382 
6383 	if (poallavg != NULL)
6384 		*poallavg = oallavg;
6385 
6386 	if (satthresh > 0.0 && avgoverth > 0.0)
6387 		rv |= 2;
6388 
6389 	norm = fabs(0.5 * (maxavg+minavg));
6390 	a1logd(p->log,4,"norm = %f, dark thresh = %f\n",norm,darkthresh);
6391 	if (norm < (2.0 * darkthresh))
6392 		norm = 2.0 * darkthresh;
6393 
6394 	a1logd(p->log,4,"overall avg = %f, minavg = %f, maxavg = %f, variance %f, shielded avg %f\n",
6395 	                   oallavg,minavg,maxavg,(maxavg - minavg)/norm, avg[-1]);
6396 	if ((maxavg - minavg)/norm > PATCH_CONS_THR) {
6397 		a1logd(p->log,2,"Reading is inconsistent: (maxavg %f - minavg %f)/norm %f = %f > thresh %f, darkthresh %f\n",maxavg,minavg,norm,(maxavg - minavg)/norm,PATCH_CONS_THR, darkthresh);
6398 		rv |= 1;
6399 	}
6400 	return rv;
6401 }
6402 
6403 /* Minimum number of scan samples in a patch */
6404 #define MIN_SAMPLES 3
6405 
6406 /* Range of bands to detect transitions */
6407 #define BL 5	/* Start */
6408 #define BH 105	/* End */
6409 #define BW 5	/* Width */
6410 
6411 /* Record of possible patch */
6412 typedef struct {
6413 	int ss;				/* Start sample index */
6414 	int no;				/* Number of samples */
6415 	int use;			/* nz if patch is to be used */
6416 } i1pro_patch;
6417 
6418 /* Recognise the required number of ref/trans patch locations, */
6419 /* and average the measurements within each patch. */
6420 /* *flags returns zero if readings are consistent and not saturated. */
6421 /* *flags returns nz with bit 1 set if the readings are not consistent */
6422 /* *flags returns nz with bit 2 set if the readings are saturated */
6423 /* *phighest returns the highest individual element. */
6424 /* (Doesn't extract [-1] shielded values, since they have already been used) */
6425 i1pro_code i1pro_extract_patches_multimeas(
6426 	i1pro *p,
6427 	int *flags,				/* return flags */
6428 	double **pavg,			/* return patch average [naptch][-1 nraw] */
6429 	int tnpatch,			/* Target number of patches to recognise */
6430 	double **multimeas,		/* Array of [nummeas][-1 nraw] value to extract from */
6431 	int nummeas,			/* number of readings made */
6432 	double *phighest,		/* If not NULL, return highest value from all bands and msrmts. */
6433 	double satthresh,		/* Sauration threshold, 0 for none */
6434 	double inttime			/* Integration time (used to adjust consistency threshold) */
6435 ) {
6436 	i1proimp *m = (i1proimp *)p->m;
6437 	int i, j, k, pix;
6438 	double **sslope;			/* Signed difference between i and i+1 */
6439 	double *slope;				/* Accumulated absolute difference between i and i+1 */
6440 	double *fslope;				/* Filtered slope */
6441 	i1pro_patch *pat;			/* Possible patch information */
6442 	int npat, apat = 0;
6443 	double *maxval;				/* Maximum input value for each wavelength */
6444 	double fmaxslope = 0.0;
6445 	double maxslope = 0.0;
6446 	double minslope =  1e38;
6447 	double thresh = 0.4;		/* Slope threshold */
6448 	int try;					/* Thresholding try */
6449 	double avglegth;			/* Average length of patches */
6450 	int *sizepop;				/* Size popularity of potential patches */
6451 	double median;				/* median potential patch width */
6452 	double window;				/* +/- around median to accept */
6453 	double highest = -1e6;
6454 	double white_avg;			/* Average of (aproximate) white data */
6455 	int b_lo = BL;				/* Patch detection low band */
6456 	int b_hi = BH;				/* Patch detection low band */
6457 	int rv = 0;
6458 	double patch_cons_thr = PATCH_CONS_THR * m->scan_toll_ratio;
6459 #ifdef PATREC_DEBUG
6460 	double **plot;
6461 #endif
6462 
6463 	a1logd(p->log,2,"i1pro_extract_patches_multimeas looking for %d patches out of %d samples\n",tnpatch,nummeas);
6464 
6465 	/* Adjust bands if UV mode */
6466 	/* (This is insufficient for useful patch recognition) */
6467 	if (m->uv_en) {
6468 		b_lo = 91;
6469 		b_hi = 117;
6470 	}
6471 
6472 	maxval = dvectorz(-1, m->nraw-1);
6473 
6474 	/* Loosen consistency threshold for short integation time, */
6475 	/* to allow for extra noise */
6476 	if (inttime < 0.012308)		/* Smaller than Rev A minimum int. time */
6477 		patch_cons_thr *= sqrt(0.012308/inttime);
6478 
6479 	/* Discover the maximum input value for normalisation */
6480 	for (j = 0; j < m->nraw; j ++) {
6481 		for (i = 0; i < nummeas; i++) {
6482 			if (multimeas[i][j] > maxval[j])
6483 				maxval[j] = multimeas[i][j];
6484 		}
6485 		if (maxval[j] < 1.0)
6486 			maxval[j] = 1.0;
6487 	}
6488 
6489 #ifdef PATREC_DEBUG
6490 	/* Plot out 6 lots of 8 values each */
6491 	plot = dmatrixz(0, 11, 0, nummeas-1);
6492 //	for (j = 45; j <= (m->nraw-8); j += 100) 		/* Do just one band */
6493 #ifdef PATREC_ALLBANDS
6494 	for (j = 0; j <= (m->nraw-8); j += 8) 			/* Plot all the bands */
6495 #else
6496 	for (j = 5; j <= (m->nraw-8); j += 30) 			/* Do four bands */
6497 #endif
6498 	{
6499 		for (k = 0; k < 8; k ++) {
6500 			for (i = 0; i < nummeas; i++) {
6501 				plot[k][i] = multimeas[i][j+k]/maxval[j+k];
6502 			}
6503 		}
6504 		for (i = 0; i < nummeas; i++)
6505 			plot[8][i] = (double)i;
6506 		printf("Bands %d - %d\n",j,j+7);
6507 		do_plot10(plot[8], plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], plot[6], plot[7], NULL, NULL, nummeas, 0);
6508 	}
6509 #endif	/* PATREC_DEBUG */
6510 
6511 	sslope = dmatrixz(0, nummeas-1, -1, m->nraw-1);
6512 	slope = dvectorz(0, nummeas-1);
6513 	fslope = dvectorz(0, nummeas-1);
6514 	sizepop = ivectorz(0, nummeas-1);
6515 
6516 #ifndef NEVER		/* Good with this on */
6517 	/* Average bands together */
6518 	for (i = 0; i < nummeas; i++) {
6519 		for (j = b_lo + BW; j < (b_hi - BW); j++) {
6520 			for (k = -b_lo; k <= BW; k++)		/* Box averaging filter over bands */
6521 				 sslope[i][j] += multimeas[i][j + k]/maxval[j];
6522 		}
6523 	}
6524 #else
6525 	/* Don't average bands */
6526 	for (i = 0; i < nummeas; i++) {
6527 		for (j = 0; j < m->nraw; j++) {
6528 			sslope[i][j] = multimeas[i][j]/maxval[j];
6529 		}
6530 	}
6531 #endif
6532 
6533 	/* Compute slope result over readings and bands */
6534 	/* Compute signed slope result over readings and bands */
6535 
6536 #ifdef NEVER		/* Works well for non-noisy readings */
6537 	/* Median of 5 differences from 6 points */
6538 	for (i = 2; i < (nummeas-3); i++) {
6539 		for (j = b_lo; j < b_hi; j++) {
6540 			double sl, asl[5];
6541 			int r, s;
6542 			asl[0] = fabs(sslope[i-2][j] - sslope[i-1][j]);
6543 			asl[1] = fabs(sslope[i-1][j] - sslope[i-0][j]);
6544 			asl[2] = fabs(sslope[i-0][j] - sslope[i+1][j]);
6545 			asl[3] = fabs(sslope[i+1][j] - sslope[i+2][j]);
6546 			asl[4] = fabs(sslope[i+2][j] - sslope[i+3][j]);
6547 
6548 			/* Sort them */
6549 			for (r = 0; r < (5-1); r++) {
6550 				for (s = r+1; s < 5; s++) {
6551 					if (asl[s] < asl[r]) {
6552 						double tt;
6553 						tt = asl[s];
6554 						asl[s] = asl[r];
6555 						asl[r] = tt;
6556 					}
6557 				}
6558 			}
6559 			/* Pick middle one */
6560 			sl = asl[2];
6561 			if (sl > slope[i])
6562 				slope[i] = sl;
6563 		}
6564 	}
6565 
6566 #else	/* Works better for noisy readings */
6567 
6568 	/* Compute sliding window average and deviation that contains */
6569 	/* our output point, and chose the average with the minimum deviation. */
6570 #define FW 3		/* Number of delta's to average */
6571 	for (i = FW-1; i < (nummeas-FW); i++) {		/* Samples */
6572 		double basl, bdev;		/* Best average slope, Best deviation */
6573 		double sl[2 * FW -1];
6574 		double asl[FW], dev[FW];
6575 		int slopen = 0;
6576 		double slopeth = 0.0;
6577 		int m, pp;
6578 
6579 		for (pp = 0; pp < 2; pp++) { 			/* For each pass */
6580 
6581 			for (j = b_lo; j < b_hi; j++) {				/* Bands */
6582 
6583 				/* Compute differences for the range of our windows */
6584 				for (k = 0; k < (2 * FW -1); k++)
6585 					sl[k] = sslope[i+k-FW+1][j] - sslope[i+k+-FW+2][j];
6586 
6587 				/* For each window offset, compute average and deviation squared */
6588 				bdev = 1e38;
6589 				for (k = 0; k < FW; k++) {
6590 
6591 					/* Compute average of this window offset */
6592 					asl[k] = 0.0;
6593 					for (m = 0; m < FW; m++)	/* For slope in window */
6594 						asl[k] += sl[k+m];
6595 					asl[k] /= (double)FW;
6596 
6597 					/* Compute deviation squared */
6598 					dev[k] = 0.0;
6599 					for (m = 0; m < FW; m++) {
6600 						double tt;
6601 						tt = sl[k+m] - asl[k];
6602 						dev[k] += tt * tt;
6603 					}
6604 					if (dev[k] < bdev)
6605 						bdev = dev[k];
6606 				}
6607 
6608 #ifndef NEVER
6609 				/* Weight the deviations with a triangular weighting */
6610 				/* to skew slightly towards the center */
6611 				for (k = 0; k < FW; k++) {
6612 					double wt;
6613 					wt = fabs(2.0 * k - (FW -1.0))/(FW-1.0);
6614 					dev[k] += wt * bdev;
6615 				}
6616 #endif
6617 
6618 				/* For each window offset, choose the one to use. */
6619 				bdev = 1e38;
6620 				basl = 0.0;
6621 				for (k = 0; k < FW; k++) {
6622 
6623 					/* Choose window average with smallest deviation squared */
6624 					if (dev[k] < bdev) {
6625 						bdev = dev[k];
6626 						basl = fabs(asl[k]);
6627 					}
6628 				}
6629 
6630 				if (pp == 0) {		/* First pass, compute average slope over bands */
6631 					slope[i] += basl;
6632 
6633 				} else {			/* Second pass, average slopes of bands over threshold */
6634 					if (basl > slopeth) {
6635 						slope[i] += basl;
6636 						slopen++;
6637 					}
6638 				}
6639 			}	/* Next band */
6640 
6641 			if (pp == 0) {
6642 				slopeth = 1.0 * slope[i]/j;		/* Compute threshold */
6643 				slope[i] = 0.0;
6644 			} else {
6645 				if (slopen > 0)
6646 					slope[i] /= slopen;			/* Compute average of those over threshold */
6647 			}
6648 		}		/* Next pass */
6649 	}
6650 #undef FW
6651 #endif
6652 
6653 #ifndef NEVER		/* Good with this on */
6654 	/* Normalise the slope values */
6655 	/* Locate the minumum and maximum values */
6656 	maxslope = 0.0;
6657 	minslope = 1e38;
6658 	for (i = 4; i < (nummeas-4); i++) {
6659 		double avs;
6660 
6661 		if (slope[i] > maxslope)
6662 			maxslope = slope[i];
6663 
6664 		/* Simple moving average for min comp. */
6665 		avs = 0.0;
6666 		for (j = -2; j <= 2; j++)
6667 			avs += slope[i+j];
6668 		avs /= 5.0;
6669 		if (avs < minslope)
6670 			minslope = avs;
6671 	}
6672 
6673 	/* Normalise the slope */
6674 	maxslope *= 0.5;
6675 	minslope *= 3.0;
6676 	for (i = 0; i < nummeas; i++) {
6677 		slope[i] = (slope[i] - minslope) / (maxslope - minslope);
6678 		if (slope[i] < 0.0)
6679 			slope[i] = 0.0;
6680 		else if (slope[i] > 1.0)
6681 			slope[i] = 1.0;
6682 	}
6683 
6684 	/* "Automatic Gain control" the raw slope information. */
6685 #define LFW 20		/* Half width of triangular filter */
6686 	for (i = 0; i < nummeas; i++) {
6687 		double sum, twt;
6688 
6689 		sum = twt = 0.0;
6690 		for (j = -LFW; j <= LFW; j++) {
6691 			double wt;
6692 			if ((i+j) < 0 || (i+j) >= nummeas)
6693 				continue;
6694 
6695 			wt = ((LFW-abs(j))/(double)LFW);
6696 
6697 			sum += wt * slope[i+j];
6698 			twt += wt;
6699 		}
6700 		fslope[i] = sum/twt;
6701 		if (fslope[i] > fmaxslope)
6702 			fmaxslope = fslope[i];
6703 	}
6704 #undef LFW
6705 
6706 #ifdef NEVER		/* Better with the off, for very noisy samples */
6707 	/* Apply AGC with limited gain */
6708 	for (i = 0; i < nummeas; i++) {
6709 		if (fslope[i] > fmaxslope/4.0)
6710 			slope[i] = slope[i]/fslope[i];
6711 		else
6712 			slope[i] = slope[i] * 4.0/fmaxslope;
6713 	}
6714 #endif
6715 #endif /* NEVER */
6716 
6717 	/* Locate the minumum and maximum values */
6718 	maxslope = 0.0;
6719 	minslope = 1e38;
6720 	for (i = 4; i < (nummeas-4); i++) {
6721 		double avs;
6722 
6723 		if (slope[i] > maxslope)
6724 			maxslope = slope[i];
6725 
6726 		/* Simple moving average for min comp. */
6727 		avs = 0.0;
6728 		for (j = -2; j <= 2; j++)
6729 			avs += slope[i+j];
6730 		avs /= 5.0;
6731 		if (avs < minslope)
6732 			minslope = avs;
6733 	}
6734 
6735 #ifndef NEVER		/* Good with this on */
6736 	/* Normalise the slope again */
6737 	maxslope *= 0.3;
6738 	minslope *= 3.0;
6739 	for (i = 0; i < nummeas; i++) {
6740 		slope[i] = (slope[i] - minslope) / (maxslope - minslope);
6741 		if (slope[i] < 0.0)
6742 			slope[i] = 0.0;
6743 		else if (slope[i] > 1.0)
6744 			slope[i] = 1.0;
6745 	}
6746 #endif
6747 
6748 #ifdef PATREC_DEBUG
6749 	printf("Slope filter output\n");
6750 	for (i = 0; i < nummeas; i++) {
6751 		int jj;
6752 		for (jj = 0, j = b_lo; jj < 6 && j < b_hi; jj++, j += ((b_hi-b_lo)/6)) {
6753 			double sum = 0.0;
6754 			for (k = -b_lo; k <= BW; k++)		/* Box averaging filter over bands */
6755 				sum += multimeas[i][j + k];
6756 			plot[jj][i] = sum/((2.0 * b_lo + 1.0) * maxval[j+k]);
6757 		}
6758 	}
6759 	for (i = 0; i < nummeas; i++)
6760 		plot[6][i] = (double)i;
6761 	do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas);
6762 #endif	/* PATREC_DEBUG */
6763 
6764 	free_dvector(fslope, 0, nummeas-1);
6765 	free_dmatrix(sslope, 0, nummeas-1, -1, m->nraw-1);
6766 
6767 	/* Now threshold the measurements into possible patches */
6768 	apat = 2 * nummeas;
6769 	if ((pat = (i1pro_patch *)malloc(sizeof(i1pro_patch) * apat)) == NULL) {
6770 		free_ivector(sizepop, 0, nummeas-1);
6771 		free_dvector(slope, 0, nummeas-1);
6772 		free_dvector(maxval, -1, m->nraw-1);
6773 		a1logd(p->log, 1, "i1pro: malloc of patch structures failed!\n");
6774 		return I1PRO_INT_MALLOC;
6775 	}
6776 
6777 	avglegth = 0.0;
6778 	for (npat = i = 0; i < (nummeas-1); i++) {
6779 		if (slope[i] > thresh)
6780 			continue;
6781 
6782 		/* Start of a new patch */
6783 		if (npat >= apat) {
6784 			apat *= 2;
6785 			if ((pat = (i1pro_patch *)realloc(pat, sizeof(i1pro_patch) * apat)) == NULL) {
6786 				free_ivector(sizepop, 0, nummeas-1);
6787 				free_dvector(slope, 0, nummeas-1);
6788 				free_dvector(maxval, -1, m->nraw-1);
6789 				a1logd(p->log, 1, "i1pro: reallloc of patch structures failed!\n");
6790 				return I1PRO_INT_MALLOC;
6791 			}
6792 		}
6793 		pat[npat].ss = i;
6794 		pat[npat].no = 2;
6795 		pat[npat].use = 0;
6796 		for (i++; i < (nummeas-1); i++) {
6797 			if (slope[i] > thresh)
6798 				break;
6799 			pat[npat].no++;
6800 		}
6801 
6802 		avglegth += (double) pat[npat].no;
6803 		npat++;
6804 	}
6805 	a1logd(p->log,7,"Number of patches = %d\n",npat);
6806 
6807 	/* We don't count the first and last patches, as we assume they are white leader */
6808 	if (npat < (tnpatch + 2)) {
6809 		free_ivector(sizepop, 0, nummeas-1);
6810 		free_dvector(slope, 0, nummeas-1);
6811 		free_dvector(maxval, -1, m->nraw-1);
6812 		free(pat);
6813 		a1logd(p->log,2,"Patch recog failed - unable to detect enough possible patches\n");
6814 		return I1PRO_RD_NOTENOUGHPATCHES;
6815 	} else if (npat >= (2 * tnpatch) + 2) {
6816 		free_ivector(sizepop, 0, nummeas-1);
6817 		free_dvector(slope, 0, nummeas-1);
6818 		free_dvector(maxval, -1, m->nraw-1);
6819 		free(pat);
6820 		a1logd(p->log,2,"Patch recog failed - detecting too many possible patches\n");
6821 		return I1PRO_RD_TOOMANYPATCHES;
6822 	}
6823 	avglegth /= (double)npat;
6824 
6825 	for (i = 0; i < npat; i++) {
6826 		a1logd(p->log,7,"Raw patch %d, start %d, length %d\n",i, pat[i].ss, pat[i].no);
6827 	}
6828 
6829 	/* Accumulate popularity ccount of possible patches */
6830 	for (i = 1; i < (npat-1); i++)
6831 		sizepop[pat[i].no]++;
6832 
6833 	/* Locate the median potential patch width */
6834 	for (j = 0, i = 0; i < nummeas; i++) {
6835 		j += sizepop[i];
6836 		if (j >= ((npat-2)/2))
6837 			break;
6838 	}
6839 	median = (double)i;
6840 
6841 	a1logd(p->log,7,"Median patch width %f\n",median);
6842 
6843 	/* Now decide which patches to use. */
6844 	/* Try a widening window around the median. */
6845 	for (window = 0.2, try = 0; try < 15; window *= 1.4, try++) {
6846 		int bgcount = 0, bgstart = 0;
6847 		int gcount, gstart;
6848 		double wmin = median/(1.0 + window);
6849 		double wmax = median * (1.0 + window);
6850 
6851 		a1logd(p->log,7,"Window = %f - %f\n",wmin, wmax);
6852 		/* Track which is the largest contiguous group that */
6853 		/* is within our window */
6854 		gcount = gstart = 0;
6855 		for (i = 1; i < npat; i++) {
6856 			if (i < (npat-1) && pat[i].no <= wmax) {		/* Small enough */
6857 				if (pat[i].no >= wmin) {	/* And big enough */
6858 					if (gcount == 0) {		/* Start of new group */
6859 						gcount++;
6860 						gstart = i;
6861 						a1logd(p->log,7,"Start group at %d\n",gstart);
6862 					} else {
6863 						gcount++;			/* Continuing new group */
6864 						a1logd(p->log,7,"Continue group at %d, count %d\n",gstart,gcount);
6865 					}
6866 				}
6867 			} else {	/* Too big or end of patches, end this group */
6868 				a1logd(p->log,7,"Terminating group group at %d, count %d\n",gstart,gcount);
6869 				if (gcount > bgcount) {		/* New biggest group */
6870 					bgcount = gcount;
6871 					bgstart = gstart;
6872 					a1logd(p->log,7,"New biggest\n");
6873 				}
6874 				gcount = gstart = 0;		/* End this group */
6875 			}
6876 		}
6877 		a1logd(p->log,7,"Biggest group is at %d, count %d\n",bgstart,bgcount);
6878 
6879 		if (bgcount == tnpatch) {			/* We're done */
6880 			for (i = bgstart, j = 0; i < npat && j < tnpatch; i++) {
6881 				if (pat[i].no <= wmax && pat[i].no >= wmin) {
6882 					pat[i].use = 1;
6883 					j++;
6884 					if (pat[i].no < MIN_SAMPLES) {
6885 						a1logd(p->log,7,"Too few samples\n");
6886 						free_ivector(sizepop, 0, nummeas-1);
6887 						free_dvector(slope, 0, nummeas-1);
6888 						free_dvector(maxval, -1, m->nraw-1);
6889 						free(pat);
6890 						a1logd(p->log,2,"Patch recog failed - patches sampled too sparsely\n");
6891 						return I1PRO_RD_NOTENOUGHSAMPLES;
6892 					}
6893 				}
6894 			}
6895 			break;
6896 
6897 		} else if (bgcount > tnpatch) {
6898 			a1logd(p->log,7,"Too many patches\n");
6899 			free_ivector(sizepop, 0, nummeas-1);
6900 			free_dvector(slope, 0, nummeas-1);
6901 			free_dvector(maxval, -1, m->nraw-1);
6902 			free(pat);
6903 			a1logd(p->log,2,"Patch recog failed - detected too many consistent patches\n");
6904 			return I1PRO_RD_TOOMANYPATCHES;
6905 		}
6906 	}
6907 	if (try >= 15) {
6908 		a1logd(p->log,7,"Not enough patches\n");
6909 		free_ivector(sizepop, 0, nummeas-1);
6910 		free_dvector(slope, 0, nummeas-1);
6911 		free_dvector(maxval, -1, m->nraw-1);
6912 		free(pat);
6913 		a1logd(p->log,2,"Patch recog failed - unable to find enough consistent patches\n");
6914 		return I1PRO_RD_NOTENOUGHPATCHES;
6915 	}
6916 
6917 	a1logd(p->log,7,"Got %d patches out of potential %d:\n",tnpatch, npat);
6918 	a1logd(p->log,7,"Average patch legth %f\n",avglegth);
6919 	for (i = 1; i < (npat-1); i++) {
6920 		if (pat[i].use == 0)
6921 			continue;
6922 		a1logd(p->log,7,"Patch %d, start %d, length %d:\n",i, pat[i].ss, pat[i].no, pat[i].use);
6923 	}
6924 
6925 #ifdef NEVER /* [Und] - doesn't seem as good for normal patch sizes. */
6926 	/* Now trim the patches by expanding the spacers/transitions */
6927 	for (k = 1; k < (npat-1); k++) {
6928 		int sw, xsw, trim;
6929 
6930 		if (pat[k].use == 0)
6931 			continue;
6932 
6933 printf("Patch %d @ %d len %d ->",k,pat[k].ss,pat[k].no);
6934 		/* Figure the previous spacer width */
6935 		sw = pat[k].ss - (pat[k-1].ss + pat[k-1].no);
6936 		xsw = (sw * 170 + 85)/100;			/* Expand spacer by 170% */
6937 		trim = (xsw - sw + 1)/2;			/* Move start of patch half that expansion */
6938 		pat[k].ss += trim;
6939 		pat[k].no -= trim;
6940 
6941 		/* Figure the next spacer width */
6942 		sw = pat[k+1].ss - (pat[k].ss + pat[k].no);
6943 		xsw = (sw * 170 + 85)/100;			/* Expand spacer by 170% */
6944 		trim = (xsw - sw + 1)/2;			/* Move end of patch half that expansion */
6945 		pat[k].no -= trim;
6946 
6947 		if (pat[k].no < 0)
6948 			pat[k].no = 0;
6949 printf(" @ %d len %d\n",pat[k].ss,pat[k].no);
6950 	}
6951 #else
6952 	/* Now trim the patches by shrinking their windows */
6953 	for (k = 1; k < (npat-1); k++) {
6954 		int nno, trim;
6955 
6956 		if (pat[k].use == 0)
6957 			continue;
6958 
6959 //		nno = (pat[k].no * 3 + 0)/4;		/* Trim to 75% & round down */
6960 		nno = (pat[k].no * 2 + 0)/3;		/* Trim to 66% & round down [def] */
6961 //		nno = (pat[k].no * 2 + 0)/4;		/* Trim to 50% & round down */
6962 		trim = (pat[k].no - nno + 1)/2;
6963 
6964 		pat[k].ss += trim;
6965 		pat[k].no = nno;
6966 	}
6967 #endif
6968 
6969 #ifdef PATREC_SAVETRIMMED			/* Save debugging file */
6970 	{
6971 		static int filen = 0;		/* Debug file index */
6972 		char fname[100];
6973 		FILE *fp;
6974 
6975 		sprintf(fname, "i1pro_raw_trimed_%d.csv",filen++);
6976 
6977 		if ((fp = fopen(fname, "w")) == NULL)
6978 			error("Unable to open debug output file '%'",fname);
6979 
6980 		/* Create fake "slope" value that marks patches */
6981 		for (i = 0; i < nummeas; i++)
6982 			slope[i] = 1.0;
6983 		for (k = 1; k < (npat-1); k++) {
6984 			if (pat[k].use == 0)
6985 				continue;
6986 			for (i = pat[k].ss; i < (pat[k].ss + pat[k].no); i++)
6987 				slope[i] = 0.0;
6988 		}
6989 
6990 		for (i = 0; i < nummeas; i++) {
6991 			fprintf(fp, "%f\t",slope[i]);
6992 			for (j = 0; j < m->nraw; j++)
6993 				fprintf(fp, "%f\t", multimeas[i][j]/maxval[j]);
6994 			fprintf(fp, "\n");
6995 		}
6996 		fclose(fp);
6997 	}
6998 #endif
6999 
7000 #ifdef PATREC_DEBUG
7001 	a1logd(p->log,7,"After trimming got:\n");
7002 	for (i = 1; i < (npat-1); i++) {
7003 		if (pat[i].use == 0)
7004 			continue;
7005 		printf("Patch %d, start %d, length %d:\n",i, pat[i].ss, pat[i].no, pat[i].use);
7006 	}
7007 
7008 	/* Create fake "slope" value that marks patches */
7009 	for (i = 0; i < nummeas; i++)
7010 		slope[i] = 1.0;
7011 	for (k = 1; k < (npat-1); k++) {
7012 		if (pat[k].use == 0)
7013 			continue;
7014 		for (i = pat[k].ss; i < (pat[k].ss + pat[k].no); i++)
7015 			slope[i] = 0.0;
7016 	}
7017 
7018 	printf("Trimmed output:\n");
7019 #ifdef PATREC_ALLBANDS
7020 	for (j = 0; j <= (m->nraw-9); j += 9) { 			/* Plot all the bands, 9 at a time */
7021 		for (i = 0; i < nummeas; i++) {
7022 			for (k = 0; k < 9; k++)
7023 				plot[k][i] = multimeas[i][j+k]/maxval[j+k];
7024 			plot[10][i] = (double)i;
7025 		}
7026 		printf("Bands %d - %d\n",j,j+9);
7027 		do_plot10(plot[10], slope, plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], plot[6], plot[7], plot[8], nummeas, 0);
7028 	}
7029 #else
7030 	for (i = 0; i < nummeas; i++) {
7031 		int jj;
7032 		for (jj = 0, j = b_lo; jj < 6 && j < b_hi; jj++, j += ((b_hi-b_lo)/6)) {
7033 			double sum = 0.0;
7034 			for (k = -b_lo; k <= BW; k++)		/* Box averaging filter over bands */
7035 				sum += multimeas[i][j + k];
7036 			plot[jj][i] = sum/((2.0 * b_lo + 1.0) * maxval[j+k]);
7037 		}
7038 	}
7039 	for (i = 0; i < nummeas; i++)
7040 		plot[10][i] = (double)i;
7041 	do_plot6(plot[10], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas);
7042 #endif
7043 #endif	/* PATREC_DEBUG */
7044 
7045 #ifdef PATREC_DEBUG
7046 	free_dmatrix(plot, 0, 6, 0, nummeas-1);
7047 #endif	/* PATREC_DEBUG */
7048 
7049 	/* Compute average of (aproximate) white */
7050 	white_avg = 0.0;
7051 	for (j = 1; j < (m->nraw-1); j++)
7052 		white_avg += maxval[j];
7053 	white_avg /= (m->nraw - 2.0);
7054 
7055 	/* Now process the buffer values */
7056 	for (i = 0; i < tnpatch; i++) {
7057 		for (j = 0; j < m->nraw; j++)
7058 			pavg[i][j] = 0.0;
7059 	}
7060 
7061 	for (pix = 0, k = 1; k < (npat-1); k++) {
7062 		double maxavg = -1e38;	/* Track min and max averages of readings for consistency */
7063 		double minavg = 1e38;
7064 		double avgoverth = 0.0;	/* Average over saturation threshold */
7065 		double cons;			/* Consistency */
7066 
7067 		if (pat[k].use == 0)
7068 			continue;
7069 
7070 		if (pat[k].no <= MIN_SAMPLES) {
7071 			a1logd(p->log,7,"Too few samples (%d, need %d)\n",pat[k].no,MIN_SAMPLES);
7072 			free_dvector(slope, 0, nummeas-1);
7073 			free_ivector(sizepop, 0, nummeas-1);
7074 			free_dvector(maxval, -1, m->nraw-1);
7075 			free(pat);
7076 			a1logd(p->log,2,"Patch recog failed - patches sampled too sparsely\n");
7077 			return I1PRO_RD_NOTENOUGHSAMPLES;
7078 		}
7079 
7080 		/* Measure samples that make up patch value */
7081 		for (i = pat[k].ss; i < (pat[k].ss + pat[k].no); i++) {
7082 			double measavg = 0.0;
7083 
7084 			for (j = 1; j < m->nraw-1; j++) {
7085 				double val;
7086 
7087 				val = multimeas[i][j];
7088 
7089 				if (val > highest)
7090 					highest = val;
7091 				if (val > satthresh)
7092 					avgoverth++;
7093 				measavg += val;
7094 				pavg[pix][j] += val;
7095 			}
7096 			measavg /= (m->nraw-2.0);
7097 			if (measavg < minavg)
7098 				minavg = measavg;
7099 			if (measavg > maxavg)
7100 				maxavg = measavg;
7101 
7102 			/* and the duplicated values at the end */
7103 			pavg[pix][0]   += multimeas[i][0];
7104 			pavg[pix][127] += multimeas[i][127];
7105 		}
7106 
7107 		for (j = 0; j < m->nraw; j++)
7108 			pavg[pix][j] /= (double)pat[k].no;
7109 		avgoverth /= (double)pat[k].no;
7110 
7111 		if (satthresh > 0.0 && avgoverth >= 10.0)
7112 			rv |= 2;
7113 
7114 		cons = (maxavg - minavg)/white_avg;
7115 		a1logd(p->log,7,"Patch %d: consistency = %f%%, thresh = %f%%\n",pix,100.0 * cons, 100.0 * patch_cons_thr);
7116 		if (cons > patch_cons_thr) {
7117 			a1logd(p->log,2,"Patch recog failed - patch %d is inconsistent (%f%% > %f)\n",pix,cons, patch_cons_thr);
7118 			rv |= 1;
7119 		}
7120 		pix++;
7121 	}
7122 
7123 	if (phighest != NULL)
7124 		*phighest = highest;
7125 	if (flags != NULL)
7126 		*flags = rv;
7127 
7128 	free_dvector(slope, 0, nummeas-1);
7129 	free_ivector(sizepop, 0, nummeas-1);
7130 	free_dvector(maxval, -1, m->nraw-1);
7131 	free(pat);
7132 
7133 	if (rv & 2)
7134 		a1logd(p->log,2,"Patch recog failed - some patches are saturated\n");
7135 
7136 	a1logd(p->log,2,"i1pro_extract_patches_multimeas done, sat = %s, inconsist = %s\n",
7137 	                  rv & 2 ? "true" : "false", rv & 1 ? "true" : "false");
7138 
7139 	return I1PRO_OK;
7140 }
7141 #undef BL
7142 #undef BH
7143 #undef BW
7144 
7145 
7146 /* Recognise any flashes in the readings, and */
7147 /* and average their values together as well as summing their duration. */
7148 /* Return nz on an error */
7149 /* (Doesn't extract [-1] shielded values, since they have already been used) */
7150 i1pro_code i1pro_extract_patches_flash(
7151 	i1pro *p,
7152 	int *flags,				/* return flags */
7153 	double *duration,		/* return duration */
7154 	double *pavg,			/* return patch average [-1 nraw] */
7155 	double **multimeas,		/* Array of [nummeas][-1 nraw] value to extract from */
7156 	int nummeas,			/* number of readings made */
7157 	double inttime			/* Integration time (used to compute duration) */
7158 ) {
7159 	i1proimp *m = (i1proimp *)p->m;
7160 	int i, j, k, pix;
7161 	double minval, maxval;		/* min and max input value at wavelength of maximum input */
7162 	double mean;				/* Mean of the max wavelength band */
7163 	int maxband;				/* Band of maximum value */
7164 	double thresh;				/* Level threshold */
7165 	int fsampl;					/* Index of the first sample over the threshold */
7166 	int nsampl;					/* Number of samples over the threshold */
7167 	double *aavg;				/* ambient average [-1 nraw] */
7168 	double finttime;			/* Flash integration time */
7169 	int rv = 0;
7170 #ifdef PATREC_DEBUG
7171 	double **plot;
7172 #endif
7173 
7174 	a1logd(p->log,2,"i1pro_extract_patches_flash looking for flashes in %d measurements\n",nummeas);
7175 
7176 	/* Discover the maximum input value for flash dection */
7177 	maxval = -1e6;
7178 	maxband = 0;
7179 	for (j = 0; j < m->nraw; j ++) {
7180 		for (i = 0; i < nummeas; i++) {
7181 			if (multimeas[i][j] > maxval) {
7182 				maxval = multimeas[i][j];
7183 				maxband = j;
7184 			}
7185 		}
7186 	}
7187 
7188 	if (maxval <= 0.0) {
7189 		a1logd(p->log,2,"No flashes found in measurement\n");
7190 		return I1PRO_RD_NOFLASHES;
7191 	}
7192 
7193 	minval = 1e6;
7194 	mean = 0.0;
7195 	for (i = 0; i < nummeas; i++) {
7196 		mean += multimeas[i][maxband];
7197 		if (multimeas[i][maxband] < minval)
7198 			minval = multimeas[i][maxband];
7199 	}
7200 	mean /= (double)nummeas;
7201 
7202 	/* Set the threshold at 5% from mean towards max */
7203 	thresh = (3.0 * mean + maxval)/4.0;
7204 	a1logd(p->log,7,"i1pro_extract_patches_flash band %d minval %f maxval %f, mean = %f, thresh = %f\n",maxband,minval,maxval,mean, thresh);
7205 
7206 #ifdef PATREC_DEBUG
7207 	/* Plot out 6 lots of 6 values each */
7208 	plot = dmatrixz(0, 6, 0, nummeas-1);
7209 	for (j = maxband -3; j>= 0 && j < (m->nraw-6); j += 100)		/* Do one set around max */
7210 	{
7211 		for (k = 0; k < 6; k ++) {
7212 			for (i = 0; i < nummeas; i++) {
7213 				plot[k][i] = multimeas[i][j+k]/maxval;
7214 			}
7215 		}
7216 		for (i = 0; i < nummeas; i++)
7217 			plot[6][i] = (double)i;
7218 		printf("Bands %d - %d\n",j,j+5);
7219 		do_plot6(plot[6], plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], nummeas);
7220 	}
7221 	free_dmatrix(plot,0,6,0,nummeas-1);
7222 #endif	/* PATREC_DEBUG */
7223 
7224 #ifdef PATREC_DEBUG
7225 	/* Plot just the pulses */
7226 	{
7227 		int start, end;
7228 
7229 		plot = dmatrixz(0, 6, 0, nummeas-1);
7230 
7231 		for(j = 0, start = -1, end = 0;;) {
7232 
7233 			for (start = -1, i = end; i < nummeas; i++) {
7234 				if (multimeas[i][maxband] >= thresh) {
7235 					if (start < 0)
7236 						start = i;
7237 				} else if (start >= 0) {
7238 					end = i;
7239 					break;
7240 				}
7241 			}
7242 			if (start < 0)
7243 				break;
7244 			start -= 3;
7245 			if (start < 0)
7246 				start = 0;
7247 			end += 4;
7248 			if (end > nummeas)
7249 				end = nummeas;
7250 
7251 			for (i = start; i < end; i++, j++) {
7252 				int q;
7253 
7254 				plot[6][j] = (double)j;
7255 #ifdef NEVER	/* Plot +/-3 around maxband */
7256 				for (q = 0, k = maxband -3; k < (maxband+3) && k >= 0 && k < m->nraw; k++, q++) {
7257 					plot[q][j] = multimeas[i][k]/maxval;
7258 				}
7259 #else
7260 				/* plot max of bands in 6 segments */
7261 				for (q = 0; q < 6; q++) {
7262 					int ss, ee;
7263 
7264 					plot[q][j] = -1e60;
7265 					ss = q * (m->nraw/6);
7266 					ee = (q+1) * (m->nraw/6);
7267 					for (k = ss; k < ee; k++) {
7268 						if (multimeas[i][k]/maxval > plot[q][j])
7269 							plot[q][j] = multimeas[i][k]/maxval;
7270 					}
7271 				}
7272 #endif
7273 			}
7274 		}
7275 		do_plot6(plot[6], plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], j);
7276 		free_dmatrix(plot,0,6,0,nummeas-1);
7277 	}
7278 #endif
7279 
7280 	/* Locate the first sample over the threshold, and the */
7281 	/* total number of samples in the pulses. */
7282 	fsampl = -1;
7283 	for (nsampl = i = 0; i < nummeas; i++) {
7284 		for (j = 0; j < m->nraw-1; j++) {
7285 			if (multimeas[i][j] >= thresh)
7286 				break;
7287 		}
7288 		if (j < m->nraw-1) {
7289 			if (fsampl < 0)
7290 				fsampl = i;
7291 			nsampl++;
7292 		}
7293 	}
7294 	a1logd(p->log,7,"Number of flash patches = %d\n",nsampl);
7295 	if (nsampl == 0)
7296 		return I1PRO_RD_NOFLASHES;
7297 
7298 	/* See if there are as many samples before the first flash */
7299 	if (nsampl < 6)
7300 		nsampl = 6;
7301 
7302 	/* Average nsample samples of ambient */
7303 	i = (fsampl-3-nsampl);
7304 	if (i < 0)
7305 		return I1PRO_RD_NOAMBB4FLASHES;
7306 	a1logd(p->log,7,"Ambient samples %d to %d \n",i,fsampl-3);
7307 	aavg = dvectorz(-1, m->nraw-1);
7308 	for (nsampl = 0; i < (fsampl-3); i++) {
7309 		for (j = 0; j < m->nraw-1; j++)
7310 			aavg[j] += multimeas[i][j];
7311 		nsampl++;
7312 	}
7313 
7314 	/* Integrate all the values over the threshold, */
7315 	/* and also one either side of flash */
7316 	for (j = 0; j < m->nraw-1; j++)
7317 		pavg[j] = 0.0;
7318 
7319 	for (k = 0, i = 1; i < (nummeas-1); i++) {
7320 		int sample = 0;
7321 		for (j = 0; j < m->nraw-1; j++) {
7322 			if (multimeas[i-1][j] >= thresh) {
7323 				sample = 1;
7324 				break;
7325 			}
7326 			if (multimeas[i][j] >= thresh) {
7327 				sample = 1;
7328 				break;
7329 			}
7330 			if (multimeas[i+1][j] >= thresh) {
7331 				sample = 1;
7332 				break;
7333 			}
7334 		}
7335 		if (j < m->nraw-1) {
7336 			a1logd(p->log,7,"Integrating flash sample no %d \n",i);
7337 			for (j = 0; j < m->nraw-1; j++)
7338 				pavg[j] += multimeas[i][j];
7339 			k++;
7340 		}
7341 	}
7342 	for (j = 0; j < m->nraw-1; j++)
7343 		pavg[j] = pavg[j]/(double)k - aavg[j]/(double)nsampl;
7344 
7345 	a1logd(p->log,7,"Number of flash patches integrated = %d\n",k);
7346 
7347 	finttime = inttime * (double)k;
7348 	if (duration != NULL)
7349 		*duration = finttime;
7350 
7351 	/* Convert to cd/m^2 seconds */
7352 	for (j = 0; j < m->nraw-1; j++)
7353 		pavg[j] *= finttime;
7354 
7355 	if (flags != NULL)
7356 		*flags = rv;
7357 
7358 	free_dvector(aavg, -1, m->nraw-1);
7359 
7360 	return I1PRO_OK;
7361 }
7362 
7363 
7364 /* Subtract the black level. */
7365 /* If Rev E, also adjust according to shielded cells, and linearise. */
7366 void i1pro_sub_absraw(
7367 	i1pro *p,
7368 	int nummeas,			/* Return number of readings measured */
7369 	double inttime,			/* Integration time used */
7370 	int gainmode,			/* Gain mode, 0 = normal, 1 = high */
7371 	double **absraw,		/* Source/Desination array [-1 nraw] */
7372 	double *sub				/* Black value to subtract [-1 nraw] */
7373 ) {
7374 	i1proimp *m = (i1proimp *)p->m;
7375 	double gain;
7376 	int npoly;			/* Number of linearisation coefficients */
7377 	double *polys;		/* the coeficients */
7378 	double scale;		/* Absolute scale value */
7379 	double submax = -1e6;	/* Subtraction value maximum */
7380 	int i, j;
7381 
7382 	if (gainmode) {
7383 		gain = m->highgain;
7384 		npoly = m->nlin1;
7385 		polys = m->lin1;
7386 	} else {
7387 		gain = 1.0;
7388 		npoly = m->nlin0;
7389 		polys = m->lin0;
7390 	}
7391 	scale = 1.0/(inttime * gain);	/* To scale RevE linearity */
7392 
7393 	/* Adjust black to allow for temperature change by using the */
7394 	/* shielded cell values as a reference. */
7395 	/* We use a heuristic to compute a zero based scale for adjusting the */
7396 	/* black. It's not clear why it works best this way, or how */
7397 	/* dependent on the particular instrument the magic numbers are, */
7398 	/* but it reduces the black level error from over 10% to about 0.3% */
7399 	if (p->itype == instI1Pro2) {
7400 //		double xx[NSEN_MAX], in[NSEN_MAX], res[NSEN_MAX];
7401 		double asub[NSEN_MAX];
7402 		double avgscell, zero;
7403 
7404 		/* Locate largest of black */
7405 		for (j = 0; j < m->nraw; j++) {
7406 			if (sub[j] > submax)
7407 				submax = sub[j];
7408 		}
7409 
7410 		/* Average the shielded cell value of all the readings */
7411 		avgscell = 0.0;
7412 		for (i = 0; i < nummeas; i++)
7413 			avgscell += absraw[i][-1];
7414 		avgscell /= (double)nummeas;
7415 
7416 		/* Compute scaling zero */
7417 		zero = 1.144 * 0.5 * (avgscell + sub[-1]);
7418 
7419 		/* make sure that the zero point is above any black value */
7420 		if (zero < (1.01 * avgscell))
7421 			zero = 1.01 * avgscell;
7422 		if (zero < (1.01 * sub[-1]))
7423 			zero = 1.01 * sub[-1];
7424 		if (zero < (1.01 * submax))
7425 			zero = 1.01 * submax;
7426 
7427 		a1logd(p->log,2,"Black shielded value = %f, Reading shielded value = %f\n",sub[-1], avgscell);
7428 		/* Compute the adjusted black */
7429 		/* [ Unlike the ColorMunki, using the black drift comp. for reflective */
7430 		/*   seems to be OK and even beneficial. ] */
7431 		for (j = 0; j < m->nraw; j++) {
7432 #ifdef ENABLE_BKDRIFTC
7433 # ifdef HEURISTIC_BKDRIFTC
7434 			/* heuristic scaled correction */
7435 			asub[j] = zero - (zero - sub[j]) * (zero - avgscell)/(zero - sub[-1]);
7436 # else
7437 			/* simple additive correction */
7438 #  pragma message("######### i1pro2 Simple shielded cell temperature correction! ########")
7439 			asub[j] = sub[j] + avgscell - sub[-1];
7440 # endif
7441 #else
7442 #  pragma message("######### i1pro2 No shielded cell temperature correction! ########")
7443 			asub[j] = sub[j];		/* Just use the calibration dark data */
7444 #endif
7445 		}
7446 
7447 		/* Subtract the black */
7448 		for (i = 0; i < nummeas; i++) {
7449 			for (j = 0; j < m->nraw; j++) {
7450 //				xx[j] = j, in[j] = absraw[i][j];
7451 
7452 				absraw[i][j] -= asub[j]; 		/* Subtract adjusted black */
7453 
7454 //				 res[j] = absraw[i][j] + (double)((int)(avgscell/20.0)) * 20.0;
7455 #ifdef ENABLE_NONLINCOR
7456 				/* Linearise */
7457 				{
7458 					int k;
7459 					double fval, lval;
7460 
7461 					fval = absraw[i][j] / scale;		/* Scale back to sensor value range */
7462 
7463 					for (lval = polys[npoly-1], k = npoly-2; k >= 0; k--)
7464 						lval = lval * fval + polys[k];
7465 
7466 					absraw[i][j] = scale * lval;		/* Rescale back to absolute range */
7467 				}
7468 #endif
7469 			}
7470 #ifdef PLOT_BLACK_SUBTRACT	/* Plot black adjusted levels */
7471 			printf("black = meas, red = black, green = adjuste black, blue = result\n");
7472 			do_plot6(xx, in, sub, adjsub, res, NULL, NULL, m->nraw);
7473 #endif
7474 		}
7475 
7476 	/* Rev A-D don't have shielded reference cells */
7477 	} else {
7478 
7479 		/* For each measurement */
7480 		for (i = 0; i < nummeas; i++) {
7481 			for (j = -1; j < m->nraw; j++) {
7482 				absraw[i][j] -= sub[j];
7483 			}
7484 		}
7485 	}
7486 }
7487 
7488 /* Convert an absraw array from raw wavelengths to output wavelenths */
7489 /* for a given [std res, high res] and [emis/tras, reflective] mode */
7490 void i1pro_absraw_to_abswav(
7491 	i1pro *p,
7492 	int highres,			/* 0 for std res, 1 for high res */
7493 	int refl,				/* 0 for emis/trans, 1 for reflective */
7494 	int nummeas,			/* Return number of readings measured */
7495 	double **abswav,		/* Desination array [nwav] */
7496 	double **absraw			/* Source array [-1 nraw] */
7497 ) {
7498 	i1proimp *m = (i1proimp *)p->m;
7499 	int i, j, k, cx, sx;
7500 	double *tm;			/* Temporary array */
7501 
7502 	tm = dvector(0, m->nwav[highres]-1);
7503 
7504 	/* For each measurement */
7505 	for (i = 0; i < nummeas; i++) {
7506 
7507 		/* For each output wavelength */
7508 		for (cx = j = 0; j < m->nwav[highres]; j++) {
7509 			double oval = 0.0;
7510 
7511 			/* For each matrix value */
7512 			sx = m->mtx[highres][refl].index[j];		/* Starting index */
7513 			for (k = 0; k < m->mtx[highres][refl].nocoef[j]; k++, cx++, sx++) {
7514 				oval += m->mtx[highres][refl].coef[cx] * absraw[i][sx];
7515 			}
7516 			abswav[i][j] = tm[j] = oval;
7517 		}
7518 
7519 		if (p->itype == instI1Pro2) {
7520 			/* Now apply stray light compensation */
7521 			/* For each output wavelength */
7522 			for (j = 0; j < m->nwav[highres]; j++) {
7523 				double oval = 0.0;
7524 
7525 				/* For each matrix value */
7526 				for (k = 0; k < m->nwav[highres]; k++)
7527 					oval += m->straylight[highres][j][k] * tm[k];
7528 				abswav[i][j] = oval;
7529 			}
7530 #ifdef PLOT_DEBUG
7531 			printf("Before & after stray light correction:\n");
7532 			plot_wav_2(m, highres, tm, abswav[i]);
7533 #endif /* PLOT_DEBUG */
7534 		}
7535 	}
7536 	free_dvector(tm, 0, m->nwav[highres]-1);
7537 }
7538 
7539 /* Convert an abswav array of output wavelengths to scaled output readings. */
7540 void i1pro_scale_specrd(
7541 	i1pro *p,
7542 	double **outspecrd,		/* Destination */
7543 	int numpatches,			/* Number of readings/patches */
7544 	double **inspecrd		/* Source */
7545 ) {
7546 	i1proimp *m = (i1proimp *)p->m;
7547 	i1pro_state *s = &m->ms[m->mmode];
7548 	int i, j;
7549 
7550 	/* For each measurement */
7551 	for (i = 0; i < numpatches; i++) {
7552 
7553 		/* For each output wavelength */
7554 		for (j = 0; j < m->nwav[m->highres]; j++) {
7555 			outspecrd[i][j] = inspecrd[i][j] * s->cal_factor[m->highres][j];
7556 		}
7557 	}
7558 }
7559 
7560 
7561 /* =============================================== */
7562 /* Rev E wavelength calibration                    */
7563 
7564 /*
7565 	The Rev E has a wavelength reference LED amd
7566 	stores a reference raw spectrum of it in its
7567 	calibrated state, together with an polinomial
7568 	defining the raw bin no. to wavelength conversion.
7569 
7570 	By measuring the wavelength LED and finding
7571 	the best positional match against the reference
7572 	spectrum, a CCD bin offset can be computed
7573 	to compensate for any shift in the optical or
7574 	physical alignment of spectrum against CCD.
7575 
7576 	To use the adjustment, the raw to wave subsampling
7577 	filters need to be regenerated, and to ensure that
7578 	the instrument returns readings very close to the
7579 	manufacturers driver, the same underlying filter
7580 	creation mathematics needs to be used.
7581 
7582 	The manufacturers filter weights are the accumulated
7583 	third order Lagrange polynomial weights of the
7584 	integration of a 20 nm wide triange spectrum
7585 	centered at each output wavelength, discretely
7586 	integrated between the range of the middle two points
7587 	of the Lagrange interpolator. The triangle response
7588 	being integrated has an area of exactly 1.0.
7589 
7590  */
7591 
7592 /* Invert a raw2wavlength polinomial equation. */
7593 /* Use simple Newton inversion will suffice. */
7594 static double inv_raw2wav(double *polys, int npoly, double inv) {
7595 	double outv = 560.0, lval, del = 100.0;
7596 	int i, k;
7597 
7598 	for (i = 0; i < 200 && fabs(del) > 1e-7; i++) {
7599 		for (lval = polys[npoly-1], k = npoly-2; k >= 0; k--) {
7600 			lval = lval * outv + polys[k];
7601 		}
7602 		del = (inv - lval);
7603 		outv += 0.4 * del;
7604 	}
7605 
7606 	return 128.0 - outv;
7607 }
7608 
7609 /* return the uncalibrated wavelength given a raw bin value */
7610 /* (Always uses reflective RevE wav2cal) */
7611 static double i1pro_raw2wav_uncal(i1pro *p, double raw) {
7612 	i1proimp *m = (i1proimp *)p->m;
7613 	double ov;
7614 	int k;
7615 
7616 	if (p->itype == instI1Pro2) {
7617 		raw = 128.0 - raw;		/* Quadratic expects +ve correlation */
7618 
7619 		/* Compute polinomial */
7620 		for (ov = m->wlpoly1[4-1], k = 4-2; k >= 0; k--)
7621 			ov = ov * raw + m->wlpoly1[k];
7622 	} else {
7623 		co pp;
7624 
7625 		if (m->raw2wav == NULL) {
7626 			a1loge(p->log,1,"i1pro_raw2wav_uncal called when hi-res not inited\n");
7627 			return I1PRO_INT_ASSERT;
7628 		}
7629 
7630 		pp.p[0] = raw;
7631 		m->raw2wav->interp(m->raw2wav, &pp);
7632 		ov = pp.v[0];
7633 	}
7634 
7635 	return ov;
7636 }
7637 
7638 /* return the calibrated wavelength given a raw bin value for the given mode */
7639 static double i1pro_raw2wav(i1pro *p, int refl, double raw) {
7640 	i1proimp *m = (i1proimp *)p->m;
7641 	double ov;
7642 	int k;
7643 
7644 	if (p->itype == instI1Pro2) {
7645 		i1pro_state *s = &m->ms[m->mmode];
7646 
7647 		/* Correct for CCD offset and scale back to reference */
7648 		raw = raw - s->wl_led_off + m->wl_led_ref_off;
7649 
7650 		raw = 128.0 - raw;		/* Quadratic expects +ve correlation */
7651 
7652 		/* Compute polinomial */
7653 		if (refl) {
7654 			for (ov = m->wlpoly1[4-1], k = 4-2; k >= 0; k--)
7655 				ov = ov * raw + m->wlpoly1[k];
7656 		} else {
7657 			for (ov = m->wlpoly2[4-1], k = 4-2; k >= 0; k--)
7658 				ov = ov * raw + m->wlpoly2[k];
7659 		}
7660 	} else {
7661 		co pp;
7662 
7663 		/* If not RevE there is no WL calibration */
7664 		if (m->raw2wav == NULL) {
7665 			a1loge(p->log,1,"i1pro_raw2wav_uncal called when hi-res not inited\n");
7666 			return I1PRO_INT_ASSERT;
7667 		}
7668 
7669 		pp.p[0] = raw;
7670 		m->raw2wav->interp(m->raw2wav, &pp);
7671 		ov = pp.v[0];
7672 	}
7673 
7674 	return ov;
7675 }
7676 
7677 /* Powell minimisation contxt for WL calibration */
7678 typedef struct {
7679 	double ref_max;			/* reference maximum level */
7680 	double *wl_ref;		/* Wavlength reference samples */
7681 	int wl_ref_n;		/* Number of wavelength references */
7682 	double *wl_meas;	/* Wavelength measurement samples */
7683 	int wl_meas_n;		/* Number of wavelength measurement samples */
7684 	int plot;			/* Plot each try */
7685 } wlcal_cx;
7686 
7687 /* Powell minimisation callback function */
7688 /* Parameters being optimized are magnitude, offset and scale */
7689 static double wlcal_opt1(void *vcx, double tp[]) {
7690 #ifdef PLOT_DEBUG
7691 	int pix = 0;
7692 	double xx[1024];
7693 	double y1[1024];		/* interpolate ref */
7694 	double y2[1024];		/* Measurement */
7695 	double y3[1024];		/* Error */
7696 #endif
7697 	wlcal_cx *cx = (wlcal_cx *)vcx;
7698 	double vv, rv = 0.0;
7699 	int si, i;
7700 
7701 	si = (int)tp[1];
7702 
7703 	/* i = Measurement index */
7704 	for (i = si; i < cx->wl_meas_n; i++) {
7705 
7706 		double xv;			/* offset & scaled measurement index */
7707 		int ix;				/* Lagrange base offset */
7708 		double yv;
7709 
7710 		xv = ((double)i - tp[1]);	/* fitted measurement location in reference no scale */
7711 
7712 		ix = ((int)xv) - 1;			/* Reference index of Lagrange for this xv */
7713 		if (ix < 0)
7714 			continue;
7715 		if ((ix + 4) > cx->wl_ref_n)
7716 			break;
7717 
7718 		/* Compute interpolated value of reference using Lagrange: */
7719 		yv =  cx->wl_ref[ix+0] * (xv-(ix+1)) * (xv-(ix+2)) * (xv-(ix+3))
7720 		                                             /((0.0-1.0) * (0.0-2.0) * (0.0-3.0))
7721 		   +  cx->wl_ref[ix+1] * (xv-(ix+0)) * (xv-(ix+2)) * (xv-(ix+3))
7722 		                                             /((1.0-0.0) * (1.0-2.0) * (1.0-3.0))
7723 		   +  cx->wl_ref[ix+2] * (xv-(ix+0)) * (xv-(ix+1)) * (xv-(ix+3))
7724 		                                             /((2.0-0.0) * (2.0-1.0) * (2.0-3.0))
7725 		   +  cx->wl_ref[ix+3] * (xv-(ix+0)) * (xv-(ix+1)) * (xv-(ix+2))
7726 		                                             /((3.0-0.0) * (3.0-1.0) * (3.0-2.0));
7727 		vv = yv - tp[0] * cx->wl_meas[i];
7728 
7729 		/* Weight error linearly with magnitude, to emphasise peak error */
7730 		/* rather than what's happening down in the noise */
7731 		vv = vv * vv * (yv + 1.0)/(cx->ref_max+1.0);
7732 
7733 #ifdef PLOT_DEBUG
7734 		if (cx->plot) {
7735 			xx[pix] = (double)i;
7736 			y1[pix] = yv;
7737 			y2[pix] = tp[0] * cx->wl_meas[i];
7738 //			y3[pix] = 2000.0 * (0.02 + yv/cx->ref_max);		/* Weighting */
7739 			y3[pix] = 0.5 * vv;		/* Error squared */
7740 			pix++;
7741 		}
7742 #endif
7743 		rv += vv;
7744 	}
7745 #ifdef PLOT_DEBUG
7746 	if (cx->plot) {
7747 		printf("Params %f %f -> err %f\n", tp[0], tp[1], rv);
7748 		do_plot(xx, y1, y2, y3, pix);
7749 	}
7750 #endif
7751 //printf("~1 %f %f -> %f\n", tp[0], tp[1], rv);
7752 	return rv;
7753 }
7754 
7755 #ifdef SALONEINSTLIB
7756 /* Do a rudimetrary 2d optimization that uses exaustive */
7757 /* search with hierarchical step sizes */
7758 int wloptimize(double *cparm,
7759 	double *ss,
7760 	double tol,
7761 	double (*funk)(void *fdata, double tp[]),
7762 	void *fdata
7763 ) {
7764 	double range[2][2];			/* [dim][min/max] */
7765 	double val[2];				/* Current test values */
7766 	double bfit = 1e38;			/* Current best fit values */
7767 	int dim;
7768 
7769 	for (dim = 0; dim < 2; dim++) {
7770 		range[dim][0] = cparm[dim] - ss[dim];
7771 		range[dim][1] = cparm[dim] + ss[dim];
7772 		val[dim] = cparm[dim];
7773 	}
7774 
7775 	/* Until we reach the tollerance */
7776 	for (;;) {
7777 		double mstep = 1e38;
7778 
7779 		for (dim = 0; dim < 2; dim++) {
7780 			double stepsz;
7781 			stepsz = (range[dim][1] - range[dim][0])/10.0;
7782 			if (stepsz < mstep)
7783 				mstep = stepsz;
7784 
7785 			/* Search in this dimension */
7786 			for (val[dim] = range[dim][0]; val[dim] <= range[dim][1]; val[dim] += stepsz) {
7787 				double fit;
7788 				fit = funk(fdata, val);
7789 				if (fit < bfit) {
7790 					cparm[dim] = val[dim];
7791 					bfit = fit;
7792 				}
7793 			}
7794 			val[dim] = cparm[dim];
7795 			range[dim][0] = val[dim] - stepsz;
7796 			range[dim][1] = val[dim] + stepsz;
7797 		}
7798 		if (mstep <= tol)
7799 			break;
7800 	}
7801 	return 0;
7802 }
7803 #endif /* SALONEINSTLIB */
7804 
7805 
7806 /* Given a raw measurement of the wavelength LED, */
7807 /* Compute the base offset that best fits it to the reference */
7808 i1pro_code i1pro2_match_wl_meas(i1pro *p, double *pled_off, double *wlraw) {
7809 	i1proimp *m = (i1proimp *)p->m;
7810 	i1pro_code ev = I1PRO_OK;
7811 	int i;
7812 	int rpoff, mpoff;		/* Peak offset */
7813 	int roff, moff;			/* Base index */
7814 	double lhalf, rhalf;
7815 	double fwhm;			/* Measured half width */
7816 	double rmax, mmax;
7817 	double magscale;
7818 	double led_off, off_nm;
7819 
7820 	/* Do simple match first - locate maximum */
7821 	rmax = -1e6;
7822 	rpoff = -1;
7823 	for (i = 0; i < m->wl_led_count; i++) {
7824 		if (m->wl_led_spec[i] > rmax) {
7825 			rmax = m->wl_led_spec[i];			/* Max of reference */
7826 			rpoff = i;
7827 		}
7828 	}
7829 
7830 	mmax = -1e6;
7831 	mpoff = -1;
7832 	for (i = 0; i < m->nraw; i++) {
7833 		if (wlraw[i] > mmax) {
7834 			mmax = wlraw[i];					/* Max of measurement */
7835 			mpoff = i;
7836 		}
7837 	}
7838 
7839 	if (mpoff < 0 || mpoff >= m->nraw) {
7840 		a1logd(p->log,1,"Couldn't locate WL measurement peak\n");
7841 		return I1PRO_WL_SHAPE;
7842 	}
7843 
7844 	/* Check magnitude is sufficient (not sure this is right, typically 5900 > 882) */
7845 	a1logd(p->log,2,"Measured WL level = %f, minimum needed = %f\n",mmax, m->wl_cal_min_level);
7846 	if (mmax < m->wl_cal_min_level) {
7847 		a1logd(p->log,1,"i1pro2_match_wl_meas peak magnitude too low\n");
7848 		return I1PRO_WL_TOOLOW;
7849 	}
7850 
7851 	/* Locate the half peak values */
7852 	for (i = 1; i < mpoff; i++) {
7853 		if (wlraw[i] > (mmax/2.0)) {	/* Use linear interp */
7854 			lhalf = (wlraw[i] - mmax/2.0)/(wlraw[i] - wlraw[i-1]);
7855 			lhalf = lhalf * (i-1.0) + (1.0 - lhalf) * (double)i;
7856 			break;
7857 		}
7858 	}
7859 	if (i >= mpoff) {
7860 		a1logd(p->log,1,"Couldn't locate WL left half level\n");
7861 		return I1PRO_WL_SHAPE;
7862 	}
7863 	for (; i < m->nraw; i++) {
7864 		if (wlraw[i] < (mmax/2.0)) {	/* Use linear interp */
7865 			rhalf = (mmax/2.0 - wlraw[i])/(wlraw[i-1] - wlraw[i]);
7866 			rhalf = rhalf * (i-1.0) + (1.0 - rhalf) * (double)i;
7867 			break;
7868 		}
7869 	}
7870 	if (i >= m->nraw) {
7871 		a1logd(p->log,1,"Couldn't locate WL righ half level\n");
7872 		return I1PRO_WL_SHAPE;
7873 	}
7874 	a1logd(p->log,5,"WL half levels at %f (%f nm) and %f (%f nm)\n",lhalf, i1pro_raw2wav_uncal(p, lhalf), rhalf, i1pro_raw2wav_uncal(p, rhalf));
7875 	fwhm = i1pro_raw2wav_uncal(p, lhalf) - i1pro_raw2wav_uncal(p, rhalf);
7876 	a1logd(p->log,3, "WL spectrum fwhm = %f\n",fwhm);
7877 	if (fwhm < (m->wl_cal_fwhm - m->wl_cal_fwhm_tol)
7878 	 || fwhm > (m->wl_cal_fwhm + m->wl_cal_fwhm_tol)) {
7879 		a1logd(p->log,1,"WL fwhm %f is out of range %f .. %f\n",fwhm,m->wl_cal_fwhm - m->wl_cal_fwhm_tol,m->wl_cal_fwhm + m->wl_cal_fwhm_tol);
7880 		return I1PRO_WL_SHAPE;
7881 	}
7882 
7883 	roff = m->wl_led_ref_off;		/* reference raw offset */
7884 	moff = mpoff - rpoff;			/* rough measured raw offset */
7885 
7886 	a1logd(p->log,3, "Preliminary WL peak match at ref base offset %d into measurement\n", moff);
7887 
7888 	magscale = rmax/mmax;		/* Initial scale to make them match */
7889 
7890 #ifdef PLOT_DEBUG
7891 	/* Plot the match */
7892 	{
7893 		double xx[1024];
7894 		double y1[1024];
7895 		double y2[1024];
7896 
7897 		for (i = 0; i < m->nraw; i++) {
7898 			xx[i] = (double)i;
7899 			y1[i] = 0.0;
7900 			if (i >= moff && (i - moff) < m->wl_led_count) {
7901 				y1[i] = m->wl_led_spec[i- moff];
7902 			}
7903 			y2[i] = wlraw[i] * magscale;
7904 		}
7905 		printf("Simple WL match, ref = black, meas = red:\n");
7906 		do_plot(xx, y1, y2, NULL, m->nraw);
7907 	}
7908 #endif
7909 
7910 	/* Now do a good match */
7911 	/*
7912 		Do Lagrange interpolation on the reference curve,
7913 		and use a minimizer to find the best fit (minimum weighted y error)
7914 		by optimizing the magnitude, offset and scale.
7915 	 */
7916 
7917 	{
7918 		wlcal_cx cx;
7919 		double cparm[2];		/* fit parameters */
7920 		double ss[2];			/* Search range */
7921 
7922 		cparm[0] = magscale;
7923 		ss[0] = 0.2;
7924 		cparm[1] = (double)moff;
7925 		ss[1] = 4.0;		/* == +- 12 nm */
7926 
7927 		cx.ref_max = rmax;
7928 		cx.wl_ref = m->wl_led_spec;
7929 		cx.wl_ref_n = m->wl_led_count;
7930 		cx.wl_meas = wlraw;
7931 		cx.wl_meas_n = m->nraw;
7932 //		cx.plot = 1;		/* Plot each trial */
7933 
7934 		/* We could use the scale to adjust the whole CCD range, */
7935 		/* but the manufacturers driver doesn't seem to do this, */
7936 		/* and it may be making the calibration sensitive to any */
7937 		/* changes in the WL LED spectrum shape. Instead we minimize */
7938 		/* the error weighted for the peak of the shape. */
7939 
7940 #ifdef SALONEINSTLIB
7941 		if (wloptimize(cparm, ss, 1e-7, wlcal_opt1, &cx))
7942 			a1logw(p->log,"wlcal_opt1 failed\n");
7943 #else
7944 		if (powell(NULL, 2, cparm, ss, 1e-6, 1000, wlcal_opt1, &cx, NULL, NULL))
7945 			a1logw(p->log,"wlcal_opt1 failed\n");
7946 #endif
7947 		a1logd(p->log,3,"WL best fit parameters: %f %f\n", cparm[0], cparm[1]);
7948 
7949 		led_off = cparm[1];
7950 
7951 #ifdef PLOT_DEBUG
7952 		/* Plot the final result */
7953 		printf("Best WL match, ref = black, meas = red, err = green:\n");
7954 		cx.plot = 1;
7955 		wlcal_opt1(&cx, cparm);
7956 #endif
7957 
7958 		/* If we have calibrated on the ambient cap, correct */
7959 		/* for the emissive vs. reflective raw2wav scaling factor */
7960 		if (mmax < 2500.0) {
7961 			double wlraw2 = m->wl_led_ref_off + (double)rpoff;
7962 			double raw, wlnm, wlraw1, refnm;
7963 			int k;
7964 
7965 			/* Convert from raw to wavelength using poly2 (emission) */
7966 			raw = 128.0 - wlraw2;		/* Quadratic expects +ve correlation */
7967 			for (wlnm = m->wlpoly2[4-1], k = 4-2; k >= 0; k--)
7968 				wlnm = wlnm * raw + m->wlpoly2[k];
7969 
7970 			/* Convert from wavelength to raw using poly1 (reflectance) */
7971 			wlraw1 = inv_raw2wav(m->wlpoly1, 4, wlnm);
7972 //printf("emiss raw %f -> ref raw %f\n",wlraw2, wlraw1);
7973 
7974 			/* Adjust the raw correction to account for measuring it in emissive mode */
7975 			led_off = led_off + wlraw2 - wlraw1;
7976 
7977 			/* Hmm. This is rather suspect. The difference between the white reference */
7978 			/* calibrated wavelength offset and the ambient cap one is about -0.2788 raw. */
7979 			/* This is not explained by the poly1 vs. poly2 difference at the WL LED peak */
7980 			/* at 550 nm. (see above), which amounts to about +0.026, leaving 0.2528 */
7981 			/* unexplained. It appears the CCD wavelength has a dependence on the */
7982 			/* angle that the light enters the optics ?? */
7983 
7984 			led_off += 0.2528;	/* Hack to make ambient cap correction == white tile correction */
7985 
7986 			a1logd(p->log,3,"Adjusted raw correction by %f to account for measurement using ambient cap\n",wlraw2 - wlraw1 + 0.2528);
7987 		}
7988 
7989 		/* Check that the correction is not excessive */
7990 		off_nm = i1pro_raw2wav_uncal(p, led_off) - i1pro_raw2wav_uncal(p, m->wl_led_ref_off);
7991 		a1logd(p->log,2, "Final WL offset = %f, correction %f nm\n",led_off, off_nm);
7992 		if (fabs(off_nm)> m->wl_err_max) {
7993 			a1logd(p->log,1,"Final WL correction of %f nm is too big\n",off_nm);
7994 			return I1PRO_WL_ERR2BIG;
7995 		}
7996 
7997 		/* Do a verification plot */
7998 		/* Plot the measurement against calibrated wavelength, */
7999 		/* and reference measurement verses reference wavelength */
8000 
8001 #ifdef PLOT_DEBUG
8002 		{
8003 			double xx[1024];
8004 			double y1[1024];		/* interpolate ref */
8005 			double y2[1024];		/* Measurement */
8006 			int ii;
8007 
8008 			/* i = index into measurement */
8009 			for (ii = 0, i = m->wl_led_ref_off; i < (m->wl_led_ref_off + m->wl_led_count); i++) {
8010 				double raw;
8011 				double mwl;			/* Measurment wavelength */
8012 				double rraw;		/* Reference raw value */
8013 				int ix;				/* Lagrange base offset */
8014 				int k;
8015 				double yv;
8016 
8017 				raw = (double)i;
8018 				raw = raw - led_off + m->wl_led_ref_off;
8019 				raw = 128.0 - raw;		/* Quadratic expects +ve correlation */
8020 				if (mmax < 2500.0) {
8021 					for (mwl = m->wlpoly2[4-1], k = 4-2; k >= 0; k--)
8022 						mwl = mwl * raw + m->wlpoly2[k];
8023 				} else {
8024 					for (mwl = m->wlpoly1[4-1], k = 4-2; k >= 0; k--)
8025 						mwl = mwl * raw + m->wlpoly1[k];
8026 				}
8027 				xx[ii] = mwl;
8028 				y1[ii] = cparm[0] * wlraw[i];
8029 				y2[ii] = 0.0;
8030 
8031 				/* Compute the reference index corresponding to this wavelength */
8032 				rraw = inv_raw2wav(m->wlpoly1, 4, mwl) - (double)m->wl_led_ref_off;
8033 
8034 				/* Use Lagrange to interpolate the reference level for this wavelength */
8035 				ix = ((int)rraw) - 1;	/* Reference index of Lagrange for this xv */
8036 				if (ix < 0)
8037 					continue;
8038 				if ((ix + 3) >= m->wl_led_count)
8039 					break;
8040 
8041 				/* Compute interpolated value of reference using Lagrange: */
8042 				yv =  m->wl_led_spec[ix+0] * (rraw-(ix+1)) * (rraw-(ix+2)) * (rraw-(ix+3))
8043 				                                             /((0.0-1.0) * (0.0-2.0) * (0.0-3.0))
8044 				   +  m->wl_led_spec[ix+1] * (rraw-(ix+0)) * (rraw-(ix+2)) * (rraw-(ix+3))
8045 				                                             /((1.0-0.0) * (1.0-2.0) * (1.0-3.0))
8046 				   +  m->wl_led_spec[ix+2] * (rraw-(ix+0)) * (rraw-(ix+1)) * (rraw-(ix+3))
8047 				                                             /((2.0-0.0) * (2.0-1.0) * (2.0-3.0))
8048 				   +  m->wl_led_spec[ix+3] * (rraw-(ix+0)) * (rraw-(ix+1)) * (rraw-(ix+2))
8049 				                                             /((3.0-0.0) * (3.0-1.0) * (3.0-2.0));
8050 				y2[ii] = yv;
8051 				ii++;
8052 			}
8053 			printf("Verification fit in nm:\n");
8054 			do_plot(xx, y1, y2, NULL, ii);
8055 		}
8056 #endif
8057 
8058 		if (pled_off != NULL)
8059 			*pled_off = led_off;
8060 	}
8061 
8062 	return ev;
8063 }
8064 
8065 /* Compute standard/high res. downsampling filters for the given mode */
8066 /* given the current wl_led_off, and set them as current, */
8067 /* using triangular filters of the lagrange interpolation of the */
8068 /* CCD values (i.e. the same type of filter used by the OEM driver) */
8069 /* [ Interestingly, the resulting filter shape is a bit like lanczos2, */
8070 /*   but not identical. ] */
8071 i1pro_code i1pro_compute_wav_filters(i1pro *p, int hr, int refl) {
8072 	i1proimp *m = (i1proimp *)p->m;
8073 	i1pro_state *s = &m->ms[m->mmode];
8074 	i1pro_code ev = I1PRO_OK;
8075 	double twidth;		/* Target filter width */
8076 	int six, eix;		/* raw starting index and one past end index */
8077 	int wlix;			/* current wavelenght index */
8078 	double *wlcop;		/* This wavelength base filter coefficient pointer */
8079 	double trh, trx;	/* Triangle height and triangle equation x weighting */
8080 	int i, j, k;
8081 
8082 	a1logd(p->log,2,"i1pro_compute_wav_filters called with correction %f raw\n",s->wl_led_off - m->wl_led_ref_off);
8083 
8084 	twidth = (m->wl_long[hr] - m->wl_short[hr])/(m->nwav[hr] - 1.0);	/* Filter width */
8085 
8086 	trh = 1.0/twidth;		/* Triangle height */
8087 	trx = trh/twidth;		/* Triangle equation x weighting */
8088 
8089 	/* Allocate separate space for the calibrated versions, so that the */
8090 	/* original eeprom values are preserved */
8091 	if (m->mtx_c[hr][refl].index == NULL) {
8092 
8093 		if ((m->mtx_c[hr][refl].index = (int *)calloc(m->nwav[hr], sizeof(int))) == NULL) {
8094 			a1logd(p->log,1,"i1pro: malloc ndex1 failed!\n");
8095 			return I1PRO_INT_MALLOC;
8096 		}
8097 
8098 		if ((m->mtx_c[hr][refl].nocoef = (int *)calloc(m->nwav[hr], sizeof(int))) == NULL) {
8099 			a1logd(p->log,1,"i1pro: malloc nocoef failed!\n");
8100 			return I1PRO_INT_MALLOC;
8101 		}
8102 
8103 		if ((m->mtx_c[hr][refl].coef = (double *)calloc(16 * m->nwav[hr], sizeof(double)))
8104 		                                                                        == NULL) {
8105 			a1logd(p->log,1,"i1pro: malloc coef failed!\n");
8106 			return I1PRO_INT_MALLOC;
8107 		}
8108 	}
8109 
8110 	/* For each output wavelength */
8111 	wlcop = m->mtx_c[hr][refl].coef;
8112 	for (wlix = 0; wlix < m->nwav[hr]; wlix++) {
8113 		double owl = wlix/(m->nwav[hr]-1.0) * (m->wl_long[hr] - m->wl_short[hr]) + m->wl_short[hr];
8114 		int lip;	/* Lagrange interpolation position */
8115 
8116 //		printf("Generating filter for %.1f nm width %.1f nm\n",owl, twidth);
8117 
8118 		/* The filter is based on a triangle centered at owl and extending */
8119 		/* from owl - twidth to owl + twidth. We therefore need to locate the */
8120 		/* raw values that will overlap this range */
8121 
8122 		/* Do a dumb search from high to low nm */
8123 		for (six = 0; six < m->nraw; six++) {
8124 //printf("~1 (raw2wav (six %d) %f <? (owl %f + twidth %f) %f\n",six,i1pro_raw2wav(p, refl, (double)six),owl,twidth,owl + twidth);
8125 
8126 			if (i1pro_raw2wav(p, refl, (double)six) < (owl + twidth))
8127 				break;
8128 		}
8129 
8130 		if (six < 2 || six >= m->nraw) {
8131 			a1loge(p->log,1,"i1pro: compute_wav_filters() six %d, exceeds raw range to cover output filter %.1f nm width %.1f nm\n",six, owl, twidth);
8132 			return I1PRO_INT_ASSERT;
8133 		}
8134 		eix = six;
8135 		six -= 2;		/* Outside */
8136 
8137 		for (; eix < m->nraw; eix++) {
8138 			if (i1pro_raw2wav(p, refl, (double)eix) <= (owl - twidth))
8139 				break;
8140 		}
8141 		if (eix > (m->nraw - 2) ) {
8142 			a1loge(p->log,1,"i1pro: compute_wav_filters() eix %d, exceeds raw range to cover output filter %.1f nm width %.1f nm\n",eix, owl, twidth);
8143 			return I1PRO_INT_ASSERT;
8144 		}
8145 		eix += 2;		/* Outside */
8146 
8147 //		for (j = six; j < eix; j++)
8148 //			printf("Using raw %d @ %.1f nm\n",j, i1pro_raw2wav(p, refl, (double)j));
8149 
8150 		/* Set start index for this wavelength */
8151 		m->mtx_c[hr][refl].index[wlix] = six;
8152 
8153 		/* Set number of filter coefficients */
8154 		m->mtx_c[hr][refl].nocoef[wlix] = eix - six;
8155 
8156 		if (m->mtx_c[hr][refl].nocoef[wlix] > 16) {
8157 			a1loge(p->log,1,"i1pro: compute_wav_filters() too many filter %d\n",m->mtx_c[hr][refl].nocoef[wlix]);
8158 			return I1PRO_INT_ASSERT;
8159 		}
8160 
8161 		/* Start with zero filter weightings */
8162 		for (i = 0; i < m->mtx_c[hr][refl].nocoef[wlix]; i++)
8163 			wlcop[i] = 0.0;
8164 
8165 		/* for each Lagrange interpolation position (adjacent CCD locations) */
8166 		/* create the Lagrange and then accuumulate the integral of the convolution */
8167 		/* of the overlap of the central region, with the triangle of our */
8168 		/* underlying re-sampling filter. */
8169 		/* (If we were to run out of enough source points for the Lagrange to */
8170 		/* encompas the region, then in theory we could use the Lagrange to */
8171 		/* extrapolate beyond the end from points within.) */
8172 		for (lip = six; (lip + 3) < eix; lip++) {
8173 			double rwav[4];		/* Relative wavelength of these Lagrange points */
8174 			double den[4];		/* Denominator values for points */
8175 			double num[4][4];	/* Numerator polinomial components x^3, x^2, x, 1 */
8176 			double ilow, ihigh;	/* Integration points */
8177 
8178 			/* Relative wavelengths to owl of each basis point */
8179 			for (i = 0; i < 4; i++)
8180 				rwav[i] = i1pro_raw2wav(p, refl, (double)lip + i) - owl;
8181 //			printf("\n~1 rwav = %f %f %f %f\n", rwav[0], rwav[1], rwav[2], rwav[3]);
8182 
8183 			/* Compute each basis points Lagrange denominator values */
8184 			den[0] = (rwav[0]-rwav[1]) * (rwav[0]-rwav[2]) * (rwav[0]-rwav[3]);
8185 			den[1] = (rwav[1]-rwav[0]) * (rwav[1]-rwav[2]) * (rwav[1]-rwav[3]);
8186 			den[2] = (rwav[2]-rwav[0]) * (rwav[2]-rwav[1]) * (rwav[2]-rwav[3]);
8187 			den[3] = (rwav[3]-rwav[0]) * (rwav[3]-rwav[1]) * (rwav[3]-rwav[2]);
8188 //			printf("~1 denominators = %f %f %f %f\n", den[0], den[1], den[2], den[3]);
8189 
8190 			/* Compute each basis points Langrange numerator components. */
8191 			/* We make the numerator have polinomial form, so that it is easy */
8192 			/* to compute the integral equation from it. */
8193 			num[0][0] = 1.0;
8194 			num[0][1] = -rwav[1] - rwav[2] - rwav[3];
8195 			num[0][2] = rwav[1] * rwav[2] + rwav[1] * rwav[3] + rwav[2] * rwav[3];
8196 			num[0][3] = -rwav[1] * rwav[2] * rwav[3];
8197 			num[1][0] = 1.0;
8198 			num[1][1] = -rwav[0] - rwav[2] - rwav[3];
8199 			num[1][2] = rwav[0] * rwav[2] + rwav[0] * rwav[3] + rwav[2] * rwav[3];
8200 			num[1][3] = -rwav[0] * rwav[2] * rwav[3];
8201 			num[2][0] = 1.0;
8202 			num[2][1] = -rwav[0] - rwav[1] - rwav[3];
8203 			num[2][2] = rwav[0] * rwav[1] + rwav[0] * rwav[3] + rwav[1] * rwav[3];
8204 			num[2][3] = -rwav[0] * rwav[1] * rwav[3];
8205 			num[3][0] = 1.0;
8206 			num[3][1] = -rwav[0] - rwav[1] - rwav[2];
8207 			num[3][2] = rwav[0] * rwav[1] + rwav[0] * rwav[2] + rwav[1] * rwav[2];
8208 			num[3][3] = -rwav[0] * rwav[1] * rwav[2];
8209 
8210 //			printf("~1 num %d = %f %f %f %f\n", 0, num[0][0], num[0][1], num[0][2], num[0][3]);
8211 //			printf("~1 num %d = %f %f %f %f\n", 1, num[1][0], num[1][1], num[1][2], num[1][3]);
8212 //			printf("~1 num %d = %f %f %f %f\n", 2, num[2][0], num[2][1], num[2][2], num[2][3]);
8213 //			printf("~1 num %d = %f %f %f %f\n", 3, num[3][0], num[3][1], num[3][2], num[3][3]);
8214 
8215 			/* Now compute the integral difference between the two middle points */
8216 			/* of the Lagrange over the triangle shape, and accumulate the resulting */
8217 			/* Lagrange weightings to the filter coefficients. */
8218 
8219 			/* For high and then low side of the triangle. */
8220 			for (k = 0; k < 2; k++) {
8221 
8222 				ihigh = rwav[1];
8223 				ilow = rwav[2];
8224 
8225 				/* Over just the central portion, if it overlaps the triangle. */
8226 				if ((k == 0 && ilow <= twidth && ihigh >= 0.0)		/* Portion is +ve side */
8227 				 || (k == 1 && ilow <= 0.0 && ihigh >= -twidth)) {	/* Portion is -ve side */
8228 
8229 					if (k == 0) {
8230 						if (ilow < 0.0)
8231 							ilow = 0.0;
8232 						if (ihigh > twidth)
8233 							ihigh = twidth;
8234 //						printf("~1 doing +ve triangle between %f %f\n",ilow,ihigh);
8235 					} else {
8236 						if (ilow < -twidth)
8237 							ilow = -twidth;
8238 						if (ihigh > 0.0)
8239 							ihigh = 0.0;
8240 //						printf("~1 doing -ve triangle between %f %f\n",ilow,ihigh);
8241 					}
8242 
8243 					/* For each Lagrange point */
8244 					for (i = 0; i < 4; i++) {
8245 						double xnum[5];			/* Expanded numerator components */
8246 						double nvall, nvalh;	/* Numerator low and high values */
8247 
8248 						/* Because the y value is a function of x, we need to */
8249 						/* expand the Lagrange 3rd order polinomial into */
8250 						/* a 4th order polinomial using the triangle edge equation */
8251 						/* y = trh +- trx * x */
8252 						for (j = 0; j < 4; j++)
8253 							xnum[j] = (k == 0 ? -trx : trx) * num[i][j];
8254 						xnum[j] = 0.0;
8255 						for (j = 0; j < 4; j++)
8256 							xnum[j+1] += trh * num[i][j];
8257 
8258 						/* The 4th order equation becomes a 5th order one */
8259 						/* when we convert it to an integral, ie. x^4 becomes x^5/5 etc. */
8260 						for (j = 0; j < 4; j++)
8261 							xnum[j] /= (5.0 - (double)j);	/* Integral denom. */
8262 
8263 						/* Compute ihigh integral as 5th order polynomial */
8264 						nvalh = xnum[0];
8265 						nvalh = nvalh * ihigh + xnum[1];
8266 						nvalh = nvalh * ihigh + xnum[2];
8267 						nvalh = nvalh * ihigh + xnum[3];
8268 						nvalh = nvalh * ihigh + xnum[4];
8269 						nvalh = nvalh * ihigh;
8270 
8271 						/* Compute ilow integral as 5th order polynomial */
8272 						nvall = xnum[0];
8273 						nvall = nvall * ilow + xnum[1];
8274 						nvall = nvall * ilow + xnum[2];
8275 						nvall = nvall * ilow + xnum[3];
8276 						nvall = nvall * ilow + xnum[4];
8277 						nvall = nvall * ilow;
8278 
8279 						/* Compute ihigh - ilow and add to filter weightings */
8280 						wlcop[lip -six + i] += (nvalh - nvall)/den[i];
8281 //						printf("~1 k = %d, comp %d weight += %e now %e\n",k,lip-six+i,(nvalh - nvall)/den[i], wlcop[lip-six+i]);
8282 					}
8283 				}
8284 			}
8285 		}
8286 //		printf("~1 Weightings for for %.1f nm are:\n",owl);
8287 //		for (i = 0; i < m->mtx_c[hr][refl].nocoef[wlix]; i++)
8288 //			printf("~1 comp %d weight %e\n",i,wlcop[i]);
8289 
8290 		wlcop += m->mtx_c[hr][refl].nocoef[wlix];		/* Next group of weightings */
8291 	}
8292 #ifdef DEBUG
8293 	/* Check against orginal filters */
8294 	if (!hr) {
8295 		int ix1, ix1c;
8296 		double aerr = 0.0;
8297 
8298 		a1logd(p->log,2,"Checking genertated tables against EEProm table\n");
8299 		ix1 = ix1c = 0;
8300 		for (i = 0; i < m->nwav[0]; i++) {
8301 			double err;
8302 			int six, eix;
8303 
8304 			if (m->mtx_o.index[i] < m->mtx_o.index[i])
8305 				six = m->mtx_o.index[i];
8306 			else
8307 				six = m->mtx_o.index[i];
8308 
8309 			if ((m->mtx_o.index[i] + m->mtx_o.nocoef[i]) > (m->mtx_o.index[i] + m->mtx_o.nocoef[i]))
8310 				eix = m->mtx_o.index[i] + m->mtx_o.nocoef[i];
8311 			else
8312 				eix = m->mtx_o.index[i] + m->mtx_o.nocoef[i];
8313 //			printf("~1 filter %d from %d to %d\n",i,six,eix);
8314 
8315 			err = 0.0;
8316 			for (j = six; j < eix; j++) {
8317 		 		double w1, w1c;
8318 
8319 				if (j < m->mtx_o.index[i] || j >= (m->mtx_o.index[i] + m->mtx_o.nocoef[i]))
8320 					w1 = 0.0;
8321 				else
8322 					w1 = m->mtx_o.coef[ix1 + j - m->mtx_o.index[i]];
8323 				if (j < m->mtx_c[0][refl].index[i]
8324 				 || j >= (m->mtx_c[0][refl].index[i] + m->mtx_c[0][refl].nocoef[i]))
8325 					w1c = 0.0;
8326 				else
8327 					w1c = m->mtx_c[0][refl].coef[ix1c + j - m->mtx_c[0][refl].index[i]];
8328 
8329 				err += fabs(w1 - w1c);
8330 //				printf("Weight %d, %e should be %e\n", j, w1c, w1);
8331 			}
8332 //			printf("Filter %d average weighting error = %f\n",i, err/j);
8333 			aerr += err/j;
8334 
8335 			ix1 += m->mtx_o.nocoef[i];
8336 			ix1c += m->mtx_c[0][refl].nocoef[i];
8337 		}
8338 		a1logd(p->log,2,"Overall average filter weighting change = %f\n",aerr/m->nwav[0]);
8339 	}
8340 #endif /* DEBUG */
8341 
8342 	/* Switch normal res. to use wavelength calibrated version */
8343 	m->mtx[hr][refl] = m->mtx_c[hr][refl];
8344 
8345 	return ev;
8346 }
8347 
8348 
8349 /* =============================================== */
8350 #ifdef HIGH_RES
8351 
8352 /*
8353 	It turns out that using the sharpest possible resampling filter
8354 	may make accuracy worse (particularly on the RevE), because it
8355 	enhances bumps in the raw response that mightn't be there
8356 	after calibrating for the instrument spectral sensitivity.
8357 	A better scheme (which we could sythesise using the hi-res
8358 	emissive calibration logic) would be to calibrate the raw CCD
8359 	values and then resample with possible sharpening.
8360 	Another approach would be to sharpen after filtering with
8361 	non-sharpening resampling filters.
8362 	The bottom line is that it's best to use a gausian hi-res
8363 	filter to avoid sharpening in non calibrated spectral space.
8364  */
8365 
8366 /* High res congiguration */
8367 /* Pick one of these: */
8368 #undef USE_TRI_LAGRANGE		/* [und] Use OEM/normal res. filter shape for HiRes */
8369 #undef USE_LANCZOS2			/* [und] Use lanczos2 filter shape */
8370 #undef USE_LANCZOS3			/* [und] Use lanczos3 filter shape */
8371 #undef USE_DECONV			/* [und] Use deconvolution curve */
8372 #undef USE_BLACKMAN			/* [und] Use Blackman windowed sinc shape */
8373 #define USE_GAUSSIAN		/* [def] Use gaussian filter shape*/
8374 #undef USE_CUBIC			/* [und] Use cubic spline filter */
8375 
8376 #define DO_CCDNORM			/* [def] Normalise CCD values to original */
8377 #define DO_CCDNORMAVG		/* [und ???] Normalise averages rather than per CCD bin */
8378 							/* (We relly on fine cal & white cal to fix it) */
8379 
8380 #define BOX_INTEGRATE    	/* [und] Integrate raw samples as if they were +/-0.5 boxes */
8381 							/*       (This improves coeficient consistency a bit ?) */
8382 #undef COMPUTE_DISPERSION	/* Compute slit & optics dispersion from red laser data */
8383 
8384 #ifdef NEVER
8385 /* Plot the matrix coefficients */
8386 static void i1pro_debug_plot_mtx_coef(i1pro *p) {
8387 	i1proimp *m = (i1proimp *)p->m;
8388 	int i, j, k, cx, sx;
8389 	double *xx, *ss;
8390 	double **yy;
8391 
8392 	xx = dvectorz(-1, m->nraw-1);		/* X index */
8393 	yy = dmatrixz(0, 5, -1, m->nraw-1);	/* Curves distributed amongst 5 graphs */
8394 
8395 	for (i = 0; i < m->nraw; i++)
8396 		xx[i] = i;
8397 
8398 	/* For each output wavelength */
8399 	for (cx = j = 0; j < m->nwav; j++) {
8400 		i = j % 5;
8401 
8402 //		printf("Out wave = %d\n",j);
8403 		/* For each matrix value */
8404 		sx = m->mtx_index[j];		/* Starting index */
8405 //		printf("start index = %d, nocoef %d\n",sx,m->mtx_nocoef[j]);
8406 		for (k = 0; k < m->mtx_nocoef[j]; k++, cx++, sx++) {
8407 //			printf("offset %d, coef ix %d val %f from ccd %d\n",k, cx, m->mtx_coef[cx], sx);
8408 			yy[5][sx] += 0.5 * m->mtx_coef[cx];
8409 			yy[i][sx] = m->mtx_coef[cx];
8410 		}
8411 	}
8412 
8413 	do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
8414 	free_dvector(xx, -1, m->nraw-1);
8415 	free_dmatrix(yy, 0, 5, -1, m->nraw-1);
8416 }
8417 #endif	/* NEVER */
8418 
8419 #ifdef COMPUTE_DISPERSION
8420 
8421 /* Gausian filter implementation */
8422 /* parameters are amplidude [0], center wavelength [1], std. dev. [2] */
8423 static double gaussf(double tp[], double x) {
8424 	double y;
8425 
8426 	x = (x - tp[1])/(sqrt(2.0) * tp[2]);
8427 	y = tp[0] * exp(-(x * x));
8428 
8429 	return y;
8430 }
8431 
8432 /* Gausian integral implementatation */
8433 /* parameters are amplidude [0], center wavelength [1], std. dev. [2] */
8434 /* return an aproximation to the intergral between w1 and w2 */
8435 static double gaussint(double tp[], double w1, double w2) {
8436 	int j, nn;
8437 	double lw, ll, vv;
8438 
8439 	/* Intergate in 0.1 nm increments */
8440 	nn = (int)(fabs(w2 - w1)/0.1 + 0.5);
8441 
8442 	lw = w1;
8443 	ll = gaussf(tp, lw);
8444 	vv = 0.0;
8445 	for (j = 0; j < nn; j++) {
8446 		double cw, cl;
8447 		cw = w1 + (j+1)/(nn +1.0) * (w2 - w1);
8448 		cl = gaussf(tp, cw);
8449 		vv += 0.5 * (cl + ll) * (lw - cw);
8450 		ll = cl;
8451 		lw = cw;
8452 	}
8453 	return fabs(vv);
8454 }
8455 
8456 /* Powell minimisation context */
8457 typedef struct {
8458 	double nsp;			/* Number of samples of dispersion data */
8459 	double *llv;		/* [nsamp] laser values */
8460 	double *lwl;		/* [nsamp+1] CCD boundary wavelegths */
8461 } hropt_cx;
8462 
8463 /* Powell minimisation callback function */
8464 /* to match dispersion data */
8465 static double hropt_opt1(void *vcx, double tp[]) {
8466 	hropt_cx *cx = (hropt_cx *)vcx;
8467 	double rv = 0.0;
8468 	int i, j;
8469 
8470 	/* For each CCD sample */
8471 	for (i = 0; i < cx->nsp; i++) {
8472 		double vv;
8473 
8474 		/* Actual CCD integrated value */
8475 		vv = cx->llv[i] * (cx->lwl[i] - cx->lwl[i+1]);
8476 		/* Computed intergral with current curve */
8477 		vv -= gaussint(tp, cx->lwl[i], cx->lwl[i+1]);
8478 		rv += vv * vv;
8479 	}
8480 //	printf("~1 params %f %f %f, rv = %f\n", tp[0],tp[1],tp[2],rv);
8481 	return rv;
8482 }
8483 
8484 #endif /* COMPUTE_DISPERSION */
8485 
8486 /* Filter shape point */
8487 typedef	struct {
8488 	double wl, we;
8489 } i1pro_fs;
8490 
8491 /* Filter cooeficient values */
8492 typedef struct {
8493 	int ix;				/* Raw index */
8494 	double we;			/* Weighting */
8495 } i1pro_fc;
8496 
8497 /* Wavelenth calibration crossover point information */
8498 typedef struct {
8499 	double wav;				/* Wavelegth of point */
8500 	double raw;				/* Raw index of point */
8501 	double wei;				/* Weigting of the point */
8502 } i1pro_xp;
8503 
8504 /* Linearly interpolate the filter shape */
8505 static double lin_fshape(i1pro_fs *fsh, int n, double x) {
8506 	int i;
8507 	double y;
8508 
8509 	if (x <= fsh[0].wl)
8510 		return fsh[0].we;
8511 	else if (x >= fsh[n-1].wl)
8512 		return fsh[n-1].we;
8513 
8514 	for (i = 0; i < (n-2); i++)
8515 		if (x >= fsh[i].wl && x <= fsh[i+1].wl)
8516 			break;
8517 
8518 	x = (x - fsh[i].wl)/(fsh[i+1].wl - fsh[i].wl);
8519 	y = fsh[i].we + (fsh[i+1].we - fsh[i].we) * x;
8520 
8521 	return y;
8522 }
8523 
8524 /* Generate a sample from a lanczos2 filter shape */
8525 /* wi is the width of the filter */
8526 static double lanczos2(double wi, double x) {
8527 	double y = 0.0;
8528 
8529 #ifdef USE_DECONV
8530 	/* For 3.333, created by i1deconv.c */
8531 	static i1pro_fs fshape[49] = {
8532 		{ -7.200000, 0.0 },
8533 		{ -6.900000, 0.013546 },
8534 		{ -6.600000, 0.035563 },
8535 		{ -6.300000, 0.070500 },
8536 		{ -6.000000, 0.106543 },
8537 		{ -5.700000, 0.148088 },
8538 		{ -5.400000, 0.180888 },
8539 		{ -5.100000, 0.186637 },
8540 		{ -4.800000, 0.141795 },
8541 		{ -4.500000, 0.046101 },
8542 		{ -4.200000, -0.089335 },
8543 		{ -3.900000, -0.244652 },
8544 		{ -3.600000, -0.391910 },
8545 		{ -3.300000, -0.510480 },
8546 		{ -3.000000, -0.573177 },
8547 		{ -2.700000, -0.569256 },
8548 		{ -2.400000, -0.489404 },
8549 		{ -2.100000, -0.333957 },
8550 		{ -1.800000, -0.116832 },
8551 		{ -1.500000, 0.142177 },
8552 		{ -1.200000, 0.411639 },
8553 		{ -0.900000, 0.658382 },
8554 		{ -0.600000, 0.851521 },
8555 		{ -0.300000, 0.967139 },
8556 		{ 0.000000, 1.000000 },
8557 		{ 0.300000, 0.967139 },
8558 		{ 0.600000, 0.851521 },
8559 		{ 0.900000, 0.658382 },
8560 		{ 1.200000, 0.411639 },
8561 		{ 1.500000, 0.142177 },
8562 		{ 1.800000, -0.116832 },
8563 		{ 2.100000, -0.333957 },
8564 		{ 2.400000, -0.489404 },
8565 		{ 2.700000, -0.569256 },
8566 		{ 3.000000, -0.573177 },
8567 		{ 3.300000, -0.510480 },
8568 		{ 3.600000, -0.391910 },
8569 		{ 3.900000, -0.244652 },
8570 		{ 4.200000, -0.089335 },
8571 		{ 4.500000, 0.046101 },
8572 		{ 4.800000, 0.141795 },
8573 		{ 5.100000, 0.186637 },
8574 		{ 5.400000, 0.180888 },
8575 		{ 5.700000, 0.148088 },
8576 		{ 6.000000, 0.106543 },
8577 		{ 6.300000, 0.070500 },
8578 		{ 6.600000, 0.035563 },
8579 		{ 6.900000, 0.013546 },
8580 		{ 7.200000, 0.0 }
8581 	};
8582 
8583 	return lin_fshape(fshape, 49, x);
8584 #endif
8585 
8586 #ifdef USE_GAUSSIAN
8587 	/* gausian */
8588 	wi = wi/(sqrt(2.0 * log(2.0)));	/* Convert width at half max to std. dev. */
8589     x = x/wi;
8590 //    y = 1.0/(wi * sqrt(2.0 * DBL_PI)) * exp(-(x * x));		/* Unity area */
8591     y = exp(-(x * x));											/* Center at 1.0 */
8592 #endif
8593 
8594 #ifdef USE_LANCZOS2
8595 	/* lanczos2 */
8596 	wi *= 1.05;			// Improves smoothness. Why ?
8597 	x = fabs(1.0 * x/wi);
8598 	if (x >= 2.0)
8599 		return 0.0;
8600 	if (x < 1e-6)
8601 		return 1.0;
8602 	y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/2.0)/(DBL_PI * x/2.0);
8603 #endif
8604 
8605 #ifdef USE_LANCZOS3
8606 	/* lanczos3 */
8607 	x = fabs(1.0 * x/wi);
8608 	if (x >= 3.0)
8609 		return 0.0;
8610 	if (x < 1e-6)
8611 		return 1.0;
8612 	y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/3.0)/(DBL_PI * x/3.0);
8613 #endif
8614 
8615 #ifdef USE_BLACKMAN		/* Use Blackman windowed sinc shape */
8616 	double xx = x, w;
8617 	double a0, a1, a2, a3;
8618 	double bb, cc;
8619 
8620 	xx = fabs(1.0 * x/wi);
8621 	if (xx >= 2.0)
8622 		return 0.0;
8623 	if (xx < 1e-5)
8624 		return 1.0;
8625 	y = sin(DBL_PI * xx)/(DBL_PI * xx);	/* sinc */
8626 
8627 	/* gausian window */
8628 //	wi *= 1.5;
8629 //	wi = wi/(2.0 * sqrt(2.0 * log(2.0)));	/* Convert width at half max to std. dev. */
8630 //	x = x/(sqrt(2.0) * wi);
8631 //	w = exp(-(x * x));
8632 
8633 	xx = (xx/4.0 + 0.5);		/* Convert to standard window cos() range */
8634 
8635 	/* Hamming window */
8636 //	a0 = 0.54; a1 = 0.46;
8637 //	w = a0 - a1 * cos(2.0 * DBL_PI * xx);
8638 
8639 	/* Blackman window */
8640 	a0 = 7938.0/18608.0; a1 = 9240.0/18608.0; a2 = 1430.0/18608.0;
8641 	w = a0 - a1 * cos(2.0 * DBL_PI * xx) + a2 * cos(4.0 * DBL_PI * xx);
8642 
8643 	/* Nuttall window */
8644 //	a0 = 0.355768; a1=0.487396; a2=0.144232; a3=0.012604;
8645 //	w = a0 - a1 * cos(2.0 * DBL_PI * xx) + a2 * cos(4.0 * DBL_PI * xx) - a3 * cos(6.0 * DBL_PI * xx);
8646 
8647 	/* Blackman Harris window */
8648 //	a0=0.35875; a1=0.48829; a2=0.14128; a3=0.01168;
8649 //	w = a0 - a1 * cos(2.0 * DBL_PI * xx) + a2 * cos(4.0 * DBL_PI * xx) - a3 * cos(6.0 * DBL_PI * xx);
8650 
8651 	/* Blackman Nuttall window */
8652 //	a0=0.3635819; a1=0.4891775; a2=0.1365995; a3=0.0106411;
8653 //	w = a0 - a1 * cos(2.0 * DBL_PI * xx) + a2 * cos(4.0 * DBL_PI * xx) - a3 * cos(6.0 * DBL_PI * xx);
8654 
8655 	y *= w;
8656 #endif
8657 #ifdef USE_CUBIC		/* Use cubic sline */
8658 	double xx = x;
8659 	double bb, cc;
8660 
8661 	xx = fabs(1.0 * x/wi);
8662 
8663 //	bb = cc = 1.0/3.0;		/* Mitchell */
8664 	bb = 0.5;
8665 	cc = 0.5;
8666 
8667 	if (xx < 1.0) {
8668 		y = ( 12.0 -  9.0 * bb - 6.0 * cc) * xx * xx * xx
8669 		  + (-18.0 + 12.0 * bb + 6.0 * cc) * xx * xx
8670 		  + (  6.0 -  2.0 * bb);
8671 		y /= (6.0 - 2.0 * bb);
8672 	} else if (xx < 2.0) {
8673 		y = ( -1.0 * bb -  6.0 * cc) * xx * xx * xx
8674 		  + (  6.0 * bb + 30.0 * cc) * xx * xx
8675 		  + (-12.0 * bb - 48.0 * cc) * xx
8676 		  + (  8.0 * bb + 24.0 * cc);
8677 		y /= (6.0 - 2.0 * bb);
8678 	} else {
8679 		y = 0.0;
8680 	}
8681 #endif
8682 	return y;
8683 }
8684 
8685 #if defined(__APPLE__) && defined(__POWERPC__)
8686 
8687 /* Workaround for a ppc gcc 3.3 optimiser bug... */
8688 static int gcc_bug_fix(int i) {
8689 	static int nn;
8690 	nn += i;
8691 	return nn;
8692 }
8693 #endif	/* APPLE */
8694 
8695 /* Re-create calibration factors for hi-res */
8696 /* Set emisonly to only recompute emissive factors */
8697 i1pro_code i1pro_create_hr_calfactors(i1pro *p, int eonly) {
8698 	i1proimp *m = (i1proimp *)p->m;
8699 	i1pro_code ev = I1PRO_OK;
8700 	int i, j;
8701 
8702 	/* Generate high res. per mode calibration factors. */
8703 	if (m->hr_inited) {
8704 
8705 		for (i = 0; i < i1p_no_modes; i++) {
8706 			i1pro_state *s = &m->ms[i];
8707 
8708 			if (s->cal_factor[1] == NULL)
8709 				s->cal_factor[1] = dvectorz(0, m->nwav[1]-1);
8710 
8711 			switch(i) {
8712 				case i1p_refl_spot:
8713 				case i1p_refl_scan:
8714 					if (eonly)
8715 						continue;
8716 					if (s->cal_valid) {
8717 						/* (Using cal_factor[] as temp. for i1pro_absraw_to_abswav()) */
8718 #ifdef NEVER
8719 						printf("~1 regenerating calibration for reflection\n");
8720 						printf("~1 raw white data:\n");
8721 						plot_raw(s->white_data);
8722 #endif	/* NEVER */
8723 						i1pro_absraw_to_abswav(p, 0, s->reflective, 1, &s->cal_factor[0], &s->white_data);
8724 #ifdef NEVER
8725 						printf("~1 Std res intmd. cal_factor:\n");
8726 						plot_wav(m, 0, s->cal_factor[0]);
8727 #endif	/* NEVER */
8728 						i1pro_absraw_to_abswav(p, 1, s->reflective, 1, &s->cal_factor[1], &s->white_data);
8729 #ifdef NEVER
8730 						printf("~1 High intmd. cal_factor:\n");
8731 						plot_wav(m, 1, s->cal_factor[1]);
8732 						printf("~1 Std res white ref:\n");
8733 						plot_wav(m, 0, m->white_ref[0]);
8734 						printf("~1 High res white ref:\n");
8735 						plot_wav(m, 1, m->white_ref[1]);
8736 #endif	/* NEVER */
8737 						ev = i1pro_compute_white_cal(p,
8738 						                   s->cal_factor[0], m->white_ref[0], s->cal_factor[0],
8739 						                   s->cal_factor[1], m->white_ref[1], s->cal_factor[1],
8740 						                   i == i1p_refl_spot);
8741 						if (ev == I1PRO_RD_TRANSWHITEWARN)		/* Shouldn't happen ? */
8742 							ev = I1PRO_OK;
8743 						if (ev != I1PRO_OK) {
8744 							return ev;
8745 						}
8746 #ifdef NEVER
8747 						printf("~1 Std res final cal_factor:\n");
8748 						plot_wav(m, 0, s->cal_factor[0]);
8749 						printf("~1 High final cal_factor:\n");
8750 						plot_wav(m, 1, s->cal_factor[1]);
8751 #endif	/* NEVER */
8752 					}
8753 					break;
8754 
8755 				case i1p_emiss_spot_na:
8756 				case i1p_emiss_spot:
8757 				case i1p_emiss_scan:
8758 					for (j = 0; j < m->nwav[1]; j++)
8759 						s->cal_factor[1][j] = EMIS_SCALE_FACTOR * m->emis_coef[1][j];
8760 					break;
8761 
8762 				case i1p_amb_spot:
8763 				case i1p_amb_flash:
8764 #ifdef FAKE_AMBIENT
8765 					for (j = 0; j < m->nwav[1]; j++)
8766 						s->cal_factor[1][j] = EMIS_SCALE_FACTOR * m->emis_coef[1][j];
8767 					s->cal_valid = 1;
8768 #else
8769 
8770 					if (m->amb_coef[0] != NULL) {
8771 						for (j = 0; j < m->nwav[1]; j++)
8772 							s->cal_factor[1][j] = AMB_SCALE_FACTOR * m->emis_coef[1][j] * m->amb_coef[1][j];
8773 						s->cal_valid = 1;
8774 					}
8775 #endif
8776 					break;
8777 				case i1p_trans_spot:
8778 				case i1p_trans_scan:
8779 					if (eonly)
8780 						continue;
8781 					if (s->cal_valid) {
8782 						/* (Using cal_factor[] as temp. for i1pro_absraw_to_abswav()) */
8783 						i1pro_absraw_to_abswav(p, 0, s->reflective, 1, &s->cal_factor[0], &s->white_data);
8784 						i1pro_absraw_to_abswav(p, 1, s->reflective, 1, &s->cal_factor[1], &s->white_data);
8785 						ev = i1pro_compute_white_cal(p, s->cal_factor[0], NULL, s->cal_factor[0],
8786 			                                            s->cal_factor[1], NULL, s->cal_factor[1], 0);
8787 						if (ev == I1PRO_RD_TRANSWHITEWARN)		/* Ignore this ? */
8788 							ev = I1PRO_OK;
8789 						if (ev != I1PRO_OK) {
8790 							return ev;
8791 						}
8792 					}
8793 					break;
8794 			}
8795 		}
8796 	}
8797 	return ev;
8798 }
8799 
8800 #ifdef SALONEINSTLIB
8801 # define ONEDSTRAYLIGHTUS
8802 #endif
8803 
8804 /* Create or re-create high resolution mode references */
8805 i1pro_code i1pro_create_hr(i1pro *p) {
8806 	i1proimp *m = (i1proimp *)p->m;
8807 	i1pro_code ev = I1PRO_OK;
8808 	int refl;
8809 	double twidth = HIGHRES_WIDTH;
8810 	int i, j, k, cx, sx;
8811 
8812 	/* If we don't have any way of converting raw2wav (ie. RevE polinomial equations), */
8813 	/* use the orginal filters to figure this out. */
8814 	if (m->raw2wav == NULL
8815 #ifndef ANALIZE_EXISTING
8816 	 && p->itype != instI1Pro2
8817 #endif
8818 	) {
8819 		i1pro_fc coeff[100][16];	/* Existing filter cooefficients */
8820 		i1pro_xp xp[101];			/* Crossover points each side of filter */
8821 		i1pro_fs fshape[100 * 16];  /* Existing filter shape */
8822 		int ncp = 0;				/* Number of shape points */
8823 
8824 		/* Convert the native filter cooeficient representation to */
8825 		/* a 2D array we can randomly index. */
8826 		for (cx = j = 0; j < m->nwav[0]; j++) { /* For each output wavelength */
8827 			if (j >= 100) {	/* Assert */
8828 				a1loge(p->log,1,"i1pro: number of output wavelenths is > 100\n");
8829 				return I1PRO_INT_ASSERT;
8830 			}
8831 
8832 			/* For each matrix value */
8833 			sx = m->mtx_o.index[j];		/* Starting index */
8834 			for (k = 0; k < m->mtx_o.nocoef[j]; k++, cx++, sx++) {
8835 				if (k >= 16) {	/* Assert */
8836 					a1loge(p->log,1,"i1pro: number of filter coeefs is > 16\n");
8837 					return I1PRO_INT_ASSERT;
8838 				}
8839 
8840 				coeff[j][k].ix = sx;
8841 				coeff[j][k].we = m->mtx_o.coef[cx];
8842 //				printf("Output %d, filter %d weight = %e\n",j,k,coeff[j][k].we);
8843 			}
8844 		}
8845 
8846 #ifdef HIGH_RES_PLOT
8847 		/* Plot original re-sampling curves */
8848 		{
8849 			double *xx, *ss;
8850 			double **yy;
8851 
8852 			xx = dvectorz(-1, m->nraw-1);		/* X index */
8853 			yy = dmatrixz(0, 5, -1, m->nraw-1);	/* Curves distributed amongst 5 graphs */
8854 
8855 			for (i = 0; i < m->nraw; i++)
8856 				xx[i] = i;
8857 
8858 			/* For each output wavelength */
8859 			for (j = 0; j < m->nwav[0]; j++) {
8860 				i = j % 5;
8861 
8862 				/* For each matrix value */
8863 				for (k = 0; k < m->mtx_o.nocoef[j]; k++) {
8864 					yy[5][coeff[j][k].ix] += 0.5 * coeff[j][k].we;
8865 					yy[i][coeff[j][k].ix] = coeff[j][k].we;
8866 				}
8867 			}
8868 
8869 			printf("Original wavelength sampling curves:\n");
8870 			do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
8871 			free_dvector(xx, -1, m->nraw-1);
8872 			free_dmatrix(yy, 0, 2, -1, m->nraw-1);
8873 		}
8874 #endif /* HIGH_RES_PLOT */
8875 
8876 //		a1logd(p->log,3,"computing crossover points\n");
8877 		/* Compute the crossover points between each filter */
8878 		for (i = 0; i < (m->nwav[0]-1); i++) {
8879 			double den, y1, y2, y3, y4, yn, xn;	/* Location of intersection */
8880 			double eps = 1e-6;			/* Numerical tollerance */
8881 			double besty = -1e6;
8882 
8883 			/* between filter i and i+1, we want to find the two */
8884 			/* raw indexes where the weighting values cross over */
8885 			/* Do a brute force search to avoid making assumptions */
8886 			/* about the raw order. */
8887 			for (j = 0; j < (m->mtx_o.nocoef[i]-1); j++) {
8888 				for (k = 0; k < (m->mtx_o.nocoef[i+1]-1); k++) {
8889 					if (coeff[i][j].ix == coeff[i+1][k].ix
8890 					 && coeff[i][j+1].ix == coeff[i+1][k+1].ix) {
8891 
8892 //						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);
8893 
8894 						/* Compute the intersection of the two line segments */
8895 						y1 = coeff[i][j].we;
8896 						y2 = coeff[i][j+1].we;
8897 						y3 = coeff[i+1][k].we;
8898 						y4 = coeff[i+1][k+1].we;
8899 //						a1logd(p->log,3,"y1 %f, y2 %f, y3 %f, y4 %f\n",y1, y2, y3, y4);
8900 						den = -y4 + y3 + y2 - y1;
8901 						if (fabs(den) < eps)
8902 							continue;
8903 						yn = (y2 * y3 - y1 * y4)/den;
8904 						xn = (y3 - y1)/den;
8905 						if (xn < -eps || xn > (1.0 + eps))
8906 							continue;
8907 //						a1logd(p->log,3,"den = %f, yn = %f, xn = %f\n",den,yn,xn);
8908 						if (yn > besty) {
8909 							xp[i+1].wav = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i + 0.5);
8910 							xp[i+1].raw = (1.0 - xn) * coeff[i][j].ix + xn * coeff[i][j+1].ix;
8911 							xp[i+1].wei = yn;
8912 							besty = yn;
8913 //						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);
8914 //						a1logd(p->log,3,"Found new best y %f\n",yn);
8915 						}
8916 //					a1logd(p->log,3,"\n");
8917 					}
8918 				}
8919 			}
8920 			if (besty < 0.0) {	/* Assert */
8921 				a1logw(p->log,"i1pro: failed to locate crossover between resampling filters\n");
8922 				return I1PRO_INT_ASSERT;
8923 			}
8924 //			a1logd(p->log,3,"\n");
8925 		}
8926 
8927 		/* Add the two points for the end filters */
8928 		{
8929 			double x5, x6, y5, y6;				/* Points on intesecting line */
8930 			double den, y1, y2, y3, y4, yn, xn;	/* Location of intersection */
8931 
8932 			x5 = xp[1].raw;
8933 			y5 = xp[1].wei;
8934 			x6 = xp[2].raw;
8935 			y6 = xp[2].wei;
8936 
8937 			/* Search for possible intersection point with first curve */
8938 			/* Create equation for line from next two intersection points */
8939 			for (j = 0; j < (m->mtx_o.nocoef[0]-1); j++) {
8940 				/* Extrapolate line to this segment */
8941 				y3 = y5 + (coeff[0][j].ix - x5)/(x6 - x5) * (y6 - y5);
8942 				y4 = y5 + (coeff[0][j+1].ix - x5)/(x6 - x5) * (y6 - y5);
8943 				/* This segment of curve */
8944 				y1 = coeff[0][j].we;
8945 				y2 = coeff[0][j+1].we;
8946 				if ( ((  y1 >= y3 && y2 <= y4)		/* Segments overlap */
8947 				   || (  y1 <= y3 && y2 >= y4))
8948 				  && ((    coeff[0][j].ix < x5 && coeff[0][j].ix < x6
8949 				        && coeff[0][j+1].ix < x5 && coeff[0][j+1].ix < x6)
8950 				   || (    coeff[0][j+1].ix > x5 && coeff[0][j+1].ix > x6
8951 					    && coeff[0][j].ix > x5 && coeff[0][j].ix > x6))) {
8952 					break;
8953 				}
8954 			}
8955 			if (j >= m->mtx_o.nocoef[0]) {	/* Assert */
8956 				a1loge(p->log,1,"i1pro: failed to find end crossover\n");
8957 				return I1PRO_INT_ASSERT;
8958 			}
8959 
8960 			den = -y4 + y3 + y2 - y1;
8961 			yn = (y2 * y3 - y1 * y4)/den;
8962 			xn = (y3 - y1)/den;
8963 //			printf("~1 den = %f, yn = %f, xn = %f\n",den,yn,xn);
8964 			xp[0].wav = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], -0.5);
8965 			xp[0].raw = (1.0 - xn) * coeff[0][j].ix + xn * coeff[0][j+1].ix;
8966 			xp[0].wei = yn;
8967 //			printf("End 0 intersection %d: wav %f, raw %f, wei %f\n",0,xp[0].wav,xp[0].raw,xp[0].wei);
8968 //			printf("\n");
8969 
8970 			x5 = xp[m->nwav[0]-2].raw;
8971 			y5 = xp[m->nwav[0]-2].wei;
8972 			x6 = xp[m->nwav[0]-1].raw;
8973 			y6 = xp[m->nwav[0]-1].wei;
8974 
8975 //			printf("~1 x5 %f, y5 %f, x6 %f, y6 %f\n",x5,y5,x6,y6);
8976 			/* Search for possible intersection point with first curve */
8977 			/* Create equation for line from next two intersection points */
8978 			for (j = 0; j < (m->mtx_o.nocoef[0]-1); j++) {
8979 				/* Extrapolate line to this segment */
8980 				y3 = y5 + (coeff[m->nwav[0]-1][j].ix - x5)/(x6 - x5) * (y6 - y5);
8981 				y4 = y5 + (coeff[m->nwav[0]-1][j+1].ix - x5)/(x6 - x5) * (y6 - y5);
8982 				/* This segment of curve */
8983 				y1 = coeff[m->nwav[0]-1][j].we;
8984 				y2 = coeff[m->nwav[0]-1][j+1].we;
8985 				if ( ((  y1 >= y3 && y2 <= y4)		/* Segments overlap */
8986 				   || (  y1 <= y3 && y2 >= y4))
8987 				  && ((    coeff[m->nwav[0]-1][j].ix < x5 && coeff[m->nwav[0]-1][j].ix < x6
8988 				        && coeff[m->nwav[0]-1][j+1].ix < x5 && coeff[m->nwav[0]-1][j+1].ix < x6)
8989 				   || (    coeff[m->nwav[0]-1][j+1].ix > x5 && coeff[m->nwav[0]-1][j+1].ix > x6
8990 					    && coeff[m->nwav[0]-1][j].ix > x5 && coeff[m->nwav[0]-1][j].ix > x6))) {
8991 					break;
8992 				}
8993 			}
8994 			if (j >= m->mtx_o.nocoef[m->nwav[0]-1]) {	/* Assert */
8995 				a1loge(p->log,1,"i1pro: failed to find end crossover\n");
8996 				return I1PRO_INT_ASSERT;
8997 			}
8998 			den = -y4 + y3 + y2 - y1;
8999 			yn = (y2 * y3 - y1 * y4)/den;
9000 			xn = (y3 - y1)/den;
9001 //			printf("~1 den = %f, yn = %f, xn = %f\n",den,yn,xn);
9002 			xp[m->nwav[0]].wav = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], m->nwav[0]-0.5);
9003 			xp[m->nwav[0]].raw = (1.0 - xn) * coeff[m->nwav[0]-1][j].ix + xn * coeff[m->nwav[0]-1][j+1].ix;
9004 			xp[m->nwav[0]].wei = yn;
9005 //			printf("End 36 intersection %d: wav %f, raw %f, wei %f\n",m->nwav[0]+1,xp[m->nwav[0]].wav,xp[m->nwav[0]].raw,xp[m->nwav[0]].wei);
9006 //			printf("\n");
9007 		}
9008 
9009 #ifdef HIGH_RES_DEBUG
9010 		/* Check to see if the area of each filter curve is the same */
9011 		/* (yep, width times 2 * xover height is close to 1.0, and the */
9012 		/*  sum of the weightings is exactly 1.0) */
9013 		for (i = 0; i < m->nwav[0]; i++) {
9014 			double area1, area2;
9015 			area1 = fabs(xp[i].raw - xp[i+1].raw) * (xp[i].wei + xp[i+1].wei);
9016 
9017 			area2 = 0.0;
9018 			for (j = 0; j < (m->mtx_o.nocoef[i]); j++)
9019 			    area2 += coeff[i][j].we;
9020 
9021 		printf("Area of curve %d = %f, %f\n",i,area1, area2);
9022 		}
9023 #endif /* HIGH_RES_DEBUG */
9024 
9025 		/* From our crossover data, create a rspl that maps raw CCD index */
9026 		/* value into wavelegth. */
9027 		/* (Generating a 4th order polynomial would probably be better, */
9028 		/*  since this is almost certainly what was used to create the original */
9029 		/*  filters.) */
9030 		{
9031 			co sd[101];				/* Scattered data points */
9032 			datai glow, ghigh;
9033 			datao vlow, vhigh;
9034 			int gres[1];
9035 			double avgdev[1];
9036 
9037 			if ((m->raw2wav = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) {
9038 				a1logd(p->log,1,"i1pro: creating rspl for high res conversion failed\n");
9039 				return I1PRO_INT_NEW_RSPL_FAILED;
9040 			}
9041 
9042 			vlow[0] = 1e6;
9043 			vhigh[0] = -1e6;
9044 			for (i = 0; i < (m->nwav[0]+1); i++) {
9045 				sd[i].p[0] = xp[i].raw;
9046 				sd[i].v[0] = xp[i].wav;
9047 
9048 				if (sd[i].v[0] < vlow[0])
9049 					vlow[0] = sd[i].v[0];
9050 				if (sd[i].v[0] > vhigh[0])
9051 					vhigh[0] = sd[i].v[0];
9052 			}
9053 			glow[0] = 0.0;
9054 			ghigh[0] = 127.0;
9055 			gres[0] = 128;
9056 			avgdev[0] = 0.0;
9057 
9058 			m->raw2wav->fit_rspl(m->raw2wav, 0, sd, m->nwav[0]+1, glow, ghigh, gres, vlow, vhigh, 0.5, avgdev, NULL);
9059 		}
9060 
9061 #ifdef HIGH_RES_PLOT
9062 		/* Plot raw to wav lookup */
9063 		{
9064 			double *xx, *yy, *y2;
9065 
9066 			xx = dvector(0, m->nwav[0]+1);		/* X index = raw bin */
9067 			yy = dvector(0, m->nwav[0]+1);		/* Y = nm */
9068 			y2 = dvector(0, m->nwav[0]+1);		/* Y = nm */
9069 
9070 			for (i = 0; i < (m->nwav[0]+1); i++) {
9071 				co pp;
9072 				double iv, v1, v2;
9073 				xx[i] = xp[i].raw;
9074 				yy[i] = xp[i].wav;
9075 
9076 				pp.p[0] = xp[i].raw;
9077 				m->raw2wav->interp(m->raw2wav, &pp);
9078 				y2[i] = pp.v[0];
9079 			}
9080 
9081 			printf("CCD bin to wavelength mapping of original filters + rspl:\n");
9082 			do_plot6(xx, yy, y2, NULL, NULL, NULL, NULL, m->nwav[0]+1);
9083 			free_dvector(xx, 0, m->nwav[0]+1);
9084 			free_dvector(yy, 0, m->nwav[0]+1);
9085 			free_dvector(y2, 0, m->nwav[0]+1);
9086 		}
9087 #endif /* HIGH_RES_PLOT */
9088 
9089 #ifdef ANALIZE_EXISTING
9090 		/* Convert each weighting curves values into normalized values and */
9091 		/* accumulate into a single curve. */
9092 		if (!m->hr_inited) {
9093 			for (i = 0; i < m->nwav[0]; i++) {
9094 				double cwl;		/* center wavelegth */
9095 				double weight = 0.0;
9096 
9097 				for (j = 0; j < (m->mtx_o.nocoef[i]); j++) {
9098 					double w1, w2, cellw;
9099 
9100 					/* Translate CCD cell boundaries index to wavelength */
9101 					w1 = i1pro_raw2wav_uncal(p, (double)coeff[i][j].ix - 0.5);
9102 
9103 					w2 = i1pro_raw2wav_uncal(p, (double)coeff[i][j].ix + 0.5);
9104 
9105 					cellw = fabs(w2 - w1);
9106 
9107 					cwl = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i);
9108 
9109 					/* Translate CCD index to wavelength */
9110 					fshape[ncp].wl = i1pro_raw2wav_uncal(p, (double)coeff[i][j].ix) - cwl;
9111 					fshape[ncp].we = coeff[i][j].we / (0.09 * cellw);
9112 					ncp++;
9113 				}
9114 			}
9115 
9116 			/* Now sort by wavelength */
9117 #define HEAP_COMPARE(A,B) (A.wl < B.wl)
9118 			HEAPSORT(i1pro_fs, fshape, ncp)
9119 #undef HEAP_COMPARE
9120 
9121 			/* Strip out leading zero's */
9122 			for (i = 0; i < ncp; i++) {
9123 				if (fshape[i].we != 0.0)
9124 					break;
9125 			}
9126 			if (i > 1 && i < ncp) {
9127 				memmove(&fshape[0], &fshape[i-1], sizeof(i1pro_fs) * (ncp - i + 1));
9128 				ncp = ncp - i + 1;
9129 				for (i = 0; i < ncp; i++) {
9130 					if (fshape[i].we != 0.0)
9131 						break;
9132 				}
9133 			}
9134 
9135 #ifdef HIGH_RES_PLOT
9136 			/* Plot the shape of the accumulated curve */
9137 			{
9138 				double *x1 = dvectorz(0, ncp-1);
9139 				double *y1 = dvectorz(0, ncp-1);
9140 
9141 				for (i = 0; i < ncp; i++) {
9142 					double x;
9143 					x1[i] = fshape[i].wl;
9144 					y1[i] = fshape[i].we;
9145 				}
9146 				printf("Original accumulated curve:\n");
9147 				do_plot(x1, y1, NULL, NULL, ncp);
9148 
9149 				free_dvector(x1, 0, ncp-1);
9150 				free_dvector(y1, 0, ncp-1);
9151 			}
9152 #endif /* HIGH_RES_PLOT */
9153 
9154 #ifdef HIGH_RES_DEBUG
9155 			/* Check that the orginal filter sums to a constant */
9156 			{
9157 				double x, sum;
9158 
9159 				for (x = 0.0; x < 10.0; x += 0.2) {
9160 					sum = 0;
9161 					sum += lin_fshape(fshape, ncp, x - 30.0);
9162 					sum += lin_fshape(fshape, ncp, x - 20.0);
9163 					sum += lin_fshape(fshape, ncp, x - 10.0);
9164 					sum += lin_fshape(fshape, ncp, x -  0.0);
9165 					sum += lin_fshape(fshape, ncp, x + 10.0);
9166 					sum += lin_fshape(fshape, ncp, x + 20.0);
9167 					printf("Offset %f, sum %f\n",x, sum);
9168 				}
9169 			}
9170 #endif /* HIGH_RES_DEBUG */
9171 		}
9172 #endif /* ANALIZE_EXISTING */
9173 	}	/* End of compute wavelength cal from existing filters */
9174 
9175 #ifdef COMPUTE_DISPERSION
9176 	if (!m->hr_inited) {
9177 		/* Fit our red laser CCD data to a slit & optics Gaussian dispersion model */
9178 		{
9179 			double spf[3];				/* Spread function parameters */
9180 
9181 			/* Measured CCD values of red laser from CCD indexes 29 to 48 inclusive */
9182 			/* (It would be nice to have similar data from a monochromic source */
9183 			/*  at other wavelegths such as green and blue!) */
9184 			double llv[20] = {
9185 				53.23,
9186 				81.3,
9187 				116.15,
9188 				176.16,
9189 				305.87,
9190 				613.71,
9191 				8500.52,
9192 				64052.0,
9193 				103134.13,
9194 				89154.03,
9195 				21742.89,
9196 				1158.86,
9197 				591.44,
9198 				369.75,
9199 				241.01,
9200 				166.48,
9201 				126.79,
9202 				97.76,
9203 				63.88,
9204 				46.46
9205 			};
9206 			double lwl[21];		/* Wavelegth of boundary between CCD cells */
9207 			double ccd;
9208 			hropt_cx cx;
9209 			double ss[3];
9210 
9211 			/* Add CCD boundary wavelengths to dispersion data */
9212 			for (ccd = 29.0 - 0.5, i = 0; i < 21; i++, ccd += 1.0) {
9213 				/* Translate CCD cell boundaries index to wavelength */
9214 				lwl[i] = i1pro_raw2wav_uncal(p, ccd);
9215 			}
9216 
9217 			/* Fit a gausian to it */
9218 			cx.nsp = 20;
9219 			cx.llv = llv;
9220 			cx.lwl = lwl;
9221 
9222 			/* parameters are amplidude [0], center wavelength [1], std. dev. [2] */
9223 			spf[0] = 115248.0;
9224 			spf[1] = 653.78;
9225 			spf[2] = 3.480308;
9226 			ss[0] = 500.0;
9227 			ss[1] = 0.5;
9228 			ss[2] = 0.5;
9229 
9230 			if (powell(NULL, 3, spf, ss, 1e-5, 2000, hropt_opt1, &cx))
9231 				a1logw(p->log,"hropt_opt1 failed\n");
9232 
9233 #ifdef HIGH_RES_PLOT
9234 			/* Plot dispersion spectra */
9235 			{
9236 				double xx[200];
9237 				double y1[200];
9238 				double y2[200];
9239 				double w1, w2;
9240 
9241 				w1 = lwl[0] + 5.0;
9242 				w2 = lwl[20] - 5.0;
9243 				for (i = 0; i < 200; i++) {
9244 					double wl;
9245 					wl = w1 + (i/199.0) * (w2-w1);
9246 					xx[i] = wl;
9247 					for (j = 0; j < 20; j++) {
9248 						if (lwl[j] >= wl && wl >= lwl[j+1])
9249 							break;
9250 					}
9251 					if (j < 20)
9252 						y1[i] = llv[j];
9253 					else
9254 						y1[i] = 0.0;
9255 					y2[i] = gaussf(spf, wl);
9256 				}
9257 				printf("Gauss Parameters %f %f %f\n",spf[0],spf[1],spf[2]);
9258 				printf("Red laser dispersion data:\n");
9259 				do_plot(xx, y1, y2, NULL, 200);
9260 			}
9261 #endif /* HIGH_RES_PLOT */
9262 
9263 			/* Normalize the gausian to have an area of 1 */
9264 			spf[0] *= 1.0/(spf[0] * spf[2] * sqrt(2.0 * DBL_PI));
9265 
9266 //			printf("~1 Normalized intergral = %f\n",gaussint(spf, spf[1] - 30.0, spf[1] + 30.0));
9267 //			printf("~1 Half width = %f\n",2.0 * sqrt(2.0 * log(2.0)) * spf[2]);
9268 		}
9269 	}
9270 #endif /* COMPUTE_DISPERSION */
9271 
9272 	/* Compute the upsampled calibration references */
9273 	if (!m->hr_inited) {
9274 		rspl *trspl;			/* Upsample rspl */
9275 		cow sd[40 * 40];		/* Scattered data points of existing references */
9276 		datai glow, ghigh;
9277 		datao vlow, vhigh;
9278 		int gres[2];
9279 		double avgdev[2];
9280 		int ix, ii;
9281 		co pp;
9282 
9283 		if ((trspl = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) {
9284 			a1logd(p->log,1,"i1pro: creating rspl for high res conversion failed\n");
9285 			return I1PRO_INT_NEW_RSPL_FAILED;
9286 		}
9287 
9288 		/* For ref, emis, ambient */
9289 		for (ii = 0; ii < 3; ii++) {
9290 			double **ref2, *ref1;
9291 			double smooth = 1.0;
9292 
9293 			if (ii == 0) {
9294 				ref1 = m->white_ref[0];
9295 				ref2 = &m->white_ref[1];
9296 //				smooth = 0.5;
9297 				smooth = 1.5;
9298 			} else if (ii == 1) {
9299 				/* Don't create high res. from low res., if there is */
9300 				/* a better, calibrated cal read from .cal file */
9301 				if (m->emis_coef[1] != NULL) {
9302 					if (m->emis_hr_cal)
9303 						continue;
9304 					free(m->emis_coef[1]);		/* Regenerate it anyway */
9305 				}
9306 				ref1 = m->emis_coef[0];
9307 				ref2 = &m->emis_coef[1];
9308 				m->emis_hr_cal = 0;
9309 				smooth = 10.0;
9310 			} else {
9311 				if (m->amb_coef[0] == NULL)
9312 					break;
9313 				ref1 = m->amb_coef[0];
9314 				ref2 = &m->amb_coef[1];
9315 				smooth = 1.0;
9316 			}
9317 
9318 			if (ref1 == NULL)
9319 				continue;		/* The instI1Monitor doesn't have a reflective cal */
9320 
9321 			vlow[0] = 1e6;
9322 			vhigh[0] = -1e6;
9323 			for (ix = i = 0; i < m->nwav[0]; i++) {
9324 
9325 				sd[ix].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i);
9326 				sd[ix].v[0] = ref1[i];
9327 				sd[ix].w = 1.0;
9328 
9329 				if (sd[ix].v[0] < vlow[0])
9330 					vlow[0] = sd[ix].v[0];
9331 				if (sd[ix].v[0] > vhigh[0])
9332 				    vhigh[0] = sd[ix].v[0];
9333 				ix++;
9334 			}
9335 
9336 			/* Our upsampling is OK for reflective and ambient cal's, */
9337 			/* but isn't so good for the emissive cal., especially */
9338 			/* on the i1pro2 which has a rather bumpy diffraction */
9339 			/* grating/sensor. We'll get an opportunity to fix it */
9340 			/* when we do a reflective calibration, by using the */
9341 			/* smoothness of the lamp as a reference. */
9342 
9343 			/* Add inbetween points to restrain dips and peaks in interp. */
9344 			for (i = 0; i < (m->nwav[0]-1); i++) {
9345 
9346 				/* Use linear interp extra points */
9347 				double wt = 0.05;
9348 
9349 				sd[ix].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i + 0.3333);
9350 				sd[ix].v[0] = (2.0 * ref1[i] + ref1[i+1])/3.0;
9351 				sd[ix].w = wt;
9352 
9353 				if (sd[ix].v[0] < vlow[0])
9354 					vlow[0] = sd[ix].v[0];
9355 				if (sd[ix].v[0] > vhigh[0])
9356 				    vhigh[0] = sd[ix].v[0];
9357 				ix++;
9358 
9359 				sd[ix].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i + 0.66667);
9360 				sd[ix].v[0] = (ref1[i] + 2.0 * ref1[i+1])/3.0;
9361 				sd[ix].w = wt;
9362 
9363 				if (sd[ix].v[0] < vlow[0])
9364 					vlow[0] = sd[ix].v[0];
9365 				if (sd[ix].v[0] > vhigh[0])
9366 				    vhigh[0] = sd[ix].v[0];
9367 				ix++;
9368 			}
9369 
9370 			glow[0] = m->wl_short[1];
9371 			ghigh[0] = m->wl_long[1];
9372 			gres[0] = 3 * m->nwav[1];
9373 			avgdev[0] = 0.0;
9374 
9375 			trspl->fit_rspl_w(trspl, 0, sd, ix, glow, ghigh, gres, vlow, vhigh, smooth, avgdev, NULL);
9376 			if ((*ref2 = (double *)calloc(m->nwav[1], sizeof(double))) == NULL) {
9377 				trspl->del(trspl);
9378 				a1logw(p->log, "i1pro: malloc ref2 failed!\n");
9379 				return I1PRO_INT_MALLOC;
9380 			}
9381 
9382 			for (i = 0; i < m->nwav[1]; i++) {
9383 				pp.p[0] = m->wl_short[1]
9384 				        + (double)i * (m->wl_long[1] - m->wl_short[1])/(m->nwav[1]-1.0);
9385 				trspl->interp(trspl, &pp);
9386 				(*ref2)[i] = pp.v[0];
9387 			}
9388 
9389 #ifdef HIGH_RES_PLOT
9390 			/* Plot original and upsampled reference */
9391 			{
9392 				double *x1 = dvectorz(0, m->nwav[1]-1);
9393 				double *y1 = dvectorz(0, m->nwav[1]-1);
9394 				double *y2 = dvectorz(0, m->nwav[1]-1);
9395 
9396 				for (i = 0; i < m->nwav[1]; i++) {
9397 					double wl = m->wl_short[1] + (double)i * (m->wl_long[1] - m->wl_short[1])/(m->nwav[1]-1.0);
9398 					x1[i] = wl;
9399 					y1[i] = wav_lerp_cv(m, 0, ref1, wl, 0.0);
9400 					y2[i] = (*ref2)[i];
9401 				}
9402 				printf("Original and up-sampled ");
9403 				if (ii == 0) {
9404 					printf("Reflective cal. curve:\n");
9405 				} else if (ii == 1) {
9406 					printf("Emission cal. curve:\n");
9407 				} else {
9408 					printf("Ambient cal. curve:\n");
9409 				}
9410 				do_plot(x1, y1, y2, NULL, m->nwav[1]);
9411 
9412 				free_dvector(x1, 0, m->nwav[1]-1);
9413 				free_dvector(y1, 0, m->nwav[1]-1);
9414 				free_dvector(y2, 0, m->nwav[1]-1);
9415 			}
9416 #endif /* HIGH_RES_PLOT */
9417 		}
9418 		trspl->del(trspl);
9419 
9420 		/* Upsample stray light */
9421 		if (p->itype == instI1Pro2) {
9422 #ifdef ONEDSTRAYLIGHTUS
9423 			double **slp;			/* 2D Array of stray light values */
9424 
9425 			/* Then the 2D stray light using linear interpolation */
9426 			slp = dmatrix(0, m->nwav[0]-1, 0, m->nwav[0]-1);
9427 
9428 			/* Set scattered points */
9429 			for (i = 0; i < m->nwav[0]; i++) {		/* Output wavelength */
9430 				for (j = 0; j < m->nwav[0]; j++) {	/* Input wavelength */
9431 
9432 					slp[i][j] = m->straylight[0][i][j];
9433 
9434 					/* Use interpolate/extrapolate for middle points */
9435 					if (j == (i-1) || j == i || j == (i+1)) {
9436 						int j0, j1;
9437 						double w0, w1;
9438 						if (j == (i-1)) {
9439 							if (j <= 0)
9440 								j0 = j+3, j1 = j+4;
9441 							else if (j >= (m->nwav[0]-3))
9442 								j0 = j-2, j1 = j-1;
9443 							else
9444 								j0 = j-1, j1 = j+3;
9445 						} else if (j == i) {
9446 							if (j <= 1)
9447 								j0 = j+2, j1 = j+3;
9448 							else if (j >= (m->nwav[0]-2))
9449 								j0 = j-3, j1 = j-2;
9450 							else
9451 								j0 = j-2, j1 = j+2;
9452 						} else if (j == (i+1)) {
9453 							if (j <= 2)
9454 								j0 = j+1, j1 = j+2;
9455 							else if (j >= (m->nwav[0]-1))
9456 								j0 = j-4, j1 = j-3;
9457 							else
9458 								j0 = j-3, j1 = j+1;
9459 						}
9460 						w1 = (j - j0)/(j1 - j0);
9461 						w0 = 1.0 - w1;
9462 						slp[i][j] = w0 * m->straylight[0][i][j0]
9463 						          + w1 * m->straylight[0][i][j1];
9464 
9465 					}
9466 				}
9467 			}
9468 #else	/* !ONEDSTRAYLIGHTUS */
9469 			/* Then setup 2D stray light using rspl */
9470 			if ((trspl = new_rspl(RSPL_NOFLAGS, 2, 1)) == NULL) {
9471 				a1logd(p->log,1,"i1pro: creating rspl for high res conversion failed\n");
9472 				return I1PRO_INT_NEW_RSPL_FAILED;
9473 			}
9474 
9475 			/* Set scattered points */
9476 			for (i = 0; i < m->nwav[0]; i++) {		/* Output wavelength */
9477 				for (j = 0; j < m->nwav[0]; j++) {	/* Input wavelength */
9478 					int ix = i * m->nwav[0] + j;
9479 
9480 					sd[ix].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i);
9481 					sd[ix].p[1] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], j);
9482 					sd[ix].v[0] = m->straylight[0][i][j];
9483 					sd[ix].w = 1.0;
9484 					if (j == (i-1) || j == i || j == (i+1))
9485 						sd[ix].w = 0.0;
9486 				}
9487 			}
9488 
9489 			glow[0] = m->wl_short[1];
9490 			glow[1] = m->wl_short[1];
9491 			ghigh[0] = m->wl_long[1];
9492 			ghigh[1] = m->wl_long[1];
9493 			gres[0] = m->nwav[1];
9494 			gres[1] = m->nwav[1];
9495 			avgdev[0] = 0.0;
9496 			avgdev[1] = 0.0;
9497 
9498 			trspl->fit_rspl_w(trspl, 0, sd, m->nwav[0] * m->nwav[0], glow, ghigh, gres, NULL, NULL, 0.5, avgdev, NULL);
9499 #endif	/* !ONEDSTRAYLIGHTUS */
9500 
9501 			m->straylight[1] = dmatrixz(0, m->nwav[1]-1, 0, m->nwav[1]-1);
9502 
9503 			/* Create upsampled version */
9504 			for (i = 0; i < m->nwav[1]; i++) {		/* Output wavelength */
9505 				for (j = 0; j < m->nwav[1]; j++) {	/* Input wavelength */
9506 					double p0, p1;
9507 					p0 = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], i);
9508 					p1 = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j);
9509 #ifdef ONEDSTRAYLIGHTUS
9510 					/* Do linear interp with clipping at ends */
9511 					{
9512 						int x0, x1, y0, y1;
9513 						double xx, yy, w0, w1, v0, v1;
9514 
9515 						xx = (m->nwav[0]-1.0) * (p0 - m->wl_short[0])/(m->wl_long[0] - m->wl_short[0]);
9516 						x0 = (int)floor(xx);
9517 						if (x0 <= 0)
9518 							x0 = 0;
9519 						else if (x0 >= (m->nwav[0]-2))
9520 							x0 = m->nwav[0]-2;
9521 						x1 = x0 + 1;
9522 						w1 = xx - (double)x0;
9523 						w0 = 1.0 - w1;
9524 
9525 						yy = (m->nwav[0]-1.0) * (p1 - m->wl_short[0])/(m->wl_long[0] - m->wl_short[0]);
9526 						y0 = (int)floor(yy);
9527 						if (y0 <= 0)
9528 							y0 = 0;
9529 						else if (y0 >= (m->nwav[0]-2))
9530 							y0 = m->nwav[0]-2;
9531 						y1 = y0 + 1;
9532 						v1 = yy - (double)y0;
9533 						v0 = 1.0 - v1;
9534 
9535 						pp.v[0] = w0 * v0 * slp[x0][y0]
9536 						        + w0 * v1 * slp[x0][y1]
9537 						        + w1 * v0 * slp[x1][y0]
9538 						        + w1 * v1 * slp[x1][y1];
9539 					}
9540 #else /* !ONEDSTRAYLIGHTUS */
9541 					pp.p[0] = p0;
9542 					pp.p[1] = p1;
9543 					trspl->interp(trspl, &pp);
9544 #endif /* !ONEDSTRAYLIGHTUS */
9545 					m->straylight[1][i][j] = pp.v[0] * HIGHRES_WIDTH/10.0;
9546 					if (m->straylight[1][i][j] > 0.0)
9547 						m->straylight[1][i][j] = 0.0;
9548 				}
9549 			}
9550 
9551 			/* Fix primary wavelength weight and neighbors */
9552 			for (i = 0; i < m->nwav[1]; i++) {		/* Output wavelength */
9553 				double sum;
9554 
9555 				if (i > 0)
9556 					m->straylight[1][i][i-1] = 0.0;
9557 				m->straylight[1][i][i] = 0.0;
9558 				if (i < (m->nwav[1]-1))
9559 					m->straylight[1][i][i+1] = 0.0;
9560 
9561 				for (sum = 0.0, j = 0; j < m->nwav[1]; j++)
9562 					sum += m->straylight[1][i][j];
9563 
9564 				m->straylight[1][i][i] = 1.0 - sum;		/* Total sum should be 1.0 */
9565 			}
9566 
9567 #ifdef HIGH_RES_PLOT_STRAYL
9568 			/* Plot original and upsampled reference */
9569 			{
9570 				double *x1 = dvectorz(0, m->nwav[1]-1);
9571 				double *y1 = dvectorz(0, m->nwav[1]-1);
9572 				double *y2 = dvectorz(0, m->nwav[1]-1);
9573 
9574 				for (i = 0; i < m->nwav[1]; i++) {		/* Output wavelength */
9575 					double wli = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], i);
9576 					int i1 = XSPECT_IX(m->wl_short[0], m->wl_long[0], m->nwav[0], wli);
9577 
9578 					for (j = 0; j < m->nwav[1]; j++) {
9579 						double wl = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j);
9580 						x1[j] = wl;
9581 						y1[j] = m->straylight[1][i][j];
9582 						if (y1[j] == 0.0)
9583 							y1[j] = -8.0;
9584 						else
9585 							y1[j] = log10(fabs(y1[j]));
9586 						if (wli < m->wl_short[0] || wli > m->wl_long[0]
9587 						 || wl < m->wl_short[0] || wl > m->wl_long[0]) {
9588 							y2[j] = -8.0;
9589 						} else {
9590 							double x, wl1, wl2;
9591 							for (k = 0; k < (m->nwav[0]-1); k++) {
9592 								wl1 = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], k);
9593 								wl2 = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], k+1);
9594 								if (wl >= wl1 && wl <= wl2)
9595 									break;
9596 							}
9597 							x = (wl - wl1)/(wl2 - wl1);
9598 							y2[j] = m->straylight[0][i1][k] + (m->straylight[0][i1][k+1]
9599 							      - m->straylight[0][i1][k]) * x;
9600 							if (y2[j] == 0.0)
9601 								y2[j] = -8.0;
9602 							else
9603 								y2[j] = log10(fabs(y2[j]));
9604 						}
9605 					}
9606 					do_plot(x1, y1, y2, NULL, m->nwav[1]);
9607 				}
9608 
9609 				free_dvector(x1, 0, m->nwav[1]-1);
9610 				free_dvector(y1, 0, m->nwav[1]-1);
9611 				free_dvector(y2, 0, m->nwav[1]-1);
9612 			}
9613 #endif /* HIGH_RES_PLOT */
9614 
9615 #ifdef ONEDSTRAYLIGHTUS
9616 			free_dmatrix(slp, 0, m->nwav[0]-1, 0, m->nwav[0]-1);
9617 #else /* !ONEDSTRAYLIGHTUS */
9618 			trspl->del(trspl);
9619 #endif /* !ONEDSTRAYLIGHTUS */
9620 		}
9621 	}
9622 
9623 	/* Create or re-create the high resolution filters */
9624 	for (refl = 0; refl < 2; refl++) {	/* for emis/trans and reflective */
9625 #define MXNOWL 200		/* Max hires bands */
9626 #define MXNOFC 32		/* Max hires coeffs */
9627 
9628 #ifndef USE_TRI_LAGRANGE				/* Use decimation filter */
9629 		int super = 0;					/* nz if we're super sampling */
9630 		double fshmax;					/* filter shape max wavelength from center */
9631 		i1pro_fc coeff2[MXNOWL][MXNOFC];	/* New filter cooefficients */
9632 
9633 		/* Construct a set of filters that uses more CCD values */
9634 
9635 		if (twidth < 3.0)
9636 			super = 1;
9637 
9638 		if (m->nwav[1] > MXNOWL) {		/* Assert */
9639 			a1loge(p->log,1,"High res filter has too many bands\n");
9640 			return I1PRO_INT_ASSERT;
9641 		}
9642 
9643 		if (super) {
9644 			fshmax = 5.0;
9645 		} else {
9646 			/* Use a simple means of determining width */
9647 			for (fshmax = 50.0; fshmax >= 0.0; fshmax -= 0.1) {
9648 				if (fabs(lanczos2(twidth, fshmax)) > 0.0001) {
9649 					fshmax += 0.1;
9650 					break;
9651 				}
9652 			}
9653 			if (fshmax <= 0.0) {
9654 				a1logw(p->log, "i1pro: fshmax search failed\n");
9655 				return I1PRO_INT_ASSERT;
9656 			}
9657 		}
9658 
9659 //		printf("~1 fshmax = %f\n",fshmax);
9660 
9661 #ifdef HIGH_RES_DEBUG
9662 		/* Check that the filter sums to a constant */
9663 		{
9664 			double x, sum;
9665 
9666 			for (x = 0.0; x < 5.0; x += 0.1) {
9667 				sum = 0;
9668 				sum += lin_fshape(fshape, ncp, x - 15.0);
9669 				sum += lin_fshape(fshape, ncp, x - 10.0);
9670 				sum += lin_fshape(fshape, ncp, x -  5.0);
9671 				sum += lin_fshape(fshape, ncp, x -  0.0);
9672 				sum += lin_fshape(fshape, ncp, x +  5.0);
9673 				sum += lin_fshape(fshape, ncp, x + 10.0);
9674 				printf("Offset %f, sum %f\n",x, sum);
9675 			}
9676 		}
9677 #endif /* HIGH_RES_DEBUG */
9678 
9679 		/* Create all the filters */
9680 		if (m->mtx_c[1][refl].nocoef != NULL)
9681 			free(m->mtx_c[1][refl].nocoef);
9682 		if ((m->mtx_c[1][refl].nocoef = (int *)calloc(m->nwav[1], sizeof(int))) == NULL) {
9683 			a1logw(p->log, "i1pro: malloc nocoef failed!\n");
9684 			return I1PRO_INT_MALLOC;
9685 		}
9686 
9687 		if (super) {	/* Use linear interpolation */
9688 
9689 			/* For all the useful CCD bands */
9690 			for (i = 1; i < (127-1); i++) {
9691 				double wl, wh;
9692 
9693 				/* Translate CCD centers to calibrated wavelength */
9694 				wh = i1pro_raw2wav(p, refl, (double)(i+0));
9695 				wl = i1pro_raw2wav(p, refl, (double)(i+1));
9696 
9697 				/* For each filter */
9698 				for (j = 0; j < m->nwav[1]; j++) {
9699 					double cwl;		/* Center wavelength */
9700 					double we;		/* Weighting */
9701 					double wwwe = 1.0;
9702 
9703 					cwl = m->wl_short[1] + (double)j * (m->wl_long[1] - m->wl_short[1])
9704 					                                  /(m->nwav[1]-1.0);
9705 //printf("~1 wl %f, wh %f, cwl %f\n",wl,wh,cwl);
9706 					if (cwl < (wl - 1e-6) || cwl > (wh + 1e-6))
9707 						continue;		/* Doesn't fall into this filter */
9708 
9709 					if ((m->mtx_c[1][refl].nocoef[j]+1) >= MXNOFC) {
9710 						a1logw(p->log, "i1pro: run out of high res filter space\n");
9711 						return I1PRO_INT_ASSERT;
9712 					}
9713 
9714 					we = (cwl - wl)/(wh - wl);
9715 //					wwwe = 3.3/(wh - wl);
9716 
9717 					coeff2[j][m->mtx_c[1][refl].nocoef[j]].ix = i;
9718 					coeff2[j][m->mtx_c[1][refl].nocoef[j]++].we = wwwe * we;
9719 //printf("~1 filter %d, cwl %f, ix %d, we %f, nocoefs %d\n",j,cwl,i,we,m->mtx_c[1][refl].nocoef[j]);
9720 					coeff2[j][m->mtx_c[1][refl].nocoef[j]].ix = i+1;
9721 					coeff2[j][m->mtx_c[1][refl].nocoef[j]++].we = wwwe * (1.0 - we);
9722 //printf("~1 filter %d, cwl %f, ix %d, we %f, nocoefs %d\n",j,cwl,i+1,1.0-we,m->mtx_c[1][refl].nocoef[j]);
9723 				}
9724 			}
9725 
9726 		} else {
9727 			/* For all the useful CCD bands */
9728 			for (i = 1; i < 127; i++) {
9729 				double w1, wl, w2;
9730 
9731 				/* Translate CCD center and boundaries to calibrated wavelength */
9732 				wl = i1pro_raw2wav(p, refl, (double)i);
9733 				w1 = i1pro_raw2wav(p, refl, (double)i - 0.5);
9734 				w2 = i1pro_raw2wav(p, refl, (double)i + 0.5);
9735 
9736 //				printf("~1 CCD %d, w1 %f, wl %f, w2 %f\n",i,w1,wl,w2);
9737 
9738 				/* For each filter */
9739 				for (j = 0; j < m->nwav[1]; j++) {
9740 					double cwl, rwl;		/* center, relative wavelegth */
9741 					double we;
9742 
9743 					cwl = m->wl_short[1] + (double)j * (m->wl_long[1] - m->wl_short[1])
9744 					                                  /(m->nwav[1]-1.0);
9745 					rwl = wl - cwl;			/* relative wavelength to filter */
9746 
9747 					if (fabs(w1 - cwl) > fshmax && fabs(w2 - cwl) > fshmax)
9748 						continue;		/* Doesn't fall into this filter */
9749 
9750 #ifdef BOX_INTEGRATE
9751 					/* Integrate in 0.05 nm increments from filter shape */
9752 					/* using triangular integration. */
9753 					{
9754 						int nn;
9755 						double lw, ll;
9756 #ifdef FAST_HIGH_RES_SETUP
9757 # define FINC 0.2
9758 #else
9759 # define FINC 0.05
9760 #endif
9761 						nn = (int)(fabs(w2 - w1)/FINC + 0.5);		/* Number to integrate over */
9762 
9763 						lw = w1;				/* start at lower boundary of CCD cell */
9764 						ll = lanczos2(twidth, w1- cwl);
9765 						we = 0.0;
9766 						for (k = 0; k < nn; k++) {
9767 							double cw, cl;
9768 
9769 #if defined(__APPLE__) && defined(__POWERPC__)
9770 							gcc_bug_fix(k);
9771 #endif
9772 							cw = w1 + (k+1.0)/(nn +1.0) * (w2 - w1);	/* wl to sample */
9773 							cl = lanczos2(twidth, cw- cwl);
9774 							we += 0.5 * (cl + ll) * (lw - cw);			/* Area under triangle */
9775 							ll = cl;
9776 							lw = cw;
9777 						}
9778 					}
9779 #else
9780 					we = fabs(w2 - w1) * lanczos2(twidth, rwl);
9781 #endif
9782 
9783 					if (m->mtx_c[1][refl].nocoef[j] >= MXNOFC) {
9784 						a1logw(p->log, "i1pro: run out of high res filter space\n");
9785 						return I1PRO_INT_ASSERT;
9786 					}
9787 
9788 					coeff2[j][m->mtx_c[1][refl].nocoef[j]].ix = i;
9789 					coeff2[j][m->mtx_c[1][refl].nocoef[j]++].we = we;
9790 //				printf("~1 filter %d, cwl %f, rwl %f, ix %d, we %f, nocoefs %d\n",j,cwl,rwl,i,we,m->mtx_c[1][refl].nocoef[j]);
9791 				}
9792 			}
9793 		}
9794 
9795 #ifdef HIGH_RES_PLOT
9796 		/* Plot resampled curves */
9797 		{
9798 			double *xx, *ss;
9799 			double **yy;
9800 
9801 			xx = dvectorz(-1, m->nraw-1);		/* X index */
9802 			yy = dmatrixz(0, 5, -1, m->nraw-1);	/* Curves distributed amongst 5 graphs */
9803 
9804 			for (i = 0; i < m->nraw; i++)
9805 				xx[i] = i;
9806 
9807 			/* For each output wavelength */
9808 			for (j = 0; j < m->nwav[1]; j++) {
9809 				i = j % 5;
9810 
9811 				/* For each matrix value */
9812 				for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) {
9813 					yy[5][coeff2[j][k].ix] += 0.5 * coeff2[j][k].we;
9814 					yy[i][coeff2[j][k].ix] = coeff2[j][k].we;
9815 				}
9816 			}
9817 
9818 			printf("Hi-Res wavelength sampling curves %s:\n",refl ? "refl" : "emis");
9819 			do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
9820 			free_dvector(xx, -1, m->nraw-1);
9821 			free_dmatrix(yy, 0, 2, -1, m->nraw-1);
9822 		}
9823 #endif /* HIGH_RES_PLOT */
9824 
9825 		/* Convert hires filters into runtime format */
9826 		{
9827 			int xcount;
9828 
9829 			/* Allocate or reallocate high res filter tables */
9830 			if (m->mtx_c[1][refl].index != NULL)
9831 				free(m->mtx_c[1][refl].index);
9832 			if (m->mtx_c[1][refl].coef != NULL)
9833 				free(m->mtx_c[1][refl].coef);
9834 
9835 			if ((m->mtx_c[1][refl].index = (int *)calloc(m->nwav[1], sizeof(int))) == NULL) {
9836 				a1logw(p->log, "i1pro: malloc index failed!\n");
9837 				return I1PRO_INT_MALLOC;
9838 			}
9839 
9840 			xcount = 0;
9841 			for (j = 0; j < m->nwav[1]; j++) {
9842 				m->mtx_c[1][refl].index[j] = coeff2[j][0].ix;
9843 				xcount += m->mtx_c[1][refl].nocoef[j];
9844 			}
9845 
9846 			if ((m->mtx_c[1][refl].coef = (double *)calloc(xcount, sizeof(double))) == NULL) {
9847 				a1logw(p->log, "i1pro: malloc coef failed!\n");
9848 				return I1PRO_INT_MALLOC;
9849 			}
9850 
9851 			for (i = j = 0; j < m->nwav[1]; j++)
9852 				for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++, i++)
9853 					m->mtx_c[1][refl].coef[i] = coeff2[j][k].we;
9854 
9855 			/* Set high res tables to new allocations */
9856 			m->mtx[1][refl] = m->mtx_c[1][refl];
9857 		}
9858 
9859 #else	/* USE_TRI_LAGRANGE, OEM/normal res. triangle over lagrange interp */
9860 
9861 		/* Compute high res. reflective wavelength corrected filters */
9862 		if ((ev = i1pro_compute_wav_filters(p, 1, refl)) != I1PRO_OK) {
9863 			a1logd(p->log,2,"i1pro_compute_wav_filters() failed\n");
9864 			return ev;
9865 		}
9866 #endif	/* USE_TRI_LAGRANGE */
9867 
9868 		/* Normalise the filters area in CCD space, while maintaining the */
9869 		/* total contribution of each CCD at the target too. */
9870 		/* Hmm. This will wreck super-sample. We should fix it */
9871 #ifdef DO_CCDNORM			/* Normalise CCD values to original */
9872 		{
9873 			double x[4], y[4];
9874 			double avg[2], max[2];
9875 			double ccdsum[2][128];			/* Target weight/actual for each CCD */
9876 			double dth[2];
9877 
9878 			avg[0] = avg[1] = 0.0;
9879 			max[0] = max[1] = 0.0;
9880 			for (j = 0; j < 128; j++) {
9881 				ccdsum[0][j] = 0.0;
9882 				ccdsum[1][j] = 0.0;
9883 			}
9884 
9885 			/* Compute the weighting of each CCD value in the normal output */
9886 			for (cx = j = 0; j < m->nwav[0]; j++) { /* For each wavelength */
9887 
9888 				/* For each matrix value */
9889 				sx = m->mtx_o.index[j];		/* Starting index */
9890 				for (k = 0; k < m->mtx_o.nocoef[j]; k++, cx++, sx++) {
9891 					ccdsum[0][sx] += m->mtx_o.coef[cx];
9892 //printf("~1 Norm CCD [%d] %f += [%d] %f\n",sx,ccdsum[0][sx],cx, m->mtx_o.coef[cx]);
9893 				}
9894 			}
9895 
9896 			/* Compute the weighting of each CCD value in the hires output */
9897 			for (cx = j = 0; j < m->nwav[1]; j++) { /* For each wavelength */
9898 
9899 				/* For each matrix value */
9900 				sx = m->mtx_c[1][refl].index[j];		/* Starting index */
9901 				for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++, cx++, sx++) {
9902 					ccdsum[1][sx] += m->mtx_c[1][refl].coef[cx];
9903 //printf("~1 HiRes CCD [%d] %f += [%d] %f\n",sx,ccdsum[1][sx],cx, m->mtx_c[1][refl].coef[cx]);
9904 				}
9905 			}
9906 
9907 			/* Figure valid range and extrapolate to edges */
9908 			dth[0] = 0.0;		/* ref */
9909 //			dth[1] = 0.007;		/* hires */
9910 			dth[1] = 0.004;		/* hires */
9911 
9912 			for (k = 0; k < 2; k++) {
9913 
9914 				for (i = 0; i < 128; i++) {
9915 					if (ccdsum[k][i] > max[k])
9916 						max[k] = ccdsum[k][i];
9917 				}
9918 
9919 //printf("~1 max[%d] = %f\n",k, max[k]);
9920 				/* Figure out the valid range */
9921 				for (i = 64; i >= 0; i--) {
9922 					if (ccdsum[k][i] > (0.8 * max[k])) {
9923 						x[0] = (double)i;
9924 					} else {
9925 						break;
9926 					}
9927 				}
9928 				for (i = 64; i < 128; i++) {
9929 					if (ccdsum[k][i] > (0.8 * max[k])) {
9930 						x[3] = (double)i;
9931 					} else {
9932 						break;
9933 					}
9934 				}
9935 				/* Space off the last couple of entries */
9936 				x[0] += (3.0 + 3.0);
9937 				x[3] -= (3.0 + 3.0);
9938 				x[1] = floor((2 * x[0] + x[3])/3.0);
9939 				x[2] = floor((x[0] + 2 * x[3])/3.0);
9940 
9941 				for (i = 0; i < 4; i++) {
9942 					y[i] = 0.0;
9943 					for (j = -3; j < 4; j++) {
9944 						y[i] += ccdsum[k][(int)x[i]+j];
9945 					}
9946 					y[i] /= 7.0;
9947 				}
9948 
9949 //printf("~1 extrap nodes %f, %f, %f, %f\n",x[0],x[1],x[2],x[3]);
9950 //printf("~1 extrap value %f, %f, %f, %f\n",y[0],y[1],y[2],y[3]);
9951 
9952 				for (i = 0; i < 128; i++) {
9953 					double xw, yw;
9954 
9955 					xw = (double)i;
9956 
9957 					/* Compute interpolated value using Lagrange: */
9958 					yw = y[0] * (xw-x[1]) * (xw-x[2]) * (xw-x[3])
9959 						      /((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3]))
9960 					   + y[1] * (xw-x[0]) * (xw-x[2]) * (xw-x[3])
9961 						      /((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3]))
9962 					   + y[2] * (xw-x[0]) * (xw-x[1]) * (xw-x[3])
9963 						      /((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3]))
9964 					   + y[3] * (xw-x[0]) * (xw-x[1]) * (xw-x[2])
9965 						      /((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2]));
9966 
9967 					if ((xw < x[0] || xw > x[3])
9968 					 && fabs(ccdsum[k][i] - yw)/yw > dth[k]) {
9969 						ccdsum[k][i] = yw;
9970 					}
9971 					avg[k] += ccdsum[k][i];
9972 				}
9973 				avg[k] /= 128.0;
9974 			}
9975 
9976 #ifdef HIGH_RES_PLOT
9977 			/* Plot target CCD values */
9978 			{
9979 				double xx[128], y1[128], y2[128];
9980 
9981 				for (i = 0; i < 128; i++) {
9982 					xx[i] = i;
9983 					y1[i] = ccdsum[0][i]/avg[0];
9984 					y2[i] = ccdsum[1][i]/avg[1];
9985 				}
9986 
9987 				printf("Target and actual CCD weight sums:\n");
9988 				do_plot(xx, y1, y2, NULL, 128);
9989 			}
9990 #endif
9991 
9992 #ifdef DO_CCDNORMAVG	/* Just correct by average */
9993 			for (cx = j = 0; j < m->nwav[1]; j++) { /* For each wavelength */
9994 
9995 				/* For each matrix value */
9996 				sx = m->mtx_c[1][refl].index[j];		/* Starting index */
9997 				for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++, cx++, sx++) {
9998 					m->mtx_c[1][refl].coef[cx] *= 10.0/twidth * avg[0]/avg[1];
9999 				}
10000 			}
10001 
10002 #else			/* Correct by CCD bin */
10003 
10004 			/* Correct the weighting of each CCD value in the hires output */
10005 			for (i = 0; i < 128; i++) {
10006 				ccdsum[1][i] = 10.0/twidth * ccdsum[0][i]/ccdsum[1][i];		/* Correction factor */
10007 			}
10008 			for (cx = j = 0; j < m->nwav[1]; j++) { /* For each wavelength */
10009 
10010 				/* For each matrix value */
10011 				sx = m->mtx_c[1][refl].index[j];		/* Starting index */
10012 				for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++, cx++, sx++) {
10013 					m->mtx_c[1][refl].coef[cx] *= ccdsum[1][sx];
10014 				}
10015 			}
10016 #endif
10017 		}
10018 #endif /* DO_CCDNORM */
10019 
10020 #ifdef HIGH_RES_PLOT
10021 		{
10022 			static i1pro_fc coeff2[MXNOWL][MXNOFC];
10023 			double *xx, *ss;
10024 			double **yy;
10025 
10026 			/* Convert the native filter cooeficient representation to */
10027 			/* a 2D array we can randomly index. */
10028 			for (cx = j = 0; j < m->nwav[1]; j++) { /* For each output wavelength */
10029 				if (j >= MXNOWL) {	/* Assert */
10030 					a1loge(p->log,1,"i1pro: number of hires output wavelenths is > %d\n",MXNOWL);
10031 					return I1PRO_INT_ASSERT;
10032 				}
10033 
10034 				/* For each matrix value */
10035 				sx = m->mtx[1][refl].index[j];		/* Starting index */
10036 				for (k = 0; k < m->mtx[1][refl].nocoef[j]; k++, cx++, sx++) {
10037 					if (k >= MXNOFC) {	/* Assert */
10038 						a1loge(p->log,1,"i1pro: number of hires filter coeefs is > %d\n",MXNOFC);
10039 						return I1PRO_INT_ASSERT;
10040 					}
10041 					coeff2[j][k].ix = sx;
10042 					coeff2[j][k].we = m->mtx[1][refl].coef[cx];
10043 //				printf("Output %d, filter %d weight = %e\n",j,k,coeff2[j][k].we);
10044 				}
10045 			}
10046 
10047 			xx = dvectorz(-1, m->nraw-1);		/* X index */
10048 			yy = dmatrixz(0, 5, -1, m->nraw-1);	/* Curves distributed amongst 5 graphs */
10049 
10050 			for (i = 0; i < m->nraw; i++)
10051 				xx[i] = i;
10052 
10053 			/* For each output wavelength */
10054 			for (j = 0; j < m->nwav[1]; j++) {
10055 				i = j % 5;
10056 
10057 				/* For each matrix value */
10058 				for (k = 0; k < m->mtx_c[1][refl].nocoef[j]; k++) {
10059 					yy[5][coeff2[j][k].ix] += 0.5 * coeff2[j][k].we;
10060 					yy[i][coeff2[j][k].ix] = coeff2[j][k].we;
10061 				}
10062 			}
10063 
10064 			printf("Normalized Hi-Res wavelength sampling curves: %s\n",refl ? "refl" : "emis");
10065 			do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
10066 			free_dvector(xx, -1, m->nraw-1);
10067 			free_dmatrix(yy, 0, 2, -1, m->nraw-1);
10068 		}
10069 #endif /* HIGH_RES_PLOT */
10070 #undef MXNOWL
10071 #undef MXNOFC
10072 	}	/* Do next filter */
10073 
10074 	/* Hires has been initialised */
10075 	m->hr_inited = 1;
10076 
10077 	/* Generate high res. per mode calibration factors. */
10078 	if ((ev = i1pro_create_hr_calfactors(p, 0)) != I1PRO_OK)
10079 		return ev;
10080 
10081 	return ev;
10082 }
10083 
10084 #endif /* HIGH_RES */
10085 
10086 
10087 /* return nz if high res is supported */
10088 int i1pro_imp_highres(i1pro *p) {
10089 #ifdef HIGH_RES
10090 	return 1;
10091 #else
10092 	return 0;
10093 #endif /* HIGH_RES */
10094 }
10095 
10096 /* Set to high resolution mode */
10097 i1pro_code i1pro_set_highres(i1pro *p) {
10098 	int i;
10099 	i1proimp *m = (i1proimp *)p->m;
10100 	i1pro_code ev = I1PRO_OK;
10101 
10102 #ifdef HIGH_RES
10103 	if (m->hr_inited == 0) {
10104 		if ((ev = i1pro_create_hr(p)) != I1PRO_OK)
10105 			return ev;
10106 	}
10107 	m->highres = 1;
10108 #else
10109 	ev = I1PRO_UNSUPPORTED;
10110 #endif /* HIGH_RES */
10111 
10112 	return ev;
10113 }
10114 
10115 /* Set to standard resolution mode */
10116 i1pro_code i1pro_set_stdres(i1pro *p) {
10117 	int i;
10118 	i1proimp *m = (i1proimp *)p->m;
10119 	i1pro_code ev = I1PRO_OK;
10120 
10121 #ifdef HIGH_RES
10122 	m->highres = 0;
10123 #else
10124 	ev = I1PRO_UNSUPPORTED;
10125 #endif /* HIGH_RES */
10126 
10127 	return ev;
10128 }
10129 
10130 /* =============================================== */
10131 
10132 /* Modify the scan consistency tolerance */
10133 i1pro_code i1pro_set_scan_toll(i1pro *p, double toll_ratio) {
10134 	i1proimp *m = (i1proimp *)p->m;
10135 	i1pro_code ev = I1PRO_OK;
10136 
10137 	m->scan_toll_ratio = toll_ratio;
10138 
10139 	return I1PRO_OK;
10140 }
10141 
10142 
10143 /* Optical adjustment weights */
10144 static double opt_adj_weights[21] = {
10145 	1.4944496665144658e-282, 2.0036175483913455e-070, 1.2554893022685038e+232,
10146 	2.3898157055642966e+190, 1.5697625128432372e-076, 6.6912978722191457e+281,
10147 	1.2369092402930559e+277, 1.4430907501246712e-153, 3.0017439193018232e+238,
10148 	1.2978311824382444e+161, 5.5068703318775818e-311, 7.7791723264455314e-260,
10149 	6.4560484084110176e+170, 8.9481529920968425e+165, 1.3565405878488529e-153,
10150 	2.0835868791190880e-076, 5.4310198502711138e+241, 4.8689849775675438e+275,
10151 	9.2709981544886391e+122, 3.7958270103353899e-153, 7.1366083837501666e-154
10152 };
10153 
10154 /* Convert from spectral to XYZ, and transfer to the ipatch array. */
10155 /* Apply XRGA conversion if needed */
10156 i1pro_code i1pro_conv2XYZ(
10157 	i1pro *p,
10158 	ipatch *vals,		/* Values to return */
10159 	int nvals,			/* Number of values */
10160 	double **specrd,	/* Spectral readings */
10161 	instClamping clamp	/* Clamp XYZ/Lab to be +ve */
10162 ) {
10163 	i1proimp *m = (i1proimp *)p->m;
10164 	i1pro_state *s = &m->ms[m->mmode];
10165 	xsp2cie *conv;	/* Spectral to XYZ conversion object */
10166 	int i, j, k;
10167 	int six = 0;		/* Starting index */
10168 	int nwl = m->nwav[m->highres];	/* Number of wavelegths */
10169 	double wl_short = m->wl_short[m->highres];	/* Starting wavelegth */
10170 	double sms;			/* Weighting */
10171 
10172 	if (s->emiss)
10173 		conv = new_xsp2cie(icxIT_none, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, (icxClamping)clamp);
10174 	else
10175 		conv = new_xsp2cie(icxIT_D50, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, (icxClamping)clamp);
10176 	if (conv == NULL)
10177 		return I1PRO_INT_CIECONVFAIL;
10178 
10179 	/* Don't report any wavelengths below the minimum for this mode */
10180 	if ((s->min_wl-1e-3) > wl_short) {
10181 		double wl = 0.0;
10182 		for (j = 0; j < m->nwav[m->highres]; j++) {
10183 			wl = XSPECT_WL(m->wl_short[m->highres], m->wl_long[m->highres], m->nwav[m->highres], j);
10184 			if (wl >= (s->min_wl-1e-3))
10185 				break;
10186 		}
10187 		six = j;
10188 		wl_short = wl;
10189 		nwl -= six;
10190 	}
10191 
10192 	a1logd(p->log,5,"i1pro_conv2XYZ got wl_short %f, wl_long %f, nwav %d, min_wl %f\n",
10193 	                m->wl_short[m->highres], m->wl_long[m->highres], m->nwav[m->highres], s->min_wl);
10194 	a1logd(p->log,5,"      after skip got wl_short %f, nwl = %d\n", wl_short, nwl);
10195 
10196 	for (sms = 0.0, i = 1; i < 21; i++)
10197 		sms += opt_adj_weights[i];
10198 	sms *= opt_adj_weights[0];
10199 
10200 	for (i = 0; i < nvals; i++) {
10201 
10202 		vals[i].loc[0] = '\000';
10203 		vals[i].mtype = inst_mrt_none;
10204 		vals[i].XYZ_v = 0;
10205 		vals[i].sp.spec_n = 0;
10206 		vals[i].duration = 0.0;
10207 
10208 		vals[i].sp.spec_n = nwl;
10209 		vals[i].sp.spec_wl_short = wl_short;
10210 		vals[i].sp.spec_wl_long = m->wl_long[m->highres];
10211 
10212 		if (s->emiss) {
10213 			/* Leave spectral values as mW/m^2 */
10214 			for (j = six, k = 0; j < m->nwav[m->highres]; j++, k++) {
10215 				vals[i].sp.spec[k] = specrd[i][j] * sms;
10216 			}
10217 			vals[i].sp.norm = 1.0;
10218 
10219 			/* Set the XYZ */
10220 			conv->convert(conv, vals[i].XYZ, &vals[i].sp);
10221 			vals[i].XYZ_v = 1;
10222 
10223 			if (s->ambient) {
10224 				if (s->flash)
10225 					vals[i].mtype = inst_mrt_ambient_flash;
10226 				else
10227 					vals[i].mtype = inst_mrt_ambient;
10228 			} else {
10229 				if (s->flash)
10230 					vals[i].mtype = inst_mrt_emission_flash;
10231 				else
10232 					vals[i].mtype = inst_mrt_emission;
10233 			}
10234 
10235 		} else {
10236 			/* Scale spectral values to percentage reflectance */
10237 			for (j = six, k = 0; j < m->nwav[m->highres]; j++, k++) {
10238 				vals[i].sp.spec[k] = 100.0 * specrd[i][j] * sms;
10239 			}
10240 			vals[i].sp.norm = 100.0;
10241 
10242 			/* Set the XYZ */
10243 			conv->convert(conv, vals[i].XYZ, &vals[i].sp);
10244 			vals[i].XYZ_v = 1;
10245 			vals[i].XYZ[0] *= 100.0;
10246 			vals[i].XYZ[1] *= 100.0;
10247 			vals[i].XYZ[2] *= 100.0;
10248 
10249 			if (s->trans)
10250 				vals[i].mtype = inst_mrt_transmissive;
10251 			else
10252 				vals[i].mtype = inst_mrt_reflective;
10253 		}
10254 
10255 		/* Don't return spectral if not asked for */
10256 		if (!m->spec_en) {
10257 			vals[i].sp.spec_n = 0;
10258 		}
10259 
10260 	}
10261 
10262 	conv->del(conv);
10263 
10264 	/* Apply any XRGA conversion */
10265 	ipatch_convert_xrga(vals, nvals, xcalstd_nonpol, m->target_calstd, m->native_calstd, clamp);
10266 
10267 	return I1PRO_OK;
10268 }
10269 
10270 /* Check a reflective white reference measurement to see if */
10271 /* it seems reasonable. Return I1PRO_OK if it is, error if not. */
10272 i1pro_code i1pro_check_white_reference1(
10273 	i1pro *p,
10274 	double *abswav			/* [nwav[0]] Measurement to check */
10275 ) {
10276 	i1pro_code ev = I1PRO_OK;
10277 	i1proimp *m = (i1proimp *)p->m;
10278 	double *emiswav, normfac;
10279 	double avg01, avg2227;
10280 	int j;
10281 
10282 	emiswav = dvector(-1, m->nraw-1);
10283 
10284 	/* Convert from absolute wavelength converted sensor reading, */
10285 	/* to calibrated emission wavelength spectrum. */
10286 
10287 	/* For each output wavelength */
10288 	for (j = 0; j < m->nwav[0]; j++) {
10289 		emiswav[j] = m->emis_coef[0][j] * abswav[j];
10290 	}
10291 #ifdef PLOT_DEBUG
10292 	printf("White ref read converted to emissive spectrum:\n");
10293 	plot_wav(m, 0, emiswav);
10294 #endif
10295 
10296 	/* Normalise the measurement to the reflectance of the 17 wavelength */
10297 	/* of the white reference (550nm), as well as dividing out the */
10298 	/* reference white. This should leave us with the iluminant spectrum, */
10299 	normfac = m->white_ref[0][17]/emiswav[17];
10300 
10301 	for (j = 0; j < m->nwav[0]; j++) {
10302 		emiswav[j] *= normfac/m->white_ref[0][j];
10303 	}
10304 
10305 #ifdef PLOT_DEBUG
10306 	printf("normalised to reference white read:\n");
10307 	plot_wav(m, 0, emiswav);
10308 #endif
10309 
10310 	/* Compute two sample averages of the illuminant spectrum. */
10311 	avg01 = 0.5 * (emiswav[0] + emiswav[1]);
10312 
10313 	for (avg2227 = 0, j = 22; j < 28; j++) {
10314 		avg2227 += emiswav[j];
10315 	}
10316 	avg2227 /= (double)(28 - 22);
10317 
10318 	free_dvector(emiswav, -1, m->nraw-1);
10319 
10320 
10321 	/* And check them against tolerance for the illuminant. */
10322 	if (m->physfilt == 0x82) {		/* UV filter */
10323 		a1logd(p->log,2,"Checking white reference (UV): 0.0 < avg01 %f < 0.05, 1.2 < avg2227 %f < 1.76\n",avg01,avg2227);
10324 		if (0.0 < avg01 && avg01 < 0.05
10325 		 && 1.2 < avg2227 && avg2227 < 1.76) {
10326 			return I1PRO_OK;
10327 		}
10328 
10329 	} else {						/* No filter */
10330 		a1logd(p->log,2,"Checking white reference: 0.11 < avg01 %f < 0.22, 1.35 < avg2227 %f < 1.6\n",avg01,avg2227);
10331 		if (0.11 < avg01 && avg01 < 0.22
10332 		 && 1.35 < avg2227 && avg2227 < 1.6) {
10333 			return I1PRO_OK;
10334 		}
10335 	}
10336 	a1logd(p->log,2,"Checking white reference failed - out of tollerance");
10337 	return I1PRO_RD_WHITEREFERROR;
10338 }
10339 
10340 /* Compute a mode calibration factor given the reading of the white reference. */
10341 /* We will also calibrate & smooth hi-res emis_coef[1] if they are present. */
10342 /* Return I1PRO_RD_TRANSWHITEWARN if any of the transmission wavelengths are low. */
10343 /* May return some other error (malloc) */
10344 i1pro_code i1pro_compute_white_cal(
10345 	i1pro *p,
10346 	double *cal_factor0,	/* [nwav[0]] Calibration factor to compute */
10347 	double *white_ref0,		/* [nwav[0]] White reference to aim for, NULL for 1.0 */
10348 	double *white_read0,	/* [nwav[0]] The white that was read */
10349 	double *cal_factor1,	/* [nwav[1]] Calibration factor to compute */
10350 	double *white_ref1,		/* [nwav[1]] White reference to aim for, NULL for 1.0 */
10351 	double *white_read1,	/* [nwav[1]] The white that was read */
10352 	int do_emis_ft			/* Do emission hires fine tune with this info. */
10353 ) {
10354 	i1proimp *m = (i1proimp *)p->m;
10355 	i1pro_state *s = &m->ms[m->mmode];
10356 	int j, warn = I1PRO_OK;;
10357 
10358 #ifdef HIGH_RES
10359 	/* If we need to, fine calibrate the emission */
10360 	/* calibration coefficients, using the reflectance cal. */
10361 	/* illuminant as an (assumed) smooth light source reference. */
10362 	/* (Do this first, befor white_read0/cal_factor0 is overwritten by white cal.) */
10363 	if (do_emis_ft && m->hr_inited != 0 && white_ref1 != NULL) {
10364 		i1pro_code ev = I1PRO_OK;
10365 		int i;
10366 		double *lincal;
10367 		double *fudge;
10368 		xspect illA;
10369 #ifdef HIGH_RES_PLOT
10370 		double *targ_smth;		/* Hires target in smooth space */
10371 		double *old_emis;
10372 		double avgl;
10373 
10374 		if ((targ_smth = (double *)calloc(m->nwav[1], sizeof(double))) == NULL) {
10375 			return I1PRO_INT_MALLOC;
10376 		}
10377 		if ((old_emis = (double *)calloc(m->nwav[1], sizeof(double))) == NULL) {
10378 			return I1PRO_INT_MALLOC;
10379 		}
10380 		for (i = 0; i < m->nwav[1]; i++)
10381 			old_emis[i] = m->emis_coef[1][i];
10382 #endif
10383 
10384 		if ((lincal = (double *)calloc(m->nwav[0], sizeof(double))) == NULL) {
10385 			return I1PRO_INT_MALLOC;
10386 		}
10387 		if ((fudge = (double *)calloc(m->nwav[0], sizeof(double))) == NULL) {
10388 			return I1PRO_INT_MALLOC;
10389 		}
10390 
10391 		/* Fill in an xpsect with a standard illuminant spectrum */
10392 		if (standardIlluminant(&illA, icxIT_Ptemp, 2990.0)) {
10393 			a1loge(p->log,1,"i1pro_compute_white_cal: standardIlluminant() failed");
10394 			return I1PRO_INT_ASSERT;
10395 		}
10396 
10397 		for (j = 0; j < m->nwav[0]; j++) {
10398 			double wl = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], j);
10399 
10400 			lincal[j] = white_read0[j] * m->emis_coef[0][j]
10401 			          / (white_ref0[j] * value_xspect(&illA, wl));
10402 		}
10403 
10404 #ifndef NEVER
10405 		/* Generate the hires emis_coef by interpolating lincal */
10406 		/* using rspl, and reversing computation through hi-res readings */
10407 		{
10408 			rspl *trspl;			/* Upsample rspl */
10409 			cow sd[40];		/* Scattered data points of existing references */
10410 			datai glow, ghigh;
10411 			datao vlow, vhigh;
10412 			int gres[1];
10413 			double avgdev[1];
10414 			int ix;
10415 			co pp;
10416 
10417 			if ((trspl = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) {
10418 				a1logd(p->log,1,"i1pro: creating rspl for high res conversion failed\n");
10419 				return I1PRO_INT_NEW_RSPL_FAILED;
10420 			}
10421 
10422 			vlow[0] = 1e6;
10423 			vhigh[0] = -1e6;
10424 			for (ix = i = 0; i < m->nwav[0]; i++) {
10425 
10426 				sd[ix].p[0] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], i);
10427 				sd[ix].v[0] = lincal[i];
10428 				sd[ix].w = 1.0;
10429 
10430 				if (sd[ix].v[0] < vlow[0])
10431 					vlow[0] = sd[ix].v[0];
10432 				if (sd[ix].v[0] > vhigh[0])
10433 				    vhigh[0] = sd[ix].v[0];
10434 				ix++;
10435 			}
10436 
10437 			glow[0] = m->wl_short[1];
10438 			ghigh[0] = m->wl_long[1];
10439 			gres[0] = 6 * m->nwav[1];
10440 			avgdev[0] = 0.0;
10441 
10442 			/* The smoothness factor of 0.02 seems critical in tuning the RevE */
10443 			/* hires accuracy, as measured on an LCD display */
10444 			trspl->fit_rspl_w(trspl, 0, sd, ix, glow, ghigh, gres, vlow, vhigh, 0.05, avgdev, NULL);
10445 
10446 			/* Create a linear interp fudge factor to place interp at low */
10447 			/* res. exactly on lowres linear curve. This compensates */
10448 			/* if the rspl moves the target at the lowres points */
10449 			for (j = 0; j < m->nwav[0]; j++) {
10450 				double wl = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], j);
10451 
10452 				pp.p[0] = wl;
10453 				trspl->interp(trspl, &pp);
10454 
10455 				fudge[j] = lincal[j] / pp.v[0];
10456 			}
10457 
10458 			/* Compute hires emis_coef */
10459 			for (i = 0; i < m->nwav[1]; i++) {
10460 				double wl = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], i);
10461 				double ff;
10462 
10463 				pp.p[0] = wl;
10464 				trspl->interp(trspl, &pp);
10465 
10466 #ifdef HIGH_RES_PLOT
10467 				targ_smth[i] = pp.v[0];
10468 #endif
10469 				/* Invert lincal at high res */
10470 				ff = wav_lerp(m, 0, fudge, wl);
10471 				m->emis_coef[1][i] = (ff * pp.v[0] * white_ref1[i] * value_xspect(&illA, wl))
10472 				                     /white_read1[i];
10473 			}
10474 			trspl->del(trspl);
10475 		}
10476 
10477 #else
10478 		/* Generate the hires emis_coef by interpolating lincal */
10479 		/* using lagrange, and reversing computation through hi-res readings */
10480 		for (i = 0; i < m->nwav[1]; i++) {
10481 			int k;
10482 			double x[4], xw;
10483 			double y[4], yw;
10484 
10485 			xw = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], i);
10486 
10487 			/* Locate lowres index below it */
10488 			j = (int)floor(XSPECT_DIX(m->wl_short[0], m->wl_long[0], m->nwav[0], xw)) -1;
10489 			if (j < 0)
10490 				j = 0;
10491 			if (j > (m->nwav[0]-4))
10492 				j = (m->nwav[0]-4);
10493 
10494 			/* Setup the surrounding point values */
10495 			for (k = 0; k < 4; k++) {
10496 				x[k] = XSPECT_WL(m->wl_short[0], m->wl_long[0], m->nwav[0], j+k);
10497 				y[k] = lincal[j+k];
10498 			}
10499 
10500 			/* Compute interpolated value using Lagrange: */
10501 			yw = y[0] * (xw-x[1]) * (xw-x[2]) * (xw-x[3])
10502 				      /((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3]))
10503 			   + y[1] * (xw-x[0]) * (xw-x[2]) * (xw-x[3])
10504 				      /((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3]))
10505 			   + y[2] * (xw-x[0]) * (xw-x[1]) * (xw-x[3])
10506 				      /((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3]))
10507 			   + y[3] * (xw-x[0]) * (xw-x[1]) * (xw-x[2])
10508 				      /((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2]));
10509 
10510 			targ_smth[i] = yw;
10511 
10512 			/* Invert lincal at high res */
10513 			m->emis_coef[1][i] = (yw  * white_ref1[i] * value_xspect(&illA, xw))/white_read1[i];
10514 		}
10515 #endif /* NEVER */
10516 
10517 #ifdef HIGH_RES_PLOT
10518 		/* Plot linear target curve and measured curve */
10519 		{
10520 			double *xx, *y1, *y2, *y3, *y4;
10521 
10522 			xx = dvector(0, m->nwav[1]);		/* X  wl */
10523 			y1 = dvector(0, m->nwav[1]);
10524 			y2 = dvector(0, m->nwav[1]);
10525 			y3 = dvector(0, m->nwav[1]);
10526 			y4 = dvector(0, m->nwav[1]);
10527 
10528 			for (j = 0; j < (m->nwav[1]); j++) {
10529 				xx[j] = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j);
10530 				y1[j] = wav_lerp_cv(m, 0, lincal, xx[j], 0.0);
10531 				y2[j] = targ_smth[j];
10532 				y3[j] = white_read1[j] * wav_lerp(m, 0, m->emis_coef[0], xx[j])
10533 				        /(white_ref1[j] * value_xspect(&illA, xx[j]));
10534 				y4[j] = white_read1[j] * old_emis[j]
10535 				        /(white_ref1[j] * value_xspect(&illA, xx[j]));
10536 			}
10537 
10538 			printf("stdres interp targ (bk), smoothed targ (rd), measured hires resp. (gn), previous cal resp.(bu):\n");
10539 			do_plot6(xx, y1, y2, y3, y4, NULL, NULL, m->nwav[1]);
10540 			free_dvector(xx, 0, m->nwav[1]);
10541 			free_dvector(y1, 0, m->nwav[1]);
10542 			free_dvector(y2, 0, m->nwav[1]);
10543 			free_dvector(y4, 0, m->nwav[1]);
10544 		}
10545 		/* Plot target and achieved smooth space responses */
10546 		{
10547 			double *xx, *y2;
10548 
10549 			xx = dvector(0, m->nwav[1]);
10550 			y2 = dvector(0, m->nwav[1]);
10551 
10552 			for (j = 0; j < (m->nwav[1]); j++) {
10553 				xx[j] = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j);
10554 				y2[j] = white_read1[j] * m->emis_coef[1][j]
10555 				       /(white_ref1[j] * value_xspect(&illA, xx[j]));
10556 			}
10557 
10558 			printf("target smooth curve, achived smooth curve:\n");
10559 			do_plot6(xx, targ_smth, y2, NULL, NULL, NULL, NULL, m->nwav[1]);
10560 
10561 			free_dvector(xx, 0, m->nwav[1]);
10562 			free_dvector(y2, 0, m->nwav[1]);
10563 		}
10564 		/* Plot lowres and hires lamp response */
10565 		{
10566 			double *xx, *y1, *y2;
10567 
10568 			xx = dvector(0, m->nwav[1]);
10569 			y1 = dvector(0, m->nwav[1]);
10570 			y2 = dvector(0, m->nwav[1]);
10571 
10572 			for (j = 0; j < (m->nwav[1]); j++) {
10573 				xx[j] = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j);
10574 				y1[j] = wav_lerp(m, 0, white_read0, xx[j]) * wav_lerp(m, 0, m->emis_coef[0], xx[j])
10575 				        /wav_lerp(m, 0, white_ref0, xx[j]);
10576 				y2[j] = white_read1[j] * m->emis_coef[1][j]
10577 				       /white_ref1[j];
10578 			}
10579 
10580 			printf("lowres, high res lamp response:\n");
10581 			do_plot6(xx, y1, y2, NULL, NULL, NULL, NULL, m->nwav[1]);
10582 
10583 			free_dvector(xx, 0, m->nwav[1]);
10584 			free_dvector(y1, 0, m->nwav[1]);
10585 			free_dvector(y2, 0, m->nwav[1]);
10586 		}
10587 		/* Plot hires emis calibration */
10588 		{
10589 			double *xx;
10590 
10591 			xx = dvector(0, m->nwav[1]);		/* X  wl */
10592 
10593 			for (j = 0; j < (m->nwav[1]); j++) {
10594 				xx[j] = XSPECT_WL(m->wl_short[1], m->wl_long[1], m->nwav[1], j);
10595 			}
10596 
10597 			printf("orig upsampled + smoothed hires emis_coef:\n");
10598 			do_plot6(xx, old_emis, m->emis_coef[1], NULL, NULL, NULL, NULL, m->nwav[1]);
10599 			free_dvector(xx, 0, m->nwav[1]);
10600 		}
10601 		free(old_emis);
10602 		free(targ_smth);
10603 #endif /* HIGH_RES_PLOT */
10604 		free(fudge);
10605 		free(lincal);
10606 
10607 		m->emis_hr_cal = 1;		/* We don't have to do a reflective calibration */
10608 
10609 		/* Make sure these are updated */
10610 		if ((ev = i1pro_create_hr_calfactors(p, 1)) != I1PRO_OK)
10611 			return ev;
10612 	}
10613 #endif /* HIGH_RES */
10614 
10615 	if (white_ref0 == NULL) {		/* transmission white reference */
10616 		double avgwh = 0.0;
10617 
10618 		/* Compute average white reference reading */
10619 		for (j = 0; j < m->nwav[0]; j++)
10620 			avgwh += white_read0[j];
10621 		avgwh /= (double)m->nwav[0];
10622 
10623 		/* For each wavelength */
10624 		for (j = 0; j < m->nwav[0]; j++) {
10625 			/* If reference is < 0.4% of average */
10626 			if (white_read0[j]/avgwh < 0.004) {
10627 				cal_factor0[j] = 1.0/(0.004 * avgwh);
10628 				warn = I1PRO_RD_TRANSWHITEWARN;
10629 			} else {
10630 				cal_factor0[j] = 1.0/white_read0[j];
10631 			}
10632 		}
10633 
10634 	} else {					/* Reflection white reference */
10635 
10636 		/* For each wavelength */
10637 		for (j = 0; j < m->nwav[0]; j++) {
10638 			if (white_read0[j] < 1000.0)
10639 				cal_factor0[j] = white_ref0[j]/1000.0;
10640 			else
10641 				cal_factor0[j] = white_ref0[j]/white_read0[j];
10642 		}
10643 	}
10644 
10645 #ifdef HIGH_RES
10646 	if (m->hr_inited == 0)
10647 		return warn;
10648 
10649 	if (white_ref1 == NULL) {		/* transmission white reference */
10650 		double avgwh = 0.0;
10651 
10652 		/* Compute average white reference reading */
10653 		for (j = 0; j < m->nwav[1]; j++)
10654 			avgwh += white_read1[j];
10655 		avgwh /= (double)m->nwav[1];
10656 
10657 		/* For each wavelength */
10658 		for (j = 0; j < m->nwav[1]; j++) {
10659 			/* If reference is < 0.4% of average */
10660 			if (white_read1[j]/avgwh < 0.004) {
10661 				cal_factor1[j] = 1.0/(0.004 * avgwh);
10662 				warn = I1PRO_RD_TRANSWHITEWARN;
10663 			} else {
10664 				cal_factor1[j] = 1.0/white_read1[j];
10665 			}
10666 		}
10667 
10668 	} else {					/* Reflection white reference */
10669 
10670 		/* For each wavelength */
10671 		for (j = 0; j < m->nwav[1]; j++) {
10672 			if (white_read1[j] < 1000.0)
10673 				cal_factor1[j] = white_ref1[j]/1000.0;
10674 			else
10675 				cal_factor1[j] = white_ref1[j]/white_read1[j];
10676 		}
10677 #endif /* HIGH_RES */
10678 	}
10679 	return warn;
10680 }
10681 
10682 /* For adaptive mode, compute a new integration time and gain mode */
10683 /* in order to optimise the sensor values. Note that the Rev E doesn't have */
10684 /* a high gain mode. */
10685 i1pro_code i1pro_optimise_sensor(
10686 	i1pro *p,
10687 	double *pnew_int_time,
10688 	int    *pnew_gain_mode,
10689 	double cur_int_time,
10690 	int    cur_gain_mode,
10691 	int    permithg,		/* nz to permit switching to high gain mode */
10692 	int    permitclip,		/* nz to permit clipping out of range int_time, else error */
10693 	double targoscale,		/* Optimising target scale ( <= 1.0) */
10694 	double scale			/* scale needed of current int time to reach optimum */
10695 ) {
10696 	i1pro_code ev = I1PRO_OK;
10697 	i1proimp *m = (i1proimp *)p->m;
10698 	i1pro_state *s = &m->ms[m->mmode];
10699 	double new_int_time;
10700 	int    new_gain_mode;
10701 
10702 	a1logd(p->log,3,"i1pro_optimise_sensor called, inttime %f, gain mode %d, targ scale %f, scale %f\n",cur_int_time,cur_gain_mode, targoscale, scale);
10703 
10704 	/* Compute new normal gain integration time */
10705 	if (cur_gain_mode)		/* If high gain */
10706 		new_int_time = cur_int_time * scale * m->highgain;
10707 	else
10708 		new_int_time = cur_int_time * scale;
10709 	new_gain_mode = 0;
10710 
10711 	a1logd(p->log,3,"target inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
10712 
10713 	/* Adjust to low light situation by increasing the integration time. */
10714 	if (new_int_time > s->targmaxitime) {	/* Exceeding target integration time */
10715 		if (s->targmaxitime/new_int_time > s->targoscale2) {	/* But within range */
10716 			/* Compromise sensor target value to maintain targmaxitime */
10717 			new_int_time = s->targmaxitime;
10718 			a1logd(p->log,3,"Using targmaxitime with compromise sensor target\n");
10719 		} else {
10720 			/* Target reduced sensor value to give improved measurement time and continuity */
10721 			new_int_time *= s->targoscale2;
10722 			a1logd(p->log,3,"Using compromse sensor target\n");
10723 		}
10724 #ifdef USE_HIGH_GAIN_MODE
10725 		/* !! Should change this so that it doesn't affect int. time, */
10726 		/* but that we simply switch to high gain mode when the */
10727 		/* expected level is < target_level/gain */
10728 		/* Hmm. It may not be a good idea to use high gain mode if it compromises */
10729 		/* the longer integration time which reduces noise. */
10730 		if (p->itype != instI1Pro2 && new_int_time > m->max_int_time && permithg) {
10731 			new_int_time /= m->highgain;
10732 			new_gain_mode = 1;
10733 			a1logd(p->log,3,"Switching to high gain mode\n");
10734 		}
10735 #endif
10736 	}
10737 	a1logd(p->log,3,"after low light adjust, inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
10738 
10739 	/* Deal with still low light */
10740 	if (new_int_time > m->max_int_time) {
10741 		if (permitclip)
10742 			new_int_time = m->max_int_time;
10743 		else
10744 			return I1PRO_RD_LIGHTTOOLOW;
10745 	}
10746 	a1logd(p->log,3,"after low light clip, inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
10747 
10748 	/* Adjust to high light situation */
10749 	if (new_int_time < m->min_int_time && targoscale < 1.0) {
10750 		new_int_time /= targoscale;			/* Aim for non-scaled sensor optimum */
10751 		if (new_int_time > m->min_int_time)	/* But scale as much as possible */
10752 			new_int_time = m->min_int_time;
10753 	}
10754 	a1logd(p->log,3,"after high light adjust, inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
10755 
10756 	/* Deal with still high light */
10757 	if (new_int_time < m->min_int_time) {
10758 		if (permitclip)
10759 			new_int_time = m->min_int_time;
10760 		else
10761 			return I1PRO_RD_LIGHTTOOHIGH;
10762 	}
10763 	a1logd(p->log,3,"after high light clip, returning inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
10764 
10765 	if (pnew_int_time != NULL)
10766 		*pnew_int_time = new_int_time;
10767 
10768 	if (pnew_gain_mode != NULL)
10769 		*pnew_gain_mode = new_gain_mode;
10770 
10771 	return I1PRO_OK;
10772 }
10773 
10774 /* Compute the number of measurements needed, given the target */
10775 /* measurement time and integration time. Will return 0 if target time is 0 */
10776 int i1pro_comp_nummeas(
10777 	i1pro *p,
10778 	double meas_time,
10779 	double int_time
10780 ) {
10781 	int nmeas;
10782 	if (meas_time <= 0.0)
10783 		return 0;
10784 	nmeas = (int)floor(meas_time/int_time + 0.5);
10785 	if (nmeas < 1)
10786 		nmeas = 1;
10787 	return nmeas;
10788 }
10789 
10790 /* Convert the dark interpolation data to a useful state */
10791 /* (also allow for interpolating the shielded cell values) */
10792 void
10793 i1pro_prepare_idark(
10794 	i1pro *p
10795 ) {
10796 	i1proimp *m = (i1proimp *)p->m;
10797 	i1pro_state *s = &m->ms[m->mmode];
10798 	int i, j;
10799 
10800 	/* For normal and high gain */
10801 	for (i = 0; i < 4; i+= 2) {
10802 		for (j = -1; j < m->nraw; j++) {
10803 			double d01, d1;
10804 			d01 = s->idark_data[i+0][j] * s->idark_int_time[i+0];
10805 			d1 = s->idark_data[i+1][j]  * s->idark_int_time[i+1];
10806 
10807 			/* Compute increment */
10808 			s->idark_data[i+1][j] = (d1 - d01)/(s->idark_int_time[i+1] - s->idark_int_time[i+0]);
10809 
10810 			/* Compute base */
10811 			s->idark_data[i+0][j] = d01 - s->idark_data[i+1][j] * s->idark_int_time[i+0];
10812 		}
10813 		if (p->itype == instI1Pro2) 	/* Rev E doesn't have high gain mode */
10814 			break;
10815 	}
10816 }
10817 
10818 /* Create the dark reference for the given integration time and gain */
10819 /* by interpolating from the 4 readings taken earlier. */
10820 i1pro_code
10821 i1pro_interp_dark(
10822 	i1pro *p,
10823 	double *result,		/* Put result of interpolation here */
10824 	double inttime,
10825 	int gainmode
10826 ) {
10827 	i1proimp *m = (i1proimp *)p->m;
10828 	i1pro_state *s = &m->ms[m->mmode];
10829 	int i, j;
10830 
10831 	if (!s->idark_valid)
10832 		return I1PRO_INT_NOTCALIBRATED;
10833 
10834 	i = 0;
10835 #ifdef USE_HIGH_GAIN_MODE
10836 	if (gainmode)
10837 		i = 2;
10838 #endif
10839 
10840 	for (j = -1; j < m->nraw; j++) {
10841 		double tt;
10842 		tt = s->idark_data[i+0][j] + inttime * s->idark_data[i+1][j];
10843 		tt /= inttime;
10844 		result[j] = tt;
10845 	}
10846 	return I1PRO_OK;
10847 }
10848 
10849 /* Set the noinitcalib mode */
10850 void i1pro_set_noinitcalib(i1pro *p, int v, int losecs) {
10851 	i1proimp *m = (i1proimp *)p->m;
10852 
10853 	/* Ignore disabling init calib if more than losecs since instrument was open */
10854 	if (v && losecs != 0 && m->lo_secs >= losecs) {
10855 		a1logd(p->log,3,"initcalib disable ignored because %d >= %d secs\n",m->lo_secs,losecs);
10856 		return;
10857 	}
10858 	m->noinitcalib = v;
10859 }
10860 
10861 /* Set the trigger config */
10862 void i1pro_set_trig(i1pro *p, inst_opt_type trig) {
10863 	i1proimp *m = (i1proimp *)p->m;
10864 	m->trig = trig;
10865 }
10866 
10867 /* Return the trigger config */
10868 inst_opt_type i1pro_get_trig(i1pro *p) {
10869 	i1proimp *m = (i1proimp *)p->m;
10870 	return m->trig;
10871 }
10872 
10873 /* Switch thread handler */
10874 int i1pro_switch_thread(void *pp) {
10875 	int nfailed = 0;
10876 	i1pro *p = (i1pro *)pp;
10877 	i1proimp *m = (i1proimp *)p->m;
10878 	i1pro_code rv = I1PRO_OK;
10879 	a1logd(p->log,3,"Switch thread started\n");
10880 //	for (nfailed = 0;nfailed < 5;)
10881 	/* Try indefinitely, in case instrument is put to sleep */
10882 	for (;;) {
10883 		rv = i1pro_waitfor_switch_th(p, SW_THREAD_TIMEOUT);
10884 		a1logd(p->log,8,"Switch handler triggered with rv %d, th_term %d\n",rv,m->th_term);
10885 		if (m->th_term) {
10886 			m->th_termed = 1;
10887 			break;
10888 		}
10889 		if (rv == I1PRO_INT_BUTTONTIMEOUT) {
10890 			nfailed = 0;
10891 			continue;
10892 		}
10893 		if (rv != I1PRO_OK) {
10894 			nfailed++;
10895 			a1logd(p->log,3,"Switch thread failed with 0x%x\n",rv);
10896 			continue;
10897 		}
10898 		m->switch_count++;
10899 		if (!m->hide_switch && p->eventcallback != NULL) {
10900 			p->eventcallback(p->event_cntx, inst_event_switch);
10901 		}
10902 	}
10903 	a1logd(p->log,3,"Switch thread returning\n");
10904 	return rv;
10905 }
10906 
10907 /* ============================================================ */
10908 /* Low level i1pro commands */
10909 
10910 /* USB Instrument commands */
10911 
10912 /* Reset the instrument */
10913 i1pro_code
10914 i1pro_reset(
10915 	i1pro *p,
10916 	int mask	/* reset mask ?. Known values ar 0x1f, 0x07, 0x01 */
10917 				/* 0x1f = normal resent */
10918 				/* 0x01 = establish high power mode */
10919 ) {
10920 	i1proimp *m = (i1proimp *)p->m;
10921 	unsigned char pbuf[2];	/* 1 or 2 bytes to write */
10922 	int len = 1;			/* Message length */
10923 	int se, rv = I1PRO_OK;
10924 	int stime = 0;
10925 
10926 	a1logd(p->log,2,"i1pro_reset: reset with mask 0x%02x @ %d msec\n",
10927 	                          mask,(stime = msec_time()) - m->msec);
10928 
10929 	pbuf[0] = mask;
10930 
10931 	if (p->itype == instI1Pro2) {
10932 		pbuf[1] = 0;		/* Not known what i1pro2 second byte is for */
10933 		len = 2;
10934 	}
10935 
10936 	se = p->icom->usb_control(p->icom,
10937 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
10938 	                   0xCA, 0, 0, pbuf, len, 2.0);
10939 
10940 	rv = icoms2i1pro_err(se);
10941 
10942 	a1logd(p->log,2,"i1pro_reset: complete, ICOM err 0x%x (%d msec)\n",se,msec_time()-stime);
10943 
10944 	/* Allow time for hardware to stabalize */
10945 	msec_sleep(100);
10946 
10947 	/* Make sure that we re-initialize the measurement mode */
10948 	m->c_intclocks = 0;
10949 	m->c_lampclocks = 0;
10950 	m->c_nummeas = 0;
10951 	m->c_measmodeflags = 0;
10952 
10953 	return rv;
10954 }
10955 
10956 /* Read from the EEProm */
10957 i1pro_code
10958 i1pro_readEEProm(
10959 	i1pro *p,
10960 	unsigned char *buf,		/* Where to read it to */
10961 	int addr,				/* Address in EEprom to read from */
10962 	int size				/* Number of bytes to read (max 65535) */
10963 ) {
10964 	i1proimp *m = (i1proimp *)p->m;
10965 	int rwbytes;			/* Data bytes read or written */
10966 	unsigned char pbuf[8];	/* Write EEprom parameters */
10967 	int len = 8;			/* Message length */
10968 	int se, rv = I1PRO_OK;
10969 	int stime;
10970 
10971 	if (size >= 0x10000)
10972 		return I1PRO_INT_EETOOBIG;
10973 
10974 	a1logd(p->log,2,"i1pro_readEEProm: address 0x%x size 0x%x @ %d msec\n",
10975 	                           addr, size, (stime = msec_time()) - m->msec);
10976 
10977 	int2buf(&pbuf[0], addr);
10978 	short2buf(&pbuf[4], size);
10979 	pbuf[6] = pbuf[7] = 0;		/* Ignored */
10980 
10981 	if (p->itype == instI1Pro2)
10982 		len = 6;
10983 
10984   	se = p->icom->usb_control(p->icom,
10985 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
10986 	                   0xC4, 0, 0, pbuf, len, 2.0);
10987 
10988 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
10989 		a1logd(p->log,1,"i1pro_readEEProm: read failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
10990 		return rv;
10991 	}
10992 
10993 	/* Now read the bytes */
10994 	se = p->icom->usb_read(p->icom, NULL, 0x82, buf, size, &rwbytes, 5.0);
10995 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
10996 		a1logd(p->log,1,"i1pro_readEEProm: read failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
10997 		return rv;
10998 	}
10999 
11000 	if (rwbytes != size) {
11001 		a1logd(p->log,1,"i1pro_readEEProm: 0x%x bytes, short read error\n",rwbytes);
11002 		return I1PRO_HW_EE_SHORTREAD;
11003 	}
11004 
11005 	if (p->log->debug >= 7) {
11006 		int i;
11007 		char oline[100], *bp = oline;
11008 		for (i = 0; i < size; i++) {
11009 			if ((i % 16) == 0)
11010 				bp += sprintf(bp,"    %04x:",i);
11011 			bp += sprintf(bp," %02x",buf[i]);
11012 			if ((i+1) >= size || ((i+1) % 16) == 0) {
11013 				bp += sprintf(bp,"\n");
11014 				a1logd(p->log,7,oline);
11015 				bp = oline;
11016 			}
11017 		}
11018 	}
11019 
11020 	a1logd(p->log,2,"i1pro_readEEProm: 0x%x bytes, ICOM err 0x%x (%d msec)\n",
11021 	                                          rwbytes, se, msec_time()-stime);
11022 
11023 	return rv;
11024 }
11025 
11026 /* Write to the EEProm */
11027 i1pro_code
11028 i1pro_writeEEProm(
11029 	i1pro *p,
11030 	unsigned char *buf,		/* Where to write from */
11031 	int addr,				/* Address in EEprom to write to */
11032 	int size				/* Number of bytes to write (max 65535) */
11033 ) {
11034 	i1proimp *m = (i1proimp *)p->m;
11035 	int rwbytes;			/* Data bytes read or written */
11036 	unsigned char pbuf[8];	/* Write EEprom parameters */
11037 	int len = 8;			/* Message length */
11038 	int se = 0, rv = I1PRO_OK;
11039 	int i;
11040 	int stime;
11041 
11042 	/* Don't write over fixed values, as the instrument could become unusable.. */
11043 	if (addr < 0 || addr > 0x1000 || (addr + size) >= 0x1000)
11044 		return I1PRO_INT_EETOOBIG;
11045 
11046 	a1logd(p->log,2,"i1pro_writeEEProm: address 0x%x size 0x%x @ %d msec\n",
11047 	                             addr,size, (stime = msec_time()) - m->msec);
11048 
11049 	if (p->log->debug >= 6) {
11050 		int i;
11051 		char oline[100], *bp = oline;
11052 		for (i = 0; i < size; i++) {
11053 			if ((i % 16) == 0)
11054 				bp += sprintf(bp,"    %04x:",i);
11055 			bp += sprintf(bp," %02x",buf[i]);
11056 			if ((i+1) >= size || ((i+1) % 16) == 0) {
11057 				bp += sprintf(bp,"\n");
11058 				a1logd(p->log,6,oline);
11059 				bp = oline;
11060 			}
11061 		}
11062 	}
11063 
11064 #ifdef ENABLE_WRITE
11065 	int2buf(&pbuf[0], addr);
11066 	short2buf(&pbuf[4], size);
11067 	short2buf(&pbuf[6], 0x100);		/* Might be accidental, left over from getmisc.. */
11068 
11069 	if (p->itype == instI1Pro2)
11070 		len = 6;
11071 
11072   	se = p->icom->usb_control(p->icom,
11073 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11074 	                   0xC3, 0, 0, pbuf, len, 2.0);
11075 
11076 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11077 		a1logd(p->log,2,"i1pro_writeEEProm: write failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
11078 		return rv;
11079 	}
11080 
11081 	/* Now write the bytes */
11082 	se = p->icom->usb_write(p->icom, NULL, 0x03, buf, size, &rwbytes, 5.0);
11083 
11084 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11085 		a1logd(p->log,1,"i1pro_writeEEProm: failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
11086 		return rv;
11087 	}
11088 
11089 	if (rwbytes != size) {
11090 		a1logd(p->log,1,"i1pro_writeEEProm: 0x%x bytes, short write error\n",rwbytes);
11091 		return I1PRO_HW_EE_SHORTWRITE;
11092 	}
11093 
11094 	/* Now we write two separate bytes of 0 - confirm write ?? */
11095 	for (i = 0; i < 2; i++) {
11096 		pbuf[0] = 0;
11097 
11098 		/* Now write the bytes */
11099 		se = p->icom->usb_write(p->icom, NULL, 0x03, pbuf, 1, &rwbytes, 5.0);
11100 
11101 		if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11102 			a1logd(p->log,1,"i1pro_writeEEProm: write failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
11103 			return rv;
11104 		}
11105 
11106 		if (rwbytes != 1) {
11107 			a1logd(p->log,1,"i1pro_writeEEProm: 0x%x bytes, short write error\n",rwbytes);
11108 			return I1PRO_HW_EE_SHORTWRITE;
11109 		}
11110 	}
11111 	a1logd(p->log,2,"i1pro_writeEEProm: 0x%x bytes, ICOM err 0x%x (%d msec)\n",
11112 	                   size, se, msec_time()-stime);
11113 
11114 	/* The instrument needs some recovery time after a write */
11115 	msec_sleep(50);
11116 
11117 #else /* ENABLE_WRITE */
11118 
11119 	a1logd(p->log,2,"i1pro_writeEEProm: (NOT) 0x%x bytes, ICOM err 0x%x\n",size, se);
11120 
11121 #endif /* ENABLE_WRITE */
11122 
11123 	return rv;
11124 }
11125 
11126 /* Get the miscellaneous status */
11127 /* return pointers may be NULL if not needed. */
11128 i1pro_code
11129 i1pro_getmisc(
11130 	i1pro *p,
11131 	int *fwrev,		/* Return the hardware version number */
11132 	int *unkn1,		/* Unknown status, set after doing a measurement */
11133 	int *maxpve,	/* Maximum positive value in sensor readings */
11134 	int *unkn3,		/* Unknown status, usually 1 */
11135 	int *powmode	/* 0 = high power mode, 8 = low power mode */
11136 ) {
11137 	i1proimp *m = (i1proimp *)p->m;
11138 	unsigned char pbuf[8];	/* status bytes read */
11139 	int _fwrev;
11140 	int _unkn1;
11141 	int _maxpve;
11142 	int _unkn3;
11143 	int _powmode;
11144 	int se, rv = I1PRO_OK;
11145 	int stime = 0;
11146 
11147 	a1logd(p->log,2,"i1pro_getmisc: @ %d msec\n",(stime = msec_time()) - m->msec);
11148 
11149 	se = p->icom->usb_control(p->icom,
11150 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11151 	                   0xC9, 0, 0, pbuf, 8, 2.0);
11152 
11153 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11154 		a1logd(p->log,1,"i1pro_getmisc: failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
11155 		return rv;
11156 	}
11157 
11158 	_fwrev   = buf2ushort(&pbuf[0]);
11159 	_unkn1   = buf2ushort(&pbuf[2]);	/* Value set after each read. Average ?? */
11160 	_maxpve  = buf2ushort(&pbuf[4]);
11161 	_unkn3   = pbuf[6];		/* Flag values are tested, but don't seem to be used ? */
11162 	_powmode = pbuf[7];
11163 
11164 	a1logd(p->log,2,"i1pro_getmisc: returning %d, 0x%04x, 0x%04x, 0x%02x, 0x%02x ICOM err 0x%x (%d msec)\n",
11165 	                   _fwrev, _unkn1, _maxpve, _unkn3, _powmode, se, msec_time()-stime);
11166 
11167 	if (fwrev != NULL) *fwrev = _fwrev;
11168 	if (unkn1 != NULL) *unkn1 = _unkn1;
11169 	if (maxpve != NULL) *maxpve = _maxpve;
11170 	if (unkn3 != NULL) *unkn3 = _unkn3;
11171 	if (powmode != NULL) *powmode = _powmode;
11172 
11173 	return rv;
11174 }
11175 
11176 /* Get the current measurement parameters */
11177 /* Return pointers may be NULL if not needed. */
11178 i1pro_code
11179 i1pro_getmeasparams(
11180 	i1pro *p,
11181 	int *intclocks,		/* Number of integration clocks */
11182 	int *lampclocks,	/* Number of lamp turn on sub-clocks */
11183 	int *nummeas,		/* Number of measurements */
11184 	int *measmodeflags	/* Measurement mode flags */
11185 ) {
11186 	i1proimp *m = (i1proimp *)p->m;
11187 	unsigned char pbuf[8];	/* status bytes read */
11188 	int _intclocks;
11189 	int _lampclocks;
11190 	int _nummeas;
11191 	int _measmodeflags;
11192 	int se, rv = I1PRO_OK;
11193 	int stime = 0;
11194 
11195 	a1logd(p->log,2,"i1pro_getmeasparams: @ %d msec\n", (stime = msec_time()) - m->msec);
11196 
11197 	se = p->icom->usb_control(p->icom,
11198 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11199 	                   0xC2, 0, 0, pbuf, 8, 2.0);
11200 
11201 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11202 		a1logd(p->log,1,"i1pro_getmeasparams: failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
11203 		return rv;
11204 	}
11205 
11206 	_intclocks     = buf2ushort(&pbuf[0]);
11207 	_lampclocks    = buf2ushort(&pbuf[2]);
11208 	_nummeas       = buf2ushort(&pbuf[4]);
11209 	_measmodeflags = pbuf[6];
11210 
11211 	a1logd(p->log,2,"i1pro_getmeasparams: returning %d, %d, %d, 0x%02x ICOM err 0x%x (%d msec)\n",
11212 	                   _intclocks, _lampclocks, _nummeas, _measmodeflags, se, msec_time()-stime);
11213 
11214 	if (intclocks != NULL) *intclocks = _intclocks;
11215 	if (lampclocks != NULL) *lampclocks = _lampclocks;
11216 	if (nummeas != NULL) *nummeas = _nummeas;
11217 	if (measmodeflags != NULL) *measmodeflags = _measmodeflags;
11218 
11219 	return rv;
11220 }
11221 
11222 /* Set the current measurement parameters */
11223 /* Return pointers may be NULL if not needed. */
11224 /* Quirks:
11225 
11226 	Rev. A upgrade:
11227 	Rev. B:
11228 		Appears to have a bug where the measurement time
11229 		is the sum of the previous measurement plus the current measurement.
11230 		It doesn't seem to alter the integration time though.
11231 		There is no obvious way of fixing this (ie. reseting the instrument
11232 		doesn't work).
11233 
11234 	Rev. D:
11235 		It appears that setting intclocks to 0, toggles to/from
11236 		a half clock speed mode. (?)
11237 */
11238 
11239 i1pro_code
11240 i1pro_setmeasparams(
11241 	i1pro *p,
11242 	int intclocks,		/* Number of integration clocks */
11243 	int lampclocks,		/* Number of lamp turn on sub-clocks */
11244 	int nummeas,		/* Number of measurements */
11245 	int measmodeflags	/* Measurement mode flags */
11246 ) {
11247 	i1proimp *m = (i1proimp *)p->m;
11248 	unsigned char pbuf[8];	/* command bytes written */
11249 	int se, rv = I1PRO_OK;
11250 	int stime = 0;
11251 
11252 	a1logd(p->log,2,"i1pro_setmeasparams: %d, %d, %d, 0x%02x @ %d msec\n",
11253 	                   intclocks, lampclocks, nummeas, measmodeflags,
11254 	                   (stime = msec_time()) - m->msec);
11255 
11256 	short2buf(&pbuf[0], intclocks);
11257 	short2buf(&pbuf[2], lampclocks);
11258 	short2buf(&pbuf[4], nummeas);
11259 	pbuf[6] = measmodeflags;
11260 	pbuf[7] = 0;
11261 
11262 	se = p->icom->usb_control(p->icom,
11263 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11264 	                   0xC1, 0, 0, pbuf, 8, 2.0);
11265 
11266 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11267 		a1logd(p->log,1,"i1pro_setmeasparams: failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
11268 		return rv;
11269 	}
11270 
11271 	a1logd(p->log,2,"i1pro_setmeasparams: returning ICOM err 0x%x (%d msec)\n",
11272 	                                                     se,msec_time()-stime);
11273 	return rv;
11274 }
11275 
11276 /* Delayed trigger implementation, called from thread */
11277 static int
11278 i1pro_delayed_trigger(void *pp) {
11279 	i1pro *p = (i1pro *)pp;
11280 	i1proimp *m = (i1proimp *)p->m;
11281 	int se, rv = I1PRO_OK;
11282 	int stime = 0;
11283 
11284 	if ((m->c_measmodeflags & I1PRO_MMF_NOLAMP) == 0) {		/* Lamp will be on for measurement */
11285 		m->llampoffon = msec_time();						/* Record when it turned on */
11286 //		printf("~1 got lamp off -> on at %d (%f)\n",m->llampoffon, (m->llampoffon - m->llamponoff)/1000.0);
11287 
11288 	}
11289 
11290 	a1logd(p->log,2,"i1pro_delayed_trigger: start sleep @ %d msec\n", msec_time() - m->msec);
11291 
11292 #ifdef USE_RD_SYNC
11293 	p->icom->usb_wait_io(p->icom, &m->rd_sync);		/* Wait for read to start */
11294 #else
11295 	/* Delay the trigger */
11296 	msec_sleep(m->trig_delay);
11297 #endif
11298 
11299 	m->tr_t1 = msec_time();		/* Diagnostic */
11300 
11301 	a1logd(p->log,2,"i1pro_delayed_trigger: trigger @ %d msec\n",(stime = msec_time()) - m->msec);
11302 
11303 	se = p->icom->usb_control(p->icom,
11304 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11305 	                   0xC0, 0, 0, NULL, 0, 2.0);
11306 	m->trigstamp = usec_time();
11307 	m->tr_t2 = msec_time();		/* Diagnostic */
11308 
11309 	m->trig_se = se;
11310 	m->trig_rv = icoms2i1pro_err(se);
11311 
11312 	a1logd(p->log,2,"i1pro_delayed_trigger: returning ICOM err 0x%x (%d msec)\n",
11313 	                                                       se,msec_time()-stime);
11314 
11315 	return 0;
11316 }
11317 
11318 /* Trigger a measurement after the nominated delay */
11319 /* The actual return code will be in m->trig_rv after the delay. */
11320 /* This allows us to start the measurement read before the trigger, */
11321 /* ensuring that process scheduling latency can't cause the read to fail. */
11322 i1pro_code
11323 i1pro_triggermeasure(i1pro *p, int delay) {
11324 	i1proimp *m = (i1proimp *)p->m;
11325 	int rv = I1PRO_OK;
11326 
11327 	a1logd(p->log,2,"i1pro_triggermeasure: trigger after %dmsec delay @ %d msec\n",
11328 	                                                 delay, msec_time() - m->msec);
11329 
11330 	/* NOTE := would be better here to create thread once, and then trigger it */
11331 	/* using a condition variable. */
11332 	if (m->trig_thread != NULL) {
11333 		m->trig_thread->del(m->trig_thread);
11334 		m->trig_thread = NULL;
11335 	}
11336 
11337     m->tr_t1 = m->tr_t2 = m->tr_t3 = m->tr_t4 = m->tr_t5 = m->tr_t6 = m->tr_t7 = 0;
11338 	m->trig_delay = delay;
11339 
11340 	if ((m->trig_thread = new_athread(i1pro_delayed_trigger, (void *)p)) == NULL) {
11341 		a1logd(p->log,1,"i1pro_triggermeasure: creating delayed trigger thread failed\n");
11342 		return I1PRO_INT_THREADFAILED;
11343 	}
11344 
11345 #ifdef WAIT_FOR_DELAY_TRIGGER	/* hack to diagnose threading problems */
11346 	while (m->tr_t2 == 0) {
11347 		Sleep(1);
11348 	}
11349 #endif
11350 	a1logd(p->log,2,"i1pro_triggermeasure: scheduled triggering OK\n");
11351 
11352 	return rv;
11353 }
11354 
11355 /* Read a measurements results. */
11356 /* A buffer full of bytes is returned. */
11357 /* (This will fail on a Rev. A if there is more than about a 40 msec delay */
11358 /* between triggering the measurement and starting this read. */
11359 /* It appears that the read can be pending before triggering though. */
11360 /* Scan reads will also terminate if there is too great a delay beteween each read.) */
11361 static i1pro_code
11362 i1pro_readmeasurement(
11363 	i1pro *p,
11364 	int inummeas,			/* Initial number of measurements to expect */
11365 	int scanflag,			/* NZ if in scan mode to continue reading */
11366 	unsigned char *buf,		/* Where to read it to */
11367 	int bsize,				/* Bytes available in buffer */
11368 	int *nummeas,			/* Return number of readings measured */
11369 	i1p_mmodif mmodif		/* Measurement modifier enum */
11370 ) {
11371 	i1proimp *m = (i1proimp *)p->m;
11372 	unsigned char *ibuf = buf;	/* Incoming buffer */
11373 	int nmeas;					/* Number of measurements for this read */
11374 	double top, extra;			/* Time out period */
11375 	int rwbytes;				/* Data bytes read or written */
11376 	int se, rv = I1PRO_OK;
11377 	int treadings = 0;
11378 	int stime = 0;
11379 //	int gotshort = 0;			/* nz when got a previous short reading */
11380 
11381 	if ((bsize % (m->nsen * 2)) != 0) {
11382 		return I1PRO_INT_ODDREADBUF;
11383 	}
11384 
11385 	a1logd(p->log,2,"i1pro_readmeasurement: inummeas %d, scanflag %d, address %p bsize 0x%x "
11386 	          "@ %d msec\n",inummeas, scanflag, buf, bsize, (stime = msec_time()) - m->msec);
11387 
11388 	extra = 2.0;		/* Extra timeout margin */
11389 
11390 	/* Deal with Rev A+ & Rev B quirk: */
11391 	if ((m->fwrev >= 200 && m->fwrev < 300)
11392 	 || (m->fwrev >= 300 && m->fwrev < 400))
11393 		extra += m->l_inttime;
11394 	m->l_inttime = m->c_inttime;
11395 
11396 #ifdef SINGLE_READ
11397 	if (scanflag == 0)
11398 		nmeas = inummeas;
11399 	else
11400 		nmeas = bsize / (m->nsen * 2);		/* Use a single large read */
11401 #else
11402 	nmeas = inummeas;				/* Smaller initial number of measurements */
11403 #endif
11404 
11405 	top = extra + m->c_inttime * nmeas;
11406 	if ((m->c_measmodeflags & I1PRO_MMF_NOLAMP) == 0)	/* Lamp is on */
11407 		top += m->c_lamptime;
11408 
11409 	/* NOTE :- for a scan on Rev. A, if we don't read fast enough the Eye-One will */
11410 	/* assume it should stop sending, even though the user has the switch pressed. */
11411 	/* For the rev A, this is quite a small margin (aprox. 1 msec ?) */
11412 	/* The Rev D has a lot more buffering, and is quite robust. */
11413 	/* By using the delayed trigger and a single read, this problem is usually */
11414 	/* eliminated. */
11415 	/* An unexpected short read seems to lock the instrument up. Not currently */
11416 	/* sure what sequence would recover it for a retry of the read. */
11417 	for (;;) {
11418 		int size;		/* number of bytes to read */
11419 
11420 		size = m->nsen * 2 * nmeas;
11421 
11422 		if (size > bsize) {		/* oops, no room for read */
11423 			a1logd(p->log,1,"i1pro_readmeasurement: buffer was too short for scan\n");
11424 			return I1PRO_INT_MEASBUFFTOOSMALL;
11425 		}
11426 
11427 		m->tr_t6 = msec_time();	/* Diagnostic, start of subsequent reads */
11428 		if (m->tr_t3 == 0) m->tr_t3 = m->tr_t6;		/* Diagnostic, start of first read */
11429 
11430 		se = p->icom->usb_read(p->icom, &m->rd_sync, 0x82, buf, size, &rwbytes, top);
11431 
11432 		m->tr_t5 = m->tr_t7;
11433 		m->tr_t7 = msec_time();	/* Diagnostic, end of subsequent reads */
11434 		if (m->tr_t4 == 0) {
11435 			m->tr_t5 = m->tr_t2;
11436 			m->tr_t4 = m->tr_t7;	/* Diagnostic, end of first read */
11437 		}
11438 
11439 #ifdef NEVER		/* Use short + timeout to terminate scan */
11440 		if (gotshort != 0 && se == ICOM_TO) {	/* We got a timeout after a short read. */
11441 			a1logd(p->log,2,"i1pro_readmeasurement: timed out in %f secs after getting short read\n",top);
11442 			a1logd(p->log,2,"i1pro_readmeasurement: trig & rd times %d %d %d %d)\n",
11443 			    m->tr_t2-m->tr_t1, m->tr_t3-m->tr_t2, m->tr_t4-m->tr_t3, m->tr_t6-m->tr_t5);
11444 			break;		/* We're done */
11445 		} else
11446 #endif
11447 		  if (se == ICOM_SHORT) {		/* Expect this to terminate scan reading */
11448 			a1logd(p->log,2,"i1pro_readmeasurement: short read, read %d bytes, asked for %d\n",
11449 			                                                                     rwbytes,size);
11450 			a1logd(p->log,2,"i1pro_readmeasurement: trig & rd times %d %d %d %d)\n",
11451 			   m->tr_t2-m->tr_t1, m->tr_t3-m->tr_t2, m->tr_t4-m->tr_t3, m->tr_t6-m->tr_t5);
11452 		} else if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11453 			if (m->trig_rv != I1PRO_OK) {
11454 				a1logd(p->log,1,"i1pro_readmeasurement: trigger failed, ICOM err 0x%x\n",
11455 				                                                             m->trig_se);
11456 				return m->trig_rv;
11457 			}
11458 			if (se & ICOM_TO)
11459 				a1logd(p->log,1,"i1pro_readmeasurement: timed out with top = %f\n",top);
11460 			a1logd(p->log,1,"i1pro_readmeasurement: failed, bytes read 0x%x, ICOM err 0x%x\n",
11461 			                                                                     rwbytes, se);
11462 			return rv;
11463 		}
11464 
11465 		/* If we didn't read a multiple of m->nsen * 2, we've got problems */
11466 		if ((rwbytes % (m->nsen * 2)) != 0) {
11467 			a1logd(p->log,1,"i1pro_readmeasurement: read 0x%x bytes, odd read error\n",rwbytes);
11468 			return I1PRO_HW_ME_ODDREAD;
11469 		}
11470 
11471 		/* Track where we're up to */
11472 		bsize -= rwbytes;
11473 		buf   += rwbytes;
11474 		treadings += rwbytes/(m->nsen * 2);
11475 
11476 		if (scanflag == 0) {	/* Not scanning */
11477 
11478 			/* Expect to read exactly what we asked for */
11479 			if (rwbytes != size) {
11480 				a1logd(p->log,1,"i1pro_readmeasurement: unexpected short read, got %d expected %d\n"
11481 				                                                                     ,rwbytes,size);
11482 				return I1PRO_HW_ME_SHORTREAD;
11483 			}
11484 			break;	/* And we're done */
11485 		}
11486 
11487 #ifdef NEVER		/* Use short + timeout to terminate scan */
11488 		/* We expect to get a short read at the end of a scan, */
11489 		/* or we might have the USB transfer truncated by somethinge else. */
11490 		/* Note the short read, and keep reading until we get a time out */
11491 		if (rwbytes != size) {
11492 			gotshort = 1;
11493 		} else {
11494 			gotshort = 0;
11495 		}
11496 #else				/* Use short to terminate scan */
11497 		/* We're scanning and expect to get a short read at the end of the scan. */
11498 		if (rwbytes != size) {
11499 			break;
11500 		}
11501 #endif
11502 
11503 		if (bsize == 0) {		/* oops, no room for more scanning read */
11504 			unsigned char tbuf[NSEN_MAX * 2];
11505 
11506 			/* We need to clean up, so soak up all the data and throw it away */
11507 			while ((se = p->icom->usb_read(p->icom, NULL, 0x82, tbuf, m->nsen * 2, &rwbytes, top)) == ICOM_OK)
11508 				;
11509 			a1logd(p->log,1,"i1pro_readmeasurement: buffer was too short for scan\n");
11510 			return I1PRO_INT_MEASBUFFTOOSMALL;
11511 		}
11512 
11513 		/* Read a bunch more readings until the read is short or times out */
11514 		nmeas = bsize / (m->nsen * 2);
11515 		if (nmeas > 64)
11516 			nmeas = 64;
11517 		top = extra + m->c_inttime * nmeas;
11518 	}
11519 
11520 	if ((m->c_measmodeflags & I1PRO_MMF_NOLAMP) == 0) {		/* Lamp was on for measurement */
11521 		m->slamponoff = m->llamponoff;						/* remember second last */
11522 		m->llamponoff = msec_time();						/* Record when it turned off */
11523 //		printf("~1 got lamp on -> off at %d (%f)\n",m->llamponoff, (m->llamponoff - m->llampoffon)/1000.0);
11524 		m->lampage += (m->llamponoff - m->llampoffon)/1000.0;	/* Time lamp was on */
11525 	}
11526 	/* Update log values */
11527 	if (mmodif != i1p_dark_cal)
11528 		m->meascount++;
11529 
11530 	/* Must have timed out in initial readings */
11531 	if (treadings < inummeas) {
11532 		a1logd(p->log,1,"i1pro_readmeasurement: read failed, bytes read 0x%x, ICOM err 0x%x\n",
11533 		                                                                          rwbytes, se);
11534 		return I1PRO_RD_SHORTMEAS;
11535 	}
11536 
11537 	if (p->log->debug >= 6) {
11538 		int i, size = treadings * m->nsen * 2;
11539 		char oline[100], *bp = oline;
11540 		for (i = 0; i < size; i++) {
11541 			if ((i % 16) == 0)
11542 				bp += sprintf(bp,"    %04x:",i);
11543 			bp += sprintf(bp," %02x",ibuf[i]);
11544 			if ((i+1) >= size || ((i+1) % 16) == 0) {
11545 				bp += sprintf(bp,"\n");
11546 				a1logd(p->log,6,oline);
11547 				bp = oline;
11548 			}
11549 		}
11550 	}
11551 
11552 	a1logd(p->log,2,"i1pro_readmeasurement: read %d readings, ICOM err 0x%x (%d msec)\n",
11553 	                                                   treadings, se, msec_time()-stime);
11554 	a1logd(p->log,2,"i1pro_readmeasurement: (trig & rd times %d %d %d %d)\n",
11555 	    m->tr_t2-m->tr_t1, m->tr_t3-m->tr_t2, m->tr_t4-m->tr_t3, m->tr_t6-m->tr_t5);
11556 
11557 	if (nummeas != NULL) *nummeas = treadings;
11558 
11559 	return rv;
11560 }
11561 
11562 /* Set the measurement clock mode */
11563 /* Firmware Version >= 301 only */
11564 i1pro_code
11565 i1pro_setmcmode(
11566 	i1pro *p,
11567 	int mcmode	/* Measurement clock mode, 1..mxmcmode */
11568 ) {
11569 	i1proimp *m = (i1proimp *)p->m;
11570 	unsigned char pbuf[1];	/* 1 bytes to write */
11571 	int se, rv = I1PRO_OK;
11572 	int stime = 0;
11573 
11574 	a1logd(p->log,2,"i1pro_setmcmode: mode %d @ %d msec\n",
11575 	                   mcmode, (stime = msec_time()) - m->msec);
11576 
11577 	pbuf[0] = mcmode;
11578 	se = p->icom->usb_control(p->icom,
11579 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11580 	                   0xCF, 0, 0, pbuf, 1, 2.0);
11581 
11582 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11583 		a1logd(p->log,1,"i1pro_setmcmode: failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
11584 		return rv;
11585 	}
11586 
11587 	/* Hmm. Give the instrument a little time to reconfigure itself. */
11588 	/* (Probably needs about 1msec, but err on the safe side) */
11589 	msec_sleep(10);
11590 
11591 	a1logd(p->log,2,"i1pro_setmcmode: done, ICOM err 0x%x (%d msec)\n",
11592 	                                             se, msec_time()-stime);
11593 	return rv;
11594 }
11595 
11596 /* Get the current measurement clock mode */
11597 /* Return pointers may be NULL if not needed. */
11598 /* Firmware Version >= 301 only */
11599 i1pro_code
11600 i1pro_getmcmode(
11601 	i1pro *p,
11602 	int *maxmcmode,		/* mcmode must be <= maxmcmode */
11603 	int *mcmode,		/* readback current mcmode */
11604 	int *subclkdiv,		/* Sub clock divider ratio */
11605 	int *intclkusec,	/* Integration clock in usec */
11606 	int *subtmode		/* Subtract mode on read using average of value 127 */
11607 ) {
11608 	i1proimp *m = (i1proimp *)p->m;
11609 	unsigned char pbuf[8];	/* status bytes read */
11610 	int _maxmcmode;		/* mcmode must be < maxmcmode */
11611 	int _mcmode;		/* readback current mcmode */
11612 	int _unknown;		/* Unknown */
11613 	int _subclkdiv;		/* Sub clock divider ratio */
11614 	int _intclkusec;	/* Integration clock in usec */
11615 	int _subtmode;		/* Subtract mode on read using average of value 127 */
11616 	int se, rv = I1PRO_OK;
11617 	int stime = 0;
11618 
11619 	a1logd(p->log,2,"i1pro_getmcmode: called @ %d msec\n",
11620 	                   (stime = msec_time()) - m->msec);
11621 
11622 	se = p->icom->usb_control(p->icom,
11623 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11624 	                   0xD1, 0, 0, pbuf, 6, 2.0);
11625 
11626 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11627 		a1logd(p->log,1,"i1pro_getmcmode: failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
11628 		return rv;
11629 	}
11630 
11631 	_maxmcmode  = pbuf[0];
11632 	_mcmode     = pbuf[1];
11633 	_unknown    = pbuf[2];
11634 	_subclkdiv  = pbuf[3];
11635 	_intclkusec = pbuf[4];
11636 	_subtmode   = pbuf[5];
11637 
11638 	a1logd(p->log,2,"i1pro_getmcmode: returns %d, %d, (%d), %d, %d 0x%x ICOM err 0x%x (%d msec)\n",
11639 	      _maxmcmode, _mcmode, _unknown, _subclkdiv, _intclkusec, _subtmode, se, msec_time()-stime);
11640 
11641 	if (maxmcmode != NULL) *maxmcmode = _maxmcmode;
11642 	if (mcmode != NULL) *mcmode = _mcmode;
11643 	if (subclkdiv != NULL) *subclkdiv = _subclkdiv;
11644 	if (intclkusec != NULL) *intclkusec = _intclkusec;
11645 	if (subtmode != NULL) *subtmode = _subtmode;
11646 
11647 	return rv;
11648 }
11649 
11650 
11651 /* Wait for a reply triggered by an instrument switch press */
11652 i1pro_code i1pro_waitfor_switch(i1pro *p, double top) {
11653 	i1proimp *m = (i1proimp *)p->m;
11654 	int rwbytes;			/* Data bytes read */
11655 	unsigned char buf[8];	/* Result  */
11656 	int se, rv = I1PRO_OK;
11657 	int stime = 0;
11658 
11659 	a1logd(p->log,2,"i1pro_waitfor_switch: read 1 byte from switch hit port @ %d msec\n",
11660 	                                                     (stime = msec_time()) - m->msec);
11661 
11662 	/* Now read 1 byte */
11663 	se = p->icom->usb_read(p->icom, NULL, 0x84, buf, 1, &rwbytes, top);
11664 
11665 	if (se & ICOM_TO) {
11666 		a1logd(p->log,2,"i1pro_waitfor_switch: read 0x%x bytes, timed out (%d msec)\n",
11667 		                   rwbytes,msec_time()-stime);
11668 		return I1PRO_INT_BUTTONTIMEOUT;
11669 	}
11670 
11671 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11672 		a1logd(p->log,1,"i1pro_waitfor_switch: failed with ICOM err 0x%x (%d msec)\n",se, msec_time()-stime);
11673 		return rv;
11674 	}
11675 
11676 	if (rwbytes != 1) {
11677 		a1logd(p->log,1,"i1pro_waitfor_switch: read 0x%x bytes, short read error (%d msec)\n",
11678 		                                                          rwbytes,msec_time()-stime);
11679 		return I1PRO_HW_SW_SHORTREAD;
11680 	}
11681 
11682 	a1logd(p->log,2,"i1pro_waitfor_switch: read 0x%x bytes value 0x%x ICOM err 0x%x (%d msec)\n",
11683 		                                                 rwbytes, buf[0], se, msec_time()-stime);
11684 
11685 	return rv;
11686 }
11687 
11688 /* Wait for a reply triggered by a key press (thread version) */
11689 /* Returns I1PRO_OK if the switch has been pressed, */
11690 /* or I1PRO_INT_BUTTONTIMEOUT if */
11691 /* no switch was pressed befor the time expired, */
11692 /* or some other error. */
11693 i1pro_code i1pro_waitfor_switch_th(i1pro *p, double top) {
11694 	i1proimp *m = (i1proimp *)p->m;
11695 	int rwbytes;			/* Data bytes read */
11696 	unsigned char buf[8];	/* Result  */
11697 	int se, rv = I1PRO_OK;
11698 	int stime = 0;
11699 
11700 	a1logd(p->log,2,"i1pro_waitfor_switch_th: read 1 byte from switch hit port @ %d msec\n",
11701 	                                                      (stime = msec_time()) - m->msec);
11702 
11703 	/* Now read 1 byte */
11704 	se = p->icom->usb_read(p->icom, &m->sw_cancel, 0x84, buf, 1, &rwbytes, top);
11705 
11706 	if (se & ICOM_TO) {
11707 		a1logd(p->log,2,"i1pro_waitfor_switch_th: read 0x%x bytes, timed out (%d msec)\n",
11708 		                                                      rwbytes,msec_time()-stime);
11709 		return I1PRO_INT_BUTTONTIMEOUT;
11710 	}
11711 
11712 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11713 		a1logd(p->log,2,"i1pro_waitfor_switch_th: failed with ICOM err 0x%x (%d msec)\n",
11714 		                                                           se,msec_time()-stime);
11715 		return rv;
11716 	}
11717 
11718 	if (rwbytes != 1) {
11719 		a1logd(p->log,2,"i1pro_waitfor_switch_th: read 0x%x bytes, short read error (%d msec)\n",
11720 		                                                              rwbytes,msec_time()-stime);
11721 		return I1PRO_HW_SW_SHORTREAD;
11722 	}
11723 
11724 	a1logd(p->log,2,"i1pro_waitfor_switch_th: read 0x%x bytes value 0x%x ICOM err 0x%x (%d msec)\n",
11725 		                                                   rwbytes, buf[0], se,msec_time()-stime);
11726 
11727 	return rv;
11728 }
11729 
11730 /* Terminate switch handling */
11731 /* This seems to always return an error ? */
11732 i1pro_code
11733 i1pro_terminate_switch(
11734 	i1pro *p
11735 ) {
11736 	i1proimp *m = (i1proimp *)p->m;
11737 	unsigned char pbuf[8];	/* 8 bytes to write */
11738 	int se, rv = I1PRO_OK;
11739 
11740 	a1logd(p->log,2,"i1pro_terminate_switch: called\n");
11741 
11742 	/* These values may not be significant */
11743 	pbuf[0] = pbuf[1] = pbuf[2] = pbuf[3] = 0xff;
11744 	pbuf[4] = 0xfc;
11745 	pbuf[5] = 0xee;
11746 	pbuf[6] = 0x12;
11747 	pbuf[7] = 0x00;
11748 	se = p->icom->usb_control(p->icom,
11749 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11750 	                   0xD0, 3, 0, pbuf, 8, 2.0);
11751 
11752 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11753 		a1logd(p->log,2,"i1pro_terminate_switch: Warning: Terminate Switch Handling failed with ICOM err 0x%x\n",se);
11754 	} else {
11755 		a1logd(p->log,2,"i1pro_terminate_switch: done, ICOM err 0x%x\n",se);
11756 	}
11757 
11758 	/* In case the above didn't work, cancel the I/O */
11759 	msec_sleep(50);
11760 	if (m->th_termed == 0) {
11761 		a1logd(p->log,3,"i1pro terminate switch thread failed, canceling I/O\n");
11762 		p->icom->usb_cancel_io(p->icom, &m->sw_cancel);
11763 	}
11764 
11765 	return rv;
11766 }
11767 
11768 /* ============================================================ */
11769 /* Low level i1pro2 (Rev E) commands */
11770 
11771 /* Get the EEProm size */
11772 i1pro_code
11773 i1pro2_geteesize(
11774     i1pro *p,
11775     int *eesize
11776 ) {
11777 	int se, rv = I1PRO_OK;
11778 	unsigned char buf[4];	/* Result  */
11779 	int _eesize = 0;
11780 
11781 	a1logd(p->log,2,"i1pro2_geteesize: called\n");
11782 
11783 	se = p->icom->usb_control(p->icom,
11784 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11785 	                   0xD9, 0, 0, buf, 4, 2.0);
11786 
11787 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11788 		a1logd(p->log,1,"i1pro2_geteesize: failed with ICOM err 0x%x\n",se);
11789 		return rv;
11790 	}
11791 
11792 	_eesize = buf2int(buf);
11793 
11794 	a1logd(p->log,2,"i1pro2_geteesize: returning %d ICOM err 0x%x\n", _eesize, se);
11795 
11796 	if (eesize != NULL)
11797 		*eesize = _eesize;
11798 
11799 	return rv;
11800 }
11801 
11802 /* Get the Chip ID */
11803 /* This does actually work with the Rev D. */
11804 /* (It returns all zero's unless you've read the EEProm first !) */
11805 i1pro_code
11806 i1pro2_getchipid(
11807 	i1pro *p,
11808 	unsigned char chipid[8]
11809 ) {
11810 	int se, rv = I1PRO_OK;
11811 
11812 	a1logd(p->log,2,"i1pro2_getchipid: called\n");
11813 
11814 	se = p->icom->usb_control(p->icom,
11815 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11816 	                   0xD2, 0, 0, chipid, 8, 2.0);
11817 
11818 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11819 		a1logd(p->log,1,"i1pro2_getchipid: failed with ICOM err 0x%x\n",se);
11820 		return rv;
11821 	}
11822 
11823 	a1logd(p->log,2,"i1pro2_getchipid: returning %02X-%02X%02X%02X%02X%02X%02X%02X ICOM err 0x%x\n",
11824                            chipid[0], chipid[1], chipid[2], chipid[3],
11825                            chipid[4], chipid[5], chipid[6], chipid[7], se);
11826 	return rv;
11827 }
11828 
11829 /* Get Rev E measure characteristics. */
11830 i1pro_code
11831 i1pro2_getmeaschar(
11832     i1pro *p,
11833     int *clkusec,		/* Return integration clock length in usec ?      (ie. 36)  */
11834     int *xraw,			/* Return number of extra non-reading (dark) raw bands ? (ie. 6)   */
11835     int *nraw,			/* Return number of reading raw bands ?           (ie. 128) */
11836     int *subdiv			/* Sub divider and minium integration clocks  ?   (ie. 136) */
11837 ) {
11838 	int se, rv = I1PRO_OK;
11839 	unsigned char buf[16];	/* Result  */
11840     int _clkusec;
11841     int _xraw;
11842     int _nraw;
11843     int _subdiv;
11844 
11845 	a1logd(p->log,2,"i1pro2_getmeaschar: called\n");
11846 
11847 	se = p->icom->usb_control(p->icom,
11848 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11849 	                   0xD5, 0, 0, buf, 16, 2.0);
11850 
11851 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11852 		a1logd(p->log,1,"i1pro2_getmeaschar: failed with ICOM err 0x%x\n",se);
11853 		return rv;
11854 	}
11855 
11856 	_clkusec = buf2int(buf + 0);
11857 	_xraw = buf2int(buf + 4);
11858 	_nraw = buf2int(buf + 8);
11859 	_subdiv = buf2int(buf + 12);
11860 
11861 	a1logd(p->log,2,"i1pro2_getmeaschar: returning clkusec %d, xraw %d, nraw %d, subdiv %d ICOM err 0x%x\n", _clkusec, _xraw, _nraw, _subdiv, se);
11862 
11863 	if (clkusec != NULL)
11864 		*clkusec = _clkusec;
11865 	if (xraw != NULL)
11866 		*xraw = _xraw;
11867 	if (nraw != NULL)
11868 		*nraw = _nraw;
11869 	if (subdiv != NULL)
11870 		*subdiv = _subdiv;
11871 
11872 	return rv;
11873 }
11874 
11875 /* Delayed trigger implementation, called from thread */
11876 /* We assume that the Rev E measurement parameters have been set in */
11877 /* the i1proimp structure c_* values */
11878 static int
11879 i1pro2_delayed_trigger(void *pp) {
11880 	i1pro *p = (i1pro *)pp;
11881 	i1proimp *m = (i1proimp *)p->m;
11882 	unsigned char pbuf[14];	/* 14 bytes to write */
11883 	int se, rv = I1PRO_OK;
11884 	int stime = 0;
11885 
11886 	int2buf(pbuf + 0, m->c_intclocks);
11887 	int2buf(pbuf + 4, m->c_lampclocks);
11888 	int2buf(pbuf + 8, m->c_nummeas);
11889 	short2buf(pbuf + 12, m->c_measmodeflags2);
11890 
11891 	if ((m->c_measmodeflags & I1PRO_MMF_NOLAMP) == 0) {		/* Lamp will be on for measurement */
11892 		m->llampoffon = msec_time();						/* Record when it turned on */
11893 	}
11894 
11895 	a1logd(p->log,2,"i1pro2_delayed_trigger: Rev E start sleep @ %d msec\n",
11896 	                                                 msec_time() - m->msec);
11897 #ifdef USE_RD_SYNC
11898 	p->icom->usb_wait_io(p->icom, &m->rd_sync);		/* Wait for read to start */
11899 #else
11900 	/* Delay the trigger */
11901 	msec_sleep(m->trig_delay);
11902 #endif
11903 
11904 	m->tr_t1 = msec_time();		/* Diagnostic */
11905 
11906 	a1logd(p->log,2,"i1pro2_delayed_trigger: trigger Rev E @ %d msec\n",
11907 	                                     (stime = msec_time()) - m->msec);
11908 
11909 	m->trigstamp = usec_time();
11910 	se = p->icom->usb_control(p->icom,
11911 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11912 	                   0xD4, 0, 0, pbuf, 14, 2.0);
11913 
11914 	m->tr_t2 = msec_time();		/* Diagnostic */
11915 
11916 	m->trig_se = se;
11917 	m->trig_rv = icoms2i1pro_err(se);
11918 
11919 	a1logd(p->log,2,"i1pro2_delayed_trigger: done ICOM err 0x%x (%d msec)\n",
11920 	                                                  se,msec_time()-stime);
11921 	return 0;
11922 }
11923 
11924 /* Trigger a measurement after the nominated delay */
11925 /* The actual return code will be in m->trig_rv after the delay. */
11926 /* This allows us to start the measurement read before the trigger, */
11927 /* ensuring that process scheduling latency can't cause the read to fail. */
11928 i1pro_code
11929 i1pro2_triggermeasure(i1pro *p, int delay) {
11930 	i1proimp *m = (i1proimp *)p->m;
11931 	int rv = I1PRO_OK;
11932 
11933 	a1logd(p->log,2,"i1pro2_triggermeasure: triggering Rev E measurement after %dmsec "
11934 	                               "delay @ %d msec\n", delay, msec_time() - m->msec);
11935 
11936 	/* NOTE := would be better here to create thread once, and then trigger it */
11937 	/* using a condition variable. */
11938 	if (m->trig_thread != NULL)
11939 		m->trig_thread->del(m->trig_thread);
11940 
11941     m->tr_t1 = m->tr_t2 = m->tr_t3 = m->tr_t4 = m->tr_t5 = m->tr_t6 = m->tr_t7 = 0;
11942 	m->trig_delay = delay;
11943 
11944 	if ((m->trig_thread = new_athread(i1pro2_delayed_trigger, (void *)p)) == NULL) {
11945 		a1logd(p->log,1,"i1pro2_triggermeasure: creating delayed trigger Rev E thread failed\n");
11946 		return I1PRO_INT_THREADFAILED;
11947 	}
11948 
11949 #ifdef WAIT_FOR_DELAY_TRIGGER	/* hack to diagnose threading problems */
11950 	while (m->tr_t2 == 0) {
11951 		Sleep(1);
11952 	}
11953 #endif
11954 	a1logd(p->log,2,"i1pro2_triggermeasure: scheduled triggering Rev E OK\n");
11955 
11956 	return rv;
11957 }
11958 
11959 /* Get the UV before and after measurement voltage drop */
11960 i1pro_code
11961 i1pro2_getUVvolts(
11962     i1pro *p,
11963     int *before,
11964     int *after
11965 ) {
11966 	int se, rv = I1PRO_OK;
11967 	unsigned char buf[4];	/* Result  */
11968 	int _before = 0;
11969 	int _after = 0;
11970 
11971 	a1logd(p->log,2,"i1pro2_getUVvolts: called\n");
11972 
11973 	se = p->icom->usb_control(p->icom,
11974 		               IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
11975 	                   0xD8, 0, 0, buf, 4, 2.0);
11976 
11977 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
11978 		a1logd(p->log,1,"i1pro2_getUVvolts: failed with ICOM err 0x%x\n",se);
11979 		return rv;
11980 	}
11981 
11982 	_before = buf2ushort(buf);
11983 	_after = buf2ushort(buf+2);
11984 
11985 	a1logd(p->log,2,"i1pro2_getUVvolts: returning %d, %d ICOM err 0x%x\n", _before, _after, se);
11986 
11987 	if (before != NULL)
11988 		*before = _before;
11989 
11990 	if (after != NULL)
11991 		*after = _after;
11992 
11993 	return rv;
11994 }
11995 
11996 /* Terminate Ruler tracking (???) */
11997 /* The parameter seems to be always 0 ? */
11998 static int
11999 i1pro2_stop_ruler(void *pp, int parm) {
12000 	i1pro *p = (i1pro *)pp;
12001 	i1proimp *m = (i1proimp *)p->m;
12002 	unsigned char pbuf[2];	/* 2 bytes to write */
12003 	int se, rv = I1PRO_OK;
12004 
12005 	short2buf(pbuf, parm);
12006 
12007 	a1logd(p->log,2,"i1pro2_stop_ruler: called with 0x%x\n", parm);
12008 
12009 	se = p->icom->usb_control(p->icom,
12010 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
12011 	                   0xD7, 0, 0, pbuf, 2, 2.0);
12012 
12013 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
12014 		a1logd(p->log,1,"i1pro2_stop_ruler: failed with ICOM err 0x%x\n",rv);
12015 		return rv;
12016 	}
12017 
12018 	a1logd(p->log,2,"i1pro2_stop_ruler: returning ICOM err 0x%x\n",rv);
12019 
12020 	return rv;
12021 }
12022 
12023 	/* Send a raw indicator LED sequence. */
12024 /*
12025 	The byte sequence has the following format:
12026 	(all values are big endian)
12027 
12028 	XXXX	Number of following blocks, BE.
12029 
12030 	Blocks are:
12031 
12032 	YYYY	Number of bytes in the block
12033 	RRRR	Number of repeats of the block, FFFFFFFF = infinite
12034 
12035 	A sequence of codes:
12036 
12037 	LL TTTT
12038 			L = Led mask:
12039 				01 = Red Right
12040 				02 = Green Right
12041 				04 = Blue Right
12042 				08 = Red Left
12043 				10 = Green Left
12044 				20 = Blue Left
12045 			TTT = clock count for this mask (ie. aprox. 73 usec clock period)
12046 	PWM typically alternates between on & off state with a period total of
12047 	0x50 clocks = 170 Hz. 255 clocks would be 54 Hz.
12048 
12049  */
12050 
12051 static int
12052 i1pro2_indLEDseq(void *pp, unsigned char *buf, int size) {
12053 	i1pro *p = (i1pro *)pp;
12054 	i1proimp *m = (i1proimp *)p->m;
12055 	int rwbytes;			/* Data bytes written */
12056 	unsigned char pbuf[4];	/* Number of bytes being send */
12057 	int se, rv = I1PRO_OK;
12058 
12059 	int2buf(pbuf, size);
12060 
12061 	a1logd(p->log,2,"i1pro2_indLEDseq: length %d bytes\n", size);
12062 
12063 	se = p->icom->usb_control(p->icom,
12064 		               IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
12065 	                   0xD6, 0, 0, pbuf, 4, 2.0);
12066 
12067 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
12068 		a1logd(p->log,1,"i1pro2_indLEDseq: failed with ICOM err 0x%x\n",rv);
12069 		return rv;
12070 	}
12071 
12072 	a1logd(p->log,2,"i1pro2_geteesize: command got ICOM err 0x%x\n", se);
12073 
12074 	/* Now write the bytes */
12075 	se = p->icom->usb_write(p->icom, NULL, 0x03, buf, size, &rwbytes, 5.0);
12076 
12077 	if ((rv = icoms2i1pro_err(se)) != I1PRO_OK) {
12078 		a1logd(p->log,1,"i1pro2_indLEDseq: data write failed with ICOM err 0x%x\n",se);
12079 		return rv;
12080 	}
12081 
12082 	if (rwbytes != size) {
12083 		a1logd(p->log,1,"i1pro2_indLEDseq: wrote 0x%x bytes, short write error\n",rwbytes);
12084 		return I1PRO_HW_LED_SHORTWRITE;
12085 	}
12086 
12087 	a1logd(p->log,2,"i1pro2_indLEDseq: wrote 0x%x bytes LED sequence, ICOM err 0x%x\n", size, rv);
12088 
12089 	return rv;
12090 }
12091 
12092 /* Turn indicator LEDs off */
12093 static int
12094 i1pro2_indLEDoff(void *pp) {
12095 	i1pro *p = (i1pro *)pp;
12096 	int rv = I1PRO_OK;
12097 	unsigned char seq[] = {
12098 		0x00, 0x00, 0x00, 0x01,
12099 
12100 		0x00, 0x00, 0x00, 0x07,
12101 		0x00, 0x00, 0x00, 0x01,
12102 		0x00, 0x00, 0x10
12103 	};
12104 
12105 	a1logd(p->log,2,"i1pro2_indLEDoff: called\n");
12106 	rv = i1pro2_indLEDseq(p, seq, sizeof(seq));
12107 	a1logd(p->log,2,"i1pro2_indLEDoff: returning ICOM err 0x%x\n",rv);
12108 
12109 	return rv;
12110 }
12111 
12112 #ifdef NEVER
12113 
12114 	// ~~99 play with LED settings
12115 	if (p->itype == instI1Pro2) {
12116 
12117 		// LED is capable of white and red
12118 
12119 		/* Turns it off */
12120 		unsigned char b1[] = {
12121 			0x00, 0x00, 0x00, 0x01,
12122 
12123 			0x00, 0x00, 0x00, 0x07,
12124 			0x00, 0x00, 0x00, 0x01,
12125 			0x00, 0x00, 0x01
12126 		};
12127 
12128 		/* Makes it white */
12129 		unsigned char b2[] = {
12130 			0x00, 0x00, 0x00, 0x02,
12131 
12132 			0x00, 0x00, 0x00, 0x0a,
12133 			0x00, 0x00, 0x00, 0x01,
12134 			0x00, 0x36, 0x00,
12135 			0x00, 0x00, 0x01,
12136 
12137 			0x00, 0x00, 0x00, 0x0a,
12138 			0xff, 0xff, 0xff, 0xff,
12139 			0x3f, 0x36, 0x40,
12140 			0x00, 0x00, 0x01
12141 		};
12142 
12143 		/* Makes it pulsing white */
12144 		unsigned char b3[] = {
12145 			0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x36, 0x40, 0x00,
12146 			0x00, 0x01, 0x00, 0x00, 0x08, 0xec, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12147 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12148 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12149 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12150 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12151 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12152 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12153 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12154 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12155 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12156 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12157 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12158 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12159 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12160 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12161 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12162 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12163 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12164 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12165 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12166 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12167 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12168 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12169 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12170 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12171 			0x3f, 0x00, 0x03, 0x00, 0x00, 0x50, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x50, 0x3f, 0x00, 0x03, 0x00,
12172 			0x00, 0x4d, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x49, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x45, 0x3f, 0x00,
12173 			0x03, 0x00, 0x00, 0x42, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x42, 0x3f, 0x00, 0x04, 0x00, 0x00, 0x4f,
12174 			0x3f, 0x00, 0x04, 0x00, 0x00, 0x4d, 0x3f, 0x00, 0x04, 0x00, 0x00, 0x49, 0x3f, 0x00, 0x04, 0x00,
12175 			0x00, 0x46, 0x3f, 0x00, 0x04, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x04, 0x00, 0x00, 0x41, 0x3f, 0x00,
12176 			0x05, 0x00, 0x00, 0x4d, 0x3f, 0x00, 0x05, 0x00, 0x00, 0x49, 0x3f, 0x00, 0x05, 0x00, 0x00, 0x46,
12177 			0x3f, 0x00, 0x05, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x06, 0x00, 0x00, 0x4d, 0x3f, 0x00, 0x06, 0x00,
12178 			0x00, 0x4a, 0x3f, 0x00, 0x06, 0x00, 0x00, 0x47, 0x3f, 0x00, 0x06, 0x00, 0x00, 0x44, 0x3f, 0x00,
12179 			0x06, 0x00, 0x00, 0x41, 0x3f, 0x00, 0x07, 0x00, 0x00, 0x49, 0x3f, 0x00, 0x07, 0x00, 0x00, 0x46,
12180 			0x3f, 0x00, 0x07, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x3f, 0x00, 0x07, 0x00,
12181 			0x00, 0x3e, 0x3f, 0x00, 0x08, 0x00, 0x00, 0x44, 0x3f, 0x00, 0x09, 0x00, 0x00, 0x4a, 0x3f, 0x00,
12182 			0x09, 0x00, 0x00, 0x47, 0x3f, 0x00, 0x09, 0x00, 0x00, 0x44, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x49,
12183 			0x3f, 0x00, 0x09, 0x00, 0x00, 0x3f, 0x3f, 0x00, 0x09, 0x00, 0x00, 0x3d, 0x3f, 0x00, 0x0b, 0x00,
12184 			0x00, 0x48, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x45, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x3c, 0x3f, 0x00,
12185 			0x0c, 0x00, 0x00, 0x46, 0x3f, 0x00, 0x0c, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x0d, 0x00, 0x00, 0x46,
12186 			0x3f, 0x00, 0x0c, 0x00, 0x00, 0x3e, 0x3f, 0x00, 0x0c, 0x00, 0x00, 0x3c, 0x3f, 0x00, 0x0d, 0x00,
12187 			0x00, 0x3f, 0x3f, 0x00, 0x0d, 0x00, 0x00, 0x3d, 0x3f, 0x00, 0x0e, 0x00, 0x00, 0x3f, 0x3f, 0x00,
12188 			0x0e, 0x00, 0x00, 0x3d, 0x3f, 0x00, 0x0e, 0x00, 0x00, 0x3b, 0x3f, 0x00, 0x0f, 0x00, 0x00, 0x3d,
12189 			0x3f, 0x00, 0x0e, 0x00, 0x00, 0x37, 0x3f, 0x00, 0x0f, 0x00, 0x00, 0x39, 0x3f, 0x00, 0x10, 0x00,
12190 			0x00, 0x3b, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x40, 0x3f, 0x00, 0x10, 0x00, 0x00, 0x37, 0x3f, 0x00,
12191 			0x13, 0x00, 0x00, 0x3f, 0x3f, 0x00, 0x13, 0x00, 0x00, 0x3d, 0x3f, 0x00, 0x11, 0x00, 0x00, 0x34,
12192 			0x3f, 0x00, 0x12, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x34, 0x3f, 0x00, 0x14, 0x00,
12193 			0x00, 0x38, 0x3f, 0x00, 0x14, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x17, 0x00, 0x00, 0x3c, 0x3f, 0x00,
12194 			0x17, 0x00, 0x00, 0x3a, 0x3f, 0x00, 0x18, 0x00, 0x00, 0x3a, 0x3f, 0x00, 0x15, 0x00, 0x00, 0x31,
12195 			0x3f, 0x00, 0x17, 0x00, 0x00, 0x34, 0x3f, 0x00, 0x16, 0x00, 0x00, 0x30, 0x3f, 0x00, 0x1a, 0x00,
12196 			0x00, 0x37, 0x3f, 0x00, 0x1b, 0x00, 0x00, 0x37, 0x3f, 0x00, 0x18, 0x00, 0x00, 0x2f, 0x3f, 0x00,
12197 			0x1c, 0x00, 0x00, 0x35, 0x3f, 0x00, 0x1c, 0x00, 0x00, 0x33, 0x3f, 0x00, 0x1c, 0x00, 0x00, 0x31,
12198 			0x3f, 0x00, 0x1d, 0x00, 0x00, 0x31, 0x3f, 0x00, 0x1b, 0x00, 0x00, 0x2c, 0x3f, 0x00, 0x1c, 0x00,
12199 			0x00, 0x2c, 0x3f, 0x00, 0x1d, 0x00, 0x00, 0x2c, 0x3f, 0x00, 0x1e, 0x00, 0x00, 0x2c, 0x3f, 0x00,
12200 			0x1d, 0x00, 0x00, 0x29, 0x3f, 0x00, 0x1e, 0x00, 0x00, 0x29, 0x3f, 0x00, 0x23, 0x00, 0x00, 0x2e,
12201 			0x3f, 0x00, 0x22, 0x00, 0x00, 0x2b, 0x3f, 0x00, 0x25, 0x00, 0x00, 0x2d, 0x3f, 0x00, 0x24, 0x00,
12202 			0x00, 0x2a, 0x3f, 0x00, 0x22, 0x00, 0x00, 0x26, 0x3f, 0x00, 0x27, 0x00, 0x00, 0x2a, 0x3f, 0x00,
12203 			0x22, 0x00, 0x00, 0x23, 0x3f, 0x00, 0x23, 0x00, 0x00, 0x23, 0x3f, 0x00, 0x2a, 0x00, 0x00, 0x28,
12204 			0x3f, 0x00, 0x24, 0x00, 0x00, 0x21, 0x3f, 0x00, 0x29, 0x00, 0x00, 0x24, 0x3f, 0x00, 0x2c, 0x00,
12205 			0x00, 0x25, 0x3f, 0x00, 0x28, 0x00, 0x00, 0x20, 0x3f, 0x00, 0x2b, 0x00, 0x00, 0x21, 0x3f, 0x00,
12206 			0x2d, 0x00, 0x00, 0x21, 0x3f, 0x00, 0x2b, 0x00, 0x00, 0x1e, 0x3f, 0x00, 0x2a, 0x00, 0x00, 0x1c,
12207 			0x3f, 0x00, 0x2f, 0x00, 0x00, 0x1e, 0x3f, 0x00, 0x2b, 0x00, 0x00, 0x1a, 0x3f, 0x00, 0x2d, 0x00,
12208 			0x00, 0x1a, 0x3f, 0x00, 0x31, 0x00, 0x00, 0x1b, 0x3f, 0x00, 0x2e, 0x00, 0x00, 0x18, 0x3f, 0x00,
12209 			0x37, 0x00, 0x00, 0x1b, 0x3f, 0x00, 0x38, 0x00, 0x00, 0x1a, 0x3f, 0x00, 0x37, 0x00, 0x00, 0x18,
12210 			0x3f, 0x00, 0x31, 0x00, 0x00, 0x14, 0x3f, 0x00, 0x39, 0x00, 0x00, 0x16, 0x3f, 0x00, 0x3d, 0x00,
12211 			0x00, 0x16, 0x3f, 0x00, 0x36, 0x00, 0x00, 0x12, 0x3f, 0x00, 0x3a, 0x00, 0x00, 0x12, 0x3f, 0x00,
12212 			0x3b, 0x00, 0x00, 0x11, 0x3f, 0x00, 0x40, 0x00, 0x00, 0x11, 0x3f, 0x00, 0x3a, 0x00, 0x00, 0x0e,
12213 			0x3f, 0x00, 0x3b, 0x00, 0x00, 0x0d, 0x3f, 0x00, 0x3c, 0x00, 0x00, 0x0c, 0x3f, 0x00, 0x3d, 0x00,
12214 			0x00, 0x0b, 0x3f, 0x00, 0x3e, 0x00, 0x00, 0x0a, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x09, 0x3f, 0x00,
12215 			0x40, 0x00, 0x00, 0x08, 0x3f, 0x00, 0x42, 0x00, 0x00, 0x07, 0x3f, 0x00, 0x44, 0x00, 0x00, 0x06,
12216 			0x3f, 0x00, 0x47, 0x00, 0x00, 0x05, 0x3f, 0x00, 0x4b, 0x00, 0x00, 0x04, 0x3f, 0x00, 0x50, 0x00,
12217 			0x00, 0x03, 0x3f, 0x00, 0x44, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x45, 0x3f, 0x00, 0x45, 0x3f, 0x00,
12218 			0x45, 0x3f, 0x00, 0x45, 0x3f, 0x00, 0x44, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x50, 0x00, 0x00, 0x03,
12219 			0x3f, 0x00, 0x4b, 0x00, 0x00, 0x04, 0x3f, 0x00, 0x47, 0x00, 0x00, 0x05, 0x3f, 0x00, 0x44, 0x00,
12220 			0x00, 0x06, 0x3f, 0x00, 0x42, 0x00, 0x00, 0x07, 0x3f, 0x00, 0x40, 0x00, 0x00, 0x08, 0x3f, 0x00,
12221 			0x3f, 0x00, 0x00, 0x09, 0x3f, 0x00, 0x3e, 0x00, 0x00, 0x0a, 0x3f, 0x00, 0x3d, 0x00, 0x00, 0x0b,
12222 			0x3f, 0x00, 0x3c, 0x00, 0x00, 0x0c, 0x3f, 0x00, 0x3b, 0x00, 0x00, 0x0d, 0x3f, 0x00, 0x3a, 0x00,
12223 			0x00, 0x0e, 0x3f, 0x00, 0x40, 0x00, 0x00, 0x11, 0x3f, 0x00, 0x3b, 0x00, 0x00, 0x11, 0x3f, 0x00,
12224 			0x3a, 0x00, 0x00, 0x12, 0x3f, 0x00, 0x36, 0x00, 0x00, 0x12, 0x3f, 0x00, 0x3d, 0x00, 0x00, 0x16,
12225 			0x3f, 0x00, 0x39, 0x00, 0x00, 0x16, 0x3f, 0x00, 0x31, 0x00, 0x00, 0x14, 0x3f, 0x00, 0x37, 0x00,
12226 			0x00, 0x18, 0x3f, 0x00, 0x38, 0x00, 0x00, 0x1a, 0x3f, 0x00, 0x37, 0x00, 0x00, 0x1b, 0x3f, 0x00,
12227 			0x2e, 0x00, 0x00, 0x18, 0x3f, 0x00, 0x31, 0x00, 0x00, 0x1b, 0x3f, 0x00, 0x2d, 0x00, 0x00, 0x1a,
12228 			0x3f, 0x00, 0x2b, 0x00, 0x00, 0x1a, 0x3f, 0x00, 0x2f, 0x00, 0x00, 0x1e, 0x3f, 0x00, 0x2a, 0x00,
12229 			0x00, 0x1c, 0x3f, 0x00, 0x2b, 0x00, 0x00, 0x1e, 0x3f, 0x00, 0x2d, 0x00, 0x00, 0x21, 0x3f, 0x00,
12230 			0x2b, 0x00, 0x00, 0x21, 0x3f, 0x00, 0x28, 0x00, 0x00, 0x20, 0x3f, 0x00, 0x2c, 0x00, 0x00, 0x25,
12231 			0x3f, 0x00, 0x29, 0x00, 0x00, 0x24, 0x3f, 0x00, 0x24, 0x00, 0x00, 0x21, 0x3f, 0x00, 0x2a, 0x00,
12232 			0x00, 0x28, 0x3f, 0x00, 0x23, 0x00, 0x00, 0x23, 0x3f, 0x00, 0x22, 0x00, 0x00, 0x23, 0x3f, 0x00,
12233 			0x27, 0x00, 0x00, 0x2a, 0x3f, 0x00, 0x22, 0x00, 0x00, 0x26, 0x3f, 0x00, 0x24, 0x00, 0x00, 0x2a,
12234 			0x3f, 0x00, 0x25, 0x00, 0x00, 0x2d, 0x3f, 0x00, 0x22, 0x00, 0x00, 0x2b, 0x3f, 0x00, 0x23, 0x00,
12235 			0x00, 0x2e, 0x3f, 0x00, 0x1e, 0x00, 0x00, 0x29, 0x3f, 0x00, 0x1d, 0x00, 0x00, 0x29, 0x3f, 0x00,
12236 			0x1e, 0x00, 0x00, 0x2c, 0x3f, 0x00, 0x1d, 0x00, 0x00, 0x2c, 0x3f, 0x00, 0x1c, 0x00, 0x00, 0x2c,
12237 			0x3f, 0x00, 0x1b, 0x00, 0x00, 0x2c, 0x3f, 0x00, 0x1d, 0x00, 0x00, 0x31, 0x3f, 0x00, 0x1c, 0x00,
12238 			0x00, 0x31, 0x3f, 0x00, 0x1c, 0x00, 0x00, 0x33, 0x3f, 0x00, 0x1c, 0x00, 0x00, 0x35, 0x3f, 0x00,
12239 			0x18, 0x00, 0x00, 0x2f, 0x3f, 0x00, 0x1b, 0x00, 0x00, 0x37, 0x3f, 0x00, 0x1a, 0x00, 0x00, 0x37,
12240 			0x3f, 0x00, 0x16, 0x00, 0x00, 0x30, 0x3f, 0x00, 0x17, 0x00, 0x00, 0x34, 0x3f, 0x00, 0x15, 0x00,
12241 			0x00, 0x31, 0x3f, 0x00, 0x18, 0x00, 0x00, 0x3a, 0x3f, 0x00, 0x17, 0x00, 0x00, 0x3a, 0x3f, 0x00,
12242 			0x17, 0x00, 0x00, 0x3c, 0x3f, 0x00, 0x14, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x14, 0x00, 0x00, 0x38,
12243 			0x3f, 0x00, 0x12, 0x00, 0x00, 0x34, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x11, 0x00,
12244 			0x00, 0x34, 0x3f, 0x00, 0x13, 0x00, 0x00, 0x3d, 0x3f, 0x00, 0x13, 0x00, 0x00, 0x3f, 0x3f, 0x00,
12245 			0x10, 0x00, 0x00, 0x37, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x40, 0x3f, 0x00, 0x10, 0x00, 0x00, 0x3b,
12246 			0x3f, 0x00, 0x0f, 0x00, 0x00, 0x39, 0x3f, 0x00, 0x0e, 0x00, 0x00, 0x37, 0x3f, 0x00, 0x0f, 0x00,
12247 			0x00, 0x3d, 0x3f, 0x00, 0x0e, 0x00, 0x00, 0x3b, 0x3f, 0x00, 0x0e, 0x00, 0x00, 0x3d, 0x3f, 0x00,
12248 			0x0e, 0x00, 0x00, 0x3f, 0x3f, 0x00, 0x0d, 0x00, 0x00, 0x3d, 0x3f, 0x00, 0x0d, 0x00, 0x00, 0x3f,
12249 			0x3f, 0x00, 0x0c, 0x00, 0x00, 0x3c, 0x3f, 0x00, 0x0c, 0x00, 0x00, 0x3e, 0x3f, 0x00, 0x0d, 0x00,
12250 			0x00, 0x46, 0x3f, 0x00, 0x0c, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x0c, 0x00, 0x00, 0x46, 0x3f, 0x00,
12251 			0x0a, 0x00, 0x00, 0x3c, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x45, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x48,
12252 			0x3f, 0x00, 0x09, 0x00, 0x00, 0x3d, 0x3f, 0x00, 0x09, 0x00, 0x00, 0x3f, 0x3f, 0x00, 0x0a, 0x00,
12253 			0x00, 0x49, 0x3f, 0x00, 0x09, 0x00, 0x00, 0x44, 0x3f, 0x00, 0x09, 0x00, 0x00, 0x47, 0x3f, 0x00,
12254 			0x09, 0x00, 0x00, 0x4a, 0x3f, 0x00, 0x08, 0x00, 0x00, 0x44, 0x3f, 0x00, 0x07, 0x00, 0x00, 0x3e,
12255 			0x3f, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x3f, 0x00, 0x07, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x07, 0x00,
12256 			0x00, 0x46, 0x3f, 0x00, 0x07, 0x00, 0x00, 0x49, 0x3f, 0x00, 0x06, 0x00, 0x00, 0x41, 0x3f, 0x00,
12257 			0x06, 0x00, 0x00, 0x44, 0x3f, 0x00, 0x06, 0x00, 0x00, 0x47, 0x3f, 0x00, 0x06, 0x00, 0x00, 0x4a,
12258 			0x3f, 0x00, 0x06, 0x00, 0x00, 0x4d, 0x3f, 0x00, 0x05, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x05, 0x00,
12259 			0x00, 0x46, 0x3f, 0x00, 0x05, 0x00, 0x00, 0x49, 0x3f, 0x00, 0x05, 0x00, 0x00, 0x4d, 0x3f, 0x00,
12260 			0x04, 0x00, 0x00, 0x41, 0x3f, 0x00, 0x04, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x04, 0x00, 0x00, 0x46,
12261 			0x3f, 0x00, 0x04, 0x00, 0x00, 0x49, 0x3f, 0x00, 0x04, 0x00, 0x00, 0x4d, 0x3f, 0x00, 0x04, 0x00,
12262 			0x00, 0x4f, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x42, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x42, 0x3f, 0x00,
12263 			0x03, 0x00, 0x00, 0x45, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x49, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x4d,
12264 			0x3f, 0x00, 0x03, 0x00, 0x00, 0x50, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x50, 0x3f, 0x00, 0x02, 0x00,
12265 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12266 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12267 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12268 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12269 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12270 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12271 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12272 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12273 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12274 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12275 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12276 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12277 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12278 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12279 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12280 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12281 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12282 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12283 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12284 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12285 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12286 			0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00,
12287 			0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43,
12288 			0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x43, 0x3f, 0x00, 0x02, 0x00,
12289 			0x00, 0x43
12290 		};
12291 
12292 		unsigned char b4[] = {
12293 			0x00, 0x00, 0x00, 0x01,			// blocks
12294 
12295 			0x00, 0x00, 0x00, 0x0a,			// bytes
12296 			0x00, 0x00, 0x00, 0x04,			// repeat
12297 //			0xff, 0xff, 0xff, 0xff,			// repeat
12298 //			0x3f, 0x00, 0x04,				// Level ????
12299 //			0x00, 0x00, 0x3c				// msec ????
12300 			0x3f, 0x20, 0x00,				//  "
12301 			0x00, 0x20, 0x00				// LED mask + clocks
12302 		};
12303 
12304 		printf("~1 send led sequence length %d\n",sizeof(b4));
12305 		if ((ev = i1pro2_indLEDseq(p, b4, sizeof(b4))) != I1PRO_OK)
12306 			return ev;
12307 	}
12308 #endif /* NEVER */
12309 
12310 /* ============================================================ */
12311 /* key/value dictionary support for EEProm contents */
12312 
12313 /* Search the linked list for the given key */
12314 /* Return NULL if not found */
12315 static i1keyv *i1data_find_key(i1data *d, i1key key) {
12316 	i1keyv *k;
12317 
12318 	for (k = d->head; k != NULL; k = k->next) {
12319 		if (k->key == key)
12320 			return k;
12321 	}
12322 	return NULL;
12323 }
12324 
12325 /* Search the linked list for the given key and */
12326 /* return it, or add it to the list if it doesn't exist. */
12327 /* Return NULL on error */
12328 static i1keyv *i1data_make_key(i1data *d, i1key key) {
12329 	i1keyv *k;
12330 
12331 	for (k = d->head; k != NULL; k = k->next)
12332 		if (k->key == key)
12333 			return k;
12334 
12335 	if ((k = (i1keyv *)calloc(1, sizeof(i1keyv))) == NULL) {
12336 		a1logw(d->log, "i1data: malloc failed!\n");
12337 		return NULL;
12338 	}
12339 
12340 	k->key = key;
12341 	k->next = NULL;
12342 	if (d->last == NULL) {
12343 		d->head = d->last = k;
12344 	} else {
12345 		d->last->next = k;
12346 		d->last = k;
12347 	}
12348 	return k;
12349 }
12350 
12351 /* Return type of data associated with key. Return i1_dtype_unknown if not found */
12352 static i1_dtype i1data_get_type(i1data *d, i1key key) {
12353 	i1keyv *k;
12354 
12355 	if ((k = d->find_key(d, key)) != NULL)
12356 		return k->type;
12357 	return i1_dtype_unknown;
12358 }
12359 
12360 /* Return the number of data items in a keyv. Return 0 if not found */
12361 static unsigned int i1data_get_count(i1data *d, i1key key) {
12362 	i1keyv *k;
12363 
12364 	if ((k = d->find_key(d, key)) != NULL)
12365 		return k->count;
12366 	return 0;
12367 }
12368 
12369 /* Return a pointer to the short data for the key. */
12370 /* Return NULL if not found or wrong type */
12371 static int *i1data_get_shorts(i1data *d, unsigned int *count, i1key key) {
12372 	i1keyv *k;
12373 
12374 	if ((k = d->find_key(d, key)) == NULL)
12375 		return NULL;
12376 
12377 	if (k->type != i1_dtype_short)
12378 		return NULL;
12379 
12380 	if (count != NULL)
12381 		*count = k->count;
12382 
12383 	return (int *)k->data;
12384 }
12385 
12386 /* Return a pointer to the int data for the key. */
12387 /* Return NULL if not found or wrong type */
12388 static int *i1data_get_ints(i1data *d, unsigned int *count, i1key key) {
12389 	i1keyv *k;
12390 
12391 	if ((k = d->find_key(d, key)) == NULL)
12392 		return NULL;
12393 
12394 	if (k->type != i1_dtype_int)
12395 		return NULL;
12396 
12397 	if (count != NULL)
12398 		*count = k->count;
12399 
12400 	return (int *)k->data;
12401 }
12402 
12403 /* Return a pointer to the double data for the key. */
12404 /* Return NULL if not found or wrong type */
12405 static double *i1data_get_doubles(i1data *d, unsigned int *count, i1key key) {
12406 	i1keyv *k;
12407 
12408 	if ((k = d->find_key(d, key)) == NULL)
12409 		return NULL;
12410 
12411 	if (k->type != i1_dtype_double)
12412 		return NULL;
12413 
12414 	if (count != NULL)
12415 		*count = k->count;
12416 
12417 	return (double *)k->data;
12418 }
12419 
12420 
12421 /* Return pointer to one of the int data for the key. */
12422 /* Return NULL if not found or wrong type or out of range index. */
12423 static int *i1data_get_int(i1data *d, i1key key, unsigned int index) {
12424 	i1keyv *k;
12425 
12426 	if ((k = d->find_key(d, key)) == NULL)
12427 		return NULL;
12428 
12429 	if (k->type != i1_dtype_int)
12430 		return NULL;
12431 
12432 	if (index >= k->count)
12433 		return NULL;
12434 
12435 	return ((int *)k->data) + index;
12436 }
12437 
12438 /* Return pointer to one of the double data for the key. */
12439 /* Return NULL if not found or wrong type or out of range index. */
12440 static double *i1data_get_double(i1data *d, i1key key, double *data, unsigned int index) {
12441 	i1keyv *k;
12442 
12443 	if ((k = d->find_key(d, key)) == NULL)
12444 		return NULL;
12445 
12446 	if (k->type != i1_dtype_double)
12447 		return NULL;
12448 
12449 	if (index >= k->count)
12450 		return NULL;
12451 
12452 	return ((double *)k->data) + index;
12453 }
12454 
12455 /* Un-serialize a char buffer into an i1key keyv */
12456 static i1pro_code i1data_unser_shorts(
12457 	i1data *d,
12458 	i1key key,
12459 	int addr,
12460 	unsigned char *buf,
12461 	unsigned int size
12462 ) {
12463 	i1keyv *k;
12464 	int i, count;
12465 
12466 	count = size/2;
12467 
12468 	if (count == 0)
12469 		return I1PRO_DATA_COUNT;
12470 
12471 	if ((k = d->make_key(d, key)) == NULL)
12472 		return I1PRO_DATA_MAKE_KEY;
12473 
12474 	if (k->data != NULL)
12475 		free(k->data);
12476 
12477 	if ((k->data = (void *)malloc(sizeof(int) * count)) == NULL)
12478 		return I1PRO_DATA_MEMORY;
12479 
12480 	for (i = 0; i < count; i++, buf += 2) {
12481 		((int *)k->data)[i] = buf2short(buf);
12482 	}
12483 
12484 	k->count = count;
12485 	k->size = size;
12486 	k->type = i1_dtype_short;
12487 	if (addr != -1)
12488 		k->addr = addr;
12489 
12490 	return I1PRO_OK;
12491 }
12492 
12493 /* Un-serialize a char buffer into an i1key keyv */
12494 static i1pro_code i1data_unser_ints(
12495 	i1data *d,
12496 	i1key key,
12497 	int addr,
12498 	unsigned char *buf,
12499 	unsigned int size
12500 ) {
12501 	i1keyv *k;
12502 	int i, count;
12503 
12504 	count = size/4;
12505 
12506 	if (count == 0)
12507 		return I1PRO_DATA_COUNT;
12508 
12509 	if ((k = d->make_key(d, key)) == NULL)
12510 		return I1PRO_DATA_MAKE_KEY;
12511 
12512 	if (k->data != NULL)
12513 		free(k->data);
12514 
12515 	if ((k->data = (void *)malloc(sizeof(int) * count)) == NULL)
12516 		return I1PRO_DATA_MEMORY;
12517 
12518 	for (i = 0; i < count; i++, buf += 4) {
12519 		((int *)k->data)[i] = buf2int(buf);
12520 	}
12521 
12522 	k->count = count;
12523 	k->size = size;
12524 	k->type = i1_dtype_int;
12525 	if (addr != -1)
12526 		k->addr = addr;
12527 
12528 	return I1PRO_OK;
12529 }
12530 
12531 /* Create an entry for an end of section marker */
12532 static i1pro_code i1data_add_eosmarker(
12533 	i1data *d,
12534 	i1key key,		/* section number */
12535 	int addr
12536 ) {
12537 	i1keyv *k;
12538 
12539 	if ((k = d->make_key(d, key)) == NULL)
12540 		return I1PRO_DATA_MAKE_KEY;
12541 
12542 	if (k->data != NULL) {
12543 		free(k->data);
12544 		k->data = NULL;
12545 	}
12546 
12547 	k->count = 0;
12548 	k->size = 0;
12549 	k->type = i1_dtype_section;
12550 	if (addr != -1)
12551 		k->addr = addr;
12552 
12553 	return I1PRO_OK;
12554 }
12555 
12556 /* Un-serialize a char buffer of floats into a double keyv */
12557 static i1pro_code i1data_unser_doubles(
12558 	i1data *d,
12559 	i1key key,
12560 	int addr,
12561 	unsigned char *buf,
12562 	unsigned int size
12563 ) {
12564 	i1keyv *k;
12565 	int i, count;
12566 
12567 	count = size/4;
12568 
12569 	if (count == 0)
12570 		return I1PRO_DATA_COUNT;
12571 
12572 	if ((k = d->make_key(d, key)) == NULL)
12573 		return I1PRO_DATA_MAKE_KEY;
12574 
12575 	if (k->data != NULL)
12576 		free(k->data);
12577 
12578 	if ((k->data = (void *)malloc(sizeof(double) * count)) == NULL)
12579 		return I1PRO_DATA_MEMORY;
12580 
12581 	for (i = 0; i < count; i++, buf += 4) {
12582 		int val;
12583 		val = buf2int(buf);
12584 		((double *)k->data)[i] = IEEE754todouble((unsigned int)val);
12585 	}
12586 
12587 	k->count = count;
12588 	k->size = size;
12589 	k->type = i1_dtype_double;
12590 	if (addr != -1)
12591 		k->addr = addr;
12592 
12593 	return I1PRO_OK;
12594 }
12595 
12596 
12597 /* Serialize an i1key keyv into a char buffer. Error if it is outside the buffer */
12598 static i1pro_code i1data_ser_ints(
12599 	i1data *d,
12600 	i1keyv *k,
12601 	unsigned char *buf,
12602 	unsigned int size
12603 ) {
12604 	i1pro *p = d->p;
12605 	int i, len;
12606 
12607 	if (k->type != i1_dtype_int)
12608 		return I1PRO_DATA_WRONGTYPE;
12609 
12610 	len = k->count * 4;
12611 	if (len > k->size)
12612 		return I1PRO_DATA_BUFSIZE;
12613 
12614 	if (k->addr < 0 || k->addr >= size || (k->addr + k->size) > size)
12615 		return I1PRO_DATA_BUFSIZE;
12616 
12617 	buf += k->addr;
12618 	for (i = 0; i < k->count; i++, buf += 4) {
12619 		int2buf(buf, ((int *)k->data)[i]);
12620 	}
12621 
12622 	return I1PRO_OK;
12623 }
12624 
12625 /* Serialize a double keyv as floats into a char buffer. Error if the buf is not big enough */
12626 static i1pro_code i1data_ser_doubles(
12627 	i1data *d,
12628 	i1keyv *k,
12629 	unsigned char *buf,
12630 	unsigned int size
12631 ) {
12632 	i1pro *p = d->p;
12633 	int i, len;
12634 
12635 	if (k->type != i1_dtype_double)
12636 		return I1PRO_DATA_WRONGTYPE;
12637 
12638 	len = k->count * 4;
12639 	if (len > k->size)
12640 		return I1PRO_DATA_BUFSIZE;
12641 
12642 	if (k->addr < 0 || k->addr >= size || (k->addr + k->size) > size)
12643 		return I1PRO_DATA_BUFSIZE;
12644 
12645 	buf += k->addr;
12646 	for (i = 0; i < k->count; i++, buf += 4) {
12647 		int2buf(buf, doubletoIEEE754(((double *)k->data)[i]));
12648 	}
12649 
12650 	return I1PRO_OK;
12651 }
12652 
12653 /* Copy an array full of ints to the key */
12654 /* Note the count must be the same as the existing key value, */
12655 /* since we are not prepared to re-allocate key/values within */
12656 /* the EEProm, or re-write the directory. */
12657 static i1pro_code i1data_add_ints(i1data *d, i1key key, int *data, unsigned int count) {
12658 	i1keyv *k;
12659 	int i;
12660 
12661 	if ((k = d->make_key(d, key)) == NULL)
12662 		return I1PRO_DATA_MAKE_KEY;
12663 
12664 	if (count != k->count)
12665 		return I1PRO_DATA_COUNT;
12666 
12667 	if (k->data != NULL)
12668 		free(k->data);
12669 
12670 	if ((k->data = (void *)malloc(sizeof(int) * count)) == NULL)
12671 		return I1PRO_DATA_MEMORY;
12672 
12673 	for (i = 0; i < count; i++) {
12674 		((int *)k->data)[i] = data[i];
12675 	}
12676 
12677 	k->count = count;
12678 	k->type = i1_dtype_int;
12679 
12680 	return I1PRO_OK;
12681 }
12682 
12683 /* Copy an array full of doubles to the key */
12684 /* Note the count must be the same as the existing key value, */
12685 /* since we are not prepared to re-allocate key/values within */
12686 /* the EEProm, or re-write the directory. */
12687 static i1pro_code i1data_add_doubles(i1data *d, i1key key, double *data, unsigned int count) {
12688 	i1keyv *k;
12689 	int i;
12690 
12691 	if ((k = d->make_key(d, key)) == NULL)
12692 		return I1PRO_DATA_MAKE_KEY;
12693 
12694 	if (count != k->count)
12695 		return I1PRO_DATA_COUNT;
12696 
12697 	if (k->data != NULL)
12698 		free(k->data);
12699 
12700 	if ((k->data = (void *)malloc(sizeof(double) * count)) == NULL)
12701 		return I1PRO_DATA_MEMORY;
12702 
12703 	for (i = 0; i < count; i++) {
12704 		((double *)k->data)[i] = data[i];
12705 	}
12706 
12707 	k->count = count;
12708 	k->type = i1_dtype_double;
12709 
12710 	return I1PRO_OK;
12711 }
12712 
12713 /* Initialise the data from the EEProm contents */
12714 static i1pro_code i1data_parse_eeprom(i1data *d, unsigned char *buf, unsigned int len, int extra) {
12715 	i1pro *p = d->p;
12716 	int rv = I1PRO_OK;
12717 	int dir = 0x1000;		/* Location of key directory */
12718 	int minkeys = 300;		/* Expected minumum number of bytes for keys */
12719 	int maxkeys = 512;		/* Expected maxumum number of bytes for keys */
12720 	int block_id;			/* Block id */
12721 	int nokeys;
12722 	i1key key, off, nkey = 0, noff = 0;
12723 	int size;				/* size of key in bytes */
12724 	unsigned char *bp;
12725 	int i;
12726 
12727 	if (extra) {
12728 		dir = 0x2000;	/* Directory is at half way in i1pro2 2nd table */
12729 		minkeys = 200;	/* Hmm. Had a report that the i1Pro2 failed to parse */
12730 	}
12731 
12732 	a1logd(p->log,3,"i1pro_parse_eeprom called with %d bytes, table %d\n",len,extra);
12733 
12734 	/* Room for minimum number of keys ? */
12735 	if ((dir + minkeys) > len)
12736 		return I1PRO_DATA_KEY_COUNT_SMALL;
12737 
12738 	block_id = buf2ushort(buf + dir);
12739 	if ((extra == 0 && block_id != 1)	/* Must be 1 for base data */
12740 	 || (extra == 1 && block_id != 2))	/* Must be 2 for i1pro2 extra data*/
12741 		return I1PRO_DATA_KEY_CORRUPT;
12742 
12743 	nokeys = buf2ushort(buf + dir + 2);	/* Bytes in key table */
12744 	a1logd(p->log,3,"%d bytes for keys in EEProm table %d\n",nokeys, extra);
12745 	if (nokeys < minkeys)
12746 		return I1PRO_DATA_KEY_COUNT_SMALL;
12747 	if (nokeys > maxkeys)
12748 		return I1PRO_DATA_KEY_COUNT_LARGE;
12749 
12750 	nokeys = (nokeys - 4)/6;				/* Number of 6 byte entries */
12751 
12752 	a1logd(p->log,3,"%d keys & values in EEProm table %d\n",nokeys, extra);
12753 
12754 	/* We need current and next value to figure data size out */
12755 	bp = buf + dir + 4;
12756 	key = buf2ushort(bp);
12757 	off = buf2int(bp+2);
12758 	bp += 6;
12759 	for (i = 0; i < nokeys; i++, bp += 6, key = nkey, off = noff) {
12760 		i1_dtype type;
12761 
12762 		if (i < (nokeys-1)) {
12763 			nkey = buf2ushort(bp);
12764 			noff = buf2int(bp+2);
12765 		}
12766 		size = noff - off;
12767 		if (size < 0)
12768 			size = 0;
12769 		type = d->det_type(d, key);
12770 
12771 		a1logd(p->log,3,"Table entry %d is Key 0x%04x, type %d addr 0x%x, size %d\n",
12772 			                                                   i,key,type,off,size);
12773 
12774 		/* Check data is within range */
12775 		if (off >= len || noff < off || noff > len) {
12776 			a1logd(p->log,3,"Key 0x%04x offset %d and length %d out of range\n",key,off,noff);
12777 			return I1PRO_DATA_KEY_MEMRANGE;
12778 		}
12779 
12780 		if (type == i1_dtype_unknown) {
12781 			if (d->log->debug >= 7) {
12782 				int i;
12783 				char oline[100], *bp = oline;
12784 				bp = oline;
12785 				a1logd(d->log,7,"Key 0x%04x is unknown type\n",key);
12786 				for (i = 0; i < size; i++) {
12787 					if ((i % 16) == 0)
12788 						bp += sprintf(bp,"    %04x:",i);
12789 					bp += sprintf(bp," %02x",buf[off + i]);
12790 					if ((i+1) >= size || ((i+1) % 16) == 0) {
12791 						bp += sprintf(bp,"\n");
12792 						a1logd(p->log,7,oline);
12793 						bp = oline;
12794 					}
12795 				}
12796 			}
12797 			if (type == i1_dtype_unknown)
12798 				continue;				/* Ignore it */
12799 		}
12800 		if (type == i1_dtype_section) {
12801 			if ((rv = i1data_add_eosmarker(d, key, off)) != I1PRO_OK) {
12802 				a1logd(p->log,3,"Key 0x%04x section marker failed with 0x%x\n",key,rv);
12803 				return rv;
12804 			}
12805 			continue;
12806 		}
12807 		if (i >= nokeys) {
12808 			a1logd(p->log,3,"Last key wasn't a section marker!\n");
12809 			return I1PRO_DATA_KEY_ENDMARK;
12810 		}
12811 		if (type == i1_dtype_short) {
12812 			if ((rv = i1data_unser_shorts(d, key, off, buf + off, size)) != I1PRO_OK) {
12813 				a1logd(p->log,3,"Key 0x%04x short unserialise failed with 0x%x\n",key,rv);
12814 				return rv;
12815 			}
12816 		} else if (type == i1_dtype_int) {
12817 			if ((rv = i1data_unser_ints(d, key, off, buf + off, size)) != I1PRO_OK) {
12818 				a1logd(p->log,3,"Key 0x%04x int unserialise failed with 0x%x\n",key,rv);
12819 				return rv;
12820 			}
12821 		} else if (type == i1_dtype_double) {
12822 			if ((rv = i1data_unser_doubles(d, key, off, buf + off, size)) != I1PRO_OK) {
12823 				a1logd(p->log,3,"Key 0x%04x double unserialise failed with 0x%x\n",key,rv);
12824 				return rv;
12825 			}
12826 		} else {
12827 			a1logd(p->log,3,"Key 0x%04x has type we can't handle!\n",key);
12828 		}
12829 	}
12830 
12831 	return I1PRO_OK;
12832 }
12833 
12834 /* Compute and set the checksum, then serialise all the keys up */
12835 /* to the first marker into a buffer, ready for writing back to */
12836 /* the EEProm. It is an error if this buffer is not located at */
12837 /* zero in the EEProm */
12838 static i1pro_code i1data_prep_section1(
12839 i1data *d,
12840 unsigned char **buf,		/* return allocated buffer */
12841 unsigned int *len
12842 ) {
12843 	i1pro *p = d->p;
12844 	i1proimp *m = d->m;
12845 	int chsum1, *chsum2;
12846 	i1keyv *k, *sk, *j;
12847 	i1pro_code ev = I1PRO_OK;
12848 
12849 	a1logd(p->log,5,"i1data_prep_section1 called\n");
12850 
12851 	/* Compute the checksum for the first copy of the log data */
12852 	chsum1 = m->data->checksum(m->data, 0);
12853 
12854 	/* Locate and then set the checksum */
12855 	if ((chsum2 = m->data->get_int(m->data, key_checksum, 0)) == NULL) {
12856 		a1logd(p->log,2,"i1data_prep_section1 failed to locate checksum\n");
12857 		return I1PRO_INT_PREP_LOG_DATA;
12858 	}
12859 	*chsum2 = chsum1;
12860 
12861 	/* Locate the first section marker */
12862 	for (sk = d->head; sk != NULL; sk = sk->next) {
12863 		if (sk->type == i1_dtype_section)
12864 			break;
12865 	}
12866 	if (sk == NULL) {
12867 		a1logd(p->log,2,"i1data_prep_section1 failed to find section marker\n");
12868 		return I1PRO_INT_PREP_LOG_DATA;
12869 	}
12870 
12871 	/* for each key up to the first section marker */
12872 	/* check it resides within that section, and doesn't */
12873 	/* overlap any other key. */
12874 	for (k = d->head; k != NULL; k = k->next) {
12875 		if (k->type == i1_dtype_section)
12876 			break;
12877 		if (k->addr < 0 || k->addr >= sk->addr || (k->addr + k->size) > sk->addr) {
12878 			a1logd(p->log,2,"i1data_prep_section1 found key outside section\n");
12879 			return I1PRO_INT_PREP_LOG_DATA;
12880 		}
12881 		for (j = k->next; j != NULL; j = j->next) {
12882 			if (j->type == i1_dtype_section)
12883 				break;
12884 			if ((j->addr >= k->addr && j->addr < (k->addr + k->size))
12885 			 || ((j->addr + j->size) > k->addr && (j->addr + j->size) <= (k->addr + k->size))) {
12886 				a1logd(p->log,2,"i1data_prep_section1 found key overlap section, 0x%x %d and 0x%x %d\n",
12887 				k->addr, k->size, j->addr, j->size);
12888 				return I1PRO_INT_PREP_LOG_DATA;
12889 			}
12890 		}
12891 	}
12892 
12893 	/* Allocate the buffer for the data */
12894 	*len = sk->addr;
12895 	if ((*buf = (unsigned char *)calloc(sk->addr, sizeof(unsigned char))) == NULL) {
12896 		a1logw(p->log, "i1data: malloc failed!\n");
12897 		return I1PRO_INT_MALLOC;
12898 	}
12899 
12900 	/* Serialise it into the buffer */
12901 	for (k = d->head; k != NULL; k = k->next) {
12902 		if (k->type == i1_dtype_section)
12903 			break;
12904 		else if (k->type == i1_dtype_int) {
12905 			if ((ev = m->data->ser_ints(m->data, k, *buf, *len)) != I1PRO_OK) {
12906 				a1logd(p->log,2,"i1data_prep_section1 serializing ints failed\n");
12907 				return ev;
12908 			}
12909 		} else if (k->type == i1_dtype_double) {
12910 			if ((ev = m->data->ser_doubles(m->data, k, *buf, *len)) != I1PRO_OK) {
12911 				a1logd(p->log,2,"i1data_prep_section1 serializing doubles failed\n");
12912 				return ev;
12913 			}
12914 		} else {
12915 			a1logd(p->log,2,"i1data_prep_section1 tried to serialise unknown type\n");
12916 			return I1PRO_INT_PREP_LOG_DATA;
12917 		}
12918 	}
12919 	a1logd(p->log,5,"a_prep_section1 done\n");
12920 	return ev;
12921 }
12922 
12923 
12924 /* Return the data type for the given key identifier */
12925 static i1_dtype i1data_det_type(i1data *d, i1key key) {
12926 
12927 	if (key < 0x100)
12928 		return i1_dtype_section;
12929 
12930 	switch(key) {
12931 		/* Log keys */
12932 		case key_meascount:
12933 		case key_meascount + 1000:
12934 			return i1_dtype_int;
12935 		case key_darkreading:
12936 		case key_darkreading + 1000:
12937 			return i1_dtype_int;
12938 		case key_whitereading:
12939 		case key_whitereading + 1000:
12940 			return i1_dtype_int;
12941 		case key_gainmode:
12942 		case key_gainmode + 1000:
12943 			return i1_dtype_int;
12944 		case key_inttime:
12945 		case key_inttime + 1000:
12946 			return i1_dtype_double;
12947 		case key_caldate:
12948 		case key_caldate + 1000:
12949 			return i1_dtype_int;
12950 		case key_calcount:
12951 		case key_calcount + 1000:
12952 			return i1_dtype_int;
12953 		case key_checksum:
12954 		case key_checksum + 1000:
12955 			return i1_dtype_int;
12956 		case key_rpinttime:
12957 		case key_rpinttime + 1000:
12958 			return i1_dtype_double;
12959 		case key_rpcount:
12960 		case key_rpcount + 1000:
12961 			return i1_dtype_int;
12962 		case key_acount:
12963 		case key_acount + 1000:
12964 			return i1_dtype_int;
12965 		case key_lampage:
12966 		case key_lampage + 1000:
12967 			return i1_dtype_double;
12968 
12969 
12970 		/* Intstrument calibration keys */
12971 		case key_ng_lin:
12972 			return i1_dtype_double;
12973 		case key_hg_lin:
12974 			return i1_dtype_double;
12975 		case key_min_int_time:
12976 			return i1_dtype_double;
12977 		case key_max_int_time:
12978 			return i1_dtype_double;
12979 		case key_mtx_index:
12980 			return i1_dtype_int;
12981 		case key_mtx_nocoef:
12982 			return i1_dtype_int;
12983 		case key_mtx_coef:
12984 			return i1_dtype_double;
12985 		case key_0bb9:
12986 			return i1_dtype_int;
12987 		case key_0bba:
12988 			return i1_dtype_int;
12989 		case key_white_ref:
12990 			return i1_dtype_double;
12991 		case key_emis_coef:
12992 			return i1_dtype_double;
12993 		case key_amb_coef:
12994 			return i1_dtype_double;
12995 		case key_0fa0:
12996 			return i1_dtype_int;
12997 		case key_0bbf:
12998 			return i1_dtype_int;
12999 		case key_cpldrev:
13000 			return i1_dtype_int;
13001 		case key_0bc1:
13002 			return i1_dtype_int;
13003 		case key_capabilities:
13004 			return i1_dtype_int;
13005 		case key_0bc3:
13006 			return i1_dtype_int;
13007 		case key_physfilt:
13008 			return i1_dtype_int;
13009 		case key_0bc5:
13010 			return i1_dtype_int;
13011 		case key_0bc6:
13012 			return i1_dtype_double;
13013 		case key_sens_target:
13014 			return i1_dtype_int;
13015 		case key_sens_dark:
13016 			return i1_dtype_int;
13017 		case key_ng_sens_sat:
13018 			return i1_dtype_int;
13019 		case key_hg_sens_sat:
13020 			return i1_dtype_int;
13021 		case key_serno:
13022 			return i1_dtype_int;
13023 		case key_dom:
13024 			return i1_dtype_int;
13025 		case key_hg_factor:
13026 			return i1_dtype_double;
13027 		default:
13028 			return i1_dtype_unknown;
13029 
13030 		/* i1pro2 keys */
13031 		case 0x2ee0:
13032 			return i1_dtype_unknown;	// ~~
13033 		case 0x2ee1:
13034 			return i1_dtype_char;
13035 		case 0x2ee2:
13036 			return i1_dtype_int;
13037 		case 0x2ee3:
13038 			return i1_dtype_int;
13039 		case 0x2ee4:
13040 			return i1_dtype_unknown;	// ~~
13041 
13042 		case 0x2eea:
13043 			return i1_dtype_int;
13044 		case 0x2eeb:
13045 			return i1_dtype_int;
13046 		case 0x2eec:
13047 			return i1_dtype_int;
13048 
13049 		case 0x2ef4:
13050 			return i1_dtype_double;
13051 		case 0x2ef5:
13052 			return i1_dtype_double;
13053 		case 0x2ef6:
13054 			return i1_dtype_double;
13055 		case 0x2ef9:
13056 			return i1_dtype_double;
13057 		case 0x2efa:
13058 			return i1_dtype_double;
13059 		case 0x2efe:
13060 			return i1_dtype_int;
13061 		case 0x2eff:
13062 			return i1_dtype_int;
13063 
13064 		case 0x2f08:
13065 			return i1_dtype_double;
13066 		case 0x2f09:
13067 			return i1_dtype_double;
13068 		case 0x2f12:
13069 			return i1_dtype_double;
13070 		case 0x2f13:
13071 			return i1_dtype_double;
13072 		case 0x2f14:
13073 			return i1_dtype_double;
13074 		case 0x2f15:
13075 			return i1_dtype_double;
13076 
13077 		case 0x2f44:					/* Wavelength LED reference shape ? */
13078 			return i1_dtype_double;
13079 		case 0x2f45:
13080 			return i1_dtype_int;
13081 		case 0x2f46:
13082 			return i1_dtype_double;
13083 		case 0x2f4e:
13084 			return i1_dtype_double;
13085 		case 0x2f4f:
13086 			return i1_dtype_double;
13087 		case 0x2f50:
13088 			return i1_dtype_double;
13089 		case 0x2f58:
13090 			return i1_dtype_short;		/* Stray light compensation table */
13091 		case 0x2f59:
13092 			return i1_dtype_double;		/* Stray light scale factor ? */
13093 		case 0x2f62:
13094 			return i1_dtype_double;
13095 		case 0x2f63:
13096 			return i1_dtype_double;
13097 		case 0x2f6c:
13098 			return i1_dtype_double;
13099 		case 0x2f6d:
13100 			return i1_dtype_double;
13101 		case 0x2f6e:
13102 			return i1_dtype_double;
13103 		case 0x2f76:
13104 			return i1_dtype_double;
13105 		case 0x2f77:
13106 			return i1_dtype_double;
13107 
13108 		case 0x32c8:
13109 			return i1_dtype_int;		// Date
13110 		case 0x32c9:
13111 			return i1_dtype_int;		// Date
13112 		case 0x32ca:
13113 			return i1_dtype_unknown;    // ~~
13114 
13115 		case 0x36b0:
13116 			return i1_dtype_int;		// Date
13117 		case 0x36b1:
13118 			return i1_dtype_int;		// Date
13119 		case 0x36b2:
13120 			return i1_dtype_unknown;    // ~~
13121 
13122 		case 0x3a99:
13123 			return i1_dtype_unknown;    // ~~
13124 		case 0x3a9a:
13125 			return i1_dtype_unknown;    // ~~
13126 		case 0x3a9b:
13127 			return i1_dtype_unknown;    // ~~
13128 		case 0x3a9c:
13129 			return i1_dtype_unknown;    // ~~
13130 		case 0x3a9d:
13131 			return i1_dtype_unknown;    // ~~
13132 
13133 		case 0x3e81:
13134 			return i1_dtype_char;		// "X-Rite"
13135 		case 0x3e82:
13136 			return i1_dtype_unknown;    // ~~
13137 		case 0x3e8a:
13138 			return i1_dtype_unknown;    // ~~
13139 
13140 		case 0x3e94:
13141 			return i1_dtype_unknown;    // ~~
13142 
13143 	}
13144 	return i1_dtype_unknown;
13145 }
13146 
13147 /* Given an index starting at 0, return the matching key code */
13148 /* for keys that get checksummed. Return 0 if outside range. */
13149 static i1key i1data_chsum_keys(
13150 	i1data *d,
13151 	int index
13152 ) {
13153 	switch(index) {
13154 		case 0:
13155 			return key_meascount;
13156 		case 1:
13157 			return key_darkreading;
13158 		case 2:
13159 			return key_whitereading;
13160 		case 3:
13161 			return key_gainmode;
13162 		case 4:
13163 			return key_inttime;
13164 		case 5:
13165 			return key_caldate;
13166 		case 6:
13167 			return key_calcount;
13168 		case 7:
13169 			return key_rpinttime;
13170 		case 8:
13171 			return key_rpcount;
13172 		case 9:
13173 			return key_acount;
13174 		case 10:
13175 			return key_lampage;
13176 	}
13177 	return 0;
13178 }
13179 
13180 /* Compute a checksum. */
13181 static int i1data_checksum(
13182 	i1data *d,
13183 	i1key keyoffset 	/* Offset to apply to keys */
13184 ) {
13185 	int i, n, j;
13186 	int chsum = 0;
13187 
13188 	for (i = 0; ; i++) {
13189 		i1key key;
13190 		i1keyv *k;
13191 
13192 		if ((key = d->chsum_keys(d, i)) == 0)
13193 			break;			/* we're done */
13194 
13195 		key += keyoffset;
13196 
13197 		if ((k = d->find_key(d, key)) == NULL)
13198 			continue;		/* Hmm */
13199 
13200 		if (k->type == i1_dtype_int) {
13201 			for (j = 0; j < k->count; j++)
13202 				chsum += ((int *)k->data)[j];
13203 		} else if (k->type == i1_dtype_double) {
13204 			for (j = 0; j < k->count; j++)
13205 				chsum += doubletoIEEE754(((double *)k->data)[j]);
13206 		}
13207 	}
13208 
13209 	return chsum;
13210 }
13211 
13212 /* Destroy ourselves */
13213 static void i1data_del(i1data *d) {
13214 	i1keyv *k, *nk;
13215 
13216 	del_a1log(d->log);		/* Unref it */
13217 
13218 	/* Free all the keys and their data */
13219 	for (k = d->head; k != NULL; k = nk) {
13220 		nk = k->next;
13221 		if (k->data != NULL)
13222 			free(k->data);
13223 		free(k);
13224 	}
13225 	free(d);
13226 }
13227 
13228 /* Constructor for i1data */
13229 i1data *new_i1data(i1proimp *m) {
13230 	i1data *d;
13231 	if ((d = (i1data *)calloc(1, sizeof(i1data))) == NULL) {
13232 		a1loge(m->p->log, 1, "new_i1data: malloc failed!\n");
13233 		return NULL;
13234 	}
13235 
13236 	d->p = m->p;
13237 	d->m = m;
13238 
13239 	d->log = new_a1log_d(m->p->log);	/* Take reference */
13240 
13241 	d->find_key      = i1data_find_key;
13242 	d->make_key      = i1data_make_key;
13243 	d->get_type      = i1data_get_type;
13244 	d->get_count     = i1data_get_count;
13245 	d->get_shorts    = i1data_get_shorts;
13246 	d->get_ints      = i1data_get_ints;
13247 	d->get_doubles   = i1data_get_doubles;
13248 	d->get_int       = i1data_get_int;
13249 	d->get_double    = i1data_get_double;
13250 	d->unser_ints    = i1data_unser_ints;
13251 	d->unser_doubles = i1data_unser_doubles;
13252 	d->ser_ints      = i1data_ser_ints;
13253 	d->ser_doubles   = i1data_ser_doubles;
13254 	d->parse_eeprom  = i1data_parse_eeprom;
13255 	d->prep_section1 = i1data_prep_section1;
13256 	d->add_ints      = i1data_add_ints;
13257 	d->add_doubles   = i1data_add_doubles;
13258 	d->del           = i1data_del;
13259 
13260 	d->det_type      = i1data_det_type;
13261 	d->chsum_keys    = i1data_chsum_keys;
13262 	d->checksum      = i1data_checksum;
13263 
13264 	return d;
13265 }
13266 
13267 /* ----------------------------------------------------------------- */
13268