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