1
2 /*
3 * Argyll Color Correction System
4 *
5 * Author: Graeme W. Gill
6 * Date: 12/1/2009
7 *
8 * Copyright 2006 - 2014, Graeme W. Gill
9 * All rights reserved.
10 *
11 * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
12 * see the License2.txt file for licencing details.
13 *
14 * (Base on i1pro_imp.c)
15 */
16
17 /*
18 If you make use of the instrument driver code here, please note
19 that it is the author(s) of the code who take responsibility
20 for its operation. Any problems or queries regarding driving
21 instruments with the Argyll drivers, should be directed to
22 the Argyll's author(s), and not to any other party.
23
24 If there is some instrument feature or function that you
25 would like supported here, it is recommended that you
26 contact Argyll's author(s) first, rather than attempt to
27 modify the software yourself, if you don't have firm knowledge
28 of the instrument communicate protocols. There is a chance
29 that an instrument could be damaged by an incautious command
30 sequence, and the instrument companies generally cannot and
31 will not support developers that they have not qualified
32 and agreed to support.
33 */
34
35 /* TTBD:
36
37 */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <time.h>
44 #include <stdarg.h>
45 #include <math.h>
46 #if defined(UNIX)
47 # include <utime.h>
48 #else
49 # include <sys/utime.h>
50 #endif
51 #include <sys/stat.h>
52 #ifndef SALONEINSTLIB
53 #include "copyright.h"
54 #include "aconfig.h"
55 #include "numlib.h"
56 #include "rspl.h"
57 #else /* SALONEINSTLIB */
58 #include <fcntl.h>
59 #include "sa_config.h"
60 #include "numsup.h"
61 #include "rspl1.h"
62 #endif /* SALONEINSTLIB */
63 #include "xspect.h"
64 #include "insttypes.h"
65 #include "conv.h"
66 #include "icoms.h"
67 #include "sort.h"
68
69 /* Configuration */
70 #undef USE_HIGH_GAIN_MODE /* [Und] Make use of high gain mode in emissive measurements */
71 #define USE_THREAD /* [Def] Need to use thread, or there are 1.5 second internal */
72 /* instrument delays ! */
73 #define ENABLE_NONVCAL /* [Def] Enable saving calibration state between program runs to a file */
74 #define ENABLE_NONLINCOR /* [Def] Enable non-linear correction */
75 /* NOTE :- high gain scaling will be stuffed if disabled! */
76 #define ENABLE_LEDTEMPC /* [Def] Enable LED temperature compensation */
77 #define ENABLE_BKDRIFTC /* [Def] Enable Emis. Black drift compensation using sheilded cell values */
78 #define HEURISTIC_BKDRIFTC /* [Def] Use heusristic black drift correction version */
79 #undef ENABLE_REFSTRAYC /* [Und] Enable Reflective stray light compensation */
80 #define REFSTRAYC_FACTOR 0.000086 /* [0.00043] Compensation factor */
81 #undef ENABLE_REFLEDINTER /* [Und] Enable Reflective LED interference correction */
82
83 #define ENABLE_SPOS_CHECK /* [Def] Check the sensor position is reasonable for measurement */
84 #define FILTER_SPOS_EVENTS /* [Def] Use a thread to filter SPOS event changes */
85 #define FILTER_TIME 500 /* [500] Filter time in msec */
86 #define DCALTOUT (1 * 60 * 60) /* [1 Hrs] Dark Calibration timeout in seconds */
87 #define WCALTOUT (24 * 60 * 60) /* [24 Hrs] White Calibration timeout in seconds */
88 #define MAXSCANTIME 20.0 /* [20 Sec] Maximum scan time in seconds */
89 #define SW_THREAD_TIMEOUT (10 * 60.0) /* [10 Min] Switch read thread timeout */
90
91 #define SINGLE_READ /* [Def] Use a single USB read for scan to eliminate latency issues. */
92 #define HIGH_RES /* [Def] Enable high resolution spectral mode code. Disable */
93 /* to break dependency on rspl library. */
94 # undef FAST_HIGH_RES_SETUP /* Slightly better accuracy ? */
95
96 /* Debug [Und] */
97 #undef DEBUG /* Turn on extra messages & plots */
98 #undef PLOT_DEBUG /* Use plot to show readings & processing */
99 #undef PLOT_REFRESH /* Plot refresh rate measurement info */
100 #undef PLOT_UPDELAY /* Plot data used to determine display update delay */
101 #undef RAWR_DEBUG /* Print out raw reading processing values */
102 #undef DUMP_SCANV /* Dump scan readings to a file "mkdump.txt" */
103 #undef DUMP_DARKM /* Append raw dark readings to file "mkddump.txt" */
104 #undef DUMP_BKLED /* Save REFSTRAYC & REFLEDNOISE comp plot to "refbk1.txt" & "refbk2.txt" */
105 #undef APPEND_MEAN_EMMIS_VAL /* Append averaged uncalibrated reading to file "mkdump.txt" */
106 #undef IGNORE_WHITE_INCONS /* Ignore define reference reading inconsistency */
107 #undef TEST_DARK_INTERP /* Test out the dark interpolation (need DEBUG for plot) */
108 #undef PLOT_RCALCURVE /* Plot the reflection reference curve */
109 #undef PLOT_ECALCURVES /* Plot the emission reference curves */
110 #undef PLOT_TEMPCOMP /* Plot before and after LED temp. compensation */
111 #undef PLOT_PATREC /* Print & Plot patch/flash recognition information */
112 #undef HIGH_RES_DEBUG
113 #undef HIGH_RES_PLOT
114 #undef HIGH_RES_PLOT_STRAYL /* Plot stray light upsample */
115
116
117 #define DISP_INTT 0.7 /* Seconds per reading in display spot mode */
118 /* More improves repeatability in dark colors, but limits */
119 /* the maximum brightness level befor saturation. */
120 /* A value of 2.0 seconds has a limit of about 110 cd/m^2 */
121 #define DISP_INTT2 0.3 /* High brightness display spot mode seconds per reading, */
122 /* Should be good up to 275 cd/m^2 */
123 #define DISP_INTT3 0.1 /* High brightness display spot mode seconds per reading, */
124 /* Should be good up to 700 cd/m^2 */
125
126 #define ADARKINT_MIN 0.01 /* Min cal time for adaptive dark cal */
127 #define ADARKINT_MAX 2.0 /* Max cal time for adaptive dark cal with high gain mode */
128 #define ADARKINT_MAX2 4.0 /* Max cal time for adaptive dark for no high gain */
129
130 #define RDEAD_TIME 0.004432 /* Fudge figure to make reflecting intn. time scale linearly */
131
132 #define EMIS_SCALE_FACTOR 1.0 /* Emission mode scale factor */
133 #define AMB_SCALE_FACTOR 1.0 /* Ambient mode scale factor for Lux */
134
135 #define NSEN_MAX 140 /* Maximum nsen/raw value we can cope with */
136
137 /* Wavelength to start duplicating values below, because it is too noisy */
138 #define WL_REF_MIN 420.0
139 #define WL_EMIS_MIN 400.0
140
141 /* High res mode settings */
142 #define HIGHRES_SHORT 380 /* Wavelength to calculate. Too noisy to try expanding range. */
143 #define HIGHRES_LONG 730
144 #define HIGHRES_WIDTH (10.0/3.0) /* (The 3.3333 spacing and lanczos2 seems a good combination) */
145
146
147 #include "munki.h"
148 #include "munki_imp.h"
149 #include "xrga.h"
150
151 /* - - - - - - - - - - - - - - - - - - */
152
153 #define PATCH_CONS_THR 0.05 /* Dark measurement consistency threshold */
154 #define DARKTHSCAMIN 5000.0 /* Dark threshold scaled/offset minimum */
155
156 /* - - - - - - - - - - - - - - - - - - */
157
158 /* Three levels of runtime debugging messages:
159
160 ~~~ this is no longer accurate. a1logd calls
161 ~~~ probably need to be tweaked.
162 1 = default, typical I/O messages etc.
163 2 = more internal operation messages
164 3 = dump extra detailes
165 4 = dump EEPROM data
166 5 = dump tables etc
167 */
168
169 /* ============================================================ */
170 /* Debugging plot support */
171
172 #if defined(DEBUG) || defined(PLOT_DEBUG) || defined(PLOT_PATREC) || defined(HIGH_RES_PLOT) || defined(HIGH_RES_PLOT_STRAYL)
173
174 # include <plot.h>
175
176 static int disdebplot = 0;
177
178 # define DISDPLOT disdebplot = 1;
179 # define ENDPLOT disdebplot = 0;
180
181 /* Plot a CCD spectra */
plot_raw(double * data)182 void plot_raw(double *data) {
183 int i;
184 double xx[NSEN_MAX];
185 double yy[NSEN_MAX];
186
187 if (disdebplot)
188 return;
189
190 for (i = 0; i < 128; i++) {
191 xx[i] = (double)i;
192 yy[i] = data[i];
193 }
194 do_plot(xx, yy, NULL, NULL, 128);
195 }
196
197 /* Plot two CCD spectra */
plot_raw2(double * data1,double * data2)198 void plot_raw2(double *data1, double *data2) {
199 int i;
200 double xx[NSEN_MAX];
201 double y1[NSEN_MAX];
202 double y2[NSEN_MAX];
203
204 if (disdebplot)
205 return;
206
207 for (i = 0; i < 128; i++) {
208 xx[i] = (double)i;
209 y1[i] = data1[i];
210 y2[i] = data2[i];
211 }
212 do_plot(xx, y1, y2, NULL, 128);
213 }
214
215 /* Plot a converted spectra */
plot_wav(munkiimp * m,double * data)216 void plot_wav(munkiimp *m, double *data) {
217 int i;
218 double xx[NSEN_MAX];
219 double yy[NSEN_MAX];
220
221 if (disdebplot)
222 return;
223
224 for (i = 0; i < m->nwav; i++) {
225 xx[i] = XSPECT_WL(m->wl_short, m->wl_long, m->nwav, i);
226 yy[i] = data[i];
227 }
228 do_plot(xx, yy, NULL, NULL, m->nwav);
229 }
230
231 /* Plot a standard res spectra */
plot_wav1(munkiimp * m,double * data)232 void plot_wav1(munkiimp *m, double *data) {
233 int i;
234 double xx[36];
235 double yy[36];
236
237 if (disdebplot)
238 return;
239
240 for (i = 0; i < m->nwav1; i++) {
241 xx[i] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
242 yy[i] = data[i];
243 }
244 do_plot(xx, yy, NULL, NULL, m->nwav1);
245 }
246
247 /* Plot a high res spectra */
plot_wav2(munkiimp * m,double * data)248 void plot_wav2(munkiimp *m, double *data) {
249 int i;
250 double xx[NSEN_MAX];
251 double yy[NSEN_MAX];
252
253 if (disdebplot)
254 return;
255
256 for (i = 0; i < m->nwav2; i++) {
257 xx[i] = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
258 yy[i] = data[i];
259 }
260 do_plot(xx, yy, NULL, NULL, m->nwav2);
261 }
262
263 #else /* !PLOT_DEBUG */
264
265 # define DISDPLOT
266 # define ENDPLOT
267
268 #endif /* !PLOT_DEBUG */
269
270 /* ============================================================ */
271
272 munki_code munki_touch_calibration(munki *p);
273
274 /* Implementation struct */
275
276 /* Add an implementation structure */
add_munkiimp(munki * p)277 munki_code add_munkiimp(munki *p) {
278 munkiimp *m;
279
280 if ((m = (munkiimp *)calloc(1, sizeof(munkiimp))) == NULL) {
281 a1logd(p->log,3,"add_munkiimp malloc %lu bytes failed (1)\n",sizeof(munkiimp));
282 return MUNKI_INT_MALLOC;
283 }
284 m->p = p;
285
286 m->lo_secs = 2000000000; /* A very long time */
287
288 p->m = (void *)m;
289 return MUNKI_OK;
290 }
291
292 /* Shutdown instrument, and then destroy */
293 /* implementation structure */
del_munkiimp(munki * p)294 void del_munkiimp(munki *p) {
295
296 a1logd(p->log,3,"munki_del called\n");
297
298 #ifdef ENABLE_NONVCAL
299 /* Touch it so that we know when the instrument was last open */
300 munki_touch_calibration(p);
301 #endif /* ENABLE_NONVCAL */
302
303 if (p->m != NULL) {
304 int i;
305 munkiimp *m = (munkiimp *)p->m;
306 munki_state *s;
307
308 #ifdef FILTER_SPOS_EVENTS
309 if (m->spos_th != NULL)
310 m->spos_th_term = 1;
311 #endif
312
313 if (m->th != NULL) { /* Terminate switch monitor thread by simulating an event */
314 m->th_term = 1; /* Tell thread to exit on error */
315 munki_simulate_event(p, mk_eve_spos_change, 0);
316 for (i = 0; m->th_termed == 0 && i < 5; i++)
317 msec_sleep(50); /* Wait for thread to terminate */
318 if (i >= 5) {
319 a1logd(p->log,3,"Munki switch thread termination failed\n");
320 }
321 m->th->del(m->th);
322 usb_uninit_cancel(&m->sw_cancel); /* Don't need cancel token now */
323 }
324
325 #ifdef FILTER_SPOS_EVENTS
326 if (m->spos_th != NULL) {
327 for (i = 0; m->spos_th_termed == 0 && i < 5; i++)
328 msec_sleep(50); /* Wait for thread to terminate */
329 if (i >= 5) {
330 a1logd(p->log,3,"Munki spos thread termination failed\n");
331 }
332 m->spos_th->del(m->spos_th);
333 }
334 #endif
335
336 /* Free any per mode data */
337 for (i = 0; i < mk_no_modes; i++) {
338 s = &m->ms[i];
339
340 free_dvector(s->dark_data, -1, m->nraw-1);
341 free_dvector(s->dark_data2, -1, m->nraw-1);
342 free_dvector(s->dark_data3, -1, m->nraw-1);
343 free_dvector(s->white_data, -1, m->nraw-1);
344 free_dmatrix(s->iwhite_data, 0, 1, -1, m->nraw-1);
345 free_dmatrix(s->idark_data, 0, 3, -1, m->nraw-1);
346
347 free_dvector(s->cal_factor1, 0, m->nwav1-1);
348 free_dvector(s->cal_factor2, 0, m->nwav2-1);
349 }
350
351 /* Free EEProm key data */
352 if (m->data != NULL)
353 m->data->del(m->data);
354
355 /* Free arrays */
356
357 if (m->lin0 != NULL)
358 free(m->lin0);
359 if (m->lin1 != NULL)
360 free(m->lin1);
361
362 if (m->white_ref1 != NULL)
363 free(m->white_ref1);
364 if (m->emis_coef1 != NULL)
365 free(m->emis_coef1);
366 if (m->amb_coef1 != NULL)
367 free(m->amb_coef1);
368 if (m->proj_coef1 != NULL)
369 free(m->proj_coef1);
370
371 if (m->white_ref2 != NULL)
372 free(m->white_ref2);
373 if (m->emis_coef2 != NULL)
374 free(m->emis_coef2);
375 if (m->amb_coef2 != NULL)
376 free(m->amb_coef2);
377 if (m->proj_coef2 != NULL)
378 free(m->proj_coef2);
379
380 if (m->straylight1 != NULL)
381 free_dmatrix(m->straylight1, 0, m->nwav1-1, 0, m->nwav1-1);
382
383 if (m->straylight2 != NULL)
384 free_dmatrix(m->straylight2, 0, m->nwav1-2, 0, m->nwav1-2);
385
386 if (m->rmtx_index1 != NULL)
387 free(m->rmtx_index1);
388 if (m->rmtx_nocoef1 != NULL)
389 free(m->rmtx_nocoef1);
390 if (m->rmtx_coef1 != NULL)
391 free(m->rmtx_coef1);
392
393 if (m->rmtx_index2 != NULL)
394 free(m->rmtx_index2);
395 if (m->rmtx_nocoef2 != NULL)
396 free(m->rmtx_nocoef2);
397 if (m->rmtx_coef2 != NULL)
398 free(m->rmtx_coef2);
399
400 if (m->emtx_index1 != NULL)
401 free(m->emtx_index1);
402 if (m->emtx_nocoef1 != NULL)
403 free(m->emtx_nocoef1);
404 if (m->emtx_coef1 != NULL)
405 free(m->emtx_coef1);
406
407 if (m->emtx_index2 != NULL)
408 free(m->emtx_index2);
409 if (m->emtx_nocoef2 != NULL)
410 free(m->emtx_nocoef2);
411 if (m->emtx_coef2 != NULL)
412 free(m->emtx_coef2);
413
414 free(m);
415 p->m = NULL;
416 }
417 }
418
419 /* ============================================================ */
420 /* Little endian wire format conversion routines */
421
422 /* Take an int, and convert it into a byte buffer little endian */
int2buf(unsigned char * buf,int inv)423 static void int2buf(unsigned char *buf, int inv) {
424 buf[3] = (inv >> 24) & 0xff;
425 buf[2] = (inv >> 16) & 0xff;
426 buf[1] = (inv >> 8) & 0xff;
427 buf[0] = (inv >> 0) & 0xff;
428 }
429
430 /* Take a short, and convert it into a byte buffer little endian */
short2buf(unsigned char * buf,int inv)431 static void short2buf(unsigned char *buf, int inv) {
432 buf[1] = (inv >> 8) & 0xff;
433 buf[0] = (inv >> 0) & 0xff;
434 }
435
436 /* Take a word sized buffer, and convert it to an int */
buf2int(unsigned char * buf)437 static int buf2int(unsigned char *buf) {
438 int val;
439 val = ((signed char *)buf)[3];
440 val = ((val << 8) + (0xff & buf[2]));
441 val = ((val << 8) + (0xff & buf[1]));
442 val = ((val << 8) + (0xff & buf[0]));
443 return val;
444 }
445
446 /* Take a word sized buffer, and convert it to an unsigned int */
buf2uint(unsigned char * buf)447 static unsigned int buf2uint(unsigned char *buf) {
448 unsigned int val;
449 val = (0xff & buf[3]);
450 val = ((val << 8) + (0xff & buf[2]));
451 val = ((val << 8) + (0xff & buf[1]));
452 val = ((val << 8) + (0xff & buf[0]));
453 return val;
454 }
455
456 /* Take a short sized buffer, and convert it to an int */
buf2short(unsigned char * buf)457 static int buf2short(unsigned char *buf) {
458 int val;
459 val = ((signed char *)buf)[1];
460 val = ((val << 8) + (0xff & buf[0]));
461 return val;
462 }
463
464 /* Take a unsigned short sized buffer, and convert it to an int */
buf2ushort(unsigned char * buf)465 static int buf2ushort(unsigned char *buf) {
466 int val;
467 val = (0xff & buf[1]);
468 val = ((val << 8) + (0xff & buf[0]));
469 return val;
470 }
471
472 /* ============================================================ */
473 /* High level functions */
474
475 /* Initialise our software state from the hardware */
munki_imp_init(munki * p)476 munki_code munki_imp_init(munki *p) {
477 munkiimp *m = (munkiimp *)p->m;
478 munki_code ev = MUNKI_OK;
479 unsigned char buf[4];
480 int calsize = 0, rucalsize;
481 unsigned char *calbuf; /* EEProm contents */
482 char *envv;
483
484 a1logd(p->log,2,"munki_init:\n");
485
486 if (p->itype != instColorMunki)
487 return MUNKI_UNKNOWN_MODEL;
488
489
490 m->native_calstd = xcalstd_xrga;
491 m->target_calstd = xcalstd_native; /* Default to native calibration standard*/
492
493 /* Honour Environment override */
494 if ((envv = getenv("ARGYLL_XCALSTD")) != NULL) {
495 if (strcmp(envv, "XRGA") == 0)
496 m->target_calstd = xcalstd_xrga;
497 else if (strcmp(envv, "XRDI") == 0)
498 m->target_calstd = xcalstd_xrdi;
499 else if (strcmp(envv, "GMDI") == 0)
500 m->target_calstd = xcalstd_gmdi;
501 }
502
503 #ifdef ENABLE_SPOS_CHECK
504 m->nosposcheck = 0;
505 #else
506 # pragma message("####### ColorMunki Sensor Position Check is OFF! ########")
507 m->nosposcheck = 1;
508 #endif
509
510 m->trig = inst_opt_trig_user;
511 m->scan_toll_ratio = 1.0;
512
513 /* Get the firmware parameters so that we can check eeprom range. */
514 if ((ev = munki_getfirm(p, &m->fwrev, &m->tickdur, &m->minintcount, &m->noeeblocks, &m->eeblocksize)) != MUNKI_OK)
515 return ev;
516 a1logd(p->log,2,"Firmware rev = %d.%d\n",m->fwrev/256, m->fwrev % 256);
517
518 /* Check the EEProm */
519 if (m->noeeblocks != 2 || m->eeblocksize != 8192) {
520 a1logw(p->log,"EEProm is unexpected size\n");
521 return MUNKI_INT_ASSERT;
522 }
523
524 /* Tick in seconds */
525 m->intclkp = (double)m->tickdur * 1e-6;
526
527 /* Set these to reasonable values */
528 m->min_int_time = m->intclkp * (double)m->minintcount;
529 m->max_int_time = 4.5;
530
531 a1logd(p->log,3, "minintcount %d, min_int_time = %f\n", m->minintcount, m->min_int_time);
532
533 /* Get the Chip ID */
534 if ((ev = munki_getchipid(p, m->chipid)) != MUNKI_OK)
535 return ev;
536
537 /* Get the Version String */
538 if ((ev = munki_getversionstring(p, m->vstring)) != MUNKI_OK)
539 return ev;
540
541 /* Dump the eeprom contents as a block */
542 if (p->log->debug >= 9) {
543 int base, size;
544
545 a1logd(p->log,7, "EEPROM contents:\n");
546
547 size = 1024; /* read size == buffer size */
548 for (base = 0; base < (2 * 8192); base += size) {
549 unsigned char eeprom[1024];
550
551 if ((ev = munki_readEEProm(p, eeprom, base, size)) != MUNKI_OK)
552 return ev;
553
554 adump_bytes(p->log, " ", eeprom, base, size);
555 }
556 }
557
558 /* Read the calibration size */
559 if ((ev = munki_readEEProm(p, buf, 4, 4)) != MUNKI_OK)
560 return ev;
561 calsize = buf2int(buf);
562 rucalsize = (calsize + 3) & ~3; /* Round up to next 32 bits */
563
564 if (calsize < 12)
565 return MUNKI_INT_CALTOOSMALL;
566 if (calsize > (m->noeeblocks * m->eeblocksize))
567 return MUNKI_INT_CALTOOBIG;
568
569 /* Read the calibration raw data from the EEProm */
570 if ((calbuf = (unsigned char *)calloc(rucalsize, sizeof(unsigned char))) == NULL) {
571 a1logd(p->log,3,"munki_imp_init malloc %d bytes failed\n",rucalsize);
572 return MUNKI_INT_MALLOC;
573 }
574 if ((ev = munki_readEEProm(p, calbuf, 0, calsize)) != MUNKI_OK)
575 return ev;
576
577 if ((ev = munki_parse_eeprom(p, calbuf, rucalsize)) != MUNKI_OK)
578 return ev;
579
580 free(calbuf);
581 calbuf = NULL;
582
583 #ifdef USE_THREAD
584 /* Setup the switch monitoring thread */
585 usb_init_cancel(&m->sw_cancel); /* Get cancel token ready */
586 if ((m->th = new_athread(munki_switch_thread, (void *)p)) == NULL)
587 return MUNKI_INT_THREADFAILED;
588 #endif
589
590 #ifdef FILTER_SPOS_EVENTS
591 /* Setup the sensor position filter thread */
592 if ((m->spos_th = new_athread(munki_spos_thread, (void *)p)) == NULL)
593 return MUNKI_INT_THREADFAILED;
594 #endif
595
596 /* Set up the current state of each mode */
597 {
598 int i, j;
599 munki_state *s;
600
601 /* First set state to basic configuration */
602 for (i = 0; i < mk_no_modes; i++) {
603 s = &m->ms[i];
604
605 s->mode = i;
606
607 /* Default to an emissive configuration */
608 s->targoscale = 0.90; /* Allow extra 10% margine by default */
609 s->targmaxitime = 2.0; /* Maximum integration time to aim for */
610 s->targoscale2 = 0.15; /* Proportion of targoscale to meed targmaxitime */
611
612 #ifdef USE_HIGH_GAIN_MODE
613 s->auto_gain = 1; /* No high gain by default */
614 #else
615 s->auto_gain = 0; /* No high gain by default */
616 #endif
617 s->gainmode = 0; /* Normal gain mode */
618 s->inttime = 0.5; /* Initial integration time */
619
620
621 s->dark_valid = 0; /* Dark cal invalid */
622 s->dark_data = dvectorz(-1, m->nraw-1);
623 s->dark_data2 = dvectorz(-1, m->nraw-1);
624 s->dark_data3 = dvectorz(-1, m->nraw-1);
625
626 s->cal_valid = 0; /* Scale cal invalid */
627 s->cal_factor1 = dvectorz(0, m->nwav1-1);
628 s->cal_factor2 = dvectorz(0, m->nwav2-1);
629 s->cal_factor = s->cal_factor1; /* Default to standard resolution */
630 s->white_data = dvectorz(-1, m->nraw-1);
631 s->iwhite_data = dmatrixz(0, 1, -1, m->nraw-1);
632
633 s->idark_valid = 0; /* Interpolatable Dark cal invalid */
634 s->idark_data = dmatrixz(0, 3, -1, m->nraw-1);
635
636 s->dark_int_time = DISP_INTT; /* 0.7 */
637 s->dark_int_time2 = DISP_INTT2; /* 0.3 */
638 s->dark_int_time3 = DISP_INTT3; /* 0.1 */
639
640 s->idark_int_time[0] = s->idark_int_time[2] = m->min_int_time;
641 s->idark_int_time[1] = ADARKINT_MAX; /* 2.0 */
642 s->idark_int_time[3] = ADARKINT_MAX2; /* 4.0 */
643
644 s->want_calib = 1; /* By default want an initial calibration */
645 s->want_dcalib = 1;
646 }
647
648 /* Then add mode specific settings */
649 for (i = 0; i < mk_no_modes; i++) {
650 s = &m->ms[i];
651 switch(i) {
652 case mk_refl_spot:
653 s->auto_gain = 0; /* Don't automatically set gain */
654 s->targoscale = 1.0; /* Optimised sensor scaling to full */
655 s->reflective = 1;
656 s->adaptive = 0;
657 s->inttime = s->targoscale * m->cal_int_time;
658 s->dark_int_time = s->inttime;
659
660 s->dpretime = 0.20; /* Pre-measure time */
661 s->wpretime = 0.20;
662 s->dcaltime = 0.5; /* same as reading */
663 s->wcaltime = 0.5; /* same as reading */
664 s->dreadtime = 0.5; /* same as reading */
665 s->wreadtime = 0.5;
666 s->maxscantime = 0.0;
667 break;
668
669 case mk_refl_scan:
670 s->auto_gain = 0; /* Don't automatically set gain */
671 s->targoscale = 1.0; /* Maximize level */
672 s->reflective = 1;
673 s->scan = 1;
674 s->adaptive = 0;
675 // s->inttime = (s->targoscale * m->cal_int_time - RDEAD_TIME) + RDEAD_TIME;
676 // if (s->inttime < m->min_int_time)
677 // s->inttime = m->min_int_time;
678 // s->dark_int_time = s->inttime;
679 s->inttime = s->targoscale * m->cal_int_time;
680 s->dark_int_time = s->inttime;
681
682 s->dpretime = 0.20; /* Pre-measure time */
683 s->wpretime = 0.20;
684 s->dcaltime = 0.5;
685 s->wcaltime = 0.5;
686 s->dreadtime = 0.10;
687 s->wreadtime = 0.10;
688 s->maxscantime = MAXSCANTIME;
689 break;
690
691 case mk_emiss_spot_na: /* Emissive spot not adaptive */
692 case mk_tele_spot_na: /* Tele spot not adaptive */
693 s->targoscale = 0.90; /* Allow extra 10% margine */
694 if (i == mk_emiss_spot_na) {
695 for (j = 0; j < m->nwav1; j++)
696 s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->emis_coef1[j];
697 } else {
698 for (j = 0; j < m->nwav1; j++)
699 s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->proj_coef1[j];
700 s->projector = 1;
701 }
702 s->cal_valid = 1;
703 s->emiss = 1;
704 s->adaptive = 0;
705
706 s->inttime = DISP_INTT; /* Default disp integration time (ie. 0.7 sec) */
707 s->dark_int_time = s->inttime;
708 s->dark_int_time2 = DISP_INTT2; /* Alternate disp integration time (ie. 0.3) */
709 s->dark_int_time3 = DISP_INTT3; /* Alternate disp integration time (ie. 0.1) */
710
711 s->dpretime = 0.0;
712 s->wpretime = 0.20;
713 s->dcaltime = 1.0; /* ie. determines number of measurements */
714 s->dcaltime2 = 1.0;
715 s->dcaltime3 = 1.0;
716 s->wcaltime = 0.0;
717 s->dreadtime = 0.0;
718 s->wreadtime = DISP_INTT;
719 s->maxscantime = 0.0;
720 break;
721
722 case mk_emiss_spot:
723 case mk_amb_spot:
724 case mk_tele_spot: /* Adaptive projector */
725 s->targoscale = 0.90; /* Allow extra 5% margine */
726 if (i == mk_emiss_spot) {
727 for (j = 0; j < m->nwav1; j++)
728 s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->emis_coef1[j];
729 } else if (i == mk_amb_spot) {
730 for (j = 0; j < m->nwav1; j++)
731 s->cal_factor1[j] = AMB_SCALE_FACTOR * m->amb_coef1[j];
732 s->ambient = 1;
733 } else {
734 for (j = 0; j < m->nwav1; j++)
735 s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->proj_coef1[j];
736 s->projector = 1;
737 }
738
739 s->cal_valid = 1;
740 s->emiss = 1;
741 s->adaptive = 1;
742
743 s->dpretime = 0.0;
744 s->wpretime = 0.10;
745 s->dcaltime = 1.0;
746 s->wcaltime = 0.0;
747 s->dreadtime = 0.0;
748 s->wreadtime = 1.0;
749 s->maxscantime = 0.0;
750 break;
751
752 case mk_emiss_scan:
753 case mk_amb_flash:
754 s->targoscale = 0.90; /* Allow extra 10% margine */
755 if (i == mk_emiss_scan) {
756 for (j = 0; j < m->nwav1; j++)
757 s->cal_factor1[j] = EMIS_SCALE_FACTOR * m->emis_coef1[j];
758 } else {
759 for (j = 0; j < m->nwav1; j++)
760 s->cal_factor1[j] = AMB_SCALE_FACTOR * m->amb_coef1[j];
761 s->ambient = 1;
762 s->flash = 1;
763 }
764 s->cal_valid = 1;
765 s->emiss = 1;
766 s->scan = 1;
767 s->adaptive = 0;
768 s->inttime = m->min_int_time;
769 s->dark_int_time = s->inttime;
770
771 s->dpretime = 0.0;
772 s->wpretime = 0.10;
773 s->dcaltime = 1.0;
774 s->wcaltime = 0.0;
775 s->dreadtime = 0.0;
776 s->wreadtime = 0.10;
777 s->maxscantime = MAXSCANTIME;
778 break;
779
780 /* Transparency has a white reference, and interpolated dark ref. */
781 case mk_trans_spot:
782 s->targoscale = 0.90; /* Allow extra 10% margine */
783 s->trans = 1;
784 s->adaptive = 1;
785
786 s->dpretime = 0.20;
787 s->wpretime = 0.20;
788 s->dcaltime = 1.0;
789 s->wcaltime = 1.0;
790 s->dreadtime = 0.0;
791 s->wreadtime = 1.0;
792 s->maxscantime = 0.0;
793 break;
794
795 case mk_trans_scan:
796 // ~~99 should we use high gain mode ??
797 s->targoscale = 0.10; /* Scan as fast as possible */
798 s->trans = 1;
799 s->scan = 1;
800 s->inttime = s->targoscale * m->cal_int_time;
801 if (s->inttime < m->min_int_time)
802 s->inttime = m->min_int_time;
803 s->dark_int_time = s->inttime;
804 s->adaptive = 0;
805
806 s->dpretime = 0.20;
807 s->wpretime = 0.20;
808 s->dcaltime = 1.0;
809 s->wcaltime = 1.0;
810 s->dreadtime = 0.00;
811 s->wreadtime = 0.10;
812 s->maxscantime = MAXSCANTIME;
813 break;
814 }
815 }
816 }
817
818 #ifdef ENABLE_NONVCAL
819 /* Restore the all modes calibration from the local system */
820 munki_restore_calibration(p);
821 /* Touch it so that we know when the instrument was last opened */
822 munki_touch_calibration(p);
823 #endif
824
825 a1logv(p->log, 1,
826 "Instrument Type: ColorMunki\n" // ~~ should get this from version string ?
827 "Serial Number: %s\n"
828 "Firmware version: %d\n"
829 "Chip ID: %02X-%02X%02X%02X%02X%02X%02X%02X\n"
830 "Version string: '%s'\n"
831 "Calibration Ver.: %d\n"
832 "Production No.: %d\n",
833 m->serno,
834 m->fwrev,
835 m->chipid[0], m->chipid[1], m->chipid[2], m->chipid[3],
836 m->chipid[4], m->chipid[5], m->chipid[6], m->chipid[7],
837 m->vstring,
838 m->calver,
839 m->prodno);
840
841 /* Flash the LED, just cos we can! */
842 if ((ev = munki_setindled(p, 1000,0,0,-1,0)) != MUNKI_OK)
843 return ev;
844 msec_sleep(200);
845 if ((ev = munki_setindled(p, 0,0,0,0,0)) != MUNKI_OK)
846 return ev;
847
848 return ev;
849 }
850
851 /* Return a pointer to the serial number */
munki_imp_get_serial_no(munki * p)852 char *munki_imp_get_serial_no(munki *p) {
853 munkiimp *m = (munkiimp *)p->m;
854
855 return m->serno;
856 }
857
858 /* Set the measurement mode. It may need calibrating */
munki_imp_set_mode(munki * p,mk_mode mmode,inst_mode mode)859 munki_code munki_imp_set_mode(
860 munki *p,
861 mk_mode mmode, /* Operating mode */
862 inst_mode mode /* Full mode mask for options */
863 ) {
864 munkiimp *m = (munkiimp *)p->m;
865
866 a1logd(p->log,2,"munki_imp_set_mode called with mode no. %d and mask 0x%x\n",mmode,m);
867 switch(mmode) {
868 case mk_refl_spot:
869 case mk_refl_scan:
870 case mk_emiss_spot_na:
871 case mk_tele_spot_na:
872 case mk_emiss_spot:
873 case mk_tele_spot:
874 case mk_emiss_scan:
875 case mk_amb_spot:
876 case mk_amb_flash:
877 case mk_trans_spot:
878 case mk_trans_scan:
879 m->mmode = mmode;
880 break;
881 default:
882 return MUNKI_INT_ILLEGALMODE;
883 }
884 m->spec_en = (mode & inst_mode_spectral) != 0;
885
886 if ((mode & inst_mode_highres) != 0) {
887 munki_code rv;
888 if ((rv = munki_set_highres(p)) != MUNKI_OK)
889 return rv;
890 } else {
891 munki_set_stdres(p); /* Ignore any error */
892 }
893
894 return MUNKI_OK;
895 }
896
897 /* Return needed and available inst_cal_type's */
munki_imp_get_n_a_cals(munki * p,inst_cal_type * pn_cals,inst_cal_type * pa_cals)898 munki_code munki_imp_get_n_a_cals(munki *p, inst_cal_type *pn_cals, inst_cal_type *pa_cals) {
899 munkiimp *m = (munkiimp *)p->m;
900 munki_state *cs = &m->ms[m->mmode];
901 time_t curtime = time(NULL);
902 inst_cal_type n_cals = inst_calt_none;
903 inst_cal_type a_cals = inst_calt_none;
904 int idark_valid = cs->idark_valid; /* Current state of calib */
905 int dark_valid = cs->dark_valid;
906 int cal_valid = cs->cal_valid;
907
908 a1logd(p->log,3,"munki_imp_get_n_a_cals: checking mode %d\n",m->mmode);
909
910 /* Timout calibrations that are too old */
911 a1logd(p->log,4,"curtime %u, iddate %u, ddate %u, cfdate %u\n",curtime,cs->iddate,cs->ddate,cs->cfdate);
912 if ((curtime - cs->iddate) > DCALTOUT) {
913 a1logd(p->log,3,"Invalidating adaptive dark cal as %d secs from last cal\n",curtime - cs->iddate);
914 idark_valid = 0;
915 }
916 if ((curtime - cs->ddate) > DCALTOUT) {
917 a1logd(p->log,3,"Invalidating dark cal as %d secs from last cal\n",curtime - cs->ddate);
918 dark_valid = 0;
919 }
920 if (!cs->emiss && (curtime - cs->cfdate) > WCALTOUT) {
921 a1logd(p->log,3,"Invalidating white cal as %d secs from last cal\n",curtime - cs->cfdate);
922 cal_valid = 0;
923 }
924
925 if (cs->reflective) {
926 if (!dark_valid
927 || (cs->want_dcalib && !m->noinitcalib))
928 n_cals |= inst_calt_ref_dark;
929 a_cals |= inst_calt_ref_dark;
930
931 if (!cal_valid
932 || (cs->want_calib && !m->noinitcalib))
933 n_cals |= inst_calt_ref_white;
934 a_cals |= inst_calt_ref_white;
935 }
936 if (cs->emiss) {
937 if ((!cs->adaptive && !dark_valid)
938 || (cs->adaptive && !idark_valid)
939 || (cs->want_dcalib && !m->noinitcalib))
940 n_cals |= inst_calt_em_dark;
941 a_cals |= inst_calt_em_dark;
942 }
943 if (cs->trans) {
944 if ((!cs->adaptive && !dark_valid)
945 || (cs->adaptive && !idark_valid)
946 || (cs->want_dcalib && !m->noinitcalib))
947 n_cals |= inst_calt_trans_dark;
948 a_cals |= inst_calt_trans_dark;
949
950 if (!cal_valid
951 || (cs->want_calib && !m->noinitcalib))
952 n_cals |= inst_calt_trans_vwhite;
953 a_cals |= inst_calt_trans_vwhite;
954 }
955 if (cs->emiss && !cs->scan && !cs->adaptive) {
956 if (!cs->done_dintsel)
957 n_cals |= inst_calt_emis_int_time;
958 a_cals |= inst_calt_emis_int_time;
959 }
960
961 if (pn_cals != NULL)
962 *pn_cals = n_cals;
963
964 if (pa_cals != NULL)
965 *pa_cals = a_cals;
966
967 a1logd(p->log,3,"munki_imp_get_n_a_cals: returning n_cals 0x%x, a_cals 0x%x\n",n_cals, a_cals);
968
969 return MUNKI_OK;
970 }
971
972 /* - - - - - - - - - - - - - - - - */
973 /* Calibrate for the current mode. */
974 /* Request an instrument calibration of the current mode. */
munki_imp_calibrate(munki * p,inst_cal_type * calt,inst_cal_cond * calc,inst_calc_id_type * idtype,char id[CALIDLEN])975 munki_code munki_imp_calibrate(
976 munki *p,
977 inst_cal_type *calt, /* Calibration type to do/remaining */
978 inst_cal_cond *calc, /* Current condition/desired condition */
979 inst_calc_id_type *idtype, /* Condition identifier type */
980 char id[CALIDLEN] /* Condition identifier (ie. white reference ID) */
981 ) {
982 munki_code ev = MUNKI_OK;
983 munkiimp *m = (munkiimp *)p->m;
984 int mmode = m->mmode; /* Current actual mode */
985 munki_state *cs = &m->ms[m->mmode];
986 int sx1, sx2, sx;
987 time_t cdate = time(NULL);
988 int nummeas = 0;
989 mk_spos spos;
990 int i, j, k;
991 inst_cal_type needed, available;
992
993 a1logd(p->log,3,"munki_imp_calibrate called with calt 0x%x, calc 0x%x\n",*calt, *calc);
994
995 if ((ev = munki_imp_get_n_a_cals(p, &needed, &available)) != MUNKI_OK)
996 return ev;
997
998 /* Translate inst_calt_all/needed into something specific */
999 if (*calt == inst_calt_all
1000 || *calt == inst_calt_needed
1001 || *calt == inst_calt_available) {
1002 if (*calt == inst_calt_all)
1003 *calt = (needed & inst_calt_n_dfrble_mask) | inst_calt_ap_flag;
1004 else if (*calt == inst_calt_needed)
1005 *calt = needed & inst_calt_n_dfrble_mask;
1006 else if (*calt == inst_calt_available)
1007 *calt = available & inst_calt_n_dfrble_mask;
1008
1009 a1logd(p->log,4,"munki_imp_calibrate: doing calt 0x%x\n",*calt);
1010
1011 if ((*calt & inst_calt_n_dfrble_mask) == 0) /* Nothing todo */
1012 return MUNKI_OK;
1013 }
1014
1015 /* See if it's a calibration we understand */
1016 if (*calt & ~available & inst_calt_all_mask) {
1017 return MUNKI_UNSUPPORTED;
1018 }
1019
1020 /* Get current sensor position */
1021 if ((ev = munki_getstatus(p, &spos, NULL)) != MUNKI_OK) {
1022 return ev;
1023 }
1024 a1logd(p->log,4,"munki sensor position = 0x%x\n",spos);
1025
1026 /* We can set the *calc to the actual conditions, in which case */
1027 /* the calibration will commence immediately. */
1028 if (m->nocalibask && !m->nosposcheck && spos == mk_spos_calib) {
1029 *calc = inst_calc_man_cal_smode;
1030 a1logd(p->log,4,"munki set calc to cal conditions\n",spos);
1031 }
1032
1033 /* Make sure that the instrument configuration matches the */
1034 /* conditions */
1035 if ((*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode) {
1036 if (!m->nosposcheck && spos != mk_spos_calib) {
1037 return MUNKI_SPOS_CALIB;
1038 }
1039 } else if ((*calc & inst_calc_cond_mask) == inst_calc_man_trans_white) {
1040 if (!m->nosposcheck && spos != mk_spos_surf) {
1041 return MUNKI_SPOS_SURF;
1042 }
1043 }
1044
1045 a1logd(p->log,4,"munki_imp_calibrate has right conditions\n");
1046
1047 if (*calt & inst_calt_ap_flag) {
1048 sx1 = 0; sx2 = mk_no_modes; /* Go through all the modes */
1049 } else {
1050 sx1 = m->mmode; sx2 = sx1 + 1; /* Just current mode */
1051 }
1052
1053 /* Go through the modes we are going to cover */
1054 for (sx = sx1; sx < sx2; sx++) {
1055 munki_state *s = &m->ms[sx];
1056 m->mmode = sx; /* A lot of functions we call rely on this */
1057
1058 a1logd(p->log,3,"\nCalibrating mode %d\n", s->mode);
1059
1060 /* Sanity check scan mode settings, in case something strange */
1061 /* has been restored from the persistence file. */
1062 // if (s->scan && s->inttime > (2.1 * m->min_int_time)) {
1063 // s->inttime = m->min_int_time; /* Maximize scan rate */
1064 // }
1065
1066 /* We are now either in inst_calc_man_cal_smode, */
1067 /* inst_calc_man_trans_white, inst_calc_disp_white or inst_calc_proj_white */
1068 /* sequenced in that order, and in the appropriate condition for it. */
1069
1070 /* Fixed int. time black calibration: */
1071 /* Reflective uses on the fly black, even for adaptive. */
1072 /* Emiss and trans can use single black ref only for non-adaptive */
1073 /* using the current inttime & gainmode, while display mode */
1074 /* does an extra fallback black cal for bright displays. */
1075 if ((*calt & (inst_calt_ref_dark
1076 | inst_calt_em_dark
1077 | inst_calt_trans_dark | inst_calt_ap_flag))
1078 && (*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode
1079 && ( s->reflective
1080 || (s->emiss && !s->adaptive && !s->scan)
1081 || (s->trans && !s->adaptive))) {
1082 int stm;
1083 int usesdct23 = 0; /* Is a mode that uses dcaltime2 & 3 */
1084
1085 if (s->emiss && !s->adaptive && !s->scan)
1086 usesdct23 = 1;
1087
1088 nummeas = munki_comp_nummeas(p, s->dcaltime, s->inttime);
1089
1090 a1logd(p->log,3,"\nDoing initial black calibration with dcaltime %f, int_time %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode);
1091 stm = msec_time();
1092 if ((ev = munki_dark_measure(p, s->dark_data, nummeas, &s->inttime, s->gainmode))
1093 != MUNKI_OK) {
1094 m->mmode = mmode; /* Restore actual mode */
1095 return ev;
1096 }
1097 a1logd(p->log,4,"Execution time of dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1098
1099 /* Special display mode alternate integration time black measurement */
1100 if (usesdct23) {
1101 nummeas = munki_comp_nummeas(p, s->dcaltime2, s->dark_int_time2);
1102 a1logd(p->log,3,"Doing 2nd initial black calibration with dcaltime2 %f, dark_int_time2 %f, nummeas %d, gainmode %d\n", s->dcaltime2, s->dark_int_time2, nummeas, s->gainmode);
1103 stm = msec_time();
1104 if ((ev = munki_dark_measure(p, s->dark_data2, nummeas, &s->dark_int_time2,
1105 s->gainmode)) != MUNKI_OK) {
1106 m->mmode = mmode; /* Restore actual mode */
1107 return ev;
1108 }
1109 a1logd(p->log,4,"Execution time of 2nd dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1110
1111 nummeas = munki_comp_nummeas(p, s->dcaltime3, s->dark_int_time3);
1112 a1logd(p->log,3,"Doing 3rd initial black calibration with dcaltime3 %f, dark_int_time3 %f, nummeas %d, gainmode %d\n", s->dcaltime3, s->dark_int_time3, nummeas, s->gainmode);
1113 nummeas = munki_comp_nummeas(p, s->dcaltime3, s->dark_int_time3);
1114 stm = msec_time();
1115 if ((ev = munki_dark_measure(p, s->dark_data3, nummeas, &s->dark_int_time3,
1116 s->gainmode)) != MUNKI_OK) {
1117 m->mmode = mmode; /* Restore actual mode */
1118 return ev;
1119 }
1120 a1logd(p->log,4,"Execution time of 3rd dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1121
1122 }
1123 s->dark_valid = 1;
1124 s->want_dcalib = 0;
1125 s->ddate = cdate;
1126 s->dark_int_time = s->inttime;
1127 s->dark_gain_mode = s->gainmode;
1128 *calt &= ~(inst_calt_ref_dark
1129 | inst_calt_em_dark
1130 | inst_calt_trans_dark);
1131
1132 /* Save the calib to all similar modes */
1133 for (i = 0; i < mk_no_modes; i++) {
1134 munki_state *ss = &m->ms[i];
1135 if (ss == s || ss->ddate == cdate)
1136 continue;
1137 if ( (s->reflective
1138 || (ss->emiss && !ss->adaptive && !ss->scan)
1139 || (ss->trans && !ss->adaptive))
1140 && ss->dark_int_time == s->dark_int_time
1141 && ss->dark_gain_mode == s->dark_gain_mode) {
1142
1143 ss->dark_valid = s->dark_valid;
1144 ss->want_dcalib = s->want_dcalib;
1145 ss->ddate = s->ddate;
1146 ss->dark_int_time = s->dark_int_time;
1147 ss->dark_gain_mode = s->dark_gain_mode;
1148 for (k = -1; k < m->nraw; k++)
1149 ss->dark_data[k] = s->dark_data[k];
1150 /* If this is a mode with dark_data2/3, tranfer it too */
1151 if (usesdct23 && ss->emiss && !ss->adaptive && !ss->scan) {
1152 ss->dark_int_time2 = s->dark_int_time2;
1153 #ifndef NEVER // ~~99
1154 if (ss->dark_int_time2 != s->dark_int_time2
1155 || ss->dark_int_time3 != s->dark_int_time3)
1156 a1logd(p->log,1,"copying cal to mode with different cal/gain mode: %d -> %d\n",s->mode, ss->mode);
1157 #endif
1158 ss->dark_int_time3 = s->dark_int_time2;
1159 for (k = -1; k < m->nraw; k++) {
1160 ss->dark_data2[k] = s->dark_data2[k];
1161 ss->dark_data3[k] = s->dark_data3[k];
1162 }
1163 }
1164 }
1165 }
1166 }
1167
1168 /* Emissive scan black calibration: */
1169 /* Emsissive scan (flash) uses the fastest possible scan rate (??) */
1170 if ((*calt & (inst_calt_em_dark | inst_calt_ap_flag))
1171 && (*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode
1172 && (s->emiss && !s->adaptive && s->scan)) {
1173 int stm;
1174
1175 nummeas = munki_comp_nummeas(p, s->dcaltime, s->inttime);
1176
1177 a1logd(p->log,3,"\nDoing emissive (flash) black calibration with dcaltime %f, int_time %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode);
1178 stm = msec_time();
1179 if ((ev = munki_dark_measure(p, s->dark_data, nummeas, &s->inttime, s->gainmode))
1180 != MUNKI_OK) {
1181 m->mmode = mmode; /* Restore actual mode */
1182 return ev;
1183 }
1184 a1logd(p->log,4,"Execution time of dark calib time %f sec = %d msec\n",s->inttime,msec_time() - stm);
1185
1186 s->dark_valid = 1;
1187 s->want_dcalib = 0;
1188 s->ddate = cdate;
1189 s->dark_int_time = s->inttime;
1190 s->dark_gain_mode = s->gainmode;
1191 *calt &= ~inst_calt_em_dark;
1192
1193 /* Save the calib to all similar modes */
1194 for (i = 0; i < mk_no_modes; i++) {
1195 munki_state *ss = &m->ms[i];
1196 if (ss == s || ss->ddate == cdate)
1197 continue;
1198 if ((ss->emiss && !ss->adaptive && ss->scan)
1199 && ss->dark_int_time == s->dark_int_time
1200 && ss->dark_gain_mode == s->dark_gain_mode) {
1201 ss->dark_valid = s->dark_valid;
1202 ss->want_dcalib = s->want_dcalib;
1203 ss->ddate = s->ddate;
1204 ss->dark_int_time = s->dark_int_time;
1205 ss->dark_gain_mode = s->dark_gain_mode;
1206 for (k = -1; k < m->nraw; k++) {
1207 ss->dark_data[k] = s->dark_data[k];
1208 }
1209 }
1210 }
1211 }
1212
1213 /* Adaptive black calibration: */
1214 /* Emmissive adaptive and transmissive black reference. */
1215 /* in non-scan mode, where the integration time and gain may vary. */
1216 if ((*calt & (inst_calt_ref_dark
1217 | inst_calt_em_dark
1218 | inst_calt_trans_dark | inst_calt_ap_flag))
1219 && (*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode
1220 && ((s->emiss && s->adaptive && !s->scan)
1221 || (s->trans && s->adaptive && !s->scan))) {
1222 /* Adaptive where we can't measure the black reference on the fly, */
1223 /* so bracket it and interpolate. */
1224 /* The black reference is probably temperature dependent, but */
1225 /* there's not much we can do about this. */
1226
1227 s->idark_int_time[0] = m->min_int_time;
1228 nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[0]);
1229 a1logd(p->log,3,"\nDoing adaptive interpolated black calibration, dcaltime %f, idark_int_time[0] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[0], nummeas, 0);
1230 if ((ev = munki_dark_measure(p, s->idark_data[0], nummeas, &s->idark_int_time[0], 0))
1231 != MUNKI_OK) {
1232 m->mmode = mmode; /* Restore actual mode */
1233 return ev;
1234 }
1235
1236 nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[1]);
1237 a1logd(p->log,3,"Doing adaptive interpolated black calibration, dcaltime %f, idark_int_time[1] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[1], nummeas, 0);
1238 if ((ev = munki_dark_measure(p, s->idark_data[1], nummeas, &s->idark_int_time[1], 0))
1239 != MUNKI_OK) {
1240 m->mmode = mmode; /* Restore actual mode */
1241 return ev;
1242 }
1243
1244 if (s->auto_gain) { /* If high gain is permitted */
1245 s->idark_int_time[2] = m->min_int_time; /* 0.01 */
1246 nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]);
1247 a1logd(p->log,3,"Doing adaptive interpolated black calibration, dcaltime %f, idark_int_time[2] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[2], nummeas, 1);
1248 if ((ev = munki_dark_measure(p, s->idark_data[2], nummeas, &s->idark_int_time[2], 1))
1249 != MUNKI_OK) {
1250 m->mmode = mmode; /* Restore actual mode */
1251 return ev;
1252 }
1253
1254 s->idark_int_time[3] = ADARKINT_MAX; /* 2.0 */
1255 a1logd(p->log,3,"Doing adaptive interpolated black calibration, dcaltime %f, idark_int_time[3] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[3], nummeas, 1);
1256 nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[3]);
1257 if ((ev = munki_dark_measure(p, s->idark_data[3], nummeas, &s->idark_int_time[3], 1))
1258 != MUNKI_OK) {
1259 m->mmode = mmode; /* Restore actual mode */
1260 return ev;
1261 }
1262 }
1263
1264 munki_prepare_idark(p);
1265
1266 s->idark_valid = 1;
1267 s->iddate = cdate;
1268
1269 if ((ev = munki_interp_dark(p, s->dark_data, s->inttime, s->gainmode)) != MUNKI_OK) {
1270 m->mmode = mmode; /* Restore actual mode */
1271 return ev;
1272 }
1273 s->dark_valid = 1;
1274 s->want_dcalib = 0;
1275 s->ddate = s->iddate;
1276 s->dark_int_time = s->inttime;
1277 s->dark_gain_mode = s->gainmode;
1278 *calt &= ~(inst_calt_ref_dark
1279 | inst_calt_em_dark
1280 | inst_calt_trans_dark);
1281
1282 /* Save the calib to all similar modes */
1283 /* We're assuming they have the same int times */
1284 a1logd(p->log,3,"Saving adaptive black calib to similar modes\n");
1285 for (i = 0; i < mk_no_modes; i++) {
1286 munki_state *ss = &m->ms[i];
1287 if (ss == s || ss->iddate == cdate)
1288 continue;
1289 if ((ss->emiss || ss->trans) && ss->adaptive && !ss->scan) {
1290 ss->idark_valid = s->idark_valid;
1291 ss->want_dcalib = s->want_dcalib;
1292 ss->iddate = s->iddate;
1293 ss->dark_int_time = s->dark_int_time;
1294 ss->dark_gain_mode = s->dark_gain_mode;
1295 #ifndef NEVER // ~~99
1296 if (ss->dark_int_time != s->dark_int_time
1297 || ss->dark_gain_mode != s->dark_gain_mode)
1298 a1logd(p->log,1,"copying cal to mode with different cal/gain mode: %d -> %d\n",s->mode, ss->mode);
1299 #endif
1300 for (j = 0; j < (s->auto_gain ? 4 : 2); j++) {
1301 ss->idark_int_time[j] = s->idark_int_time[j];
1302 #ifndef NEVER // ~~99
1303 if (ss->idark_int_time[j] != s->idark_int_time[j])
1304 a1logd(p->log,1,"copying cal to mode with different cal/gain mode: %d -> %d\n",s->mode, ss->mode);
1305 #endif
1306 for (k = -1; k < m->nraw; k++)
1307 ss->idark_data[j][k] = s->idark_data[j][k];
1308 }
1309 }
1310 }
1311
1312 a1logd(p->log,3,"Done adaptive interpolated black calibration\n");
1313
1314 /* Test accuracy of dark level interpolation */
1315 #ifdef TEST_DARK_INTERP
1316 {
1317 double tinttime;
1318 double ref[NSEN_MAX], interp[NSEN_MAX];
1319
1320 // fprintf(stderr,"Normal gain offsets, base:\n");
1321 // plot_raw(s->idark_data[0]);
1322 // fprintf(stderr,"Normal gain offsets, multiplier:\n");
1323 // plot_raw(s->idark_data[1]);
1324
1325 #ifdef DUMP_DARKM
1326 extern int ddumpdarkm;
1327 ddumpdarkm = 1;
1328 #endif
1329 for (tinttime = m->min_int_time; ; tinttime *= 2.0) {
1330 if (tinttime >= m->max_int_time)
1331 tinttime = m->max_int_time;
1332
1333 nummeas = munki_comp_nummeas(p, s->dcaltime, tinttime);
1334 if ((ev = munki_dark_measure(p, ref, nummeas, &tinttime, 0)) != MUNKI_OK) {
1335 m->mmode = mmode; /* Restore actual mode */
1336 return ev;
1337 }
1338 munki_interp_dark(p, interp, tinttime, 0);
1339 #ifdef DEBUG
1340 fprintf(stderr,"Normal gain, int time %f:\n",tinttime);
1341 plot_raw2(ref, interp);
1342 #endif
1343 if ((tinttime * 1.1) > m->max_int_time)
1344 break;
1345 }
1346 #ifdef DUMP_DARKM
1347 ddumpdarkm = 0;
1348 #endif
1349
1350 if (s->auto_gain) {
1351 // fprintf(stderr,"High gain offsets, base:\n");
1352 // plot_raw(s->idark_data[2]);
1353 // fprintf(stderr,"High gain offsets, multiplier:\n");
1354 // plot_raw(s->idark_data[3]);
1355
1356 for (tinttime = m->min_int_time; ; tinttime *= 2.0) {
1357 if (tinttime >= m->max_int_time)
1358 tinttime = m->max_int_time;
1359
1360 nummeas = munki_comp_nummeas(p, s->dcaltime, tinttime);
1361 if ((ev = munki_dark_measure(p, ref, nummeas, &tinttime, 1)) != MUNKI_OK) {
1362 m->mmode = mmode; /* Restore actual mode */
1363 return ev;
1364 }
1365 munki_interp_dark(p, interp, tinttime, 1);
1366 #ifdef DEBUG
1367 printf("High gain, int time %f:\n",tinttime);
1368 plot_raw2(ref, interp);
1369 #endif
1370 if ((tinttime * 1.1) > m->max_int_time)
1371 break;
1372 }
1373 }
1374 }
1375 #endif /* TEST_DARK_INTERP */
1376
1377 }
1378
1379 /* Deal with an emissive/transmisive adaptive black reference */
1380 /* when in scan mode. */
1381 if ((*calt & (inst_calt_ref_dark
1382 | inst_calt_em_dark
1383 | inst_calt_trans_dark | inst_calt_ap_flag))
1384 && (*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode
1385 && ((s->emiss && s->adaptive && s->scan)
1386 || (s->trans && s->adaptive && s->scan))) {
1387 int j;
1388 /* We know scan is locked to the minimum integration time, */
1389 /* so we can measure the dark data at that integration time, */
1390 /* but we don't know what gain mode will be used, so measure both, */
1391 /* and choose the appropriate one on the fly. */
1392
1393 s->idark_int_time[0] = s->inttime;
1394 nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[0]);
1395 a1logd(p->log,3,"\nDoing adaptive scan black calibration, dcaltime %f, idark_int_time[0] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[0], nummeas, s->gainmode);
1396 if ((ev = munki_dark_measure(p, s->idark_data[0], nummeas, &s->idark_int_time[0], 0))
1397 != MUNKI_OK) {
1398 m->mmode = mmode; /* Restore actual mode */
1399 return ev;
1400 }
1401
1402 if (s->auto_gain) {
1403 s->idark_int_time[2] = s->inttime;
1404 nummeas = munki_comp_nummeas(p, s->dcaltime, s->idark_int_time[2]);
1405 a1logd(p->log,3,"Doing adaptive scan black calibration, dcaltime %f, idark_int_time[2] %f, nummeas %d, gainmode %d\n", s->dcaltime, s->idark_int_time[2], nummeas, s->gainmode);
1406 if ((ev = munki_dark_measure(p, s->idark_data[2], nummeas, &s->idark_int_time[2], 1))
1407 != MUNKI_OK) {
1408 m->mmode = mmode; /* Restore actual mode */
1409 return ev;
1410 }
1411 }
1412
1413 s->idark_valid = 1;
1414 s->iddate = cdate;
1415
1416 if (s->auto_gain && s->gainmode) {
1417 for (j = -1; j < m->nraw; j++)
1418 s->dark_data[j] = s->idark_data[2][j];
1419 } else {
1420 for (j = -1; j < m->nraw; j++)
1421 s->dark_data[j] = s->idark_data[0][j];
1422 }
1423 s->dark_valid = 1;
1424 s->want_dcalib = 0;
1425 s->ddate = s->iddate;
1426 s->dark_int_time = s->inttime;
1427 s->dark_gain_mode = s->gainmode;
1428 *calt &= ~(inst_calt_ref_dark
1429 | inst_calt_em_dark
1430 | inst_calt_trans_dark);
1431
1432 a1logd(p->log,3,"Done adaptive scan black calibration\n");
1433
1434 /* Save the calib to all similar modes */
1435 /* We're assuming they have the same int times */
1436 a1logd(p->log,3,"Saving adaptive scan black calib to similar modes\n");
1437 for (i = 0; i < mk_no_modes; i++) {
1438 munki_state *ss = &m->ms[i];
1439 if (ss == s || s->iddate == cdate)
1440 continue;
1441 if ((ss->emiss || ss->trans) && ss->adaptive && s->scan) {
1442 ss->idark_valid = s->idark_valid;
1443 ss->want_dcalib = s->want_dcalib;
1444 ss->iddate = s->iddate;
1445 ss->dark_int_time = s->dark_int_time;
1446 ss->dark_gain_mode = s->dark_gain_mode;
1447
1448 for (j = 0; j < (s->auto_gain ? 4 : 2); j += 2) {
1449 ss->idark_int_time[j] = s->idark_int_time[j];
1450 for (k = -1; k < m->nraw; k++)
1451 ss->idark_data[j][k] = s->idark_data[j][k];
1452 }
1453 }
1454 }
1455 }
1456
1457 /* Now deal with white calibrations */
1458
1459 /* If we are doing a reflective white reference calibrate */
1460 /* or a we are doing a tranmisive white reference calibrate */
1461 if ((*calt & (inst_calt_ref_white
1462 | inst_calt_trans_vwhite | inst_calt_ap_flag))
1463 && (((*calc & inst_calc_cond_mask) == inst_calc_man_cal_smode && s->reflective)
1464 || ((*calc & inst_calc_cond_mask) == inst_calc_man_trans_white && s->trans))) {
1465 // && s->cfdate < cdate)
1466 double dead_time = 0.0; /* Dead integration time */
1467 double scale;
1468 int i;
1469 double ulimit = m->optsval / m->minsval; /* Upper scale needed limit */
1470 double fulimit = sqrt(ulimit); /* Fast exit limit */
1471 double llimit = m->optsval / m->maxsval; /* Lower scale needed limit */
1472 double fllimit = sqrt(llimit); /* Fast exit limit */
1473
1474 a1logd(p->log,3,"\nDoing initial white calibration with current inttime %f, gainmode %d\n",
1475 s->inttime, s->gainmode);
1476 a1logd(p->log,3,"ulimit %f, llimit %f\n",ulimit,llimit);
1477 a1logd(p->log,3,"fulimit %f, fllimit %f\n",fulimit,fllimit);
1478 if (s->reflective) {
1479 dead_time = RDEAD_TIME; /* Fudge value that makes int time calcs work */
1480 /* Heat up the LED to put in in a nominal state for int time adjustment */
1481 munki_heatLED(p, m->ledpreheattime);
1482 }
1483
1484 /* Until we're done */
1485 for (i = 0; i < 6; i++) {
1486
1487 a1logd(p->log,3,"Doing a white calibration with trial int_time %f, gainmode %d\n",
1488 s->inttime,s->gainmode);
1489
1490 if (s->trans && s->adaptive) {
1491 /* compute interpolated dark refence for chosen inttime & gainmode */
1492 a1logd(p->log,3,"Interpolate dark calibration reference\n");
1493 if ((ev = munki_interp_dark(p, s->dark_data, s->inttime, s->gainmode))
1494 != MUNKI_OK) {
1495 m->mmode = mmode; /* Restore actual mode */
1496 return ev;
1497 }
1498 s->dark_valid = 1;
1499 s->ddate = s->iddate;
1500 s->dark_int_time = s->inttime;
1501 s->dark_gain_mode = s->gainmode;
1502 }
1503 nummeas = munki_comp_nummeas(p, s->wcaltime, s->inttime);
1504 ev = munki_whitemeasure(p, s->white_data, &scale, nummeas, &s->inttime, s->gainmode,
1505 s->targoscale);
1506 a1logd(p->log,3,"Needed scale is %f\n",scale);
1507
1508 if (ev == MUNKI_RD_SENSORSATURATED) {
1509 scale = 0.0; /* Signal it this way */
1510 ev = MUNKI_OK;
1511 }
1512 if (ev != MUNKI_OK) {
1513 m->mmode = mmode; /* Restore actual mode */
1514 return ev;
1515 }
1516
1517 if (scale >= fllimit && scale <= fulimit) {
1518 a1logd(p->log,3,"Close enough for early exit\n");
1519 break; /* OK, we can stop straight away */
1520 }
1521
1522 if (scale == 0.0) { /* If sensor was saturated */
1523 s->inttime = m->min_int_time;
1524 s->gainmode = 0;
1525 s->dark_valid = 0;
1526 } else {
1527 double ninttime;
1528
1529 /* Compute a new integration time and gain mode */
1530 /* in order to optimise the sensor values. Error if can't get */
1531 /* scale we want. */
1532 if ((ev = munki_optimise_sensor(p, &ninttime, &s->gainmode, s->inttime,
1533 s->gainmode, s->trans, 0, &s->targoscale, scale, dead_time)) != MUNKI_OK) {
1534 m->mmode = mmode; /* Restore actual mode */
1535 return ev;
1536 }
1537 s->inttime = ninttime;
1538 a1logd(p->log,3,"New inttime = %f\n",s->inttime);
1539 }
1540 }
1541 if (i >= 6) {
1542 if (scale == 0.0) { /* If sensor was saturated */
1543 a1logd(p->log,1, "White calibration failed - sensor is saturated\n");
1544 m->mmode = mmode; /* Restore actual mode */
1545 return MUNKI_RD_SENSORSATURATED;
1546 }
1547 if (scale > ulimit || scale < llimit) {
1548 a1logd(p->log,1,"White calibration failed - didn't converge (%f %f %f)\n",llimit,scale,ulimit);
1549 m->mmode = mmode; /* Restore actual mode */
1550 return MUNKI_RD_REFWHITENOCONV;
1551 }
1552 }
1553
1554 /* We've settled on the inttime and gain mode to get a good white reference. */
1555 if (s->reflective) { /* We read the write reference - check it */
1556
1557 /* Let the LED cool down */
1558 a1logd(p->log,3,"Waiting %f secs for LED to cool\n",m->ledwaittime);
1559 msec_sleep((int)(m->ledwaittime * 1000.0 + 0.5));
1560
1561 /* Re-calibrate the black with the given integration time */
1562 nummeas = munki_comp_nummeas(p, s->dcaltime, s->inttime);
1563
1564 a1logd(p->log,3,"Doing another reflective black calibration with dcaltime %f, int_time %f, nummeas %d, gainmode %d\n", s->dcaltime, s->inttime, nummeas, s->gainmode);
1565 if ((ev = munki_dark_measure(p, s->dark_data, nummeas, &s->inttime, s->gainmode))
1566 != MUNKI_OK) {
1567 m->mmode = mmode; /* Restore actual mode */
1568 return ev;
1569 }
1570
1571 /* Take a reflective white reference measurement, */
1572 /* subtracts black and decompose into base + LED temperature components, */
1573 /* and compute reftemp white reference. */
1574 nummeas = munki_comp_nummeas(p, m->calscantime, s->inttime);
1575 if ((ev = munki_ledtemp_whitemeasure(p, s->white_data, s->iwhite_data, &s->reftemp,
1576 nummeas, s->inttime, s->gainmode)) != MUNKI_OK) {
1577 m->mmode = mmode; /* Restore actual mode */
1578 return ev;
1579 }
1580
1581 /* Compute wavelength white readings from ref temp sensor reading */
1582 if ((ev = munki_compute_wav_whitemeas(p, s->cal_factor1, s->cal_factor2,
1583 s->white_data)) != MUNKI_OK) {
1584 m->mmode = mmode; /* Restore actual mode */
1585 return ev;
1586 }
1587
1588 /* We don't seem to sanity check the white reference. Presumably */
1589 /* this is because a LED isn't going to burn out... */
1590
1591 /* Compute a calibration factor given the reading of the white reference. */
1592 munki_compute_white_cal(p, s->cal_factor1, m->white_ref1, s->cal_factor1,
1593 s->cal_factor2, m->white_ref2, s->cal_factor2);
1594
1595 } else {
1596 /* Compute wavelength white readings from sensor */
1597 if ((ev = munki_compute_wav_whitemeas(p, s->cal_factor1, s->cal_factor2,
1598 s->white_data)) != MUNKI_OK) {
1599 m->mmode = mmode; /* Restore actual mode */
1600 return ev;
1601 }
1602
1603 /* Compute a calibration factor given the reading of the white reference. */
1604 m->transwarn |= munki_compute_white_cal(p, s->cal_factor1, NULL, s->cal_factor1,
1605 s->cal_factor2, NULL, s->cal_factor2);
1606 }
1607 s->cal_valid = 1;
1608 s->cfdate = cdate;
1609 s->want_calib = 0;
1610 *calt &= ~(inst_calt_ref_white
1611 | inst_calt_trans_vwhite);
1612 }
1613
1614 /* Deal with a display integration time selection */
1615 if ((*calt & (inst_calt_emis_int_time | inst_calt_ap_flag))
1616 && (*calc & inst_calc_cond_mask) == inst_calc_emis_white
1617 // && s->cfdate < cdate
1618 && (s->emiss && !s->adaptive && !s->scan)) {
1619 double scale;
1620 double *data;
1621 double *tt, tv;
1622
1623 data = dvectorz(-1, m->nraw-1);
1624
1625 a1logd(p->log,3,"\nDoing display integration time calibration\n");
1626
1627 /* Undo any previous swaps */
1628 if (s->dispswap == 1) {
1629 tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
1630 tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
1631 } else if (s->dispswap == 2) {
1632 tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
1633 tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
1634 }
1635 s->dispswap = 0;
1636
1637 /* Simply measure the full display white, and if it's close to */
1638 /* saturation, switch to the alternate display integration time */
1639 nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
1640 ev = munki_whitemeasure(p, data , &scale, nummeas,
1641 &s->inttime, s->gainmode, s->targoscale);
1642 /* Switch to the alternate if things are too bright */
1643 /* We do this simply by swapping the alternate values in. */
1644 if (ev == MUNKI_RD_SENSORSATURATED || scale < 1.0) {
1645 a1logd(p->log,3,"Switching to alternate display integration time %f seconds\n",s->dark_int_time2);
1646 tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
1647 tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
1648 s->dispswap = 1;
1649
1650 /* Do another measurement of the full display white, and if it's close to */
1651 /* saturation, switch to the 3rd alternate display integration time */
1652 nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
1653 ev = munki_whitemeasure(p, data , &scale, nummeas,
1654 &s->inttime, s->gainmode, s->targoscale);
1655 /* Switch to the 3rd alternate if things are too bright */
1656 /* We do this simply by swapping the alternate values in. */
1657 if (ev == MUNKI_RD_SENSORSATURATED || scale < 1.0) {
1658 a1logd(p->log,3,"Switching to 3rd alternate display integration time %f seconds\n",s->dark_int_time3);
1659 /* Undo previous swap */
1660 tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
1661 tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
1662 /* swap in 2nd alternate */
1663 tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
1664 tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
1665 s->dispswap = 2;
1666 }
1667 }
1668 free_dvector(data, -1, m->nraw-1);
1669 if (ev != MUNKI_OK) {
1670 m->mmode = mmode; /* Restore actual mode */
1671 return ev;
1672 }
1673 s->done_dintsel = 1;
1674 s->diseldate = cdate;
1675 *calt &= ~inst_calt_emis_int_time;
1676
1677 a1logd(p->log,3,"Done display integration time selection\n");
1678 }
1679
1680 } /* Look at next mode */
1681 m->mmode = mmode; /* Restore actual mode */
1682
1683 /* Make sure there's the right condition for the calibration */
1684 if (*calt & (inst_calt_ref_dark | inst_calt_ref_white)) { /* Reflective calib */
1685 if ((*calc & inst_calc_cond_mask) != inst_calc_man_cal_smode) {
1686 *calc = inst_calc_man_cal_smode;
1687 return MUNKI_CAL_SETUP;
1688 }
1689 } else if (*calt & inst_calt_em_dark) { /* Emissive Dark calib */
1690 *idtype = inst_calc_id_none;
1691 id[0] = '\000';
1692 if ((*calc & inst_calc_cond_mask) != inst_calc_man_cal_smode) {
1693 *calc = inst_calc_man_cal_smode;
1694 return MUNKI_CAL_SETUP;
1695 }
1696 } else if (*calt & inst_calt_trans_dark) { /* Transmissive dark */
1697 *idtype = inst_calc_id_none;
1698 id[0] = '\000';
1699 if ((*calc & inst_calc_cond_mask) != inst_calc_man_cal_smode) {
1700 *calc = inst_calc_man_cal_smode;
1701 return MUNKI_CAL_SETUP;
1702 }
1703 } else if (*calt & inst_calt_trans_vwhite) { /* Transmissive white for emulated trans. */
1704 *idtype = inst_calc_id_none;
1705 id[0] = '\000';
1706 if ((*calc & inst_calc_cond_mask) != inst_calc_man_trans_white) {
1707 *calc = inst_calc_man_trans_white;
1708 return MUNKI_CAL_SETUP;
1709 }
1710 } else if (*calt & inst_calt_emis_int_time) {
1711 *idtype = inst_calc_id_none;
1712 id[0] = '\000';
1713 if ((*calc & inst_calc_cond_mask) != inst_calc_emis_white) {
1714 *calc = inst_calc_emis_white;
1715 return MUNKI_CAL_SETUP;
1716 }
1717 }
1718
1719 /* Go around again if we've still got calibrations to do */
1720 if (*calt & inst_calt_all_mask) {
1721 return MUNKI_CAL_SETUP;
1722 }
1723
1724 /* We must be done */
1725
1726 #ifdef ENABLE_NONVCAL
1727 /* Save the calibration to a file */
1728 munki_save_calibration(p);
1729 #endif
1730
1731 if (m->transwarn) {
1732 *calc = inst_calc_message;
1733 if (m->transwarn & 2) {
1734 *idtype = inst_calc_id_trans_low;
1735 strcpy(id, "Warning: Transmission light source is too low for accuracy!");
1736 } else {
1737 *idtype = inst_calc_id_trans_wl;
1738 strcpy(id, "Warning: Transmission light source is low at some wavelengths!");
1739 }
1740 m->transwarn = 0;
1741 return MUNKI_OK;
1742 }
1743
1744 a1logd(p->log,3,"Finished cal with dark_valid = %d, cal_valid = %d\n",cs->dark_valid, cs->cal_valid);
1745
1746 return ev;
1747 }
1748
1749 /* Interpret an icoms error into a MUNKI error */
icoms2munki_err(int se)1750 int icoms2munki_err(int se) {
1751 if (se != ICOM_OK)
1752 return MUNKI_COMS_FAIL;
1753 return MUNKI_OK;
1754 }
1755
1756 /* - - - - - - - - - - - - - - - - */
1757 /* Measure a display update delay. It is assumed that */
1758 /* white_stamp(init) has been called, and then a */
1759 /* white to black change has been made to the displayed color, */
1760 /* and this will measure the time it took for the update to */
1761 /* be noticed by the instrument, up to 2.0 seconds. */
1762 /* (It is assumed that white_change() will be called at the time the patch */
1763 /* changes color.) */
1764 /* inst_misread will be returned on failure to find a transition to black. */
1765 #define NDMXTIME 2.0 /* Maximum time to take */
1766 #define NDSAMPS 500 /* Debug samples */
1767
1768 typedef struct {
1769 double sec;
1770 double rgb[3];
1771 double tot;
1772 } i1rgbdsamp;
1773
munki_imp_meas_delay(munki * p,int * pdispmsec,int * pinstmsec)1774 munki_code munki_imp_meas_delay(
1775 munki *p,
1776 int *pdispmsec, /* Return display update delay in msec */
1777 int *pinstmsec) { /* Return instrument reaction time in msec */
1778 munki_code ev = MUNKI_OK;
1779 munkiimp *m = (munkiimp *)p->m;
1780 munki_state *s = &m->ms[m->mmode];
1781 int i, j, k, mm;
1782 double **multimeas; /* Spectral measurements */
1783 int nummeas;
1784 double rgbw[3] = { 610.0, 520.0, 460.0 };
1785 double ucalf = 1.0; /* usec_time calibration factor */
1786 double inttime;
1787 double rstart;
1788 i1rgbdsamp *samp;
1789 double stot, etot, del, thr;
1790 double stime, etime;
1791 int isdeb;
1792 int dispmsec, instmsec;
1793
1794 if (pinstmsec != NULL)
1795 *pinstmsec = 0;
1796
1797 if ((rstart = usec_time()) < 0.0) {
1798 a1loge(p->log, inst_internal_error, "munki_imp_meas_delay: No high resolution timers\n");
1799 return inst_internal_error;
1800 }
1801
1802 /* Read the samples */
1803 inttime = m->min_int_time;
1804 nummeas = (int)(NDMXTIME/inttime + 0.5);
1805 multimeas = dmatrix(0, nummeas-1, -1, m->nwav-1);
1806 if ((samp = (i1rgbdsamp *)calloc(sizeof(i1rgbdsamp), nummeas)) == NULL) {
1807 a1logd(p->log, 1, "munki_meas_delay: malloc failed\n");
1808 return MUNKI_INT_MALLOC;
1809 }
1810
1811 if ((ev = munki_read_patches_all(p, multimeas, nummeas, &inttime, 0)) != inst_ok) {
1812 free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav-1);
1813 free(samp);
1814 return ev;
1815 }
1816
1817 if (m->whitestamp < 0.0) {
1818 a1logd(p->log, 1, "munki_meas_delay: White transition wasn't timestamped\n");
1819 return inst_internal_error;
1820 }
1821
1822 /* Convert the samples to RGB */
1823 /* Add 10 msec fudge factor */
1824 for (i = 0; i < nummeas; i++) {
1825 samp[i].sec = i * inttime + (m->trigstamp - m->whitestamp)/1000000.0 + 0.01;
1826 samp[i].rgb[0] = samp[i].rgb[1] = samp[i].rgb[2] = 0.0;
1827 for (j = 0; j < m->nwav; j++) {
1828 double wl = XSPECT_WL(m->wl_short, m->wl_long, m->nwav, j);
1829
1830 //printf("~1 multimeas %d %d = %f\n",i, j, multimeas[i][j]);
1831 for (k = 0; k < 3; k++) {
1832 double tt = (double)(wl - rgbw[k]);
1833 tt = (50.0 - fabs(tt))/50.0;
1834 if (tt < 0.0)
1835 tt = 0.0;
1836 samp[i].rgb[k] += sqrt(tt) * multimeas[i][j];
1837 }
1838 }
1839 samp[i].tot = samp[i].rgb[0] + samp[i].rgb[1] + samp[i].rgb[2];
1840 }
1841 free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav-1);
1842
1843 a1logd(p->log, 3, "munki_measure_refresh: Read %d samples for refresh calibration\n",nummeas);
1844
1845 /* Over the first 100msec, locate the maximum value */
1846 stime = samp[0].sec;
1847 stot = -1e9;
1848 for (i = 0; i < nummeas; i++) {
1849 if (samp[i].tot > stot)
1850 stot = samp[i].tot;
1851 if ((samp[i].sec - stime) > 0.1)
1852 break;
1853 }
1854
1855 /* Over the last 100msec, locate the maximum value */
1856 etime = samp[nummeas-1].sec;
1857 etot = -1e9;
1858 for (i = nummeas-1; i >= 0; i--) {
1859 if (samp[i].tot > etot)
1860 etot = samp[i].tot;
1861 if ((etime - samp[i].sec) > 0.1)
1862 break;
1863 }
1864
1865 del = etot - stot;
1866 thr = stot + 0.30 * del; /* 30% of transition threshold */
1867
1868 #ifdef PLOT_UPDELAY
1869 a1logd(p->log, 0, "munki_meas_delay: start tot %f end tot %f del %f, thr %f\n", stot, etot, del, thr);
1870 #endif
1871
1872 #ifdef PLOT_UPDELAY
1873 /* Plot the raw sensor values */
1874 {
1875 double xx[NDSAMPS];
1876 double y1[NDSAMPS];
1877 double y2[NDSAMPS];
1878 double y3[NDSAMPS];
1879 double y4[NDSAMPS];
1880
1881 for (i = 0; i < nummeas && i < NDSAMPS; i++) {
1882 xx[i] = samp[i].sec;
1883 y1[i] = samp[i].rgb[0];
1884 y2[i] = samp[i].rgb[1];
1885 y3[i] = samp[i].rgb[2];
1886 y4[i] = samp[i].tot;
1887 //printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].tot);
1888 }
1889 printf("Display update delay measure sensor values and time (sec)\n");
1890 do_plot6(xx, y1, y2, y3, y4, NULL, NULL, nummeas);
1891 }
1892 #endif
1893
1894 /* Check that there has been a transition */
1895 if (del < 5.0) {
1896 free(samp);
1897 a1logd(p->log, 1, "munki_meas_delay: can't detect change from black to white\n");
1898 return MUNKI_RD_NOTRANS_FOUND;
1899 }
1900
1901 /* Working from the start, locate the time at which the level was above the threshold */
1902 for (i = 0; i < (nummeas-1); i++) {
1903 if (samp[i].tot > thr)
1904 break;
1905 }
1906
1907 a1logd(p->log, 2, "munki_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec);
1908
1909 /* Compute overall delay and subtract patch change delay */
1910 dispmsec = (int)(samp[i].sec * 1000.0 + 0.5);
1911 instmsec = (int)((m->trigstamp - rstart)/1000.0 + 0.5);
1912
1913 #ifdef PLOT_UPDELAY
1914 a1logd(p->log, 0, "munki_meas_delay: disp %d, inst %d msec\n",dispmsec,instmsec);
1915 #else
1916 a1logd(p->log, 2, "munki_meas_delay: disp %d, inst %d msec\n",dispmsec,instmsec);
1917 #endif
1918
1919 if (dispmsec < 0) /* This can happen if the patch generator delays it's return */
1920 dispmsec = 0;
1921
1922 if (pdispmsec != NULL)
1923 *pdispmsec = dispmsec;
1924
1925 if (pinstmsec != NULL)
1926 *pinstmsec = instmsec;
1927
1928 #ifdef PLOT_UPDELAY
1929 a1logd(p->log, 0, "munki_meas_delay: returning %d & %d msec\n",dispmsec,instmsec);
1930 #else
1931 a1logd(p->log, 2, "munki_meas_delay: returning %d & %d msec\n",dispmsec,instmsec);
1932 #endif
1933 free(samp);
1934
1935 return MUNKI_OK;
1936 }
1937 #undef NDSAMPS
1938 #undef NDMXTIME
1939
1940 /* Timestamp the white patch change during meas_delay() */
munki_imp_white_change(munki * p,int init)1941 inst_code munki_imp_white_change(munki *p, int init) {
1942 munkiimp *m = (munkiimp *)p->m;
1943
1944 if (init)
1945 m->whitestamp = -1.0;
1946 else {
1947 if ((m->whitestamp = usec_time()) < 0.0) {
1948 a1loge(p->log, inst_internal_error, "munki_imp_wite_change: No high resolution timers\n");
1949 return inst_internal_error;
1950 }
1951 }
1952
1953 return inst_ok;
1954 }
1955
1956 /* - - - - - - - - - - - - - - - - */
1957 /* Measure a patch or strip or flash in the current mode. */
1958 /* To try and speed up the reaction time between */
1959 /* triggering a scan measurement and being able to */
1960 /* start moving the instrument, we pre-allocate */
1961 /* all the buffers and arrays, and pospone processing */
1962 /* until after the scan is complete. */
munki_imp_measure(munki * p,ipatch * vals,int nvals,instClamping clamp)1963 munki_code munki_imp_measure(
1964 munki *p,
1965 ipatch *vals, /* Pointer to array of instrument patch value */
1966 int nvals, /* Number of values */
1967 instClamping clamp /* Clamp XYZ/Lab to be +ve */
1968 ) {
1969 munki_code ev = MUNKI_OK;
1970 munkiimp *m = (munkiimp *)p->m;
1971 munki_state *s = &m->ms[m->mmode];
1972 unsigned char *buf = NULL; /* Raw USB reading buffer for reflection dark cal */
1973 unsigned int bsize;
1974 unsigned char *mbuf = NULL; /* Raw USB reading buffer for measurement */
1975 unsigned int mbsize;
1976 int nummeas = 0, maxnummeas = 0;
1977 int nmeasuered = 0; /* Number actually measured */
1978 double invsampt = 0.0; /* Invalid sample time */
1979 int ninvmeas = 0; /* Number of invalid measurements */
1980 double **specrd = NULL; /* Cooked spectral patch values */
1981 double duration = 0.0; /* Possible flash duration value */
1982 mk_spos spos;
1983 int user_trig = 0;
1984
1985 a1logd(p->log,2,"munki_imp_measure called\n");
1986 a1logd(p->log,3,"Taking %d measurments in %s%s%s%s%s mode called\n", nvals,
1987 s->emiss ? "Emission" : s->trans ? "Trans" : "Refl",
1988 s->emiss && s->ambient ? " Ambient" : "",
1989 s->scan ? " Scan" : "",
1990 s->flash ? " Flash" : "",
1991 s->adaptive ? " Adaptive" : "");
1992
1993
1994 if ((s->emiss && s->adaptive && !s->idark_valid)
1995 || ((!s->emiss || !s->adaptive) && !s->dark_valid)
1996 || !s->cal_valid) {
1997 a1logd(p->log,3,"emis %d, adaptive %d, idark_valid %d\n",s->emiss,s->adaptive,s->idark_valid);
1998 a1logd(p->log,3,"dark_valid %d, cal_valid %d\n",s->dark_valid,s->cal_valid);
1999 a1logd(p->log,3,"munki_imp_measure need calibration\n");
2000 return MUNKI_RD_NEEDS_CAL;
2001 }
2002
2003 if (nvals <= 0
2004 || (!s->scan && nvals > 1)) {
2005 a1logd(p->log,3,"munki_imp_measure wrong number of patches\n");
2006 return MUNKI_INT_WRONGPATCHES;
2007 }
2008
2009 if (s->reflective) {
2010 /* Number of invalid samples to allow for LED warmup */
2011 invsampt = m->refinvalidsampt;
2012 ninvmeas = munki_comp_ru_nummeas(p, invsampt, s->inttime);
2013 }
2014
2015 /* Notional number of measurements, befor adaptive and not counting scan */
2016 nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2017
2018 /* Allocate buffer for dark measurement */
2019 if (s->reflective) {
2020 bsize = m->nsen * 2 * nummeas;
2021 if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
2022 a1logd(p->log,1,"munki_imp_measure malloc %d bytes failed (5)\n",bsize);
2023 return MUNKI_INT_MALLOC;
2024 }
2025 }
2026
2027 /* Allocate buffer for measurement */
2028 maxnummeas = munki_comp_nummeas(p, s->maxscantime, s->inttime);
2029 if (maxnummeas < (ninvmeas + nummeas))
2030 maxnummeas = (ninvmeas + nummeas);
2031 mbsize = m->nsen * 2 * maxnummeas;
2032 if ((mbuf = (unsigned char *)malloc(sizeof(unsigned char) * mbsize)) == NULL) {
2033 if (buf != NULL)
2034 free(buf);
2035 a1logd(p->log,1,"munki_imp_measure malloc %d bytes failed (6)\n",mbsize);
2036 return MUNKI_INT_MALLOC;
2037 }
2038 specrd = dmatrix(0, nvals-1, 0, m->nwav-1);
2039
2040 if (m->trig == inst_opt_trig_user_switch) {
2041 m->hide_switch = 1; /* Supress switch events */
2042
2043 #ifdef USE_THREAD
2044 {
2045 int currcount = m->switch_count; /* Variable set by thread */
2046 while (currcount == m->switch_count) {
2047 inst_code rc;
2048 int cerr;
2049
2050 /* Don't trigger on user key if scan, only trigger */
2051 /* on instrument switch */
2052 if (p->uicallback != NULL
2053 && (rc = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
2054 if (rc == inst_user_abort) {
2055 ev = MUNKI_USER_ABORT;
2056 break; /* Abort */
2057 }
2058 if (!s->scan && rc == inst_user_trig) {
2059 ev = MUNKI_USER_TRIG;
2060 user_trig = 1;
2061 break; /* Trigger */
2062 }
2063 }
2064 msec_sleep(100);
2065 }
2066 }
2067 #else
2068 /* Throw one away in case the switch was pressed prematurely */
2069 munki_waitfor_switch_th(p, NULL, NULL, 0.01);
2070
2071 for (;;) {
2072 mk_eve ecode;
2073 int cerr;
2074
2075 if ((ev = munki_waitfor_switch_th(p, &ecode, NULL, 0.1)) != MUNKI_OK
2076 && ev != MUNKI_INT_BUTTONTIMEOUT)
2077 break; /* Error */
2078
2079 if (ev == MUNKI_OK && ecode == mk_eve_switch_press)
2080 break; /* switch triggered */
2081
2082 /* Don't trigger on user key if scan, only trigger */
2083 /* on instrument switch */
2084 if (p->uicallback != NULL
2085 && (rc = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
2086 if (rc == inst_user_abort) {
2087 ev = MUNKI_USER_ABORT;
2088 break; /* Abort */
2089 }
2090 if (!s->scan && rc == inst_user_trig) {
2091 ev = MUNKI_USER_TRIG;
2092 user_trig = 1;
2093 break; /* Trigger */
2094 }
2095 }
2096 }
2097 #endif
2098 a1logd(p->log,3,"############# triggered ##############\n");
2099 if (p->uicallback) /* Notify of trigger */
2100 p->uicallback(p->uic_cntx, inst_triggered);
2101
2102 m->hide_switch = 0; /* Enable switch events again */
2103
2104 } else if (m->trig == inst_opt_trig_user) {
2105
2106 if (p->uicallback == NULL) {
2107 a1logd(p->log, 1, "hcfr: inst_opt_trig_user but no uicallback function set!\n");
2108 ev = MUNKI_UNSUPPORTED;
2109
2110 } else {
2111
2112 for (;;) {
2113 inst_code rc;
2114 if ((rc = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) {
2115 if (rc == inst_user_abort) {
2116 ev = MUNKI_USER_ABORT; /* Abort */
2117 break;
2118 }
2119 if (rc == inst_user_trig) {
2120 ev = MUNKI_USER_TRIG;
2121 user_trig = 1;
2122 break; /* Trigger */
2123 }
2124 }
2125 msec_sleep(200);
2126 }
2127 }
2128 a1logd(p->log,3,"############# triggered ##############\n");
2129 if (p->uicallback) /* Notify of trigger */
2130 p->uicallback(p->uic_cntx, inst_triggered);
2131
2132 /* Progromatic Trigger */
2133 } else {
2134 /* Check for abort */
2135 if (p->uicallback != NULL
2136 && (ev = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort)
2137 ev = MUNKI_USER_ABORT; /* Abort */
2138 }
2139
2140 if (ev != MUNKI_OK && ev != MUNKI_USER_TRIG) {
2141 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2142 free(mbuf);
2143 if (buf != NULL)
2144 free(buf);
2145 a1logd(p->log,3,"munki_imp_measure user aborted, terminated, command, or failure\n");
2146 return ev; /* User abort, term, command or failure */
2147 }
2148
2149 /* Get current sensor position */
2150 if ((ev = munki_getstatus(p, &spos, NULL)) != MUNKI_OK) {
2151 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2152 free(mbuf);
2153 if (buf != NULL)
2154 free(buf);
2155 a1logd(p->log,3,"munki_imp_measure getstatus failed\n");
2156 return ev;
2157 }
2158
2159 /* Check the current sensor position */
2160 if (!m->nosposcheck) {
2161 if (s->emiss) {
2162 if (s->ambient) {
2163 if (spos != mk_spos_amb)
2164 ev = MUNKI_SPOS_AMB;
2165 } else if (s->projector) {
2166 if (spos != mk_spos_proj)
2167 ev = MUNKI_SPOS_PROJ;
2168 } else { /* Display */
2169 if (spos != mk_spos_surf)
2170 ev = MUNKI_SPOS_SURF;
2171 }
2172 } else { /* Reflective or transmissive */
2173 if (spos != mk_spos_surf)
2174 ev = MUNKI_SPOS_SURF;
2175 }
2176 if (ev != MUNKI_OK) {
2177 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2178 free(mbuf);
2179 if (buf != NULL)
2180 free(buf);
2181 a1logd(p->log,3,"munki_imp_measure: Sensor in wrong position\n");
2182 return ev;
2183 }
2184 }
2185
2186 /* Emissive adaptive, non-scan */
2187 if (s->emiss && !s->scan && s->adaptive) {
2188 int saturated = 0;
2189 double optscale = 1.0;
2190 s->inttime = 0.25;
2191 s->gainmode = 0;
2192 s->dark_valid = 0;
2193
2194 a1logd(p->log,3,"Trial measure emission with inttime %f, gainmode %d\n",s->inttime,s->gainmode);
2195
2196 /* Take a trial measurement reading using the current mode. */
2197 /* Used to determine if sensor is saturated, or not optimal */
2198 nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2199 if ((ev = munki_trialmeasure(p, &saturated, &optscale, nummeas, &s->inttime, s->gainmode,
2200 s->targoscale)) != MUNKI_OK) {
2201 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2202 free(mbuf);
2203 a1logd(p->log,3,"munki_imp_measure trial measure failed\n");
2204 return ev;
2205 }
2206
2207 if (saturated) {
2208 s->inttime = m->min_int_time;
2209
2210 a1logd(p->log,3,"2nd trial measure emission with inttime %f, gainmode %d\n",
2211 s->inttime,s->gainmode);
2212 /* Take a trial measurement reading using the current mode. */
2213 /* Used to determine if sensor is saturated, or not optimal */
2214 nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2215 if ((ev = munki_trialmeasure(p, &saturated, &optscale, nummeas, &s->inttime,
2216 s->gainmode, s->targoscale)) != MUNKI_OK) {
2217 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2218 free(mbuf);
2219 a1logd(p->log,3,"munki_imp_measure trial measure failed\n");
2220 return ev;
2221 }
2222 }
2223
2224 a1logd(p->log,3,"Compute optimal integration time\n");
2225 /* For adaptive mode, compute a new integration time and gain mode */
2226 /* in order to optimise the sensor values. */
2227 if ((ev = munki_optimise_sensor(p, &s->inttime, &s->gainmode,
2228 s->inttime, s->gainmode, 1, 1, &s->targoscale, optscale, 0.0)) != MUNKI_OK) {
2229 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2230 free(mbuf);
2231 a1logd(p->log,3,"munki_imp_measure optimise sensor failed\n");
2232 return ev;
2233 }
2234 a1logd(p->log,3,"Computed optimal emiss inttime %f and gainmode %d\n",s->inttime,s->gainmode);
2235
2236 a1logd(p->log,3,"Interpolate dark calibration reference\n");
2237 if ((ev = munki_interp_dark(p, s->dark_data, s->inttime, s->gainmode)) != MUNKI_OK) {
2238 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2239 free(mbuf);
2240 a1logd(p->log,3,"munki_imp_measure interplate dark ref failed\n");
2241 return ev;
2242 }
2243 s->dark_valid = 1;
2244 s->dark_int_time = s->inttime;
2245 s->dark_gain_mode = s->gainmode;
2246
2247 /* Recompute number of measurements and realloc measurement buffer */
2248 free(mbuf);
2249 nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2250 maxnummeas = munki_comp_nummeas(p, s->maxscantime, s->inttime);
2251 if (maxnummeas < nummeas)
2252 maxnummeas = nummeas;
2253 mbsize = m->nsen * 2 * maxnummeas;
2254 if ((mbuf = (unsigned char *)malloc(sizeof(unsigned char) * mbsize)) == NULL) {
2255 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2256 a1logd(p->log,1,"munki_imp_measure malloc %d bytes failed (7)\n",mbsize);
2257 return MUNKI_INT_MALLOC;
2258 }
2259
2260 } else if (s->reflective) {
2261
2262 DISDPLOT
2263
2264 a1logd(p->log,3,"Doing on the fly black calibration_1 with nummeas %d int_time %f, gainmode %d\n",
2265 nummeas, s->inttime, s->gainmode);
2266
2267 if ((ev = munki_dark_measure_1(p, nummeas, &s->inttime, s->gainmode, buf, bsize))
2268 != MUNKI_OK) {
2269 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2270 free(buf);
2271 free(mbuf);
2272 a1logd(p->log,3,"munki_imp_measure dak measure 1 failed\n");
2273 return ev;
2274 }
2275
2276 ENDPLOT
2277 }
2278 /* Take a measurement reading using the current mode. */
2279 /* Converts to completely processed output readings. */
2280
2281 a1logd(p->log,3,"Do main measurement reading\n");
2282
2283 /* Indicate to the user that they can now scan the instrument, */
2284 /* after a little delay that allows for the instrument reaction time. */
2285 if (s->scan) {
2286 int delay = 100 + (int)(invsampt * 1000.0 + 0.9);
2287 if (p->eventcallback != NULL) {
2288 issue_scan_ready((inst *)p, delay);
2289 } else {
2290 /* delay then 1KHz for 200 msec */
2291 msec_beep(delay, 1000, 200);
2292 }
2293 }
2294
2295 /* Retry loop in case a display read is saturated */
2296 for (;;) {
2297
2298 /* Trigger measure and gather raw readings */
2299 if ((ev = munki_read_patches_1(p, ninvmeas, nummeas, maxnummeas, &s->inttime, s->gainmode,
2300 &nmeasuered, mbuf, mbsize)) != MUNKI_OK) {
2301 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2302 if (buf != NULL)
2303 free(buf);
2304 free(mbuf);
2305 a1logd(p->log,3,"munki_imp_measure failed at munki_read_patches_1\n");
2306 return ev;
2307 }
2308
2309 /* Complete processing of dark readings now that main measurement has been taken */
2310 if (s->reflective) {
2311 a1logd(p->log,3,"Calling black calibration_2 calc with nummeas %d, inttime %f, gainmode %d\n", nummeas, s->inttime,s->gainmode);
2312 if ((ev = munki_dark_measure_2(p, s->dark_data, nummeas, s->inttime,
2313 s->gainmode, buf, bsize)) != MUNKI_OK) {
2314 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2315 free(buf);
2316 free(mbuf);
2317 a1logd(p->log,3,"munki_imp_measure failed at munki_dark_measure_2\n");
2318 return ev;
2319 }
2320 s->dark_valid = 1;
2321 s->dark_int_time = s->inttime;
2322 s->dark_gain_mode = s->gainmode;
2323 free(buf);
2324 }
2325
2326 /* Process the raw measurement readings into final spectral readings */
2327 ev = munki_read_patches_2(p, &duration, specrd, nvals, s->inttime, s->gainmode,
2328 ninvmeas, nmeasuered, mbuf, mbsize);
2329 /* Special case display mode read. If the sensor is saturated, and */
2330 /* we haven't already done so, switch to the alternate integration time */
2331 /* and try again. */
2332 if (s->emiss && !s->scan && !s->adaptive
2333 && ev == MUNKI_RD_SENSORSATURATED
2334 && s->dispswap < 2) {
2335 double *tt, tv;
2336
2337 if (s->dispswap == 0) {
2338 a1logd(p->log,3,"Switching to alternate display integration time %f seconds\n",s->dark_int_time2);
2339 tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
2340 tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
2341 s->dispswap = 1;
2342 } else if (s->dispswap == 1) {
2343 a1logd(p->log,3,"Switching to 2nd alternate display integration time %f seconds\n",s->dark_int_time3);
2344 /* Undo first swap */
2345 tv = s->inttime; s->inttime = s->dark_int_time2; s->dark_int_time2 = tv;
2346 tt = s->dark_data; s->dark_data = s->dark_data2; s->dark_data2 = tt;
2347 /* Do 2nd swap */
2348 tv = s->inttime; s->inttime = s->dark_int_time3; s->dark_int_time3 = tv;
2349 tt = s->dark_data; s->dark_data = s->dark_data3; s->dark_data3 = tt;
2350 s->dispswap = 2;
2351 }
2352 /* Recompute number of measurements and realloc measurement buffer */
2353 free(mbuf);
2354 nummeas = munki_comp_nummeas(p, s->wreadtime, s->inttime);
2355 maxnummeas = munki_comp_nummeas(p, s->maxscantime, s->inttime);
2356 if (maxnummeas < nummeas)
2357 maxnummeas = nummeas;
2358 mbsize = m->nsen * 2 * maxnummeas;
2359 if ((mbuf = (unsigned char *)malloc(sizeof(unsigned char) * mbsize)) == NULL) {
2360 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2361 a1logd(p->log,1,"munki_imp_measure malloc %d bytes failed (7)\n",mbsize);
2362 return MUNKI_INT_MALLOC;
2363 }
2364 continue; /* Do the measurement again */
2365 }
2366
2367 if (ev != MUNKI_OK) {
2368 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2369 free(mbuf);
2370 a1logd(p->log,3,"munki_imp_measure failed at munki_read_patches_2\n");
2371 return ev;
2372 }
2373 break; /* Don't repeat */
2374 }
2375 free(mbuf);
2376
2377 /* Transfer spectral and convert to XYZ */
2378 if ((ev = munki_conv2XYZ(p, vals, nvals, specrd, clamp)) != MUNKI_OK) {
2379 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2380 a1logd(p->log,3,"munki_imp_measure failed at munki_conv2XYZ\n");
2381 return ev;
2382 }
2383 free_dmatrix(specrd, 0, nvals-1, 0, m->nwav-1);
2384
2385 if (nvals > 0)
2386 vals[0].duration = duration; /* Possible flash duration */
2387
2388 a1logd(p->log,3,"munki_imp_measure sucessful return\n");
2389 if (user_trig)
2390 return MUNKI_USER_TRIG;
2391 return ev;
2392 }
2393
2394 /* - - - - - - - - - - - - - - - - - - - - - - - - - - */
2395 /*
2396
2397 Determining the refresh rate for a refresh type display.
2398
2399 This is easy when the max sample rate of the i1 is above
2400 the nyquist of the display, and will always be the case
2401 for the range we are prepared to measure (up to 100Hz)
2402 when using an Rev B, D or E, but is a problem for the
2403 rev A and ColorMunki, which can only sample at 113Hz.
2404
2405 We work around this problem by detecting when
2406 we are measuring an alias of the refresh rate, and
2407 average the aliasing corrected measurements.
2408
2409 If there is no aparent refresh, or the refresh rate is not determinable,
2410 return a period of 0.0 and inst_ok;
2411 */
2412
2413 munki_code munki_measure_rgb(munki *p, double *inttime, double *rgb);
2414
2415 #ifndef PSRAND32L
2416 # define PSRAND32L(S) ((S) * 1664525L + 1013904223L)
2417 #endif
2418 #undef FREQ_SLOW_PRECISE /* [und] Interpolate then autocorrelate, else autc & filter */
2419 #define NFSAMPS 80 /* Number of samples to read */
2420 #define NFMXTIME 6.0 /* Maximum time to take (2000 == 6) */
2421 #define PBPMS 20 /* bins per msec */
2422 #define PERMIN ((1000 * PBPMS)/40) /* 40 Hz */
2423 #define PERMAX ((1000 * PBPMS)/4) /* 4 Hz*/
2424 #define NPER (PERMAX - PERMIN + 1)
2425 #define PWIDTH (8 * PBPMS) /* 8 msec bin spread to look for peak in */
2426 #define MAXPKS 20 /* Number of peaks to find */
2427 #define TRIES 8 /* Number of different sample rates to try */
2428
munki_imp_meas_refrate(munki * p,double * ref_rate)2429 munki_code munki_imp_meas_refrate(
2430 munki *p,
2431 double *ref_rate
2432 ) {
2433 munki_code ev = MUNKI_OK;
2434 munkiimp *m = (munkiimp *)p->m;
2435 munki_state *s = &m->ms[m->mmode];
2436 int i, j, k, mm;
2437 double **multimeas; /* Spectral measurements */
2438 int nummeas;
2439 double rgbw[3] = { 610.0, 520.0, 460.0 };
2440 double ucalf = 1.0; /* usec_time calibration factor */
2441 double inttime;
2442 static unsigned int randn = 0x12345678;
2443 struct {
2444 double sec;
2445 double rgb[3];
2446 } samp[NFSAMPS * 2];
2447 int nfsamps; /* Actual samples read */
2448 double minv[3]; /* Minimum reading */
2449 double maxv[3]; /* Maximum reading */
2450 double maxt; /* Time range */
2451 #ifdef FREQ_SLOW_PRECISE
2452 int nbins;
2453 double *bins[3]; /* PBPMS sample bins */
2454 #else
2455 double tcorr[NPER]; /* Temp for initial autocorrelation */
2456 int ntcorr[NPER]; /* Number accumulated */
2457 #endif
2458 double corr[NPER]; /* Filtered correlation for each period value */
2459 double mincv, maxcv; /* Max and min correlation values */
2460 double crange; /* Correlation range */
2461 double peaks[MAXPKS]; /* Peak wavelength */
2462 double peakh[MAXPKS]; /* Peak heighheight */
2463 int npeaks; /* Number of peaks */
2464 double pval; /* Period value */
2465 double rfreq[TRIES]; /* Computed refresh frequency for each try */
2466 double rsamp[TRIES]; /* Sampling rate used to measure frequency */
2467 int tix = 0; /* try index */
2468
2469 a1logd(p->log,2,"munki_imp_meas_refrate called\n");
2470
2471 if (ref_rate != NULL)
2472 *ref_rate = 0.0;
2473
2474 if (!s->emiss) {
2475 a1logd(p->log,2,"munki_imp_meas_refrate not in emissive mode\n");
2476 return MUNKI_UNSUPPORTED;
2477 }
2478
2479 for (mm = 0; mm < TRIES; mm++) {
2480 rfreq[mm] = 0.0;
2481 npeaks = 0; /* Number of peaks */
2482 nummeas = NFSAMPS;
2483 multimeas = dmatrix(0, nummeas-1, -1, m->nwav-1);
2484
2485 if (mm == 0)
2486 inttime = m->min_int_time;
2487 else {
2488 double rval, dmm;
2489 randn = PSRAND32L(randn);
2490 rval = (double)randn/4294967295.0;
2491 dmm = ((double)mm + rval - 0.5)/(TRIES - 0.5);
2492 inttime = m->min_int_time * (1.0 + dmm * 0.80);
2493 }
2494
2495 if ((ev = munki_read_patches_all(p, multimeas, nummeas, &inttime, 0)) != inst_ok) {
2496 free_dmatrix(multimeas, 0, nummeas-1, 0, m->nwav-1);
2497 return ev;
2498 }
2499
2500 rsamp[tix] = 1.0/inttime;
2501
2502 /* Convert the samples to RGB */
2503 for (i = 0; i < nummeas && i < NFSAMPS; i++) {
2504 samp[i].sec = i * inttime;
2505 samp[i].rgb[0] = samp[i].rgb[1] = samp[i].rgb[2] = 0.0;
2506 for (j = 0; j < m->nwav; j++) {
2507 double wl = XSPECT_WL(m->wl_short, m->wl_long, m->nwav, j);
2508
2509 //printf("~1 multimeas %d %d = %f\n",i, j, multimeas[i][j]);
2510 for (k = 0; k < 3; k++) {
2511 double tt = (double)(wl - rgbw[k]);
2512 tt = (40.0 - fabs(tt))/40.0;
2513 if (tt < 0.0)
2514 tt = 0.0;
2515 samp[i].rgb[k] += sqrt(tt) * multimeas[i][j];
2516 }
2517 }
2518 }
2519 nfsamps = i;
2520
2521 a1logd(p->log, 3, "munki_measure_refresh: Read %d samples for refresh calibration\n",nfsamps);
2522
2523 #ifdef NEVER
2524 /* Plot the raw sensor values */
2525 {
2526 double xx[NFSAMPS];
2527 double y1[NFSAMPS];
2528 double y2[NFSAMPS];
2529 double y3[NFSAMPS];
2530
2531 for (i = 0; i < nfsamps; i++) {
2532 xx[i] = samp[i].sec;
2533 y1[i] = samp[i].rgb[0];
2534 y2[i] = samp[i].rgb[1];
2535 y3[i] = samp[i].rgb[2];
2536 // printf("%d: %f -> %f\n",i,samp[i].sec, samp[i].rgb[0]);
2537 }
2538 printf("Fast scan sensor values and time (sec)\n");
2539 do_plot6(xx, y1, y2, y3, NULL, NULL, NULL, nfsamps);
2540 }
2541 #endif
2542
2543 /* Locate the smallest values and maximum time */
2544 maxt = -1e6;
2545 minv[0] = minv[1] = minv[2] = 1e20;
2546 maxv[0] = maxv[1] = maxv[2] = -11e20;
2547 for (i = nfsamps-1; i >= 0; i--) {
2548 if (samp[i].sec > maxt)
2549 maxt = samp[i].sec;
2550 for (j = 0; j < 3; j++) {
2551 if (samp[i].rgb[j] < minv[j])
2552 minv[j] = samp[i].rgb[j];
2553 if (samp[i].rgb[j] > maxv[j])
2554 maxv[j] = samp[i].rgb[j];
2555 }
2556 }
2557 /* Re-zero the sample times, and normalise the readings */
2558 for (i = nfsamps-1; i >= 0; i--) {
2559 samp[i].sec -= samp[0].sec;
2560 samp[i].sec *= ucalf;
2561 if (samp[i].sec > maxt)
2562 maxt = samp[i].sec;
2563 for (j = 0; j < 3; j++) {
2564 samp[i].rgb[j] -= minv[j];
2565 }
2566 }
2567
2568 #ifdef FREQ_SLOW_PRECISE /* Interpolate then autocorrelate */
2569
2570 /* Create PBPMS bins and interpolate readings into them */
2571 nbins = 1 + (int)(maxt * 1000.0 * PBPMS + 0.5);
2572 for (j = 0; j < 3; j++) {
2573 if ((bins[j] = (double *)calloc(sizeof(double), nbins)) == NULL) {
2574 a1loge(p->log, inst_internal_error, "munki_measure_refresh: malloc failed\n");
2575 return MUNKI_INT_MALLOC;
2576 }
2577 }
2578
2579 /* Do the interpolation */
2580 for (k = 0; k < (nfsamps-1); k++) {
2581 int sbin, ebin;
2582 sbin = (int)(samp[k].sec * 1000.0 * PBPMS + 0.5);
2583 ebin = (int)(samp[k+1].sec * 1000.0 * PBPMS + 0.5);
2584 for (i = sbin; i <= ebin; i++) {
2585 double bl;
2586 #if defined(__APPLE__) && defined(__POWERPC__)
2587 gcc_bug_fix(i);
2588 #endif
2589 bl = (i - sbin)/(double)(ebin - sbin); /* 0.0 to 1.0 */
2590 for (j = 0; j < 3; j++) {
2591 bins[j][i] = (1.0 - bl) * samp[k].rgb[j] + bl * samp[k+1].rgb[j];
2592 }
2593 }
2594 }
2595
2596 #ifdef NEVER
2597 /* Plot interpolated values */
2598 {
2599 double *xx;
2600 double *y1;
2601 double *y2;
2602 double *y3;
2603
2604 xx = malloc(sizeof(double) * nbins);
2605 y1 = malloc(sizeof(double) * nbins);
2606 y2 = malloc(sizeof(double) * nbins);
2607 y3 = malloc(sizeof(double) * nbins);
2608
2609 if (xx == NULL || y1 == NULL || y2 == NULL || y3 == NULL) {
2610 a1loge(p->log, inst_internal_error, "munki_measure_refresh: malloc failed\n");
2611 for (j = 0; j < 3; j++)
2612 free(bins[j]);
2613 return MUNKI_INT_MALLOC;
2614 }
2615 for (i = 0; i < nbins; i++) {
2616 xx[i] = i / (double)PBPMS; /* msec */
2617 y1[i] = bins[0][i];
2618 y2[i] = bins[1][i];
2619 y3[i] = bins[2][i];
2620 }
2621 printf("Interpolated fast scan sensor values and time (msec) for inttime %f\n",inttime);
2622 do_plot6(xx, y1, y2, y3, NULL, NULL, NULL, nbins);
2623
2624 free(xx);
2625 free(y1);
2626 free(y2);
2627 free(y3);
2628 }
2629 #endif /* PLOT_REFRESH */
2630
2631 /* Compute auto-correlation at 1/PBPMS msec intervals */
2632 /* from 25 msec (40Hz) to 100msec (10 Hz) */
2633 mincv = 1e48, maxcv = -1e48;
2634 for (i = 0; i < NPER; i++) {
2635 int poff = PERMIN + i; /* Offset to corresponding sample */
2636
2637 corr[i] = 0;
2638 for (k = 0; (k + poff) < nbins; k++) {
2639 corr[i] += bins[0][k] * bins[0][k + poff]
2640 + bins[1][k] * bins[1][k + poff]
2641 + bins[2][k] * bins[2][k + poff];
2642 }
2643 corr[i] /= (double)k; /* Normalize */
2644
2645 if (corr[i] > maxcv)
2646 maxcv = corr[i];
2647 if (corr[i] < mincv)
2648 mincv = corr[i];
2649 }
2650 /* Free the bins */
2651 for (j = 0; j < 3; j++)
2652 free(bins[j]);
2653
2654 #else /* !FREQ_SLOW_PRECISE Fast - autocorrellate then filter */
2655
2656 /* Upsample by a factor of 2 */
2657 for (i = nfsamps-1; i >= 0; i--) {
2658 j = 2 * i;
2659 samp[j].sec = samp[i].sec;
2660 samp[j].rgb[0] = samp[i].rgb[0];
2661 samp[j].rgb[1] = samp[i].rgb[1];
2662 samp[j].rgb[2] = samp[i].rgb[2];
2663 if (i > 0) {
2664 j--;
2665 samp[j].sec = 0.5 * (samp[i].sec + samp[i-1].sec);
2666 samp[j].rgb[0] = 0.5 * (samp[i].rgb[0] + samp[i-1].rgb[0]);
2667 samp[j].rgb[1] = 0.5 * (samp[i].rgb[1] + samp[i-1].rgb[1]);
2668 samp[j].rgb[2] = 0.5 * (samp[i].rgb[2] + samp[i-1].rgb[2]);
2669 }
2670 }
2671 nfsamps = 2 * nfsamps - 1;
2672
2673 /* Do point by point correllation of samples */
2674 for (i = 0; i < NPER; i++) {
2675 tcorr[i] = 0.0;
2676 ntcorr[i] = 0;
2677 }
2678
2679 for (j = 0; j < (nfsamps-1); j++) {
2680
2681 for (k = j+1; k < nfsamps; k++) {
2682 double del, cor;
2683 int bix;
2684
2685 del = samp[k].sec - samp[j].sec;
2686 bix = (int)(del * 1000.0 * PBPMS + 0.5);
2687 if (bix < PERMIN)
2688 continue;
2689 if (bix > PERMAX)
2690 break;
2691 bix -= PERMIN;
2692
2693 cor = samp[j].rgb[0] * samp[k].rgb[0]
2694 + samp[j].rgb[1] * samp[k].rgb[1]
2695 + samp[j].rgb[2] * samp[k].rgb[2];
2696
2697 //printf("~1 j %d k %d, del %f bix %d cor %f\n",j,k,del,bix,cor);
2698 tcorr[bix] += cor;
2699 ntcorr[bix]++;
2700 }
2701 }
2702 /* Divide out count and linearly interpolate */
2703 j = 0;
2704 for (i = 0; i < NPER; i++) {
2705 if (ntcorr[i] > 0) {
2706 tcorr[i] /= ntcorr[i];
2707 if ((i - j) > 1) {
2708 if (j == 0) {
2709 for (k = j; k < i; k++)
2710 tcorr[k] = tcorr[i];
2711
2712 } else { /* Linearly interpolate from last value */
2713 double ww = (double)i-j;
2714 for (k = j+1; k < i; k++) {
2715 double bl = (k-j)/ww;
2716 tcorr[k] = (1.0 - bl) * tcorr[j] + bl * tcorr[i];
2717 }
2718 }
2719 }
2720 j = i;
2721 }
2722 }
2723 if (j < (NPER-1)) {
2724 for (k = j+1; k < NPER; k++) {
2725 tcorr[k] = tcorr[j];
2726 }
2727 }
2728
2729 #ifdef PLOT_REFRESH
2730 /* Plot unfiltered auto correlation */
2731 {
2732 double xx[NPER];
2733 double y1[NPER];
2734
2735 for (i = 0; i < NPER; i++) {
2736 xx[i] = (i + PERMIN) / (double)PBPMS; /* msec */
2737 y1[i] = tcorr[i];
2738 }
2739 printf("Unfiltered auto correlation (msec)\n");
2740 do_plot6(xx, y1, NULL, NULL, NULL, NULL, NULL, NPER);
2741 }
2742 #endif /* PLOT_REFRESH */
2743
2744 /* Apply a gausian filter */
2745 #define FWIDTH 100
2746 {
2747 double gaus_[2 * FWIDTH * PBPMS + 1];
2748 double *gaus = &gaus_[FWIDTH * PBPMS];
2749 double bb = 1.0/pow(2, 5.0);
2750 double fw = inttime * 1000.0;
2751 int ifw;
2752
2753 //printf("~1 sc = %f = %f msec\n",1.0/inttime, fw);
2754 //printf("~1 fw = %f, ifw = %d\n",fw,ifw);
2755
2756 fw *= 0.9;
2757 ifw = (int)ceil(fw * PBPMS);
2758 if (ifw > FWIDTH * PBPMS)
2759 error("munki: Not enough space for lanczos 2 filter");
2760 for (j = -ifw; j <= ifw; j++) {
2761 double x, y;
2762 x = j/(PBPMS * fw);
2763 if (fabs(x) > 1.0)
2764 y = 0.0;
2765 else
2766 y = 1.0/pow(2, 5.0 * x * x) - bb;
2767 gaus[j] = y;
2768 //printf("~1 gaus[%d] = %f\n",j,y);
2769 }
2770
2771 for (i = 0; i < NPER; i++) {
2772 double sum = 0.0;
2773 double wght = 0.0;
2774
2775 for (j = -ifw; j <= ifw; j++) {
2776 double w;
2777 int ix = i + j;
2778 if (ix < 0)
2779 ix = -ix;
2780 if (ix > (NPER-1))
2781 ix = 2 * NPER-1 - ix;
2782 w = gaus[j];
2783 sum += w * tcorr[ix];
2784 wght += w;
2785 }
2786 //printf("~1 corr[%d] wgt = %f\n",i,wght);
2787 corr[i] = sum / wght;
2788 }
2789 }
2790
2791 /* Compute min & max */
2792 mincv = 1e48, maxcv = -1e48;
2793 for (i = 0; i < NPER; i++) {
2794 if (corr[i] > maxcv)
2795 maxcv = corr[i];
2796 if (corr[i] < mincv)
2797 mincv = corr[i];
2798 }
2799
2800 #endif /* !FREQ_SLOW_PRECISE Fast - autocorrellate then filter */
2801
2802 crange = maxcv - mincv;
2803 a1logd(p->log,3,"Correlation value range %f - %f = %f = %f%%\n",mincv, maxcv,crange, 100.0 * (maxcv-mincv)/maxcv);
2804
2805 #ifdef PLOT_REFRESH
2806 /* Plot this measuremnts auto correlation */
2807 {
2808 double xx[NPER];
2809 double y1[NPER];
2810
2811 for (i = 0; i < NPER; i++) {
2812 xx[i] = (i + PERMIN) / (double)PBPMS; /* msec */
2813 y1[i] = corr[i];
2814 }
2815 printf("Auto correlation (msec)\n");
2816 do_plot6(xx, y1, NULL, NULL, NULL, NULL, NULL, NPER);
2817 }
2818 #endif /* PLOT_REFRESH */
2819
2820 #define PFDB 4 // normally 4
2821 /* If there is sufficient level and distict correlations */
2822 if (crange/maxcv >= 0.1) {
2823
2824 a1logd(p->log,PFDB,"Searching for peaks\n");
2825
2826 /* Locate all the peaks starting at the longest correllation */
2827 for (i = (NPER-1-PWIDTH); i >= 0 && npeaks < MAXPKS; i--) {
2828 double v1, v2, v3;
2829 v1 = corr[i];
2830 v2 = corr[i + PWIDTH/2]; /* Peak */
2831 v3 = corr[i + PWIDTH];
2832
2833 if (fabs(v3 - v1)/crange < 0.05
2834 && (v2 - v1)/crange > 0.025
2835 && (v2 - v3)/crange > 0.025
2836 && (v2 - mincv)/crange > 0.5) {
2837 double pkv; /* Peak value */
2838 int pki; /* Peak index */
2839 double ii, bl;
2840
2841 #ifdef PLOT_REFRESH
2842 a1logd(p->log,PFDB,"Max between %f and %f msec\n",
2843 (i + PERMIN)/(double)PBPMS,(i + PWIDTH + PERMIN)/(double)PBPMS);
2844 #endif
2845
2846 /* Locate the actual peak */
2847 pkv = -1.0;
2848 pki = 0;
2849 for (j = i; j < (i + PWIDTH); j++) {
2850 if (corr[j] > pkv) {
2851 pkv = corr[j];
2852 pki = j;
2853 }
2854 }
2855 #ifdef PLOT_REFRESH
2856 a1logd(p->log,PFDB,"Peak is at %f msec, %f corr\n", (pki + PERMIN)/(double)PBPMS, pkv);
2857 #endif
2858
2859 /* Interpolate the peak value for higher precision */
2860 /* j = bigest */
2861 if (corr[pki-1] > corr[pki+1]) {
2862 j = pki-1;
2863 k = pki+1;
2864 } else {
2865 j = pki+1;
2866 k = pki-1;
2867 }
2868 bl = (corr[pki] - corr[j])/(corr[pki] - corr[k]);
2869 bl = (bl + 1.0)/2.0;
2870 ii = bl * pki + (1.0 - bl) * j;
2871 pval = (ii + PERMIN)/(double)PBPMS;
2872 #ifdef PLOT_REFRESH
2873 a1logd(p->log,PFDB,"Interpolated peak is at %f msec\n", pval);
2874 #endif
2875 peaks[npeaks] = pval;
2876 peakh[npeaks] = corr[pki];
2877 npeaks++;
2878
2879 i -= PWIDTH;
2880 }
2881 #ifdef NEVER
2882 if (v2 > v1 && v2 > v3) {
2883 printf("Peak rehjected:\n");
2884 printf("(v3 - v1)/crange = %f < 0.05 ?\n",fabs(v3 - v1)/crange);
2885 printf("(v2 - v1)/crange = %f > 0.025 ?\n",(v2 - v1)/crange);
2886 printf("(v2 - v3)/crange = %f > 0.025 ?\n",(v2 - v3)/crange);
2887 printf("(v2 - mincv)/crange = %f > 0.5 ?\n",(v2 - mincv)/crange);
2888 }
2889 #endif
2890 }
2891 a1logd(p->log,3,"Number of peaks located = %d\n",npeaks);
2892
2893 } else {
2894 a1logd(p->log,3,"All rejected, crange/maxcv = %f < 0.06\n",crange/maxcv);
2895 }
2896 #undef PFDB
2897
2898 a1logd(p->log,3,"Number of peaks located = %d\n",npeaks);
2899
2900 if (npeaks > 1) { /* Compute aparent refresh rate */
2901 int nfails;
2902 double div, avg, ano;
2903 /* Try and locate a common divisor amongst all the peaks. */
2904 /* This is likely to be the underlying refresh rate. */
2905 for (k = 0; k < npeaks; k++) {
2906 for (j = 1; j < 25; j++) {
2907 avg = ano = 0.0;
2908 div = peaks[k]/(double)j;
2909 if (div < 5.0)
2910 continue; /* Skip anything higher than 200Hz */
2911 //printf("~1 trying %f Hz\n",1000.0/div);
2912 for (nfails = i = 0; i < npeaks; i++) {
2913 double rem, cnt;
2914
2915 rem = peaks[i]/div;
2916 cnt = floor(rem + 0.5);
2917 rem = fabs(rem - cnt);
2918
2919 #ifdef PLOT_REFRESH
2920 a1logd(p->log, 3, "remainder for peak %d = %f\n",i,rem);
2921 #endif
2922 if (rem > 0.06) {
2923 if (++nfails > 2)
2924 break; /* Fail this divisor */
2925 } else {
2926 avg += peaks[i]; /* Already weighted by cnt */
2927 ano += cnt;
2928 }
2929 }
2930
2931 if (nfails == 0 || (nfails <= 2 && npeaks >= 6))
2932 break; /* Sucess */
2933 /* else go and try a different divisor */
2934 }
2935 if (j < 25)
2936 break; /* Success - found common divisor */
2937 }
2938 if (k >= npeaks) {
2939 a1logd(p->log,3,"Failed to locate common divisor\n");
2940
2941 } else {
2942 pval = 0.001 * avg/ano;
2943 if (pval < inttime) {
2944 a1logd(p->log,3,"Discarding frequency %f > sample rate %f\n",1.0/pval, 1.0/inttime);
2945 } else {
2946 pval = 1.0/pval; /* Convert to frequency */
2947 rfreq[tix++] = pval;
2948 a1logd(p->log,3,"Located frequency %f sum %f dif %f\n",pval, pval + 1.0/inttime, fabs(pval - 1.0/inttime));
2949 }
2950 }
2951 }
2952 }
2953
2954 if (tix >= 3) {
2955
2956 for (mm = 0; mm < tix; mm++) {
2957 a1logd(p->log, 3, "Try %d, samp %f Hz, Meas %f Hz, Sum %f Hz, Dif %f Hz\n",mm,rsamp[mm],rfreq[mm], rsamp[mm] + rfreq[mm], fabs(rsamp[mm] - rfreq[mm]));
2958 }
2959
2960 /* Decide if we are above the nyquist, or whether */
2961 /* we have aliases of the fundamental */
2962 {
2963 double brange = 1e38;
2964 double brate = 0.0;
2965 int bsplit = -1;
2966 double min, max, avg, range;
2967 int split, mul, niia;
2968
2969 /* Compute fundamental and sub aliases at all possible splits. */
2970 /* Skip the reading at the split. */
2971 for (split = tix; split >= -1; split--) {
2972 min = 1e38; max = -1e38; avg = 0.0; niia = 0;
2973 for (mm = 0; mm < tix; mm++) {
2974 double alias;
2975
2976 if (mm == split)
2977 continue;
2978 if (mm < split)
2979 alias = rfreq[mm];
2980 else
2981 alias = fabs(rsamp[mm] - rfreq[mm]);
2982
2983 avg += alias;
2984 niia++;
2985
2986 if (alias < min)
2987 min = alias;
2988 if (alias > max)
2989 max = alias;
2990 }
2991 avg /= (double)niia;
2992 range = (max - min)/(max + min);
2993 //printf("~1 split %d avg = %f, range = %f\n",split,avg,range);
2994 if (range < brange) {
2995 brange = range;
2996 brate = avg;
2997 bsplit = split;
2998 }
2999 }
3000
3001 /* Compute sub and add aliases at all possible splits */
3002 /* Skip the reading at the split. */
3003 for (split = tix; split >= -1; split--) {
3004 min = 1e38; max = -1e38; avg = 0.0; niia = 0;
3005 for (mm = 0; mm < tix; mm++) {
3006 double alias;
3007
3008 if (mm == split)
3009 continue;
3010 if (mm < split)
3011 alias = fabs(rsamp[mm] - rfreq[mm]);
3012 else
3013 alias = rsamp[mm] + rfreq[mm];
3014
3015 avg += alias;
3016 niia++;
3017
3018 if (alias < min)
3019 min = alias;
3020 if (alias > max)
3021 max = alias;
3022 }
3023 avg /= (double)niia;
3024 range = (max - min)/(max + min);
3025 //printf("~1 split %d avg = %f, range = %f\n",100 + split,avg,range);
3026 if (range < brange) {
3027 brange = range;
3028 brate = avg;
3029 bsplit = 100 + split;
3030 }
3031 }
3032
3033 a1logd(p->log, 3, "Selected split %d range %f\n",bsplit,brange);
3034
3035 /* Hmm. Could reject result and re-try if brange is too large ? ( > 0.005 ?) */
3036
3037 if (brange > 0.05) {
3038 a1logd(p->log, 3, "Readings are too inconsistent (brange %.1f%%) - should retry ?\n",brange * 100.0);
3039 } else {
3040 if (ref_rate != NULL)
3041 *ref_rate = brate;
3042
3043 /* Error against my 85Hz CRT - GWG */
3044 // a1logd(p->log, 1, "Refresh rate %f Hz, error = %.4f%%\n",brate,100.0 * fabs(brate - 85.0)/(85.0));
3045 return MUNKI_OK;
3046 }
3047 }
3048 } else {
3049 a1logd(p->log, 3, "Not enough tries suceeded to determine refresh rate\n");
3050 }
3051
3052 return MUNKI_RD_NOREFR_FOUND;
3053 }
3054 #undef NFSAMPS
3055 #undef PBPMS
3056 #undef PERMIN
3057 #undef PERMAX
3058 #undef NPER
3059 #undef PWIDTH
3060
3061 /* - - - - - - - - - - - - - - - - - - - - - - - - - - */
3062 /* Save the calibration for all modes, stored on local system */
3063
3064 #ifdef ENABLE_NONVCAL
3065
3066 /* non-volatile save/restor state to/from a file */
3067 typedef struct {
3068 int ef; /* Error flag, 1 = write failed, 2 = close failed */
3069 unsigned int chsum; /* Checksum */
3070 } mknonv;
3071
update_chsum(mknonv * x,unsigned char * p,int nn)3072 static void update_chsum(mknonv *x, unsigned char *p, int nn) {
3073 int i;
3074 for (i = 0; i < nn; i++, p++)
3075 x->chsum = ((x->chsum << 13) | (x->chsum >> (32-13))) + *p;
3076 }
3077
3078 /* Write an array of chars to the file. Set the error flag to nz on error */
write_chars(mknonv * x,FILE * fp,char * dp,int n)3079 static void write_chars(mknonv *x, FILE *fp, char *dp, int n) {
3080
3081 if (fwrite((void *)dp, sizeof(char), n, fp) != n) {
3082 x->ef = 1;
3083 } else {
3084 update_chsum(x, (unsigned char *)dp, n * sizeof(char));
3085 }
3086 }
3087
3088 /* Write an array of ints to the file. Set the error flag to nz on error */
write_ints(mknonv * x,FILE * fp,int * dp,int n)3089 static void write_ints(mknonv *x, FILE *fp, int *dp, int n) {
3090
3091 if (fwrite((void *)dp, sizeof(int), n, fp) != n) {
3092 x->ef = 1;
3093 } else {
3094 update_chsum(x, (unsigned char *)dp, n * sizeof(int));
3095 }
3096 }
3097
3098 /* Write an array of doubles to the file. Set the error flag to nz on error */
write_doubles(mknonv * x,FILE * fp,double * dp,int n)3099 static void write_doubles(mknonv *x, FILE *fp, double *dp, int n) {
3100
3101 if (fwrite((void *)dp, sizeof(double), n, fp) != n) {
3102 x->ef = 1;
3103 } else {
3104 update_chsum(x, (unsigned char *)dp, n * sizeof(double));
3105 }
3106 }
3107
3108 /* Write an array of time_t's to the file. Set the error flag to nz on error */
3109 /* (This will cause file checksum fail if different executables on the same */
3110 /* system have different time_t values) */
write_time_ts(mknonv * x,FILE * fp,time_t * dp,int n)3111 static void write_time_ts(mknonv *x, FILE *fp, time_t *dp, int n) {
3112
3113 if (fwrite((void *)dp, sizeof(time_t), n, fp) != n) {
3114 x->ef = 1;
3115 } else {
3116 update_chsum(x, (unsigned char *)dp, n * sizeof(time_t));
3117 }
3118 }
3119
3120 /* Read an array of ints from the file. Set the error flag to nz on error */
read_ints(mknonv * x,FILE * fp,int * dp,int n)3121 static void read_ints(mknonv *x, FILE *fp, int *dp, int n) {
3122
3123 if (fread((void *)dp, sizeof(int), n, fp) != n) {
3124 x->ef = 1;
3125 } else {
3126 update_chsum(x, (unsigned char *)dp, n * sizeof(int));
3127 }
3128 }
3129
3130 /* Read an array of chars from the file. Set the error flag to nz on error */
read_chars(mknonv * x,FILE * fp,char * dp,int n)3131 static void read_chars(mknonv *x, FILE *fp, char *dp, int n) {
3132
3133 if (fread((void *)dp, sizeof(char), n, fp) != n) {
3134 x->ef = 1;
3135 } else {
3136 update_chsum(x, (unsigned char *)dp, n * sizeof(char));
3137 }
3138 }
3139
3140
3141 /* Read an array of doubles from the file. Set the error flag to nz on error */
read_doubles(mknonv * x,FILE * fp,double * dp,int n)3142 static void read_doubles(mknonv *x, FILE *fp, double *dp, int n) {
3143
3144 if (fread((void *)dp, sizeof(double), n, fp) != n) {
3145 x->ef = 1;
3146 } else {
3147 update_chsum(x, (unsigned char *)dp, n * sizeof(double));
3148 }
3149 }
3150
3151 /* Read an array of time_t's from the file. Set the error flag to nz on error */
3152 /* (This will cause file checksum fail if different executables on the same */
3153 /* system have different time_t values) */
read_time_ts(mknonv * x,FILE * fp,time_t * dp,int n)3154 static void read_time_ts(mknonv *x, FILE *fp, time_t *dp, int n) {
3155
3156 if (fread((void *)dp, sizeof(time_t), n, fp) != n) {
3157 x->ef = 1;
3158 } else {
3159 update_chsum(x, (unsigned char *)dp, n * sizeof(time_t));
3160 }
3161 }
3162
munki_save_calibration(munki * p)3163 munki_code munki_save_calibration(munki *p) {
3164 munkiimp *m = (munkiimp *)p->m;
3165 munki_code ev = MUNKI_OK;
3166 munki_state *s;
3167 int i;
3168 char nmode[10];
3169 char cal_name[100]; /* Name */
3170 char **cal_paths = NULL;
3171 int no_paths = 0;
3172 FILE *fp;
3173 mknonv x;
3174 int ss;
3175 int argyllversion = ARGYLL_VERSION;
3176
3177 strcpy(nmode, "w");
3178 #if !defined(O_CREAT) && !defined(_O_CREAT)
3179 # error "Need to #include fcntl.h!"
3180 #endif
3181 #if defined(O_BINARY) || defined(_O_BINARY)
3182 strcat(nmode, "b");
3183 #endif
3184
3185 sprintf(cal_name, "ArgyllCMS/.mk_%s.cal", m->serno);
3186 if ((no_paths = xdg_bds(NULL, &cal_paths, xdg_cache, xdg_write, xdg_user, cal_name)) < 1) {
3187 a1logd(p->log,1,"munki_save_calibration xdg_bds returned no paths\n");
3188 return MUNKI_INT_CAL_SAVE;
3189 }
3190
3191 a1logd(p->log,3,"munki_save_calibration saving to file '%s'\n",cal_paths[0]);
3192
3193 if (create_parent_directories(cal_paths[0])
3194 || (fp = fopen(cal_paths[0], nmode)) == NULL) {
3195 a1logd(p->log,3,"munki_save_calibration failed to open file for writing\n");
3196 xdg_free(cal_paths, no_paths);
3197 return MUNKI_INT_CAL_SAVE;
3198 }
3199
3200 x.ef = 0;
3201 x.chsum = 0;
3202
3203 /* A crude structure signature */
3204 ss = sizeof(munki_state) + sizeof(munkiimp);
3205
3206 /* Some file identification */
3207 write_ints(&x, fp, &argyllversion, 1);
3208 write_ints(&x, fp, &ss, 1);
3209 write_chars(&x, fp, m->serno, 17);
3210 write_ints(&x, fp, &m->nraw, 1);
3211 write_ints(&x, fp, (int *)&m->nwav1, 1);
3212 write_ints(&x, fp, (int *)&m->nwav2, 1);
3213
3214 /* For each mode, save the calibration if it's valid */
3215 for (i = 0; i < mk_no_modes; i++) {
3216 s = &m->ms[i];
3217
3218 /* Mode identification */
3219 write_ints(&x, fp, &s->emiss, 1);
3220 write_ints(&x, fp, &s->trans, 1);
3221 write_ints(&x, fp, &s->reflective, 1);
3222 write_ints(&x, fp, &s->scan, 1);
3223 write_ints(&x, fp, &s->flash, 1);
3224 write_ints(&x, fp, &s->ambient, 1);
3225 write_ints(&x, fp, &s->projector, 1);
3226 write_ints(&x, fp, &s->adaptive, 1);
3227
3228 /* Configuration calibration is valid for */
3229 write_ints(&x, fp, &s->gainmode, 1);
3230 write_doubles(&x, fp, &s->inttime, 1);
3231
3232 /* Calibration information */
3233 write_ints(&x, fp, &s->dark_valid, 1);
3234 write_time_ts(&x, fp, &s->ddate, 1);
3235 write_doubles(&x, fp, &s->dark_int_time, 1);
3236 write_doubles(&x, fp, s->dark_data-1, m->nraw+1);
3237 write_doubles(&x, fp, &s->dark_int_time2, 1);
3238 write_doubles(&x, fp, s->dark_data2-1, m->nraw+1);
3239 write_doubles(&x, fp, &s->dark_int_time3, 1);
3240 write_doubles(&x, fp, s->dark_data3-1, m->nraw+1);
3241 write_ints(&x, fp, &s->dark_gain_mode, 1);
3242
3243 if (!s->emiss) {
3244 write_ints(&x, fp, &s->cal_valid, 1);
3245 write_time_ts(&x, fp, &s->cfdate, 1);
3246 write_doubles(&x, fp, s->cal_factor1, m->nwav1);
3247 write_doubles(&x, fp, s->cal_factor2, m->nwav2);
3248 write_doubles(&x, fp, s->white_data-1, m->nraw+1);
3249 write_doubles(&x, fp, &s->reftemp, 1);
3250 write_doubles(&x, fp, s->iwhite_data[0]-1, m->nraw+1);
3251 write_doubles(&x, fp, s->iwhite_data[1]-1, m->nraw+1);
3252 }
3253
3254 write_ints(&x, fp, &s->idark_valid, 1);
3255 write_time_ts(&x, fp, &s->iddate, 1);
3256 write_doubles(&x, fp, s->idark_int_time, 4);
3257 write_doubles(&x, fp, s->idark_data[0]-1, m->nraw+1);
3258 write_doubles(&x, fp, s->idark_data[1]-1, m->nraw+1);
3259 write_doubles(&x, fp, s->idark_data[2]-1, m->nraw+1);
3260 write_doubles(&x, fp, s->idark_data[3]-1, m->nraw+1);
3261 }
3262
3263 a1logd(p->log,3,"Checkum = 0x%x\n",x.chsum);
3264 write_ints(&x, fp, (int *)&x.chsum, 1);
3265
3266 if (fclose(fp) != 0)
3267 x.ef = 2;
3268
3269 if (x.ef != 0) {
3270 a1logd(p->log,3,"Writing calibration file failed with %d\n",x.ef);
3271 delete_file(cal_paths[0]);
3272 } else {
3273 a1logd(p->log,3,"Writing calibration file succeeded\n");
3274 }
3275 xdg_free(cal_paths, no_paths);
3276
3277 return ev;
3278 }
3279
3280 /* Restore the all modes calibration from the local system */
munki_restore_calibration(munki * p)3281 munki_code munki_restore_calibration(munki *p) {
3282 munkiimp *m = (munkiimp *)p->m;
3283 munki_code ev = MUNKI_OK;
3284 munki_state *s, ts;
3285 int i, j;
3286 char nmode[10];
3287 char cal_name[100]; /* Name */
3288 char **cal_paths = NULL;
3289 int no_paths = 0;
3290 FILE *fp;
3291 mknonv x;
3292 int argyllversion;
3293 int ss, nraw, nwav1, nwav2, chsum1, chsum2;
3294 char serno[17];
3295
3296 strcpy(nmode, "r");
3297 #if defined(O_BINARY) || defined(_O_BINARY)
3298 strcat(nmode, "b");
3299 #endif
3300
3301 sprintf(cal_name, "ArgyllCMS/.mk_%s.cal" SSEPS "color/.mk_%s.cal", m->serno, m->serno);
3302 if ((no_paths = xdg_bds(NULL, &cal_paths, xdg_cache, xdg_read, xdg_user, cal_name)) < 1) {
3303 a1logd(p->log,1,"munki_restore_calibration xdg_bds returned no paths\n");
3304 return MUNKI_INT_CAL_RESTORE;
3305 }
3306
3307 a1logd(p->log,2,"munki_restore_calibration restoring from file '%s'\n",cal_paths[0]);
3308
3309 /* Check the last modification time */
3310 {
3311 struct sys_stat sbuf;
3312
3313 if (sys_stat(cal_paths[0], &sbuf) == 0) {
3314 m->lo_secs = time(NULL) - sbuf.st_mtime;
3315 a1logd(p->log,2,"munki_restore_calibration: %d secs from instrument last open\n",m->lo_secs);
3316 } else {
3317 a1logd(p->log,2,"munki_restore_calibration: stat on file failed\n");
3318 }
3319 }
3320
3321 if ((fp = fopen(cal_paths[0], nmode)) == NULL) {
3322 a1logd(p->log,2,"munki_restore_calibration failed to open file for reading\n");
3323 xdg_free(cal_paths, no_paths);
3324 return MUNKI_INT_CAL_RESTORE;
3325 }
3326 xdg_free(cal_paths, no_paths);
3327
3328 x.ef = 0;
3329 x.chsum = 0;
3330
3331 /* Check the file identification */
3332 read_ints(&x, fp, &argyllversion, 1);
3333 read_ints(&x, fp, &ss, 1);
3334 read_chars(&x, fp, serno, 17);
3335 read_ints(&x, fp, &nraw, 1);
3336 read_ints(&x, fp, &nwav1, 1);
3337 read_ints(&x, fp, &nwav2, 1);
3338 if (x.ef != 0
3339 || argyllversion != ARGYLL_VERSION
3340 || ss != (sizeof(munki_state) + sizeof(munkiimp))
3341 || strcmp(serno, m->serno) != 0
3342 || nraw != m->nraw
3343 || nwav1 != m->nwav1
3344 || nwav2 != m->nwav2) {
3345 a1logd(p->log,3,"Identification didn't verify\n");
3346 goto reserr;
3347 }
3348
3349 /* Do a dummy read to check the checksum */
3350 for (i = 0; i < mk_no_modes; i++) {
3351 int di;
3352 double dd;
3353 time_t dt;
3354 int emiss, trans, reflective, ambient, projector, scan, flash, adaptive;
3355
3356 s = &m->ms[i];
3357
3358 /* Mode identification */
3359 read_ints(&x, fp, &emiss, 1);
3360 read_ints(&x, fp, &trans, 1);
3361 read_ints(&x, fp, &reflective, 1);
3362 read_ints(&x, fp, &scan, 1);
3363 read_ints(&x, fp, &flash, 1);
3364 read_ints(&x, fp, &ambient, 1);
3365 read_ints(&x, fp, &projector, 1);
3366 read_ints(&x, fp, &adaptive, 1);
3367
3368 /* Configuration calibration is valid for */
3369 read_ints(&x, fp, &di, 1); /* gainmode */
3370 read_doubles(&x, fp, &dd, 1); /* inttime */
3371
3372 /* Calibration information */
3373 read_ints(&x, fp, &di, 1); /* dark_valid */
3374 read_time_ts(&x, fp, &dt, 1); /* ddate */
3375 read_doubles(&x, fp, &dd, 1); /* dark_int_time */
3376 for (j = -1; j < m->nraw; j++)
3377 read_doubles(&x, fp, &dd, 1); /* dark_data */
3378 read_doubles(&x, fp, &dd, 1); /* dark_int_time2 */
3379 for (j = -1; j < m->nraw; j++)
3380 read_doubles(&x, fp, &dd, 1); /* dark_data2 */
3381 read_doubles(&x, fp, &dd, 1); /* dark_int_time3 */
3382 for (j = -1; j < m->nraw; j++)
3383 read_doubles(&x, fp, &dd, 1); /* dark_data3 */
3384 read_ints(&x, fp, &di, 1); /* dark_gain_mode */
3385
3386 if (!s->emiss) {
3387 read_ints(&x, fp, &di, 1); /* cal_valid */
3388 read_time_ts(&x, fp, &dt, 1); /* cfdate */
3389 for (j = 0; j < m->nwav1; j++)
3390 read_doubles(&x, fp, &dd, 1); /* cal_factor1 */
3391 for (j = 0; j < m->nwav2; j++)
3392 read_doubles(&x, fp, &dd, 1); /* cal_factor2 */
3393 for (j = -1; j < m->nraw; j++)
3394 read_doubles(&x, fp, &dd, 1); /* white_data */
3395 read_doubles(&x, fp, &dd, 1); /* reftemp */
3396 for (j = -1; j < m->nraw; j++)
3397 read_doubles(&x, fp, &dd, 1); /* iwhite_data[0] */
3398 for (j = -1; j < m->nraw; j++)
3399 read_doubles(&x, fp, &dd, 1); /* iwhite_data[1] */
3400 }
3401
3402 read_ints(&x, fp, &di, 1); /* idark_valid */
3403 read_time_ts(&x, fp, &dt, 1); /* iddate */
3404 for (j = 0; j < 4; j++)
3405 read_doubles(&x, fp, &dd, 1); /* idark_int_time */
3406 for (j = -1; j < m->nraw; j++)
3407 read_doubles(&x, fp, &dd, 1); /* idark_data[0] */
3408 for (j = -1; j < m->nraw; j++)
3409 read_doubles(&x, fp, &dd, 1); /* idark_data[1] */
3410 for (j = -1; j < m->nraw; j++)
3411 read_doubles(&x, fp, &dd, 1); /* idark_data[2] */
3412 for (j = -1; j < m->nraw; j++)
3413 read_doubles(&x, fp, &dd, 1); /* idark_data[3] */
3414 }
3415
3416 chsum1 = x.chsum;
3417 read_ints(&x, fp, &chsum2, 1);
3418
3419 if (x.ef != 0
3420 || chsum1 != chsum2) {
3421 a1logd(p->log,3,"Checksum didn't verify, got 0x%x, expected 0x%x\n",chsum1, chsum2);
3422 goto reserr;
3423 }
3424
3425 rewind(fp);
3426
3427 /* Allocate space in temp structure */
3428
3429 ts.dark_data = dvectorz(-1, m->nraw-1);
3430 ts.dark_data2 = dvectorz(-1, m->nraw-1);
3431 ts.dark_data3 = dvectorz(-1, m->nraw-1);
3432 ts.cal_factor1 = dvectorz(0, m->nwav1-1);
3433 ts.cal_factor2 = dvectorz(0, m->nwav2-1);
3434 ts.white_data = dvectorz(-1, m->nraw-1);
3435 ts.iwhite_data = dmatrixz(0, 2, -1, m->nraw-1);
3436 ts.idark_data = dmatrixz(0, 3, -1, m->nraw-1);
3437
3438 /* Read the identification */
3439 read_ints(&x, fp, &argyllversion, 1);
3440 read_ints(&x, fp, &ss, 1);
3441 read_chars(&x, fp, m->serno, 17);
3442 read_ints(&x, fp, &m->nraw, 1);
3443 read_ints(&x, fp, (int *)&m->nwav1, 1);
3444 read_ints(&x, fp, (int *)&m->nwav2, 1);
3445
3446 /* For each mode, save the calibration if it's valid */
3447 for (i = 0; i < mk_no_modes; i++) {
3448 s = &m->ms[i];
3449
3450 /* Mode identification */
3451 read_ints(&x, fp, &ts.emiss, 1);
3452 read_ints(&x, fp, &ts.trans, 1);
3453 read_ints(&x, fp, &ts.reflective, 1);
3454 read_ints(&x, fp, &ts.scan, 1);
3455 read_ints(&x, fp, &ts.flash, 1);
3456 read_ints(&x, fp, &ts.ambient, 1);
3457 read_ints(&x, fp, &ts.projector, 1);
3458 read_ints(&x, fp, &ts.adaptive, 1);
3459
3460 /* Configuration calibration is valid for */
3461 read_ints(&x, fp, &ts.gainmode, 1);
3462 read_doubles(&x, fp, &ts.inttime, 1);
3463
3464 /* Calibration information: */
3465
3466 /* Static Dark */
3467 read_ints(&x, fp, &ts.dark_valid, 1);
3468 read_time_ts(&x, fp, &ts.ddate, 1);
3469 read_doubles(&x, fp, &ts.dark_int_time, 1);
3470 read_doubles(&x, fp, ts.dark_data-1, m->nraw+1);
3471 read_doubles(&x, fp, &ts.dark_int_time2, 1);
3472 read_doubles(&x, fp, ts.dark_data2-1, m->nraw+1);
3473 read_doubles(&x, fp, &ts.dark_int_time3, 1);
3474 read_doubles(&x, fp, ts.dark_data3-1, m->nraw+1);
3475 read_ints(&x, fp, &ts.dark_gain_mode, 1);
3476
3477 if (!ts.emiss) {
3478 /* Reflective */
3479 read_ints(&x, fp, &ts.cal_valid, 1);
3480 read_time_ts(&x, fp, &ts.cfdate, 1);
3481 read_doubles(&x, fp, ts.cal_factor1, m->nwav1);
3482 read_doubles(&x, fp, ts.cal_factor2, m->nwav2);
3483 read_doubles(&x, fp, ts.white_data-1, m->nraw+1);
3484 read_doubles(&x, fp, &ts.reftemp, 1);
3485 read_doubles(&x, fp, ts.iwhite_data[0]-1, m->nraw+1);
3486 read_doubles(&x, fp, ts.iwhite_data[1]-1, m->nraw+1);
3487 }
3488
3489 /* Adaptive Dark */
3490 read_ints(&x, fp, &ts.idark_valid, 1);
3491 read_time_ts(&x, fp, &ts.iddate, 1);
3492 read_doubles(&x, fp, ts.idark_int_time, 4);
3493 read_doubles(&x, fp, ts.idark_data[0]-1, m->nraw+1);
3494 read_doubles(&x, fp, ts.idark_data[1]-1, m->nraw+1);
3495 read_doubles(&x, fp, ts.idark_data[2]-1, m->nraw+1);
3496 read_doubles(&x, fp, ts.idark_data[3]-1, m->nraw+1);
3497
3498 /* If the configuration for this mode matches */
3499 /* that of the calibration, restore the calibration */
3500 /* for this mode. */
3501 if (x.ef == 0 /* No read error */
3502 && s->emiss == ts.emiss
3503 && s->trans == ts.trans
3504 && s->reflective == ts.reflective
3505 && s->scan == ts.scan
3506 && s->flash == ts.flash
3507 && s->ambient == ts.ambient
3508 && s->projector == ts.projector
3509 && s->adaptive == ts.adaptive
3510 && (s->adaptive || fabs(s->inttime - ts.inttime) < 0.01)
3511 && (s->adaptive || fabs(s->dark_int_time - ts.dark_int_time) < 0.01)
3512 && (s->adaptive || fabs(s->dark_int_time2 - ts.dark_int_time2) < 0.01)
3513 && (s->adaptive || fabs(s->dark_int_time3 - ts.dark_int_time3) < 0.01)
3514 && (!s->adaptive || fabs(s->idark_int_time[0] - ts.idark_int_time[0]) < 0.01)
3515 && (!s->adaptive || fabs(s->idark_int_time[1] - ts.idark_int_time[1]) < 0.01)
3516 && (!s->adaptive || fabs(s->idark_int_time[2] - ts.idark_int_time[2]) < 0.01)
3517 && (!s->adaptive || fabs(s->idark_int_time[3] - ts.idark_int_time[3]) < 0.01)
3518 ) {
3519 /* Copy all the fields read above */
3520 s->emiss = ts.emiss;
3521 s->trans = ts.trans;
3522 s->reflective = ts.reflective;
3523 s->scan = ts.scan;
3524 s->flash = ts.flash;
3525 s->ambient = ts.ambient;
3526 s->projector = ts.projector;
3527 s->adaptive = ts.adaptive;
3528
3529 s->gainmode = ts.gainmode;
3530 s->inttime = ts.inttime;
3531 s->dark_valid = ts.dark_valid;
3532 s->ddate = ts.ddate;
3533 s->dark_int_time = ts.dark_int_time;
3534 for (j = -1; j < m->nraw; j++)
3535 s->dark_data[j] = ts.dark_data[j];
3536 s->dark_int_time2 = ts.dark_int_time2;
3537 for (j = -1; j < m->nraw; j++)
3538 s->dark_data2[j] = ts.dark_data2[j];
3539 s->dark_int_time3 = ts.dark_int_time3;
3540 for (j = -1; j < m->nraw; j++)
3541 s->dark_data3[j] = ts.dark_data3[j];
3542 s->dark_gain_mode = ts.dark_gain_mode;
3543 if (!ts.emiss) {
3544 s->cal_valid = ts.cal_valid;
3545 s->cfdate = ts.cfdate;
3546 for (j = 0; j < m->nwav1; j++)
3547 s->cal_factor1[j] = ts.cal_factor1[j];
3548 for (j = 0; j < m->nwav2; j++)
3549 s->cal_factor2[j] = ts.cal_factor2[j];
3550 for (j = -1; j < m->nraw; j++)
3551 s->white_data[j] = ts.white_data[j];
3552 s->reftemp = ts.reftemp;
3553 for (j = -1; j < m->nraw; j++)
3554 s->iwhite_data[0][j] = ts.iwhite_data[0][j];
3555 for (j = -1; j < m->nraw; j++)
3556 s->iwhite_data[1][j] = ts.iwhite_data[1][j];
3557 }
3558 s->idark_valid = ts.idark_valid;
3559 s->iddate = ts.iddate;
3560 for (j = 0; j < 4; j++)
3561 s->idark_int_time[j] = ts.idark_int_time[j];
3562 for (j = -1; j < m->nraw; j++)
3563 s->idark_data[0][j] = ts.idark_data[0][j];
3564 for (j = -1; j < m->nraw; j++)
3565 s->idark_data[1][j] = ts.idark_data[1][j];
3566 for (j = -1; j < m->nraw; j++)
3567 s->idark_data[2][j] = ts.idark_data[2][j];
3568 for (j = -1; j < m->nraw; j++)
3569 s->idark_data[3][j] = ts.idark_data[3][j];
3570
3571 } else {
3572 //printf("~1 mode %d\n",i);
3573 //printf("~1 adaptive = %d\n",s->adaptive);
3574 //printf("~1 innttime %f %f\n",s->inttime, ts.inttime);
3575 //printf("~1 dark_int_time %f %f\n",s->dark_int_time,ts.dark_int_time);
3576 //printf("~1 dark_int_time2 %f %f\n",s->dark_int_time2,ts.dark_int_time2);
3577 //printf("~1 dark_int_time3 %f %f\n",s->dark_int_time3,ts.dark_int_time3);
3578 //printf("~1 idark_int_time0 %f %f\n",s->idark_int_time[0],ts.idark_int_time[0]);
3579 //printf("~1 idark_int_time1 %f %f\n",s->idark_int_time[1],ts.idark_int_time[1]);
3580 //printf("~1 idark_int_time2 %f %f\n",s->idark_int_time[2],ts.idark_int_time[2]);
3581 //printf("~1 idark_int_time3 %f %f\n",s->idark_int_time[3],ts.idark_int_time[3]);
3582 a1logd(p->log,4,"Not restoring cal for mode %d since params don't match:\n",i);
3583 a1logd(p->log,4,"emis = %d : %d, trans = %d : %d, ref = %d : %d\n",s->emiss,ts.emiss,s->trans,ts.trans,s->reflective,ts.reflective);
3584 a1logd(p->log,4,"scan = %d : %d, flash = %d : %d, ambi = %d : %d, proj = %d : %d, adapt = %d : %d\n",s->scan,ts.scan,s->flash,ts.flash,s->ambient,ts.ambient,s->projector,ts.projector,s->adaptive,ts.adaptive);
3585 a1logd(p->log,4,"inttime = %f : %f\n",s->inttime,ts.inttime);
3586 a1logd(p->log,4,"darkit1 = %f : %f, 2 = %f : %f, 3 = %f : %f\n",s->dark_int_time,ts.dark_int_time,s->dark_int_time2,ts.dark_int_time2,s->dark_int_time3,ts.dark_int_time3);
3587 a1logd(p->log,4,"idarkit0 = %f : %f, 1 = %f : %f, 2 = %f : %f, 3 = %f : %f\n",s->idark_int_time[0],ts.idark_int_time[0],s->idark_int_time[1],ts.idark_int_time[1],s->idark_int_time[2],ts.idark_int_time[2],s->idark_int_time[3],ts.idark_int_time[3]);
3588 }
3589 }
3590
3591 /* Free up temporary space */
3592 free_dvector(ts.dark_data, -1, m->nraw-1);
3593 free_dvector(ts.dark_data2, -1, m->nraw-1);
3594 free_dvector(ts.dark_data3, -1, m->nraw-1);
3595 free_dvector(ts.white_data, -1, m->nraw-1);
3596 free_dmatrix(ts.iwhite_data, 0, 1, -1, m->nraw-1);
3597 free_dmatrix(ts.idark_data, 0, 3, -1, m->nraw-1);
3598
3599 free_dvector(ts.cal_factor1, 0, m->nwav1-1);
3600 free_dvector(ts.cal_factor2, 0, m->nwav2-1);
3601
3602 a1logd(p->log,3,"munki_restore_calibration done\n");
3603 reserr:;
3604
3605 fclose(fp);
3606
3607 return ev;
3608 }
3609
munki_touch_calibration(munki * p)3610 munki_code munki_touch_calibration(munki *p) {
3611 munkiimp *m = (munkiimp *)p->m;
3612 munki_code ev = MUNKI_OK;
3613 char cal_name[100]; /* Name */
3614 char **cal_paths = NULL;
3615 int no_paths = 0;
3616 int rv;
3617
3618 sprintf(cal_name, "ArgyllCMS/.mk_%s.cal" SSEPS "color/.mk_%s.cal", m->serno, m->serno);
3619 if ((no_paths = xdg_bds(NULL, &cal_paths, xdg_cache, xdg_read, xdg_user, cal_name)) < 1)
3620 return MUNKI_INT_CAL_TOUCH;
3621
3622 a1logd(p->log,2,"munki_touch_calibration touching file '%s'\n",cal_paths[0]);
3623
3624 if ((rv = sys_utime(cal_paths[0], NULL)) != 0) {
3625 a1logd(p->log,2,"munki_touch_calibration failed with %d\n",rv);
3626 xdg_free(cal_paths, no_paths);
3627 return MUNKI_INT_CAL_TOUCH;
3628 }
3629 xdg_free(cal_paths, no_paths);
3630
3631 return ev;
3632 }
3633
3634 #endif /* ENABLE_NONVCAL */
3635
3636
3637 /* ============================================================ */
3638 /* Intermediate routines - composite commands/processing */
3639
3640 /* Take a dark reference measurement - part 1 */
munki_dark_measure_1(munki * p,int nummeas,double * inttime,int gainmode,unsigned char * buf,unsigned int bsize)3641 munki_code munki_dark_measure_1(
3642 munki *p,
3643 int nummeas, /* Number of readings to take */
3644 double *inttime, /* Integration time to use/used */
3645 int gainmode, /* Gain mode to use, 0 = normal, 1 = high */
3646 unsigned char *buf, /* USB reading buffer to use */
3647 unsigned int bsize /* Size of buffer */
3648 ) {
3649 munki_code ev = MUNKI_OK;
3650
3651 if (nummeas <= 0)
3652 return MUNKI_INT_ZEROMEASURES;
3653
3654 if ((ev = munki_trigger_one_measure(p, nummeas, inttime, gainmode, 1, 1)) != MUNKI_OK)
3655 return ev;
3656
3657 if ((ev = munki_readmeasurement(p, nummeas, 0, buf, bsize, NULL, 1, 1)) != MUNKI_OK)
3658 return ev;
3659
3660 return ev;
3661 }
3662
3663 /* Take a dark reference measurement - part 2 */
munki_dark_measure_2(munki * p,double * sens,int nummeas,double inttime,int gainmode,unsigned char * buf,unsigned int bsize)3664 munki_code munki_dark_measure_2(
3665 munki *p,
3666 double *sens, /* Return array [-1 nraw] of sens values */
3667 int nummeas, /* Number of readings to take */
3668 double inttime, /* Integration time to use/used */
3669 int gainmode, /* Gain mode to use, 0 = normal, 1 = high */
3670 unsigned char *buf, /* raw USB reading buffer to process */
3671 unsigned int bsize /* Buffer size to process */
3672 ) {
3673 munki_code ev = MUNKI_OK;
3674 munkiimp *m = (munkiimp *)p->m;
3675 double **multimes; /* Multiple measurement results */
3676 double darkthresh; /* Dark threshold */
3677 double sensavg; /* Overall average of sensor readings */
3678 int rv;
3679
3680 multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
3681
3682 /* Take a buffer full of raw readings, and convert them to */
3683 /* floating point sensor readings. Check for saturation */
3684 if ((rv = munki_sens_to_raw(p, multimes, NULL, buf, 0, nummeas, m->satlimit, &darkthresh))
3685 != MUNKI_OK) {
3686 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
3687 return rv;
3688 }
3689
3690 /* Average a set of measurements into one. */
3691 /* Return nz if the readings are not consistent */
3692 /* Return the overall average. */
3693 rv = munki_average_multimeas(p, sens, multimes, nummeas, &sensavg, darkthresh);
3694 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
3695
3696 #ifdef PLOT_DEBUG
3697 a1logd(p->log,4,"dark_measure_2: Avg abs. sensor readings = %f, sh/darkthresh %f\n",sensavg,darkthresh);
3698 printf("sens data:\n");
3699 plot_raw(sens);
3700 #endif
3701
3702 if (rv) {
3703 a1logd(p->log,3,"munki_dark_measure_2: readings are inconsistent\n");
3704 return MUNKI_RD_DARKREADINCONS;
3705 }
3706
3707 if (sensavg > (2.0 * darkthresh)) {
3708 a1logd(p->log,3,"munki_dark_measure_2: Average %f is > 2 * darkthresh %f\n",sensavg,darkthresh);
3709 return MUNKI_RD_DARKNOTVALID;
3710 }
3711 return ev;
3712 }
3713
3714 #ifdef DUMP_DARKM
3715 int ddumpdarkm = 0;
3716 #endif
3717
3718 /* Take a dark reference measurement (combined parts 1 & 2) */
munki_dark_measure(munki * p,double * raw,int nummeas,double * inttime,int gainmode)3719 munki_code munki_dark_measure(
3720 munki *p,
3721 double *raw, /* Return array [-1 nraw] of raw values */
3722 int nummeas, /* Number of readings to take */
3723 double *inttime, /* Integration time to use/used */
3724 int gainmode /* Gain mode to use, 0 = normal, 1 = high */
3725 ) {
3726 munkiimp *m = (munkiimp *)p->m;
3727 munki_code ev = MUNKI_OK;
3728 unsigned char *buf; /* Raw USB reading buffer */
3729 unsigned int bsize;
3730
3731 a1logd(p->log,3, "munki_dark_measure with inttime %f\n",*inttime);
3732 bsize = m->nsen * 2 * nummeas;
3733 if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
3734 a1logd(p->log,1,"munki_dark_measure malloc %d bytes failed (8)\n",bsize);
3735 return MUNKI_INT_MALLOC;
3736 }
3737
3738 if ((ev = munki_dark_measure_1(p, nummeas, inttime, gainmode, buf, bsize)) != MUNKI_OK) {
3739 free(buf);
3740 return ev;
3741 }
3742
3743 if ((ev = munki_dark_measure_2(p, raw, nummeas, *inttime, gainmode, buf, bsize))
3744 != MUNKI_OK) {
3745 free(buf);
3746 return ev;
3747 }
3748 free(buf);
3749
3750 #ifdef DUMP_DARKM
3751 /* Dump raw dark readings to a file "mkddump.txt" */
3752 if (ddumpdarkm) {
3753 int j;
3754 FILE *fp;
3755
3756 if ((fp = fopen("mkddump.txt", "a")) == NULL)
3757 a1logw(p->log,"Unable to open debug file mkddump.txt\n");
3758 else {
3759 fprintf(fp, "\nDark measure: nummeas %d, inttime %f, gainmode %d, darkcells %f\n",nummeas,*inttime,gainmode, raw[-1]);
3760 fprintf(fp,"\t\t\t{ ");
3761 for (j = 0; j < (m->nraw-1); j++)
3762 fprintf(fp, "%f, ",raw[j]);
3763 fprintf(fp, "%f },\n",raw[j]);
3764 fclose(fp);
3765 }
3766 }
3767 #endif
3768 return ev;
3769 }
3770
3771 /* Heat the LED up for given number of seconds by taking a reading */
munki_heatLED(munki * p,double htime)3772 munki_code munki_heatLED(
3773 munki *p,
3774 double htime /* Heat up time */
3775 ) {
3776 munkiimp *m = (munkiimp *)p->m;
3777 double inttime = m->cal_int_time; /* Integration time to use/used */
3778 int nummeas;
3779 unsigned char *buf; /* Raw USB reading buffer */
3780 unsigned int bsize;
3781 int rv;
3782
3783 a1logd(p->log,3,"munki_heatLED called \n");
3784
3785 nummeas = munki_comp_ru_nummeas(p, htime, inttime);
3786
3787 if (nummeas <= 0)
3788 return MUNKI_OK;
3789
3790 /* Allocate temporaries */
3791 bsize = m->nsen * 2 * nummeas;
3792 if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
3793 a1logd(p->log,1,"munki_heatLED malloc %d bytes failed (10)\n",bsize);
3794 return MUNKI_INT_MALLOC;
3795 }
3796
3797 a1logd(p->log,3,"Triggering measurement cycle, nummeas %d, inttime %f\n", nummeas, inttime);
3798
3799 if ((rv = munki_trigger_one_measure(p, nummeas, &inttime, 0, 1, 0))
3800 != MUNKI_OK) {
3801 free(buf);
3802 return rv;
3803 }
3804
3805 a1logd(p->log,3,"Gathering readings\n");
3806
3807 rv = munki_readmeasurement(p, nummeas, 0, buf, bsize, NULL, 1, 0);
3808
3809 free(buf);
3810
3811 return rv;
3812 }
3813
3814
3815 /* Take a reflective or emissive white reference sens measurement, */
3816 /* subtracts black and processes into wavelenths. */
3817 /* (absraw is usually ->white_data) */
munki_whitemeasure(munki * p,double * absraw,double * optscale,int nummeas,double * inttime,int gainmode,double targoscale)3818 munki_code munki_whitemeasure(
3819 munki *p,
3820 double *absraw, /* Return array [-1 nraw] of absraw values (may be NULL) */
3821 double *optscale, /* Return scale for gain/int time to make optimal (may be NULL) */
3822 int nummeas, /* Number of readings to take */
3823 double *inttime, /* Integration time to use/used */
3824 int gainmode, /* Gain mode to use, 0 = normal, 1 = high */
3825 double targoscale /* Ratio of optimal sensor value to aim for */
3826 ) {
3827 munki_code ev = MUNKI_OK;
3828 munkiimp *m = (munkiimp *)p->m;
3829 munki_state *s = &m->ms[m->mmode];
3830 unsigned char *buf; /* Raw USB reading buffer */
3831 unsigned int bsize;
3832 int ninvmeas = 0; /* Number of invalid measurements */
3833 double **multimes; /* Multiple measurement results */
3834 double sensavg; /* Overall average of sensor readings */
3835 double darkthresh; /* Dark threshold */
3836 double trackmax[3]; /* Track optimum target & darkthresh */
3837 double maxval; /* Maximum multimeas value */
3838 int rv;
3839
3840 a1logd(p->log,3,"munki_whitemeasure called \n");
3841
3842 if (s->reflective) {
3843 /* Compute invalid samples to allow for LED warmup */
3844 ninvmeas = munki_comp_ru_nummeas(p, m->refinvalidsampt, *inttime);
3845 }
3846
3847 if (nummeas <= 0)
3848 return MUNKI_INT_ZEROMEASURES;
3849
3850 /* Allocate temporaries */
3851 bsize = m->nsen * 2 * (ninvmeas + nummeas);
3852 if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
3853 a1logd(p->log,1,"munki_whitemeasure malloc %d bytes failed (10)\n",bsize);
3854 return MUNKI_INT_MALLOC;
3855 }
3856
3857 a1logd(p->log,3,"Triggering measurement cycle, ninvmeas %d, nummeas %d, inttime %f, gainmode %d\n",
3858 ninvmeas, nummeas, *inttime, gainmode);
3859
3860 if ((ev = munki_trigger_one_measure(p, ninvmeas + nummeas, inttime, gainmode, 1, 0))
3861 != MUNKI_OK) {
3862 free(buf);
3863 return ev;
3864 }
3865
3866 a1logd(p->log,3,"Gathering readings\n");
3867
3868 if ((ev = munki_readmeasurement(p, ninvmeas + nummeas, 0, buf, bsize, NULL, 1, 0))
3869 != MUNKI_OK) {
3870 free(buf);
3871 return ev;
3872 }
3873
3874 multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
3875
3876 /* Take a buffer full of raw readings, and convert them to */
3877 /* floating point sensor readings. Check for saturation */
3878 if ((rv = munki_sens_to_raw(p, multimes, NULL, buf, ninvmeas, nummeas, m->satlimit,
3879 &darkthresh)) != MUNKI_OK) {
3880 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
3881 return rv;
3882 }
3883
3884 #ifdef PLOT_DEBUG
3885 printf("Dark data:\n");
3886 plot_raw(s->dark_data);
3887 #endif
3888
3889 trackmax[0] = darkthresh; /* Track the dark threshold value */
3890 trackmax[1] = m->optsval; /* Track the optimal sensor target value */
3891 trackmax[2] = m->satlimit; /* For debugging */
3892
3893 /* Subtract the black from sensor values and convert to */
3894 /* absolute (integration & gain scaled), zero offset based, */
3895 /* linearized sensor values. */
3896 /* Return the highest individual element. */
3897 munki_sub_raw_to_absraw(p, nummeas, *inttime, gainmode, multimes, s->dark_data,
3898 trackmax, 3, &maxval);
3899 darkthresh = trackmax[0];
3900 free(buf);
3901
3902 if (absraw != NULL) {
3903 /* Average a set of measurements into one. */
3904 /* Return nz if the readings are not consistent */
3905 /* Return the overall average. */
3906 rv = munki_average_multimeas(p, absraw, multimes, nummeas, &sensavg, darkthresh);
3907
3908 #ifndef IGNORE_WHITE_INCONS
3909 if (rv) {
3910 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
3911 return MUNKI_RD_WHITEREADINCONS;
3912 }
3913 #endif /* IGNORE_WHITE_INCONS */
3914
3915 a1logd(p->log,3,"Average absolute sensor readings, avg %f, max %f, darkth %f satth %f\n",
3916 sensavg,maxval,darkthresh,trackmax[2]);
3917 #ifdef PLOT_DEBUG
3918 printf("absraw whitemeas:\n");
3919 plot_raw(absraw);
3920 #endif
3921 }
3922
3923 if (optscale != NULL) {
3924 double opttarget; /* Optimal reading scale target value */
3925
3926 opttarget = targoscale * trackmax[1];
3927 if (maxval < 0.01) /* Could go -ve */
3928 maxval = 0.01;
3929 *optscale = opttarget/maxval;
3930
3931 a1logd(p->log,3,"Targscale %f, maxval %f, optimal target = %f, amount to scale = %f\n",
3932 targoscale, maxval, opttarget, *optscale);
3933 }
3934
3935 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); /* Free after using *pmax */
3936
3937 return ev;
3938 }
3939
3940 /* Given an absraw white reference measurement, */
3941 /* compute the wavelength equivalents. */
3942 /* (absraw is usually ->white_data) */
3943 /* (abswav1 is usually ->cal_factor1) */
3944 /* (abswav2 is usually ->cal_factor2) */
munki_compute_wav_whitemeas(munki * p,double * abswav1,double * abswav2,double * absraw)3945 munki_code munki_compute_wav_whitemeas(
3946 munki *p,
3947 double *abswav1, /* Return array [nwav1] of abswav values (may be NULL) */
3948 double *abswav2, /* Return array [nwav2] of abswav values (if hr_init, may be NULL) */
3949 double *absraw /* Given array [-1 nraw] of absraw values */
3950 ) {
3951 munkiimp *m = (munkiimp *)p->m;
3952
3953 /* Convert an absraw array from raw wavelengths to output wavelenths */
3954 if (abswav1 != NULL) {
3955 munki_absraw_to_abswav1(p, 1, &abswav1, &absraw);
3956
3957 #ifdef PLOT_DEBUG
3958 printf("White meas converted to wavelengths std res:\n");
3959 plot_wav1(m, abswav1);
3960 #endif
3961 }
3962
3963 #ifdef HIGH_RES
3964 if (abswav2 != NULL && m->hr_inited == 2) {
3965 munki_absraw_to_abswav2(p, 1, &abswav2, &absraw);
3966
3967 #ifdef PLOT_DEBUG
3968 printf("Converted to wavelengths high res:\n");
3969 plot_wav2(m, abswav2);
3970 #endif
3971 }
3972 #endif /* HIGH_RES */
3973
3974 return MUNKI_OK;
3975 }
3976
3977 /* Take a reflective white reference measurement, */
3978 /* subtracts black and decompose into base + LED temperature components */
munki_ledtemp_whitemeasure(munki * p,double * white,double ** iwhite,double * reftemp,int nummeas,double inttime,int gainmode)3979 munki_code munki_ledtemp_whitemeasure(
3980 munki *p,
3981 double *white, /* Return [-1 nraw] of temperature compensated white reference */
3982 double **iwhite, /* Return array [-1 nraw][2] of absraw base and scale values */
3983 double *reftemp, /* Return a reference temperature to normalize to */
3984 int nummeas, /* Number of readings to take */
3985 double inttime, /* Integration time to use/used */
3986 int gainmode /* Gain mode to use, 0 = normal, 1 = high */
3987 ) {
3988 munki_code ev = MUNKI_OK;
3989 munkiimp *m = (munkiimp *)p->m;
3990 munki_state *s = &m->ms[m->mmode];
3991 unsigned char *buf; /* Raw USB reading buffer */
3992 unsigned int bsize;
3993 int ninvmeas = 0; /* Number of invalid measurements */
3994 double **multimes; /* Multiple measurement results */
3995 double *ledtemp; /* LED temperature for each measurement */
3996 double darkthresh; /* Dark threshold */
3997 int rv;
3998
3999 a1logd(p->log,3,"munki_ledtemp_whitemeasure called \n");
4000
4001 /* Compute invalid samples to allow for LED warmup */
4002 ninvmeas = munki_comp_ru_nummeas(p, m->refinvalidsampt, inttime);
4003
4004 if (nummeas <= 0) {
4005 return MUNKI_INT_ZEROMEASURES;
4006 }
4007
4008 /* Allocate temporaries */
4009 bsize = m->nsen * 2 * (ninvmeas + nummeas);
4010 if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
4011 a1logd(p->log,1,"munki_whitemeasure malloc %d bytes failed (10)\n",bsize);
4012 return MUNKI_INT_MALLOC;
4013 }
4014
4015 a1logd(p->log,3,"Triggering measurement cycle, ninvmeas %d, nummeas %d, inttime %f, gainmode %d\n",
4016 ninvmeas, nummeas, inttime, gainmode);
4017
4018 if ((ev = munki_trigger_one_measure(p, ninvmeas + nummeas, &inttime, gainmode, 1, 0))
4019 != MUNKI_OK) {
4020 free(buf);
4021 return ev;
4022 }
4023
4024 a1logd(p->log,3,"Gathering readings\n");
4025
4026 if ((ev = munki_readmeasurement(p, ninvmeas + nummeas, 0, buf, bsize, NULL, 1, 0))
4027 != MUNKI_OK) {
4028 free(buf);
4029 return ev;
4030 }
4031
4032 multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
4033 ledtemp = dvector(0, nummeas-1);
4034
4035 /* Take a buffer full of raw readings, and convert them to */
4036 /* floating point sensor readings. Check for saturation */
4037 if ((ev = munki_sens_to_raw(p, multimes, ledtemp, buf, ninvmeas, nummeas, m->satlimit,
4038 &darkthresh)) != MUNKI_OK) {
4039 free(buf);
4040 free_dvector(ledtemp, 0, nummeas-1);
4041 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4042 return ev;
4043 }
4044 free(buf);
4045
4046 /* Make the reference temperature nominal */
4047 *reftemp = 0.5 * (ledtemp[0] + ledtemp[nummeas-1]);
4048
4049 #ifdef PLOT_DEBUG
4050 printf("Ledtemp Dark data:\n");
4051 plot_raw(s->dark_data);
4052 #endif
4053
4054 /* Subtract the black from sensor values and convert to */
4055 /* absolute (integration & gain scaled), zero offset based, */
4056 /* linearized sensor values. */
4057 munki_sub_raw_to_absraw(p, nummeas, inttime, gainmode, multimes, s->dark_data,
4058 &darkthresh, 1, NULL);
4059
4060
4061 /* For each raw wavelength, compute a linear regression */
4062 {
4063 int i, w;
4064 double tt, ss, sx, sy, sxdss, stt, b;
4065
4066 ss = (double)nummeas;
4067 for (sx = 0.0, i = 0; i < nummeas; i++)
4068 sx += ledtemp[i];
4069 sxdss = sx/ss;
4070
4071 for (w = -1; w < m->nraw; w++) {
4072 for (sy = 0.0, i = 0; i < nummeas; i++)
4073 sy += multimes[i][w];
4074
4075 for (stt = b = 0.0, i = 0; i < nummeas; i++) {
4076 tt = ledtemp[i] - sxdss;
4077 stt += tt * tt;
4078 b += tt * multimes[i][w];
4079 }
4080 b /= stt;
4081
4082 iwhite[0][w] = (sy - sx * b)/ss;
4083 iwhite[1][w] = b;
4084 }
4085 }
4086 #ifdef DEBUG
4087 { /* Verify the linear regression */
4088 int i, w;
4089 double x, terr = 0.0, errc = 0.0;
4090
4091 for (w = -1; w < m->nraw; w++) {
4092 for (i = 0; i < nummeas; i++) {
4093 x = iwhite[0][w] + ledtemp[i] * iwhite[1][w];
4094 terr += fabs(x - multimes[i][w]);
4095 errc++;
4096 }
4097 }
4098 terr /= errc;
4099 printf("Linear regression average error = %f\n",terr);
4100 }
4101 #endif
4102
4103 #ifdef PLOT_TEMPCOMP
4104 /* Plot the raw spectra and model, 3 at a time */
4105 {
4106 int i, j, k;
4107 double *indx;
4108 double **mod;
4109 indx = dvectorz(0, nummeas-1);
4110 mod = dmatrix(0, 5, 0, nummeas-1);
4111 for (i = 0; i < nummeas; i++)
4112 indx[i] = 3.0 * i/(nummeas-1.0);
4113
4114 for (j = 0; (j+2) < (m->nraw-1); j += 3) {
4115 for (i = 0; i < nummeas; i++) {
4116 for (k = j; k < (j + 3); k++) {
4117 mod[k-j][i] = iwhite[0][k] + ledtemp[i] * iwhite[1][k];
4118 }
4119 for (k = j; k < (j + 3); k++) {
4120 mod[k-j+3][i] = multimes[i][k];
4121 }
4122 }
4123
4124 printf("Bands %d - %d\n",j, j+2);
4125 do_plot6(indx, mod[0], mod[1], mod[2], mod[3], mod[4], mod[5], nummeas);
4126 }
4127 free_dvector(indx, 0, nummeas-1);
4128 free_dmatrix(mod, 0, 5, 0, nummeas-1);
4129 }
4130 #endif
4131
4132 a1logd(p->log,3,"Computed linear regression\n");
4133
4134 #ifdef ENABLE_LEDTEMPC
4135 /* Compute a temerature compensated set of white readings */
4136 if ((ev = munki_ledtemp_comp(p, multimes, ledtemp, nummeas, *reftemp, iwhite)) != MUNKI_OK) {
4137 free_dvector(ledtemp, 0, nummeas-1);
4138 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4139 return ev;
4140 }
4141 #endif /* ENABLE_LEDTEMPC */
4142
4143 /* Average a set of measurements into one. */
4144 if ((rv = munki_average_multimeas(p, white, multimes, nummeas, NULL, darkthresh)) != 0) {
4145 free_dvector(ledtemp, 0, nummeas-1);
4146 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4147 a1logd(p->log,3,"munki_ledtemp_whitemeasure: readings are inconsistent\n");
4148 return MUNKI_RD_DARKREADINCONS;
4149 }
4150
4151 free_dvector(ledtemp, 0, nummeas-1);
4152 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4153
4154 return ev;
4155 }
4156
4157 /* Given the ledtemp base and scale values, */
4158 /* return an absraw reflective white reference for the */
4159 /* given temperature */
munki_ledtemp_white(munki * p,double * absraw,double ** iwhite,double ledtemp)4160 munki_code munki_ledtemp_white(
4161 munki *p,
4162 double *absraw, /* Return array [-1 nraw] of absraw base and scale values */
4163 double **iwhite, /* ledtemp base and scale */
4164 double ledtemp /* LED temperature value */
4165 ) {
4166 munkiimp *m = (munkiimp *)p->m;
4167 int w;
4168
4169 for (w = -1; w < m->nraw; w++)
4170 absraw[w] = iwhite[0][w] + ledtemp * iwhite[1][w];
4171
4172 return MUNKI_OK;
4173 }
4174
4175 /* Given a set of absraw sensor readings and the corresponding temperature, */
4176 /* compensate the readings to be at the nominated temperature. */
munki_ledtemp_comp(munki * p,double ** absraw,double * ledtemp,int nummeas,double reftemp,double ** iwhite)4177 munki_code munki_ledtemp_comp(
4178 munki *p,
4179 double **absraw, /* [nummeas][raw] measurements to compensate */
4180 double *ledtemp, /* LED temperature for each measurement */
4181 int nummeas, /* Number of measurements */
4182 double reftemp, /* LED reference temperature to compensate to */
4183 double **iwhite /* ledtemp base and scale information */
4184 ) {
4185 munkiimp *m = (munkiimp *)p->m;
4186 int i, w;
4187
4188 for (i = 0; i < nummeas; i++) {
4189 for (w = 0; w < m->nraw; w++) { /* Don't try and compensate shielded values */
4190 double targ, attemp;
4191 targ = iwhite[0][w] + reftemp * iwhite[1][w];
4192 attemp = iwhite[0][w] + ledtemp[i] * iwhite[1][w];
4193
4194 absraw[i][w] *= targ/attemp;
4195 }
4196 }
4197 return MUNKI_OK;
4198 }
4199
4200 /* Trigger measure and gather raw readings using the current mode. */
munki_read_patches_1(munki * p,int ninvmeas,int minnummeas,int maxnummeas,double * inttime,int gainmode,int * nmeasuered,unsigned char * buf,unsigned int bsize)4201 munki_code munki_read_patches_1(
4202 munki *p,
4203 int ninvmeas, /* Number of extra invalid measurements at start */
4204 int minnummeas, /* Minimum number of measurements to take */
4205 int maxnummeas, /* Maximum number of measurements to allow for */
4206 double *inttime, /* Integration time to use/used */
4207 int gainmode, /* Gain mode to use, 0 = normal, 1 = high */
4208 int *nmeasuered, /* Number actually measured (excluding ninvmeas) */
4209 unsigned char *buf, /* Raw USB reading buffer */
4210 unsigned int bsize /* Raw USB readings buffer size in bytes */
4211 ) {
4212 munki_code ev = MUNKI_OK;
4213 munkiimp *m = (munkiimp *)p->m;
4214
4215 if ((ninvmeas + minnummeas) <= 0)
4216 return MUNKI_INT_ZEROMEASURES;
4217
4218 if ((minnummeas + ninvmeas) > maxnummeas)
4219 maxnummeas = (minnummeas - ninvmeas);
4220
4221 a1logd(p->log,3,"Triggering & gathering cycle, ninvmeas %d, minnummeas %d, inttime %f, gainmode %d\n",
4222 ninvmeas, minnummeas, *inttime, gainmode);
4223
4224 if ((ev = munki_trigger_one_measure(p, ninvmeas + minnummeas, inttime, gainmode, 0, 0))
4225 != MUNKI_OK) {
4226 return ev;
4227 }
4228
4229 if ((ev = munki_readmeasurement(p, ninvmeas + minnummeas, m->c_measmodeflags & MUNKI_MMF_SCAN,
4230 buf, bsize, nmeasuered, 0, 0)) != MUNKI_OK) {
4231 return ev;
4232 }
4233
4234 if (nmeasuered != NULL)
4235 *nmeasuered -= ninvmeas; /* Correct for invalid number */
4236
4237 return ev;
4238 }
4239
4240 /* Given a buffer full of raw USB values, process them into */
4241 /* completely processed spectral output patch readings. */
munki_read_patches_2(munki * p,double * duration,double ** specrd,int numpatches,double inttime,int gainmode,int ninvmeas,int nummeas,unsigned char * buf,unsigned int bsize)4242 munki_code munki_read_patches_2(
4243 munki *p,
4244 double *duration, /* Return flash duration in seconds */
4245 double **specrd, /* Return array [numpatches][nwav] of spectral reading values */
4246 int numpatches, /* Number of patches to return */
4247 double inttime, /* Integration time to used */
4248 int gainmode, /* Gain mode useed, 0 = normal, 1 = high */
4249 int ninvmeas, /* Number of extra invalid measurements at start */
4250 int nummeas, /* Number of actual measurements */
4251 unsigned char *buf, /* Raw USB reading buffer */
4252 unsigned int bsize /* Raw USB reading buffer size */
4253 ) {
4254 munki_code ev = MUNKI_OK;
4255 munkiimp *m = (munkiimp *)p->m;
4256 munki_state *s = &m->ms[m->mmode];
4257 double **multimes; /* Multiple measurement results [maxnummeas|nummeas][-1 nraw]*/
4258 double **absraw; /* Linearsised absolute sensor raw values [numpatches][-1 nraw]*/
4259 double *ledtemp; /* LED temperature values */
4260 double darkthresh; /* Dark threshold (for consistency checking) */
4261 int rv = 0;
4262
4263 if (duration != NULL)
4264 *duration = 0.0; /* default value */
4265
4266 /* Allocate temporaries */
4267 multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
4268 ledtemp = dvector(0, nummeas-1);
4269 absraw = dmatrix(0, numpatches-1, -1, m->nraw-1);
4270
4271 /* Take a buffer full of raw readings, and convert them to */
4272 /* floating point sensor readings. Check for saturation */
4273 if ((rv = munki_sens_to_raw(p, multimes, ledtemp, buf, ninvmeas, nummeas,
4274 m->satlimit, &darkthresh)) != MUNKI_OK) {
4275 free_dvector(ledtemp, 0, nummeas-1);
4276 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4277 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4278 return rv;
4279 }
4280
4281 /* Subtract the black from sensor values and convert to */
4282 /* absolute (integration & gain scaled), zero offset based, */
4283 /* linearized sensor values. */
4284 munki_sub_raw_to_absraw(p, nummeas, inttime, gainmode, multimes, s->dark_data,
4285 &darkthresh, 1, NULL);
4286
4287 #ifdef DUMP_SCANV
4288 /* Dump raw scan readings to a file "mkdump.txt" */
4289 {
4290 int i, j;
4291 FILE *fp;
4292
4293 if ((fp = fopen("mkdump.txt", "w")) == NULL)
4294 a1logw(p->log,"Unable to open debug file mkdump.txt\n");
4295 else {
4296 for (i = 0; i < nummeas; i++) {
4297 fprintf(fp, "%d ",i);
4298 for (j = 0; j < m->nraw; j++) {
4299 fprintf(fp, "%f ",multimes[i][j]);
4300 }
4301 fprintf(fp,"\n");
4302 }
4303 fclose(fp);
4304 }
4305 }
4306 #endif
4307
4308 #ifdef ENABLE_LEDTEMPC
4309
4310
4311 /* Do LED temperature compensation of absraw values */
4312 if (s->reflective) {
4313
4314 #ifdef PLOT_TEMPCOMP
4315 /* Plot the raw spectra, 6 at a time */
4316 {
4317 int i, j, k;
4318 double *indx;
4319 double **mod;
4320 indx = dvectorz(0, nummeas-1);
4321 mod = dmatrix(0, 5, 0, nummeas-1);
4322 for (i = 0; i < nummeas; i++)
4323 indx[i] = (double)i;
4324
4325 // for (j = 0; (j+5) < m->nraw; j += 6) {
4326 for (j = 50; (j+5) < 56; j += 6) {
4327 for (i = 0; i < nummeas; i++) {
4328 for (k = j; k < (j + 6); k++) {
4329 mod[k-j][i] = multimes[i][k];
4330 }
4331 }
4332
4333 printf("Before temp comp, bands %d - %d\n",j, j+5);
4334 do_plot6(indx, mod[0], mod[1], mod[2], mod[3], mod[4], mod[5], nummeas);
4335 }
4336 free_dvector(indx, 0, nummeas-1);
4337 free_dmatrix(mod, 0, 5, 0, nummeas-1);
4338 }
4339 #endif
4340 /* Do the LED temperature compensation */
4341 if ((ev = munki_ledtemp_comp(p, multimes, ledtemp, nummeas, s->reftemp, s->iwhite_data))
4342 != MUNKI_OK) {
4343 free_dvector(ledtemp, 0, nummeas-1);
4344 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4345 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4346 a1logd(p->log,3,"munki_read_patches_2 ledtemp comp failed\n");
4347 return ev;
4348 }
4349 #ifdef PLOT_TEMPCOMP
4350 /* Plot the raw spectra, 6 at a time */
4351 {
4352 int i, j, k;
4353 double *indx;
4354 double **mod;
4355 indx = dvectorz(0, nummeas-1);
4356 mod = dmatrix(0, 5, 0, nummeas-1);
4357 for (i = 0; i < nummeas; i++)
4358 indx[i] = (double)i;
4359
4360 // for (j = 0; (j+5) < m->nraw; j += 6) {
4361 for (j = 50; (j+5) < 56; j += 6) {
4362 for (i = 0; i < nummeas; i++) {
4363 for (k = j; k < (j + 6); k++) {
4364 mod[k-j][i] = multimes[i][k];
4365 }
4366 }
4367
4368 printf("After temp comp, bands %d - %d\n",j, j+5);
4369 do_plot6(indx, mod[0], mod[1], mod[2], mod[3], mod[4], mod[5], nummeas);
4370 }
4371 free_dvector(indx, 0, nummeas-1);
4372 free_dmatrix(mod, 0, 5, 0, nummeas-1);
4373 }
4374 #endif
4375 }
4376 #endif /* ENABLE_LEDTEMPC */
4377
4378
4379 if (!s->scan) {
4380 if (numpatches != 1) {
4381 free_dvector(ledtemp, 0, nummeas-1);
4382 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4383 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4384 a1logd(p->log,3,"munki_read_patches_2 spot read failed because numpatches != 1\n");
4385 return MUNKI_INT_WRONGPATCHES;
4386 }
4387
4388 /* Average a set of measurements into one. */
4389 /* Return zero if readings are consistent. */
4390 /* Return nz if the readings are not consistent */
4391 /* Return the overall average. */
4392 rv = munki_average_multimeas(p, absraw[0], multimes, nummeas, NULL, darkthresh);
4393 } else {
4394
4395 if (s->flash) {
4396
4397 if (numpatches != 1) {
4398 free_dvector(ledtemp, 0, nummeas-1);
4399 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4400 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4401 a1logd(p->log,3,"munki_read_patches_2 spot read failed because numpatches != 1\n");
4402 return MUNKI_INT_WRONGPATCHES;
4403 }
4404 if ((ev = munki_extract_patches_flash(p, &rv, duration, absraw[0], multimes,
4405 nummeas, inttime)) != MUNKI_OK) {
4406 free_dvector(ledtemp, 0, nummeas-1);
4407 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4408 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4409 a1logd(p->log,3,"munki_read_patches_2 spot read failed at munki_extract_patches_flash\n");
4410 return ev;
4411 }
4412
4413 } else {
4414 a1logd(p->log,3,"Number of patches to be measured = %d\n",nummeas);
4415
4416 /* Recognise the required number of ref/trans patch locations, */
4417 /* and average the measurements within each patch. */
4418 if ((ev = munki_extract_patches_multimeas(p, &rv, absraw, numpatches, multimes,
4419 nummeas, inttime)) != MUNKI_OK) {
4420 free_dvector(ledtemp, 0, nummeas-1);
4421 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4422 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4423 a1logd(p->log,3,"munki_read_patches_2 spot read failed at munki_extract_patches_multimeas\n");
4424 return ev;
4425 }
4426 }
4427 }
4428 free_dvector(ledtemp, 0, nummeas-1);
4429 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4430
4431 if (rv) {
4432 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4433 a1logd(p->log,3,"munki_read_patches_2 spot read failed with inconsistent readings\n");
4434 return MUNKI_RD_READINCONS;
4435 }
4436
4437 #ifdef ENABLE_REFSTRAYC /* Enable Reflective stray light compensation */
4438 if (s->reflective) {
4439 int i, j;
4440 # if defined(PLOT_DEBUG) || defined(DUMP_BKLED)
4441 double xx[140];
4442 double yy[3][140];
4443 # endif
4444
4445 double fact = REFSTRAYC_FACTOR; /* Slightly conservative */
4446
4447 for (i = 0; i < numpatches; i++) {
4448
4449 for (j = 0; j < m->nraw; j++) {
4450 # if defined(PLOT_DEBUG) || defined(DUMP_BKLED)
4451 yy[0][j] = absraw[i][j];
4452 # endif
4453 absraw[i][j] -= fact * s->white_data[j];
4454 # if defined(PLOT_DEBUG) || defined(DUMP_BKLED)
4455 xx[j] = j;
4456 yy[1][j] = fact * s->white_data[j];
4457 yy[2][j] = absraw[i][j];
4458 # endif
4459 }
4460 # ifdef PLOT_DEBUG
4461 printf("Before/After subtracting stray ref. light %d:\n",i);
4462 do_plot6(xx, yy[0], yy[1], yy[2], NULL, NULL, NULL, m->nraw);
4463 # endif
4464 # ifdef DUMP_BKLED /* Save REFSTRAYC & REFLEDNOISE comp plot to "refbk1.txt" & "refbk2.txt" */
4465 {
4466 xspect sp[3];
4467 for (i = 0; i < 3; i++) {
4468 sp[i].spec_n = 128;
4469 sp[i].spec_wl_short = 0.0;
4470 sp[i].spec_wl_long = 127.0;
4471 sp[i].norm = 1.0;
4472 for (j = 0; j < 128; j++)
4473 sp[i].spec[j] = yy[i][j];
4474 }
4475 write_nxspect("refbk2.txt", sp, 3, 0);
4476 # pragma message("######### munki DUMP_BKLED enabled! ########")
4477 }
4478 # endif /* DUMP_BKLED */
4479 }
4480 }
4481 #endif /* ENABLE_REFSTRAYC */
4482
4483 /* Convert an absraw array from raw wavelengths to output wavelenths */
4484 munki_absraw_to_abswav(p, numpatches, specrd, absraw);
4485 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4486
4487 #ifdef APPEND_MEAN_EMMIS_VAL
4488 /* Append averaged emission reading to file "mkdump.txt" */
4489 {
4490 int i, j;
4491 FILE *fp;
4492
4493 /* Create wavelegth label */
4494 if ((fp = fopen("mkdump.txt", "r")) == NULL) {
4495 if ((fp = fopen("mkdump.txt", "w")) == NULL)
4496 a1logw(p->log,"Unable to reate debug file mkdump.txt\n");
4497 else {
4498 for (j = 0; j < m->nwav; j++)
4499 fprintf(fp,"%f ",XSPECT_WL(m->wl_short, m->wl_long, m->nwav, j));
4500 fprintf(fp,"\n");
4501 fclose(fp);
4502 }
4503 }
4504 if ((fp = fopen("mkdump.txt", "a")) == NULL)
4505 a1logw(p->log,"Unable to open debug file mkdump.txt\n");
4506 else {
4507 for (j = 0; j < m->nwav; j++)
4508 fprintf(fp, "%f ",specrd[0][j] * m->emis_coef[j]);
4509 fprintf(fp,"\n");
4510 fclose(fp);
4511 }
4512 }
4513 #endif
4514
4515 #ifdef PLOT_DEBUG
4516 printf("Converted to wavelengths:\n");
4517 plot_wav(m, specrd[0]);
4518 #endif
4519
4520 /* Scale to the calibrated output values */
4521 munki_scale_specrd(p, specrd, numpatches, specrd);
4522
4523 #ifdef PLOT_DEBUG
4524 printf("Calibrated measuerment spectra:\n");
4525 plot_wav(m, specrd[0]);
4526 #endif
4527
4528 return ev;
4529 }
4530
4531 /* Given a buffer full of raw USB values, process them into */
4532 /* completely processed spectral output readings, */
4533 /* but don't average together or extract patches or flash. */
4534 /* This is used for delay & refresh rate measurement. */
4535 /* !! Doesn't do LED temperature compensation for reflective !! */
4536 /* (! Note that we aren't currently detecting saturation here!) */
munki_read_patches_2a(munki * p,double ** specrd,int numpatches,double inttime,int gainmode,unsigned char * buf,unsigned int bsize)4537 munki_code munki_read_patches_2a(
4538 munki *p,
4539 double **specrd, /* Return array [numpatches][nwav] of spectral reading values */
4540 int numpatches, /* Number of patches measured and to return */
4541 double inttime, /* Integration time to used */
4542 int gainmode, /* Gain mode useed, 0 = normal, 1 = high */
4543 unsigned char *buf, /* Raw USB reading buffer */
4544 unsigned int bsize
4545 ) {
4546 munki_code ev = MUNKI_OK;
4547 munkiimp *m = (munkiimp *)p->m;
4548 munki_state *s = &m->ms[m->mmode];
4549 double **absraw; /* Linearsised absolute sensor raw values [numpatches][-1 nraw]*/
4550 double *ledtemp; /* LED temperature values */
4551 double satthresh; /* Saturation threshold */
4552 double darkthresh; /* Dark threshold for consistency scaling limit */
4553
4554 /* Allocate temporaries */
4555 absraw = dmatrix(0, numpatches-1, -1, m->nraw-1);
4556 ledtemp = dvector(0, numpatches-1);
4557
4558 /* Take a buffer full of raw readings, and convert them to */
4559 /* floating point sensor readings. Check for saturation */
4560 if ((ev = munki_sens_to_raw(p, absraw, ledtemp, buf, 0, numpatches,
4561 m->satlimit, &darkthresh)) != MUNKI_OK) {
4562 free_dvector(ledtemp, 0, numpatches-1);
4563 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4564 return ev;
4565 }
4566
4567 /* Subtract the black from sensor values and convert to */
4568 /* absolute (integration & gain scaled), zero offset based, */
4569 /* linearized sensor values. */
4570 munki_sub_raw_to_absraw(p, numpatches, inttime, gainmode, absraw, s->dark_data,
4571 &darkthresh, 1, NULL);
4572
4573 a1logd(p->log,3,"Number of patches measured = %d\n",numpatches);
4574
4575 /* Convert an absraw array from raw wavelengths to output wavelenths */
4576 munki_absraw_to_abswav(p, numpatches, specrd, absraw);
4577
4578 free_dvector(ledtemp, 0, numpatches-1);
4579 free_dmatrix(absraw, 0, numpatches-1, -1, m->nraw-1);
4580
4581 #ifdef PLOT_DEBUG
4582 printf("Converted to wavelengths:\n");
4583 plot_wav(m, specrd[0]);
4584 #endif
4585
4586 /* Scale to the calibrated output values */
4587 munki_scale_specrd(p, specrd, numpatches, specrd);
4588
4589 #ifdef PLOT_DEBUG
4590 printf("Calibrated measuerment spectra:\n");
4591 plot_wav(m, specrd[0]);
4592 #endif
4593
4594 return ev;
4595 }
4596
4597 /* Take a measurement reading using the current mode (combined parts 1 & 2a) */
4598 /* Converts to completely processed output readings, without averaging or extracting */
4599 /* sample patches, for emissive measurement mode. */
4600 /* This is used for delay & refresh rate measurement. */
munki_read_patches_all(munki * p,double ** specrd,int numpatches,double * inttime,int gainmode)4601 munki_code munki_read_patches_all(
4602 munki *p,
4603 double **specrd, /* Return array [numpatches][nwav] of spectral reading values */
4604 int numpatches, /* Number of sample to measure */
4605 double *inttime, /* Integration time to use/used */
4606 int gainmode /* Gain mode to use, 0 = normal, 1 = high */
4607 ) {
4608 munki_code ev = MUNKI_OK;
4609 munkiimp *m = (munkiimp *)p->m;
4610 munki_state *s = &m->ms[m->mmode];
4611 unsigned char *buf; /* Raw USB reading buffer */
4612 unsigned int bsize;
4613 int rv = 0;
4614
4615 bsize = m->nsen * 2 * numpatches;
4616 if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
4617 a1logd(p->log,1,"munki_read_patches malloc %d bytes failed (11)\n",bsize);
4618 return MUNKI_INT_MALLOC;
4619 }
4620
4621 /* Trigger measure and gather raw readings */
4622 if ((ev = munki_read_patches_1(p, 0, numpatches, numpatches, inttime, gainmode,
4623 NULL, buf, bsize)) != MUNKI_OK) {
4624 free(buf);
4625 return ev;
4626 }
4627
4628 /* Process the raw readings without averaging or extraction */
4629 if ((ev = munki_read_patches_2a(p, specrd, numpatches, *inttime, gainmode,
4630 buf, bsize)) != MUNKI_OK) {
4631 free(buf);
4632 return ev;
4633 }
4634 free(buf);
4635 return ev;
4636 }
4637
4638 /* Take a trial emission measurement reading using the current mode. */
4639 /* Used to determine if sensor is saturated, or not optimal */
4640 /* in adaptive emission mode. */
munki_trialmeasure(munki * p,int * saturated,double * optscale,int nummeas,double * inttime,int gainmode,double targoscale)4641 munki_code munki_trialmeasure(
4642 munki *p,
4643 int *saturated, /* Return nz if sensor is saturated */
4644 double *optscale, /* Return scale for gain/int time to make optimal (may be NULL) */
4645 int nummeas, /* Number of readings to take */
4646 double *inttime, /* Integration time to use/used */
4647 int gainmode, /* Gain mode to use, 0 = normal, 1 = high */
4648 double targoscale /* Ratio of optimal sensor value to aim for */
4649 ) {
4650 munki_code ev = MUNKI_OK;
4651 munkiimp *m = (munkiimp *)p->m;
4652 munki_state *s = &m->ms[m->mmode];
4653 unsigned char *buf; /* Raw USB reading buffer */
4654 unsigned int bsize;
4655 double **multimes; /* Multiple measurement results */
4656 double *absraw; /* Linearsised absolute sensor raw values */
4657 int nmeasuered; /* Number actually measured */
4658 double sensavg; /* Overall average of sensor readings */
4659 double darkthresh; /* Dark threshold */
4660 double trackmax[2]; /* Track optimum target */
4661 double maxval; /* Maximum multimeas value */
4662 int rv;
4663
4664 if (s->reflective) {
4665 a1logw(p->log, "munki_trialmeasure: Assert - not meant to be used for reflective read!\n");
4666 return MUNKI_INT_ASSERT;
4667 }
4668
4669 if (nummeas <= 0)
4670 return MUNKI_INT_ZEROMEASURES;
4671
4672 /* Allocate up front to avoid delay between trigger and read */
4673 bsize = m->nsen * 2 * nummeas;
4674 if ((buf = (unsigned char *)malloc(sizeof(unsigned char) * bsize)) == NULL) {
4675 a1logd(p->log,1,"munki_trialmeasure malloc %d bytes failed (12)\n",bsize);
4676 return MUNKI_INT_MALLOC;
4677 }
4678 multimes = dmatrix(0, nummeas-1, -1, m->nraw-1);
4679 absraw = dvector(-1, m->nraw-1);
4680
4681 a1logd(p->log,3,"Triggering measurement cycle, nummeas %d, inttime %f, gainmode %d\n",
4682 nummeas, *inttime, gainmode);
4683
4684 if ((ev = munki_trigger_one_measure(p, nummeas, inttime, gainmode, 1, 0)) != MUNKI_OK) {
4685 free_dvector(absraw, -1, m->nraw-1);
4686 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4687 free(buf);
4688 return ev;
4689 }
4690
4691 a1logd(p->log,3,"Gathering readings\n");
4692 if ((ev = munki_readmeasurement(p, nummeas, m->c_measmodeflags & MUNKI_MMF_SCAN,
4693 buf, bsize, &nmeasuered, 1, 0)) != MUNKI_OK) {
4694 free_dvector(absraw, -1, m->nraw-1);
4695 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4696 free(buf);
4697 return ev;
4698 }
4699
4700 if (saturated != NULL) /* Initialize return flag */
4701 *saturated = 0;
4702
4703 /* Take a buffer full of raw readings, and convert them to */
4704 /* floating point sensor readings. Check for saturation */
4705 if ((rv = munki_sens_to_raw(p, multimes, NULL, buf, 0, nmeasuered, m->satlimit,
4706 &darkthresh)) != MUNKI_OK) {
4707 if (rv != MUNKI_RD_SENSORSATURATED) {
4708 free_dvector(absraw, -1, m->nraw-1);
4709 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4710 free(buf);
4711 return rv;
4712 }
4713 if (saturated != NULL)
4714 *saturated = 1;
4715 }
4716 free(buf);
4717
4718 /* Comute dark subtraction for this trial's parameters */
4719 if ((ev = munki_interp_dark(p, s->dark_data, *inttime, gainmode)) != MUNKI_OK) {
4720 free_dvector(absraw, -1, m->nraw-1);
4721 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1);
4722 a1logd(p->log,3,"munki_imp_measure interplate dark ref failed\n");
4723 return ev;
4724 }
4725
4726 trackmax[0] = darkthresh; /* Track dark threshold */
4727 trackmax[1] = m->optsval; /* Track the optimal sensor target value */
4728
4729 /* Subtract the black from sensor values and convert to */
4730 /* absolute (integration & gain scaled), zero offset based, */
4731 /* linearized sensor values. */
4732 /* Return the highest individual element. */
4733 munki_sub_raw_to_absraw(p, nmeasuered, *inttime, gainmode, multimes, s->dark_data,
4734 trackmax, 2, &maxval);
4735 darkthresh = trackmax[0];
4736
4737 /* Average a set of measurements into one. */
4738 /* Return nz if the readings are not consistent */
4739 /* Return the overall average. */
4740 rv = munki_average_multimeas(p, absraw, multimes, nmeasuered, &sensavg, darkthresh);
4741
4742 /* Ignore iconsistent readings ?? */
4743
4744 #ifdef PLOT_DEBUG
4745 printf("Average absolute sensor readings, average = %f, max %f\n",sensavg,maxval);
4746 plot_raw(absraw);
4747 #endif
4748
4749 if (optscale != NULL) {
4750 double opttarget; /* Optimal sensor target */
4751
4752 opttarget = targoscale * trackmax[1];
4753 if (maxval < 0.01) /* Could go -ve */
4754 maxval = 0.01;
4755 *optscale = opttarget/ maxval;
4756 a1logd(p->log,4,"Targscale %f, maxval %f, optimal target = %f, amount to scale = %f\n",
4757 targoscale, maxval, opttarget, *optscale);
4758 }
4759
4760 free_dvector(absraw, -1, m->nraw-1);
4761 free_dmatrix(multimes, 0, nummeas-1, -1, m->nraw-1); /* Free after using *pmax */
4762
4763 return ev;
4764 }
4765
4766 /* Trigger a single measurement cycle. This could be a dark calibration, */
4767 /* a calibration, or a real measurement. This is used to create the */
4768 /* higher level "calibrate" and "take reading" functions. */
4769 /* The setup for the operation is in the current mode state. */
4770 /* Call munki_readmeasurement() to collect the results */
4771 munki_code
munki_trigger_one_measure(munki * p,int nummeas,double * inttime,int gainmode,int calib_measure,int dark_measure)4772 munki_trigger_one_measure(
4773 munki *p,
4774 int nummeas, /* Minimum number of measurements to make */
4775 double *inttime, /* Integration time to use/used */
4776 int gainmode, /* Gain mode to use, 0 = normal, 1 = high */
4777 int calib_measure, /* flag - nz if this is a calibration measurement */
4778 int dark_measure /* flag - nz if this is a dark measurement */
4779 ) {
4780 munki_code ev = MUNKI_OK;
4781 munkiimp *m = (munkiimp *)p->m;
4782 munki_state *s = &m->ms[m->mmode];
4783 double dintclocks;
4784 int intclocks; /* Number of integration clocks */
4785 int measmodeflags; /* Measurement mode command flags */
4786 int holdtempduty; /* Hold temperature duty cycle */
4787
4788 /* Compute integration clocks. Take account of (seeming) dead integration time */
4789 dintclocks = floor((*inttime)/m->intclkp + 0.5);
4790 intclocks = (int)dintclocks;
4791 *inttime = (double)intclocks * m->intclkp; /* Quantized integration time */
4792
4793 /* Create measurement mode flag byte for this operation */
4794 measmodeflags = 0;
4795 if (s->scan && !calib_measure)
4796 measmodeflags |= MUNKI_MMF_SCAN; /* Never scan on a calibration */
4797 if (s->reflective && !dark_measure)
4798 measmodeflags |= MUNKI_MMF_LAMP; /* Need lamp if reflective and not dark measure */
4799 if (gainmode == 1)
4800 measmodeflags |= MUNKI_MMF_HIGHGAIN; /* High gain mode */
4801 holdtempduty = m->ledholdtempdc; /* From the EEProm value */
4802
4803 /* Trigger a measurement */
4804 if ((ev = munki_triggermeasure(p, intclocks, nummeas, measmodeflags, holdtempduty)) != MUNKI_OK)
4805 return ev;
4806
4807 m->c_measmodeflags = measmodeflags;
4808
4809 m->c_inttime = *inttime;
4810
4811 return ev;
4812 }
4813
4814 /* ============================================================ */
4815 /* lower level reading processing and computation */
4816
4817 /* Take a buffer full of raw readings, and convert them to */
4818 /* directly to floating point raw values. Return MUNKI_RD_SENSORSATURATED if any is saturated */
4819 /* (No black subtraction or linearization is performed) */
munki_sens_to_raw(munki * p,double ** raw,double * ledtemp,unsigned char * buf,int ninvalid,int nummeas,double satthresh,double * pdarkthresh)4820 munki_code munki_sens_to_raw(
4821 munki *p,
4822 double **raw, /* Array of [nummeas-ninvalid][-1 nraw] value to return */
4823 double *ledtemp, /* Optional array [nummeas-ninvalid] LED temperature values to return */
4824 unsigned char *buf, /* Raw measurement data must be 274 * nummeas */
4825 int ninvalid, /* Number of initial invalid readings to skip */
4826 int nummeas, /* Number of readings measured */
4827 double satthresh, /* Saturation threshold to trigger error in raw units (if > 0.0) */
4828 double *pdarkthresh /* Return a dark threshold value = sheilded cell values */
4829 ) {
4830 munkiimp *m = (munkiimp *)p->m;
4831 int i, j, k;
4832 unsigned char *bp;
4833 double maxval = -1e38;
4834 double darkthresh = 0.0;
4835 double ndarkthresh = 0.0;
4836 int sskip = 2 * 6; /* Bytes to skip at start */
4837 int eskip = 2 * 3; /* Bytes to skip at end */
4838
4839 if ((m->nraw * 2 + sskip + eskip) != (m->nsen * 2)) {
4840 a1logw(p->log,"NRAW %d and NRAWB %d don't match!\n",m->nraw,m->nsen * 2);
4841 return MUNKI_INT_ASSERT;
4842 }
4843
4844 if (ninvalid > 0)
4845 a1logd(p->log, 4, "munki_sens_to_raw: Skipping %d invalid readings\n",ninvalid);
4846
4847 /* Now process the buffer values */
4848 for (bp = buf + ninvalid * m->nsen * 2, i = 0; i < nummeas; i++, bp += eskip) {
4849
4850 /* The first 4 readings are shielded cells, and we use them as an */ /* estimate of the dark reading consistency, as well as for */
4851 /* compensating the dark level calibration for any temperature changes. */
4852
4853 /* raw average of all measurement shielded cell values */
4854 for (k = 0; k < 4; k++) {
4855 darkthresh += (double)buf2ushort(bp + k * 2);
4856 ndarkthresh++;
4857 }
4858
4859 /* raw of shielded cells per reading */
4860 raw[i][-1] = 0.0;
4861 for (k = 0; k < 4; k++) {
4862 raw[i][-1] += (double)buf2ushort(bp + k * 2);
4863 }
4864 raw[i][-1] /= 4.0;
4865
4866 /* The LED voltage drop is the last 16 bits in each reading */
4867 if (ledtemp != NULL)
4868 ledtemp[i] = (double)buf2ushort(bp + (m->nsen * 2) - 2);
4869
4870 /* The 128 raw spectral values */
4871 for (bp += sskip, j = 0; j < m->nraw; j++, bp += 2) {
4872 unsigned int rval;
4873 double fval;
4874
4875 rval = buf2ushort(bp);
4876 fval = (double)rval;
4877 raw[i][j] = fval;
4878 // printf("~1 i = %d, j = %d, addr % 274 = %d, val = %f\n",i,j,(bp - buf) % 274, fval);
4879
4880 if (fval > maxval)
4881 maxval = fval;
4882 }
4883 }
4884
4885 /* Check if any are over saturation threshold */
4886 if (satthresh > 0.0) {
4887 if (maxval > satthresh) {
4888 a1logd(p->log,4,"munki_sens_to_raw: Max sens %f > satthresh %f\n",maxval,satthresh);
4889 return MUNKI_RD_SENSORSATURATED;
4890 }
4891 a1logd(p->log,4,"munki_sens_to_raw: Max sens %f < satthresh %f\n",maxval,satthresh);
4892 }
4893
4894 darkthresh /= ndarkthresh;
4895 if (pdarkthresh != NULL)
4896 *pdarkthresh = darkthresh;
4897 a1logd(p->log,3,"munki_sens_to_raw: Dark thrheshold = %f\n",darkthresh);
4898
4899 return MUNKI_OK;
4900 }
4901
4902 /* Subtract the black from raw values and convert to */
4903 /* absolute (integration & gain scaled), zero offset based, */
4904 /* linearized raw values. */
4905 /* Return the highest individual element. */
munki_sub_raw_to_absraw(munki * p,int nummeas,double inttime,int gainmode,double ** absraw,double * sub,double * trackmax,int ntrackmax,double * maxv)4906 void munki_sub_raw_to_absraw(
4907 munki *p,
4908 int nummeas, /* Return number of readings measured */
4909 double inttime, /* Integration time used */
4910 int gainmode, /* Gain mode, 0 = normal, 1 = high */
4911 double **absraw, /* Source/Desination array [-1 nraw] */
4912 double *sub, /* Value to subtract [-1 nraw] (ie. cal dark data) */
4913 double *trackmax, /* absraw values that should be offset the same as max */
4914 int ntrackmax, /* Number of trackmax values */
4915 double *maxv /* If not NULL, return the maximum value */
4916 ) {
4917 munkiimp *m = (munkiimp *)p->m;
4918 munki_state *s = &m->ms[m->mmode];
4919 int npoly; /* Number of linearisation coefficients */
4920 double *polys; /* the coeficients */
4921 double scale; /* Absolute scale value */
4922 double submax = -1e6; /* Subtraction value maximum */
4923 double asub[NSEN_MAX];
4924 double avgscell, zero;
4925 double rawmax, maxval = -1e38;
4926 double maxzero = 0.0;
4927 int i, j, k;
4928
4929 /* Heusristic correction for LED interference bump for 0.018 secs int_time */
4930 int pos[] = { 0, 20, 56, 62, 75, 127 };
4931 // double off[] = { 0.7, 0.0, 0.6, -0.9, -1.2, -0.7 };
4932 double off[] = { 0.7, 0.0, 0.6, -0.9, -0.8, -0.5 };
4933
4934 if (gainmode) { /* High gain */
4935 npoly = m->nlin1; /* Encodes gain too */
4936 polys = m->lin1;
4937 } else { /* Low gain */
4938 npoly = m->nlin0;
4939 polys = m->lin0;
4940 }
4941 scale = 1.0/inttime;
4942
4943 /* Adjust black to allow for temperature change by using the */
4944 /* shielded cell values as a reference. */
4945 /* We use a heuristic to compute a zero based scale for adjusting the */
4946 /* black. It's not clear why it works best this way, or how */
4947 /* dependent on the particular instrument the magic numbers are, */
4948 /* but it reduces the black level error from over 10% to about 0.3% */
4949
4950 /* Locate largest of black */
4951 for (j = 0; j < m->nraw; j++) {
4952 if (sub[j] > submax)
4953 submax = sub[j];
4954 }
4955
4956 /* Average the shielded cell value of all the readings */
4957 avgscell = 0.0;
4958 for (i = 0; i < nummeas; i++)
4959 avgscell += absraw[i][-1];
4960 avgscell /= (double)nummeas;
4961
4962 /* Compute scaling zero */
4963 zero = 1.08 * 0.5 * (avgscell + sub[-1]);
4964
4965 /* make sure that the zero point is above any black value */
4966 if (zero < (1.005 * avgscell))
4967 zero = 1.005 * avgscell;
4968 if (zero < (1.005 * sub[-1]))
4969 zero = 1.005 * sub[-1];
4970 if (zero < (1.005 * submax))
4971 zero = 1.005 * submax;
4972
4973 a1logd(p->log,4,"Black shielded value = %f, Reading shielded value = %f\n",sub[-1], avgscell);
4974
4975 /* Compute the adjusted black for each band */
4976 if (s->reflective) {
4977
4978 /* It seems that having the LED on shifts the shielded cell values */
4979 /* by about 2.5, and this stuffs up the reflective measurement. */
4980 /* This seems to be from the LED PWM driver, which perhaps */
4981 /* is synchronous to the sensor clock, and so switches */
4982 /* at a certain point in the transfer of data from the sensor. */
4983 /* The result is a step up from 0-60, and then down from 61-128. */
4984 /* Perhaps altering the LED PWM setting and seeing if this point */
4985 /* shifts would be a way of confirming this ? */
4986 /* There is also some stray light reflected into the sensor */
4987 /* from the LED, but due to the LED step, the sensor reading is */
4988 /* less than the dark data at some wavelengths. */
4989 /* The details of the LED step seem to be integration time dependent, */
4990 /* but decresing the scanning rate therebye increasing integration */
4991 /* time and light level reduces the impact of this error. */
4992
4993 /* Since we do an on the fly black measurement before each */
4994 /* reflective measurement, ignoring the shielded cell values */
4995 /* shouldn't affect accuracy so much. */
4996
4997 #ifdef ENABLE_REFLEDINTER
4998 /* A heuristic to correct for the LED noise. */
4999 /* This is only valid for int_time of 0.0182 secs, */
5000 /* and it's not clear how well it works across different */
5001 /* temperatures or examples of the ColorMunki. */
5002 /* in another revision ?? */
5003 for (j = 0; j < m->nraw; j++) {
5004 int ix;
5005 double bl, val;
5006
5007 for (ix = 0; ; ix++) {
5008 if (j >= pos[ix] && j <= pos[ix+1])
5009 break;
5010 }
5011 bl = (j - pos[ix])/((double)pos[ix+1] - pos[ix]);
5012 val = (1.0 - bl) * off[ix] + bl * off[ix+1];
5013 asub[j] = sub[j] + val;
5014 }
5015 #else
5016 for (j = 0; j < m->nraw; j++)
5017 asub[j] = sub[j]; /* Just use the calibration dark data */
5018 #endif
5019
5020 } else {
5021 /* No LED on operation - use sheilded cell values */
5022 for (j = 0; j < m->nraw; j++) {
5023 #ifdef ENABLE_BKDRIFTC
5024
5025 # ifdef HEURISTIC_BKDRIFTC
5026 /* heuristic scaled correction */
5027 asub[j] = zero - (zero - sub[j]) * (zero - avgscell)/(zero - sub[-1]);
5028 # else
5029 /* simple additive correction */
5030 # pragma message("######### munki Simple shielded cell temperature correction! ########")
5031 asub[j] = sub[j] + (avgscell - sub[-1]);
5032 # endif
5033 #else
5034 # pragma message("######### munki No shielded cell temperature correction! ########")
5035 asub[j] = sub[j]; /* Just use the calibration dark data */
5036 #endif
5037 }
5038 }
5039
5040 #if defined(PLOT_DEBUG) || defined(DUMP_BKLED)
5041 {
5042 double xx[130];
5043 double yy[3][130];
5044
5045 for (j = -1; j < m->nraw+1; j++)
5046 yy[0][j+1] = 0.0;
5047
5048 for (i = 0; i < nummeas; i++) {
5049 for (j = -1; j < m->nraw; j++)
5050 yy[0][j+1] += absraw[i][j];
5051 }
5052 for (j = -1; j < m->nraw; j++)
5053 yy[0][j+1] /= (double)nummeas;
5054
5055 for (j = -1; j < m->nraw; j++)
5056 yy[1][j+1]= sub[j];
5057
5058 /* Show what ENABLE_REFLEDINTER would do */
5059 for (j = 0; j < m->nraw; j++) {
5060 int ix;
5061 double bl, val;
5062
5063 for (ix = 0; ; ix++) {
5064 if (j >= pos[ix] && j <= pos[ix+1])
5065 break;
5066 }
5067 bl = (j - pos[ix])/((double)pos[ix+1] - pos[ix]);
5068 val = (1.0 - bl) * off[ix] + bl * off[ix+1];
5069 yy[2][j+1] = yy[0][j+1] - val;
5070 }
5071 yy[2][0] = yy[0][0];
5072
5073 for (j = -1; j < m->nraw; j++)
5074 xx[j+1] = (double)j;
5075
5076 xx[0]= -10.0;
5077
5078 # ifdef PLOT_DEBUG
5079 printf("sub_raw_to_absraw %d samp avg - dark ref:\n",nummeas);
5080 do_plot(xx, yy[0], yy[1], yy[2], 129);
5081 # endif
5082 # ifdef DUMP_BKLED
5083 {
5084 xspect sp[3];
5085 for (i = 0; i < 3; i++) {
5086 sp[i].spec_n = 128;
5087 sp[i].spec_wl_short = 0.0;
5088 sp[i].spec_wl_long = 127.0;
5089 sp[i].norm = 1.0;
5090 for (j = 0; j < 128; j++)
5091 sp[i].spec[j] = yy[i][j+1];
5092 }
5093 write_nxspect("refbk1.txt", sp, 3, 0);
5094 }
5095 # endif /* DUMP_BKLED */
5096 }
5097 #endif /* PLOT_DEBUG || DUMP_BKLED */
5098
5099 /* For each measurement */
5100 for (i = 0; i < nummeas; i++) {
5101 double rval, sval, lval;
5102
5103 for (j = 0; j < m->nraw; j++) {
5104
5105 rval = absraw[i][j];
5106
5107 sval = rval - asub[j]; /* Make zero based */
5108
5109 #ifdef ENABLE_NONLINCOR
5110 /* Linearise */
5111 for (lval = polys[npoly-1], k = npoly-2; k >= 0; k--)
5112 lval = lval * sval + polys[k];
5113 #else
5114 lval = sval;
5115 #endif
5116 lval *= scale;
5117 absraw[i][j] = lval;
5118
5119 /* Track the maximum value and the black that was subtracted from it */
5120 if (lval > maxval) {
5121 maxval = lval;
5122 rawmax = rval;
5123 maxzero = asub[j];
5124 if (maxv != NULL)
5125 *maxv = absraw[i][j];
5126 }
5127 }
5128 }
5129
5130 /* Process the "tracked to max" values too */
5131 if (ntrackmax > 0 && trackmax != NULL) {
5132 for (i = 0; i < ntrackmax; i++) {
5133 double rval, fval, lval;
5134
5135 rval = trackmax[i];
5136 fval = rval - maxzero;
5137
5138 #ifdef ENABLE_NONLINCOR
5139 /* Linearise */
5140 for (lval = polys[npoly-1], k = npoly-2; k >= 0; k--)
5141 lval = lval * fval + polys[k];
5142 #else
5143 lval = fval;
5144 #endif
5145 lval *= scale;
5146 trackmax[i] = lval;
5147 // printf("~1 trackmax[%d] = %f, maxzero = %f\n",i,lval,maxzero);
5148 }
5149 }
5150 }
5151
5152 /* Average a set of sens or absens measurements into one. */
5153 /* (Make sure darkthresh is tracked if absens is being averaged!) */
5154 /* Return zero if readings are consistent and not saturated. */
5155 /* Return nz if the readings are not consistent */
5156 /* Return the overall average. */
munki_average_multimeas(munki * p,double * avg,double ** multimeas,int nummeas,double * poallavg,double darkthresh)5157 int munki_average_multimeas(
5158 munki *p,
5159 double *avg, /* return average [-1 nraw] */
5160 double **multimeas, /* Array of [nummeas][-1 nraw] value to average */
5161 int nummeas, /* number of readings to be averaged */
5162 double *poallavg, /* If not NULL, return overall average of bands and measurements */
5163 double darkthresh /* Dark threshold (used for consistency check scaling) */
5164 ) {
5165 munkiimp *m = (munkiimp *)p->m;
5166 int i, j;
5167 double oallavg = 0.0;
5168 double maxavg = -1e38; /* Track min and max averages of readings */
5169 double minavg = 1e38;
5170 double norm;
5171 int rv = 0;
5172
5173 a1logd(p->log,3,"munki_average_multimeas %d readings (darkthresh %f)\n",nummeas,darkthresh);
5174
5175 for (j = -1; j < m->nraw; j++)
5176 avg[j] = 0.0;
5177
5178 /* Now process the buffer values */
5179 for (i = 0; i < nummeas; i++) {
5180 double measavg = 0.0;
5181
5182 avg[-1] += multimeas[i][-1]; /* shielded cell value */
5183
5184 for (j = 0; j < m->nraw; j++) {
5185 double val;
5186
5187 val = multimeas[i][j];
5188
5189 measavg += val;
5190 avg[j] += val;
5191 }
5192 measavg /= (double)m->nraw;
5193 oallavg += measavg;
5194 if (measavg < minavg)
5195 minavg = measavg;
5196 if (measavg > maxavg)
5197 maxavg = measavg;
5198 }
5199
5200 for (j = -1; j < m->nraw; j++)
5201 avg[j] /= (double)nummeas;
5202 oallavg /= (double)nummeas;
5203
5204 if (poallavg != NULL)
5205 *poallavg = oallavg;
5206
5207 norm = fabs(0.5 * (maxavg+minavg));
5208 darkthresh = fabs(darkthresh);
5209 if (darkthresh < DARKTHSCAMIN)
5210 darkthresh = DARKTHSCAMIN;
5211 a1logd(p->log,3,"norm = %f, dark thresh = %f\n",norm,darkthresh);
5212 if (norm < (2.0 * darkthresh))
5213 norm = 2.0 * darkthresh;
5214
5215 a1logd(p->log,4,"avg_multi: overall avg = %f, minavg = %f, maxavg = %f, variance %f, THR %f (darkth %f)\n",
5216 oallavg,minavg,maxavg,(maxavg - minavg)/norm, PATCH_CONS_THR,darkthresh);
5217 if ((maxavg - minavg)/norm > PATCH_CONS_THR) {
5218 rv |= 1;
5219 }
5220 return rv;
5221 }
5222
5223 /* Minimum number of scan samples in a patch */
5224 #define MIN_SAMPLES 2
5225
5226 /* Range of bands to detect transitions */
5227 #define BL 5 /* Start */
5228 #define BH 105 /* End */
5229 #define BW 5 /* Width */
5230
5231 /* Record of possible patch within a reading buffer */
5232 typedef struct {
5233 int ss; /* Start sample index */
5234 int no; /* Number of samples */
5235 int use; /* nz if patch is to be used */
5236 } munki_patch;
5237
5238 /* Recognise the required number of ref/trans patch locations, */
5239 /* and average the measurements within each patch. */
5240 /* *flags returns zero if readings are consistent. */
5241 /* *flags returns nz if the readings are not consistent */
5242 /* (Doesn't extract [-1] shielded values, since they have already been used) */
munki_extract_patches_multimeas(munki * p,int * flags,double ** pavg,int tnpatch,double ** multimeas,int nummeas,double inttime)5243 munki_code munki_extract_patches_multimeas(
5244 munki *p,
5245 int *flags, /* return flags */
5246 double **pavg, /* return patch average [naptch][-1 nraw] */
5247 int tnpatch, /* Target number of patches to recognise */
5248 double **multimeas, /* Array of [nummeas][-1 nraw] value to extract from */
5249 int nummeas, /* number of readings made */
5250 double inttime /* Integration time (used to adjust consistency threshold) */
5251 ) {
5252 munkiimp *m = (munkiimp *)p->m;
5253 int i, j, k, pix;
5254 double **sslope; /* Signed difference between i and i+1 */
5255 double *slope; /* Accumulated absolute difference between i and i+1 */
5256 double *fslope; /* Filtered slope */
5257 munki_patch *pat; /* Possible patch information */
5258 int npat, apat = 0;
5259 double *maxval; /* Maximum input value for each wavelength */
5260 double fmaxslope = 0.0;
5261 double maxslope = 0.0;
5262 double minslope = 1e38;
5263 double thresh = 0.4; /* Slope threshold */
5264 int try; /* Thresholding try */
5265 double avglegth; /* Average length of patches */
5266 int *sizepop; /* Size popularity of potential patches */
5267 double median; /* median potential patch width */
5268 double window; /* +/- around median to accept */
5269 double white_avg; /* Average of (aproximate) white data */
5270 int rv = 0;
5271 double patch_cons_thr = PATCH_CONS_THR * m->scan_toll_ratio;
5272 #ifdef PLOT_PATREC
5273 double **plot;
5274 #endif
5275
5276 a1logd(p->log,3,"munki_extract_patches_multimeas: looking for %d patches out of %d samples\n",tnpatch,nummeas);
5277
5278 maxval = dvectorz(-1, m->nraw-1);
5279
5280 /* Loosen consistency threshold for short intergation time */
5281 if (inttime < 0.012308) /* Smaller than Rev A minimum int. time */
5282 patch_cons_thr *= sqrt(0.012308/inttime);
5283
5284 /* Discover the maximum input value for normalisation */
5285 for (j = 0; j < m->nraw; j ++) {
5286 for (i = 0; i < nummeas; i++) {
5287 if (multimeas[i][j] > maxval[j])
5288 maxval[j] = multimeas[i][j];
5289 }
5290 if (maxval[j] < 1.0)
5291 maxval[j] = 1.0;
5292 }
5293
5294 #ifdef PLOT_PATREC
5295 /* Plot out 6 lots of 6 values each */
5296 plot = dmatrixz(0, 6, 0, nummeas-1);
5297 // for (j = 1; j < (m->nraw-6); j += 6) /* Plot all the bands */
5298 // for (j = 45; j < (m->nraw-6); j += 100) /* Do just one band */
5299 for (j = 5; j < (m->nraw-6); j += 30) { /* Do four bands */
5300 for (k = 0; k < 6; k ++) {
5301 for (i = 0; i < nummeas; i++) {
5302 plot[k][i] = multimeas[i][j+k]/maxval[j+k];
5303 }
5304 }
5305 for (i = 0; i < nummeas; i++)
5306 plot[6][i] = (double)i;
5307 printf("Bands %d - %d\n",j,j+5);
5308 do_plot6(plot[6], plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], nummeas);
5309 }
5310 #endif
5311
5312 #ifdef NEVER
5313 /* Plot the shielded cell value */
5314 for (i = 0; i < nummeas; i++) {
5315 plot[0][i] = multimeas[i][-1];
5316 plot[6][i] = (double)i;
5317 }
5318 printf("Sheilded values\n");
5319 do_plot6(plot[6], plot[0], NULL, NULL, NULL, NULL, NULL, nummeas);
5320 #endif
5321
5322 sslope = dmatrixz(0, nummeas-1, -1, m->nraw-1);
5323 slope = dvectorz(0, nummeas-1);
5324 fslope = dvectorz(0, nummeas-1);
5325 sizepop = ivectorz(0, nummeas-1);
5326
5327 #ifndef NEVER /* Good with this on */
5328 /* Average bands together */
5329 for (i = 0; i < nummeas; i++) {
5330 for (j = BL + BW; j < (BH - BW); j++) {
5331 for (k = -BL; k <= BW; k++) /* Box averaging filter over bands */
5332 sslope[i][j] += multimeas[i][j + k]/maxval[j];
5333 }
5334 }
5335 #else
5336 /* Don't average bands */
5337 for (i = 0; i < nummeas; i++) {
5338 for (j = 0; j < m->nraw; j++) {
5339 sslope[i][j] = multimeas[i][j]/maxval[j];
5340 }
5341 }
5342 #endif
5343
5344 /* Compute slope result over readings and bands */
5345 /* Compute signed slope result over readings and bands */
5346
5347 #ifdef NEVER /* Works well for non-noisy readings */
5348 /* Median of 5 differences from 6 points */
5349 for (i = 2; i < (nummeas-3); i++) {
5350 for (j = BL; j < BH; j++) {
5351 double sl, asl[5];
5352 int r, s;
5353 asl[0] = fabs(sslope[i-2][j] - sslope[i-1][j]);
5354 asl[1] = fabs(sslope[i-1][j] - sslope[i-0][j]);
5355 asl[2] = fabs(sslope[i-0][j] - sslope[i+1][j]);
5356 asl[3] = fabs(sslope[i+1][j] - sslope[i+2][j]);
5357 asl[4] = fabs(sslope[i+2][j] - sslope[i+3][j]);
5358
5359 /* Sort them */
5360 for (r = 0; r < (5-1); r++) {
5361 for (s = r+1; s < 5; s++) {
5362 if (asl[s] < asl[r]) {
5363 double tt;
5364 tt = asl[s];
5365 asl[s] = asl[r];
5366 asl[r] = tt;
5367 }
5368 }
5369 }
5370 /* Pick middle one */
5371 sl = asl[2];
5372 if (sl > slope[i])
5373 slope[i] = sl;
5374 }
5375 }
5376
5377 #else /* Works better for noisy readings */
5378
5379 /* Compute sliding window average and deviation that contains */
5380 /* our output point, and chose the average with the minimum deviation. */
5381 #define FW 3 /* Number of delta's to average */
5382 for (i = FW-1; i < (nummeas-FW); i++) { /* Samples */
5383 double basl, bdev; /* Best average slope, Best deviation */
5384 double sl[2 * FW -1];
5385 double asl[FW], dev[FW];
5386 int slopen = 0;
5387 double slopeth = 0.0;
5388 int m, pp;
5389
5390 for (pp = 0; pp < 2; pp++) { /* For each pass */
5391
5392 for (j = BL; j < BH; j++) { /* Bands */
5393
5394 /* Compute differences for the range of our windows */
5395 for (k = 0; k < (2 * FW -1); k++)
5396 sl[k] = sslope[i+k-FW+1][j] - sslope[i+k+-FW+2][j];
5397
5398 /* For each window offset, compute average and deviation squared */
5399 bdev = 1e38;
5400 for (k = 0; k < FW; k++) {
5401
5402 /* Compute average of this window offset */
5403 asl[k] = 0.0;
5404 for (m = 0; m < FW; m++) /* For slope in window */
5405 asl[k] += sl[k+m];
5406 asl[k] /= (double)FW;
5407
5408 /* Compute deviation squared */
5409 dev[k] = 0.0;
5410 for (m = 0; m < FW; m++) {
5411 double tt;
5412 tt = sl[k+m] - asl[k];
5413 dev[k] += tt * tt;
5414 }
5415 if (dev[k] < bdev)
5416 bdev = dev[k];
5417 }
5418
5419 #ifndef NEVER /* Use this */
5420 /* Weight the deviations with a triangular weighting */
5421 /* to skew slightly towards the center */
5422 for (k = 0; k < FW; k++) {
5423 double wt;
5424
5425 wt = fabs(2.0 * k - (FW -1.0))/(FW-1.0);
5426 dev[k] += wt * bdev;
5427 }
5428 #endif
5429
5430 /* For each window offset, choose the one to use. */
5431 bdev = 1e38;
5432 basl = 0.0;
5433 for (k = 0; k < FW; k++) {
5434
5435 /* Choose window average with smallest deviation squared */
5436 if (dev[k] < bdev) {
5437 bdev = dev[k];
5438 basl = fabs(asl[k]);
5439 }
5440 }
5441
5442 if (pp == 0) { /* First pass, compute average slope over bands */
5443 slope[i] += basl;
5444
5445 } else { /* Second pass, average slopes of bands over threshold */
5446 if (basl > slopeth) {
5447 slope[i] += basl;
5448 slopen++;
5449 }
5450 }
5451 } /* Next band */
5452
5453 if (pp == 0) {
5454 slopeth = 1.0 * slope[i]/j; /* Compute threshold */
5455 slope[i] = 0.0;
5456 } else {
5457 if (slopen > 0)
5458 slope[i] /= slopen; /* Compute average of those over threshold */
5459 }
5460 } /* Next pass */
5461 }
5462 #undef FW
5463 #endif
5464
5465 #ifndef NEVER /* Good with this on */
5466 /* Normalise the slope values */
5467 /* Locate the minumum and maximum values */
5468 maxslope = 0.0;
5469 minslope = 1e38;
5470 for (i = 4; i < (nummeas-4); i++) {
5471 double avs;
5472
5473 if (slope[i] > maxslope)
5474 maxslope = slope[i];
5475
5476 /* Simple moving average for min comp. */
5477 avs = 0.0;
5478 for (j = -2; j <= 2; j++)
5479 avs += slope[i+j];
5480 avs /= 5.0;
5481 if (avs < minslope)
5482 minslope = avs;
5483 }
5484
5485 /* Normalise the slope */
5486 maxslope *= 0.5;
5487 minslope *= 3.0;
5488 for (i = 0; i < nummeas; i++) {
5489 slope[i] = (slope[i] - minslope) / (maxslope - minslope);
5490 if (slope[i] < 0.0)
5491 slope[i] = 0.0;
5492 else if (slope[i] > 1.0)
5493 slope[i] = 1.0;
5494 }
5495
5496 /* "Automatic Gain control" the raw slope information. */
5497 #define LFW 20 /* Half width of triangular filter */
5498 for (i = 0; i < nummeas; i++) {
5499 double sum, twt;
5500
5501 sum = twt = 0.0;
5502 for (j = -LFW; j <= LFW; j++) {
5503 double wt;
5504 if ((i+j) < 0 || (i+j) >= nummeas)
5505 continue;
5506
5507 wt = ((LFW-abs(j))/(double)LFW);
5508
5509 sum += wt * slope[i+j];
5510 twt += wt;
5511 }
5512 fslope[i] = sum/twt;
5513 if (fslope[i] > fmaxslope)
5514 fmaxslope = fslope[i];
5515 }
5516 #undef LFW
5517
5518 #ifdef NEVER /* Better with the off, for very noisy samples */
5519 /* Apply AGC with limited gain */
5520 for (i = 0; i < nummeas; i++) {
5521 if (fslope[i] > fmaxslope/4.0)
5522 slope[i] = slope[i]/fslope[i];
5523 else
5524 slope[i] = slope[i] * 4.0/fmaxslope;
5525 }
5526 #endif
5527 #endif /* NEVER */
5528
5529 /* Locate the minumum and maximum values */
5530 maxslope = 0.0;
5531 minslope = 1e38;
5532 for (i = 4; i < (nummeas-4); i++) {
5533 double avs;
5534
5535 if (slope[i] > maxslope)
5536 maxslope = slope[i];
5537
5538 /* Simple moving average for min comp. */
5539 avs = 0.0;
5540 for (j = -2; j <= 2; j++)
5541 avs += slope[i+j];
5542 avs /= 5.0;
5543 if (avs < minslope)
5544 minslope = avs;
5545 }
5546
5547 #ifndef NEVER /* Good with this on */
5548 /* Normalise the slope again */
5549 maxslope *= 0.3;
5550 minslope *= 3.0;
5551 for (i = 0; i < nummeas; i++) {
5552 slope[i] = (slope[i] - minslope) / (maxslope - minslope);
5553 if (slope[i] < 0.0)
5554 slope[i] = 0.0;
5555 else if (slope[i] > 1.0)
5556 slope[i] = 1.0;
5557 }
5558 #endif
5559
5560 #ifdef PLOT_PATREC
5561 printf("Slope filter output\n");
5562 for (i = 0; i < nummeas; i++) {
5563 int jj;
5564 for (jj = 0, j = BL; jj < 6 && j < BH; jj++, j += ((BH-BL)/6)) {
5565 double sum = 0.0;
5566 for (k = -BL; k <= BW; k++) /* Box averaging filter over bands */
5567 sum += multimeas[i][j + k];
5568 plot[jj][i] = sum/((2.0 * BL + 1.0) * maxval[j+k]);
5569 }
5570 }
5571 for (i = 0; i < nummeas; i++)
5572 plot[6][i] = (double)i;
5573 do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas);
5574 #endif
5575
5576 free_dvector(fslope, 0, nummeas-1);
5577 free_dmatrix(sslope, 0, nummeas-1, -1, m->nraw-1);
5578
5579 /* Now threshold the measurements into possible patches */
5580 apat = 2 * nummeas;
5581 if ((pat = (munki_patch *)malloc(sizeof(munki_patch) * apat)) == NULL) {
5582 a1logd(p->log,1,"munki: malloc of patch structures failed!\n");
5583 free_ivector(sizepop, 0, nummeas-1);
5584 free_dvector(slope, 0, nummeas-1);
5585 free_dvector(maxval, -1, m->nraw-1);
5586 return MUNKI_INT_MALLOC;
5587 }
5588
5589 avglegth = 0.0;
5590 for (npat = i = 0; i < (nummeas-1); i++) {
5591 if (slope[i] > thresh)
5592 continue;
5593
5594 /* Start of a new patch */
5595 if (npat >= apat) {
5596 apat *= 2;
5597 if ((pat = (munki_patch *)realloc(pat, sizeof(munki_patch) * apat)) == NULL) {
5598 free_ivector(sizepop, 0, nummeas-1);
5599 free_dvector(slope, 0, nummeas-1);
5600 free_dvector(maxval, -1, m->nraw-1);
5601 a1logd(p->log,1,"munki: reallloc of patch structures failed!\n");
5602 return MUNKI_INT_MALLOC;
5603 }
5604 }
5605 pat[npat].ss = i;
5606 pat[npat].no = 2;
5607 pat[npat].use = 0;
5608 for (i++; i < (nummeas-1); i++) {
5609 if (slope[i] > thresh)
5610 break;
5611 pat[npat].no++;
5612 }
5613 avglegth += (double) pat[npat].no;
5614 npat++;
5615 }
5616 a1logd(p->log,7,"Number of patches = %d\n",npat);
5617
5618 /* We don't count the first and last patches, as we assume they are white leader. */
5619 /* (They are marked !use in list anyway) */
5620 if (npat < (tnpatch + 2)) {
5621 free_ivector(sizepop, 0, nummeas-1);
5622 free_dvector(slope, 0, nummeas-1);
5623 free_dvector(maxval, -1, m->nraw-1);
5624 free(pat);
5625 a1logd(p->log,1,"Patch recog failed - unable to detect enough possible patches\n");
5626 return MUNKI_RD_NOTENOUGHPATCHES;
5627 } else if (npat >= (2 * tnpatch) + 2) {
5628 free_ivector(sizepop, 0, nummeas-1);
5629 free_dvector(slope, 0, nummeas-1);
5630 free_dvector(maxval, -1, m->nraw-1);
5631 free(pat);
5632 a1logd(p->log,1,"Patch recog failed - detecting too many possible patches\n");
5633 return MUNKI_RD_TOOMANYPATCHES;
5634 }
5635 avglegth /= (double)npat;
5636
5637 #ifdef PLOT_PATREC
5638 for (i = 0; i < npat; i++) {
5639 printf("Raw patch %d, start %d, length %d\n",i, pat[i].ss, pat[i].no);
5640 }
5641 #endif
5642
5643 /* Accumulate popularity ccount of possible patches */
5644 for (i = 1; i < (npat-1); i++)
5645 sizepop[pat[i].no]++;
5646
5647 /* Locate the median potential patch width */
5648 for (j = 0, i = 0; i < nummeas; i++) {
5649 j += sizepop[i];
5650 if (j > ((npat-2)/2))
5651 break;
5652 }
5653 median = (double)i;
5654
5655 a1logd(p->log,7,"Median patch width %f\n",median);
5656
5657 /* Now decide which patches to use. */
5658 /* Try a widening window around the median. */
5659 for (window = 0.2, try = 0; try < 15; window *= 1.4, try++) {
5660 int bgcount = 0, bgstart = 0;
5661 int gcount, gstart;
5662 double wmin = median/(1.0 + window);
5663 double wmax = median * (1.0 + window);
5664
5665 a1logd(p->log,7,"Window = %f - %f\n",wmin, wmax);
5666 /* Track which is the largest contiguous group that */
5667 /* is within our window */
5668 gcount = gstart = 0;
5669 for (i = 1; i < npat; i++) {
5670 if (i < (npat-1) && pat[i].no <= wmax) { /* Small enough */
5671 if (pat[i].no >= wmin) { /* And big enough */
5672 if (gcount == 0) { /* Start of new group */
5673 gcount++;
5674 gstart = i;
5675 a1logd(p->log,7,"Start group at %d\n",gstart);
5676 } else {
5677 gcount++; /* Continuing new group */
5678 a1logd(p->log,7,"Continue group at %d, count %d\n",gstart,gcount);
5679 }
5680 }
5681 } else { /* Too big or end of patches, end this group */
5682 a1logd(p->log,7,"Terminating group group at %d, count %d\n",gstart,gcount);
5683 if (gcount > bgcount) { /* New biggest group */
5684 bgcount = gcount;
5685 bgstart = gstart;
5686 a1logd(p->log,7,"New biggest\n");
5687 }
5688 gcount = gstart = 0; /* End this group */
5689 }
5690 }
5691 a1logd(p->log,7,"Biggest group is at %d, count %d\n",bgstart,bgcount);
5692
5693 if (bgcount == tnpatch) { /* We're done */
5694 for (i = bgstart, j = 0; i < npat && j < tnpatch; i++) {
5695 if (pat[i].no <= wmax && pat[i].no >= wmin) {
5696 pat[i].use = 1;
5697 j++;
5698 if (pat[i].no < MIN_SAMPLES) {
5699 a1logd(p->log,7,"Too few samples\n");
5700 free_ivector(sizepop, 0, nummeas-1);
5701 free_dvector(slope, 0, nummeas-1);
5702 free_dvector(maxval, -1, m->nraw-1);
5703 free(pat);
5704 a1logd(p->log,1,"Patch recog failed - patches sampled too sparsely\n");
5705 return MUNKI_RD_NOTENOUGHSAMPLES;
5706 }
5707 }
5708 }
5709 break;
5710
5711 } else if (bgcount > tnpatch) {
5712 a1logd(p->log,7,"Too many patches\n");
5713 free_ivector(sizepop, 0, nummeas-1);
5714 free_dvector(slope, 0, nummeas-1);
5715 free_dvector(maxval, -1, m->nraw-1);
5716 free(pat);
5717 a1logd(p->log,1,"Patch recog failed - detected too many consistent patches\n");
5718 return MUNKI_RD_TOOMANYPATCHES;
5719 }
5720 }
5721 if (try >= 15) {
5722 a1logd(p->log,7,"Not enough patches\n");
5723 free_ivector(sizepop, 0, nummeas-1);
5724 free_dvector(slope, 0, nummeas-1);
5725 free_dvector(maxval, -1, m->nraw-1);
5726 free(pat);
5727 a1logd(p->log,1,"Patch recog failed - unable to find enough consistent patches\n");
5728 return MUNKI_RD_NOTENOUGHPATCHES;
5729 }
5730
5731 #ifdef PLOT_PATREC
5732 printf("Got %d patches out of potentional %d:\n",tnpatch, npat);
5733 printf("Average patch legth %f\n",avglegth);
5734 for (i = 1; i < (npat-1); i++) {
5735 if (pat[i].use == 0)
5736 continue;
5737 printf("Patch %d, start %d, length %d, use %d\n",i, pat[i].ss, pat[i].no, pat[i].use);
5738 }
5739 #endif
5740
5741 /* Now trim the patches simply by shrinking their windows */
5742 for (k = 1; k < (npat-1); k++) {
5743 int nno, trim;
5744
5745 if (pat[k].use == 0)
5746 continue;
5747
5748 // nno = (pat[k].no * 3 + 0)/4; /* Trim to 75% & round down */
5749 nno = (pat[k].no * 2 + 0)/3; /* Trim to 66% & round down [def] */
5750 // nno = (pat[k].no * 2 + 0)/4; /* Trim to 50% & round down */
5751 trim = (pat[k].no - nno + 1)/2;
5752
5753 pat[k].ss += trim;
5754 pat[k].no = nno;
5755 }
5756
5757 #ifdef PLOT_PATREC
5758 printf("After trimming got:\n");
5759 for (i = 1; i < (npat-1); i++) {
5760 if (pat[i].use == 0)
5761 continue;
5762 printf("Patch %d, start %d, length %d, use %d\n",i, pat[i].ss, pat[i].no, pat[i].use);
5763 }
5764
5765 /* Create fake "slope" value that marks patches */
5766 for (i = 0; i < nummeas; i++)
5767 slope[i] = 1.0;
5768 for (k = 1; k < (npat-1); k++) {
5769 if (pat[k].use == 0)
5770 continue;
5771 for (i = pat[k].ss; i < (pat[k].ss + pat[k].no); i++)
5772 slope[i] = 0.0;
5773 }
5774
5775 printf("Trimmed output - averaged bands:\n");
5776 /* Plot box averaged bands */
5777 for (i = 0; i < nummeas; i++) {
5778 int jj;
5779 for (jj = 0, j = BL; jj < 6 && j < BH; jj++, j += ((BH-BL)/6)) {
5780 double sum = 0.0;
5781 for (k = -BL; k <= BW; k++) /* Box averaging filter over bands */
5782 sum += multimeas[i][j + k];
5783 plot[jj][i] = sum/((2.0 * BL + 1.0) * maxval[j+k]);
5784 }
5785 }
5786 for (i = 0; i < nummeas; i++)
5787 plot[6][i] = (double)i;
5788 do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas);
5789
5790 #ifdef NEVER
5791 /* Plot all the bands */
5792 printf("Trimmed output - all bands:\n");
5793 for (j = 0; j < (m->nraw-5); j += 5) {
5794 for (k = 0; k < 5; k ++) {
5795 for (i = 0; i < nummeas; i++) {
5796 plot[k][i] = multimeas[i][j+k]/maxval[j+k];
5797 }
5798 }
5799 for (i = 0; i < nummeas; i++)
5800 plot[6][i] = (double)i;
5801 printf("Bands %d - %d\n",j,j+5);
5802 do_plot6(plot[6], slope, plot[0], plot[1], plot[2], plot[3], plot[4], nummeas);
5803 }
5804 #endif
5805
5806 #endif
5807
5808 /* Now compute averaged patch values */
5809
5810 /* Compute average of (aproximate) white */
5811 white_avg = 0.0;
5812 for (j = 1; j < (m->nraw-1); j++)
5813 white_avg += maxval[j];
5814 white_avg /= (m->nraw - 2.0);
5815
5816 /* Now process the buffer values */
5817 for (i = 0; i < tnpatch; i++)
5818 for (j = 0; j < m->nraw; j++)
5819 pavg[i][j] = 0.0;
5820
5821 for (pix = 0, k = 1; k < (npat-1); k++) {
5822 double maxavg = -1e38; /* Track min and max averages of readings for consistency */
5823 double minavg = 1e38;
5824 double cons; /* Consistency */
5825
5826 if (pat[k].use == 0)
5827 continue;
5828
5829 if (pat[k].no <= MIN_SAMPLES) {
5830 a1logd(p->log,7,"Too few samples\n");
5831 free_dvector(slope, 0, nummeas-1);
5832 free_ivector(sizepop, 0, nummeas-1);
5833 free_dvector(maxval, -1, m->nraw-1);
5834 free(pat);
5835 a1logd(p->log,1,"Patch recog failed - patches sampled too sparsely\n");
5836 return MUNKI_RD_NOTENOUGHSAMPLES;
5837 }
5838
5839 /* Measure samples that make up patch value */
5840 for (i = pat[k].ss; i < (pat[k].ss + pat[k].no); i++) {
5841 double measavg = 0.0;
5842
5843 for (j = 0; j < m->nraw; j++) {
5844 double val;
5845
5846 val = multimeas[i][j];
5847
5848 measavg += val;
5849 pavg[pix][j] += val;
5850 }
5851 measavg /= (m->nraw-2.0);
5852 if (measavg < minavg)
5853 minavg = measavg;
5854 if (measavg > maxavg)
5855 maxavg = measavg;
5856 }
5857
5858 for (j = 0; j < m->nraw; j++)
5859 pavg[pix][j] /= (double)pat[k].no;
5860
5861 cons = (maxavg - minavg)/white_avg;
5862 a1logd(p->log,7,"Patch %d: consistency = %f%%, thresh = %f%%\n",pix,100.0 * cons, 100.0 * patch_cons_thr);
5863 if (cons > patch_cons_thr) {
5864 a1logd(p->log,1,"Patch recog failed - patch %d is inconsistent (%f%%)\n",pix, cons);
5865 rv |= 1;
5866 }
5867 pix++;
5868 }
5869
5870 if (flags != NULL)
5871 *flags = rv;
5872
5873 #ifdef PLOT_PATREC
5874 free_dmatrix(plot, 0, 6, 0, nummeas-1);
5875 #endif
5876
5877 free_dvector(slope, 0, nummeas-1);
5878 free_ivector(sizepop, 0, nummeas-1);
5879 free_dvector(maxval, -1, m->nraw-1);
5880 free(pat); /* Otherwise caller will have to do it */
5881
5882 a1logd(p->log,3,"munki_extract_patches_multimeas done, sat = %s, inconsist = %s\n",
5883 rv & 2 ? "true" : "false", rv & 1 ? "true" : "false");
5884
5885 a1logd(p->log,2,"Patch recognition returning OK\n");
5886
5887 return MUNKI_OK;
5888 }
5889
5890 #undef BL
5891 #undef BH
5892 #undef BW
5893
5894 /* Recognise any flashes in the readings, and */
5895 /* and average their values together as well as summing their duration. */
5896 /* The readings are integrated, so the the units are cd/m^2 seconds. */
5897 /* Return nz on an error */
5898 /* (Doesn't extract [-1] shielded values, since they have already been used) */
munki_extract_patches_flash(munki * p,int * flags,double * duration,double * pavg,double ** multimeas,int nummeas,double inttime)5899 munki_code munki_extract_patches_flash(
5900 munki *p,
5901 int *flags, /* return flags */
5902 double *duration, /* return duration */
5903 double *pavg, /* return patch average [-1 nraw] */
5904 double **multimeas, /* Array of [nummeas][-1 nraw] value to extract from */
5905 int nummeas, /* number of readings made */
5906 double inttime /* Integration time (used to compute duration) */
5907 ) {
5908 munkiimp *m = (munkiimp *)p->m;
5909 int i, j, k;
5910 double minval, maxval; /* min and max input value at wavelength of maximum input */
5911 double mean; /* Mean of the max wavelength band */
5912 int maxband; /* Band of maximum value */
5913 double thresh; /* Level threshold */
5914 int fsampl; /* Index of the first sample over the threshold */
5915 int nsampl; /* Number of samples over the threshold */
5916 double *aavg; /* ambient average [-1 nraw] */
5917 double finttime; /* Flash integration time */
5918 int rv = 0;
5919 #ifdef PLOT_PATREC
5920 double **plot;
5921 #endif
5922
5923 a1logd(p->log,3,"munki_extract_patches_flash: %d measurements\n",nummeas);
5924
5925 /* Discover the maximum input value for flash dection */
5926 maxval = -1e6;
5927 maxband = 0;
5928 for (j = 0; j < m->nraw; j ++) {
5929 for (i = 0; i < nummeas; i++) {
5930 if (multimeas[i][j] > maxval) {
5931 maxval = multimeas[i][j];
5932 maxband = j;
5933 }
5934 }
5935 }
5936
5937 if (maxval <= 0.0) {
5938 a1logd(p->log,1,"No flashes found in measurement\n");
5939 return MUNKI_RD_NOFLASHES;
5940 }
5941
5942 minval = 1e6;
5943 mean = 0.0;
5944 for (i = 0; i < nummeas; i++) {
5945 mean += multimeas[i][maxband];
5946 if (multimeas[i][maxband] < minval)
5947 minval = multimeas[i][maxband];
5948 }
5949 mean /= (double)nummeas;
5950
5951 /* Set the threshold at 5% from mean towards max */
5952 thresh = (3.0 * mean + maxval)/4.0;
5953 a1logd(p->log,7,"munki_extract_patches_flash band %d minval %f maxval %f, mean = %f, thresh = %f\n",maxband,minval,maxval,mean, thresh);
5954
5955 #ifdef PLOT_PATREC
5956 /* Plot out 6 lots of 6 values each */
5957 plot = dmatrixz(0, 6, 0, nummeas-1);
5958 for (j = maxband -3; j>= 0 && j < (m->nraw-6); j += 100) /* Do one set around max */
5959 {
5960 for (k = 0; k < 6; k ++) {
5961 for (i = 0; i < nummeas; i++) {
5962 plot[k][i] = multimeas[i][j+k]/maxval;
5963 }
5964 }
5965 for (i = 0; i < nummeas; i++)
5966 plot[6][i] = (double)i;
5967 printf("Bands %d - %d\n",j,j+5);
5968 do_plot6(plot[6], plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], nummeas);
5969 }
5970 free_dmatrix(plot,0,6,0,nummeas-1);
5971 #endif
5972
5973 #ifdef PLOT_PATREC
5974 /* Plot just the pulses */
5975 {
5976 int start, end;
5977
5978 plot = dmatrixz(0, 6, 0, nummeas-1);
5979
5980 for(j = 0, start = -1, end = 0;;) {
5981
5982 for (start = -1, i = end; i < nummeas; i++) {
5983 if (multimeas[i][maxband] >= thresh) {
5984 if (start < 0)
5985 start = i;
5986 } else if (start >= 0) {
5987 end = i;
5988 break;
5989 }
5990 }
5991 if (start < 0)
5992 break;
5993 start -= 3;
5994 if (start < 0)
5995 start = 0;
5996 end += 4;
5997 if (end > nummeas)
5998 end = nummeas;
5999
6000 for (i = start; i < end; i++, j++) {
6001 int q;
6002
6003 plot[6][j] = (double)j;
6004 #ifdef NEVER /* Plot +/-3 around maxband */
6005 for (q = 0, k = maxband -3; k < (maxband+3) && k >= 0 && k < m->nraw; k++, q++) {
6006 plot[q][j] = multimeas[i][k]/maxval;
6007 }
6008 #else
6009 /* plot max of bands in 6 segments */
6010 for (q = 0; q < 6; q++) {
6011 int ss, ee;
6012
6013 plot[q][j] = -1e60;
6014 ss = q * (m->nraw/6);
6015 ee = (q+1) * (m->nraw/6);
6016 for (k = ss; k < ee; k++) {
6017 if (multimeas[i][k]/maxval > plot[q][j])
6018 plot[q][j] = multimeas[i][k]/maxval;
6019 }
6020 }
6021 #endif
6022 }
6023 }
6024 do_plot6(plot[6], plot[0], plot[1], plot[2], plot[3], plot[4], plot[5], j);
6025 free_dmatrix(plot,0,6,0,nummeas-1);
6026 }
6027 #endif
6028
6029 /* Locate the first sample over the threshold, and the */
6030 /* total number of samples in the pulses. */
6031 fsampl = -1;
6032 for (nsampl = i = 0; i < nummeas; i++) {
6033 for (j = 0; j < m->nraw-1; j++) {
6034 if (multimeas[i][j] >= thresh)
6035 break;
6036 }
6037 if (j < m->nraw-1) {
6038 if (fsampl < 0)
6039 fsampl = i;
6040 nsampl++;
6041 }
6042 }
6043 a1logd(p->log,7,"Number of flash patches = %d\n",nsampl);
6044 if (nsampl == 0)
6045 return MUNKI_RD_NOFLASHES;
6046
6047 /* See if there are as many samples before the first flash */
6048 if (nsampl < 6)
6049 nsampl = 6;
6050
6051 /* Average nsample samples of ambient */
6052 i = (fsampl-3-nsampl);
6053 if (i < 0)
6054 return MUNKI_RD_NOAMBB4FLASHES;
6055 a1logd(p->log,7,"Ambient samples %d to %d \n",i,fsampl-3);
6056 aavg = dvectorz(-1, m->nraw-1);
6057 for (nsampl = 0; i < (fsampl-3); i++) {
6058 for (j = 0; j < m->nraw-1; j++)
6059 aavg[j] += multimeas[i][j];
6060 nsampl++;
6061 }
6062
6063 /* Integrate all the values over the threshold, */
6064 /* and also one either side of flash */
6065 for (j = 0; j < m->nraw-1; j++)
6066 pavg[j] = 0.0;
6067
6068 for (k = 0, i = 1; i < (nummeas-1); i++) {
6069 int sample = 0;
6070 for (j = 0; j < m->nraw-1; j++) {
6071 if (multimeas[i-1][j] >= thresh) {
6072 sample = 1;
6073 break;
6074 }
6075 if (multimeas[i][j] >= thresh) {
6076 sample = 1;
6077 break;
6078 }
6079 if (multimeas[i+1][j] >= thresh) {
6080 sample = 1;
6081 break;
6082 }
6083 }
6084 if (j < m->nraw-1) {
6085 a1logd(p->log,7,"Integrating flash sample no %d \n",i);
6086 for (j = 0; j < m->nraw-1; j++)
6087 pavg[j] += multimeas[i][j];
6088 k++;
6089 }
6090 }
6091 for (j = 0; j < m->nraw-1; j++)
6092 pavg[j] = pavg[j]/(double)k - aavg[j]/(double)nsampl;
6093
6094 a1logd(p->log,7,"Number of flash patches integrated = %d\n",k);
6095
6096 finttime = inttime * (double)k;
6097 if (duration != NULL)
6098 *duration = finttime;
6099
6100 /* Convert to cd/m^2 seconds */
6101 for (j = 0; j < m->nraw-1; j++)
6102 pavg[j] *= finttime;
6103
6104 if (flags != NULL)
6105 *flags = rv;
6106
6107 free_dvector(aavg, -1, m->nraw-1);
6108
6109 return MUNKI_OK;
6110 }
6111
6112 /* Convert an absraw array from raw wavelengths to output wavelenths */
6113 /* for the current resolution. Apply stray light compensation too. */
munki_absraw_to_abswav(munki * p,int nummeas,double ** abswav,double ** absraw)6114 void munki_absraw_to_abswav(
6115 munki *p,
6116 int nummeas, /* Return number of readings measured */
6117 double **abswav, /* Desination array [nwav] */
6118 double **absraw /* Source array [-1 nraw] */
6119 ) {
6120 munkiimp *m = (munkiimp *)p->m;
6121 munki_state *s = &m->ms[m->mmode];
6122 double *tm; /* Temporary array */
6123 int i, j, k, cx, sx;
6124
6125 tm = dvector(0, m->nwav-1);
6126
6127 /* For each measurement */
6128 for (i = 0; i < nummeas; i++) {
6129
6130 /* For each output wavelength */
6131 for (cx = j = 0; j < m->nwav; j++) {
6132 double oval = 0.0;
6133
6134 /* For each matrix value */
6135 if (s->reflective) {
6136 sx = m->rmtx_index[j]; /* Starting index */
6137 for (k = 0; k < m->rmtx_nocoef[j]; k++, cx++, sx++)
6138 oval += m->rmtx_coef[cx] * absraw[i][sx];
6139 } else {
6140 sx = m->emtx_index[j]; /* Starting index */
6141 for (k = 0; k < m->emtx_nocoef[j]; k++, cx++, sx++)
6142 oval += m->emtx_coef[cx] * absraw[i][sx];
6143 }
6144 tm[j] = oval;
6145 }
6146
6147 /* Now apply stray light compensation */
6148 /* For each output wavelength */
6149 for (j = 0; j < m->nwav; j++) {
6150 double oval = 0.0;
6151
6152 /* For each matrix value */
6153 for (k = 0; k < m->nwav; k++)
6154 oval += m->straylight[j][k] * tm[k];
6155 abswav[i][j] = oval;
6156 }
6157 }
6158 free_dvector(tm, 0, m->nwav-1);
6159 }
6160
6161 /* Convert an absraw array from raw wavelengths to output wavelenths */
6162 /* for the standard resolution. Apply stray light compensation too. */
munki_absraw_to_abswav1(munki * p,int nummeas,double ** abswav,double ** absraw)6163 void munki_absraw_to_abswav1(
6164 munki *p,
6165 int nummeas, /* Return number of readings measured */
6166 double **abswav, /* Desination array [nwav1] */
6167 double **absraw /* Source array [-1 nraw] */
6168 ) {
6169 munkiimp *m = (munkiimp *)p->m;
6170 munki_state *s = &m->ms[m->mmode];
6171 double *tm; /* Temporary array */
6172 int i, j, k, cx, sx;
6173
6174 tm = dvector(0, m->nwav1-1);
6175
6176 /* For each measurement */
6177 for (i = 0; i < nummeas; i++) {
6178
6179 /* For each output wavelength */
6180 for (cx = j = 0; j < m->nwav1; j++) {
6181 double oval = 0.0;
6182
6183 /* For each matrix value */
6184 if (s->reflective) {
6185 sx = m->rmtx_index1[j]; /* Starting index */
6186 for (k = 0; k < m->rmtx_nocoef1[j]; k++, cx++, sx++)
6187 oval += m->rmtx_coef1[cx] * absraw[i][sx];
6188 } else {
6189 sx = m->emtx_index1[j]; /* Starting index */
6190 for (k = 0; k < m->emtx_nocoef1[j]; k++, cx++, sx++)
6191 oval += m->emtx_coef1[cx] * absraw[i][sx];
6192 }
6193 tm[j] = oval;
6194 }
6195
6196 /* Now apply stray light compensation */
6197 /* For each output wavelength */
6198 for (j = 0; j < m->nwav1; j++) {
6199 double oval = 0.0;
6200
6201 /* For each matrix value */
6202 for (k = 0; k < m->nwav1; k++)
6203 oval += m->straylight1[j][k] * tm[k];
6204 abswav[i][j] = oval;
6205 }
6206 }
6207 free_dvector(tm, 0, m->nwav1-1);
6208 }
6209
6210 /* Convert an absraw array from raw wavelengths to output wavelenths */
6211 /* for the high resolution. Apply light compensation too. */
munki_absraw_to_abswav2(munki * p,int nummeas,double ** abswav,double ** absraw)6212 void munki_absraw_to_abswav2(
6213 munki *p,
6214 int nummeas, /* Return number of readings measured */
6215 double **abswav, /* Desination array [nwav2] */
6216 double **absraw /* Source array [-1 nraw] */
6217 ) {
6218 munkiimp *m = (munkiimp *)p->m;
6219 munki_state *s = &m->ms[m->mmode];
6220 double *tm; /* Temporary array */
6221 int i, j, k, cx, sx;
6222
6223 tm = dvector(0, m->nwav2-1);
6224
6225 /* For each measurement */
6226 for (i = 0; i < nummeas; i++) {
6227
6228 /* For each output wavelength */
6229 for (cx = j = 0; j < m->nwav2; j++) {
6230 double oval = 0.0;
6231
6232 /* For each matrix value */
6233 if (s->reflective) {
6234 sx = m->rmtx_index2[j]; /* Starting index */
6235 for (k = 0; k < m->rmtx_nocoef2[j]; k++, cx++, sx++)
6236 oval += m->rmtx_coef2[cx] * absraw[i][sx];
6237 } else {
6238 sx = m->emtx_index2[j]; /* Starting index */
6239 for (k = 0; k < m->emtx_nocoef2[j]; k++, cx++, sx++)
6240 oval += m->emtx_coef2[cx] * absraw[i][sx];
6241 }
6242 tm[j] = oval;
6243 }
6244
6245 /* Now apply stray light compensation */
6246 /* For each output wavelength */
6247 for (j = 0; j < m->nwav2; j++) {
6248 double oval = 0.0;
6249
6250 /* For each matrix value */
6251 for (k = 0; k < m->nwav2; k++)
6252 oval += m->straylight2[j][k] * tm[k];
6253 abswav[i][j] = oval;
6254 }
6255 }
6256 free_dvector(tm, 0, m->nwav2-1);
6257 }
6258
6259 /* Convert an abswav array of output wavelengths to scaled output readings. */
munki_scale_specrd(munki * p,double ** outspecrd,int numpatches,double ** inspecrd)6260 void munki_scale_specrd(
6261 munki *p,
6262 double **outspecrd, /* Destination */
6263 int numpatches, /* Number of readings/patches */
6264 double **inspecrd /* Source */
6265 ) {
6266 munkiimp *m = (munkiimp *)p->m;
6267 munki_state *s = &m->ms[m->mmode];
6268 int i, j;
6269
6270 /* For each measurement */
6271 for (i = 0; i < numpatches; i++) {
6272
6273 /* For each output wavelength */
6274 for (j = 0; j < m->nwav; j++) {
6275 outspecrd[i][j] = inspecrd[i][j] * s->cal_factor[j];
6276 }
6277 }
6278 }
6279
6280
6281 /* =============================================== */
6282 #ifdef HIGH_RES
6283
6284 /* High res congiguration */
6285 #undef EXISTING_SHAPE /* [und] Else generate filter shape */
6286 #define USE_GAUSSIAN /* [def] Use gaussian filter shape, else lanczos2 */
6287
6288 #define DO_CCDNORM /* [def] Normalise CCD values to original */
6289 #define DO_CCDNORMAVG /* [und???] Normalise averages rather than per CCD bin */
6290 #define BOX_INTEGRATE /* [und] Integrate raw samples as if they were +/-0.5 boxes */
6291 /* (This improves coeficient consistency a bit ?) */
6292
6293 #ifdef NEVER
6294 /* Plot the matrix coefficients */
munki_debug_plot_mtx_coef(munki * p,int ref)6295 void munki_debug_plot_mtx_coef(munki *p, int ref) {
6296 munkiimp *m = (munkiimp *)p->m;
6297 int i, j, k, cx, sx;
6298 double *xx, *ss;
6299 double **yy;
6300
6301 xx = dvectorz(-1, m->nraw-1); /* X index */
6302 yy = dmatrixz(0, 5, -1, m->nraw-1); /* Curves distributed amongst 5 graphs */
6303
6304 for (i = 0; i < m->nraw; i++)
6305 xx[i] = i;
6306
6307 /* For each output wavelength */
6308 for (cx = j = 0; j < m->nwav; j++) {
6309 i = j % 5;
6310
6311 // printf("Out wave = %d\n",j);
6312 /* For each matrix value */
6313 if (ref) {
6314 sx = m->rmtx_index[j]; /* Starting index */
6315 // printf("start index = %d, nocoef %d\n",sx,m->rmtx_nocoef[j]);
6316 for (k = 0; k < m->rmtx_nocoef[j]; k++, cx++, sx++) {
6317 // printf("offset %d, coef ix %d val %f from ccd %d\n",k, cx, m->rmtx_coef[cx], sx);
6318 yy[5][sx] += 0.5 * m->rmtx_coef[cx];
6319 yy[i][sx] = m->rmtx_coef[cx];
6320 }
6321 } else {
6322 sx = m->emtx_index[j]; /* Starting index */
6323 // printf("start index = %d, nocoef %d\n",sx,m->emtx_nocoef[j]);
6324 for (k = 0; k < m->emtx_nocoef[j]; k++, cx++, sx++) {
6325 // printf("offset %d, coef ix %d val %f from ccd %d\n",k, cx, m->emtx_coef[cx], sx);
6326 yy[5][sx] += 0.5 * m->emtx_coef[cx];
6327 yy[i][sx] = m->emtx_coef[cx];
6328 }
6329 }
6330 }
6331
6332 if (ref)
6333 printf("Reflective cooeficients\n");
6334 else
6335 printf("Emissive cooeficients\n");
6336 do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
6337 free_dvector(xx, -1, m->nraw-1);
6338 free_dmatrix(yy, 0, 2, -1, m->nraw-1);
6339 }
6340 #endif
6341
6342 /* Filter shape point */
6343 typedef struct {
6344 double wl, we;
6345 } munki_fs;
6346
6347 /* Filter cooeficient values */
6348 typedef struct {
6349 int ix; /* Raw index */
6350 double we; /* Weighting */
6351 } munki_fc;
6352
6353 /* Wavelenth calibration crossover point information */
6354 typedef struct {
6355 double wav; /* Wavelegth of point */
6356 double raw; /* Raw index of point */
6357 double wei; /* Weigting of the point */
6358 } munki_xp;
6359
6360 /* Linearly interpolate the filter shape */
lin_fshape(munki_fs * fsh,int n,double x)6361 static double lin_fshape(munki_fs *fsh, int n, double x) {
6362 int i;
6363 double y;
6364
6365 if (x <= fsh[0].wl)
6366 return fsh[0].we;
6367 else if (x >= fsh[n-1].wl)
6368 return fsh[n-1].we;
6369
6370 for (i = 0; i < (n-1); i++)
6371 if (x >= fsh[i].wl && x <= fsh[i+1].wl)
6372 break;
6373
6374 x = (x - fsh[i].wl)/(fsh[i+1].wl - fsh[i].wl);
6375 y = fsh[i].we + (fsh[i+1].we - fsh[i].we) * x;
6376
6377 return y;
6378 }
6379
6380 /* Generate a sample from a lanczos2 filter shape */
6381 /* wi is the width of the filter */
lanczos2(double wi,double x)6382 static double lanczos2(double wi, double x) {
6383 double y;
6384
6385 #ifdef USE_GAUSSIAN
6386 /* gausian */
6387 wi = wi/(sqrt(2.0 * log(2.0))); /* Convert width at half max to std. dev. */
6388 x = x/wi;
6389 // y = 1.0/(wi * sqrt(2.0 * DBL_PI)) * exp(-(x * x)); /* Unity area */
6390 y = exp(-(x * x)); /* Center at 1.0 */
6391 #else
6392
6393
6394 /* lanczos2 */
6395 wi *= 1.05; // Improves smoothness. Why ?
6396 x = fabs(1.0 * x/wi);
6397 if (x >= 2.0)
6398 return 0.0;
6399 if (x < 1e-5)
6400 return 1.0;
6401 y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/2.0)/(DBL_PI * x/2.0);
6402 #endif
6403 return y;
6404 }
6405
6406 #if defined(__APPLE__) && defined(__POWERPC__)
6407
6408 /* Workaround for a ppc gcc 3.3 optimiser bug... */
gcc_bug_fix(int i)6409 static int gcc_bug_fix(int i) {
6410 static int nn;
6411 nn += i;
6412 return nn;
6413 }
6414 #endif /* APPLE */
6415
6416 #ifdef SALONEINSTLIB
6417 # define ONEDSTRAYLIGHTUS
6418 #endif
6419
6420 /* Create high resolution mode references, */
6421 /* Create Reflective if ref nz, else create Emissive */
6422 /* We expect this to be called twice, once for each. */
munki_create_hr(munki * p,int ref)6423 munki_code munki_create_hr(munki *p, int ref) {
6424 munkiimp *m = (munkiimp *)p->m;
6425 int i, j, jj, k, cx, sx;
6426 munki_fc coeff[40][16]; /* Existing filter cooefficients */
6427 int nwav1; /* Number of filters */
6428 double wl_short1, wl_long1; /* Ouput wavelength of first and last filters */
6429 double wl_step1;
6430 munki_xp xp[41]; /* Crossover points each side of filter */
6431 munki_code ev = MUNKI_OK;
6432 rspl *raw2wav; /* Lookup from CCD index to wavelength */
6433 munki_fs fshape[40 * 16]; /* Existing filter shape */
6434 int ncp = 0; /* Number of shape points */
6435 int *mtx_index1, **pmtx_index2, *mtx_index2;
6436 int *mtx_nocoef1, **pmtx_nocoef2, *mtx_nocoef2;
6437 double *mtx_coef1, **pmtx_coef2, *mtx_coef2;
6438
6439 double min_wl = ref ? WL_REF_MIN : WL_EMIS_MIN;
6440
6441 /* Start with nominal values. May alter these if filters are not unique */
6442 nwav1 = m->nwav1;
6443 wl_short1 = m->wl_short1;
6444 wl_long1 = m->wl_long1;
6445 wl_step1 = (wl_long1 - m->wl_short1)/(m->nwav1-1.0);
6446
6447 if (ref) {
6448 mtx_index1 = m->rmtx_index1;
6449 mtx_nocoef1 = m->rmtx_nocoef1;
6450 mtx_coef1 = m->rmtx_coef1;
6451 mtx_index2 = NULL;
6452 mtx_nocoef2 = NULL;
6453 mtx_coef2 = NULL;
6454 pmtx_index2 = &m->rmtx_index2;
6455 pmtx_nocoef2 = &m->rmtx_nocoef2;
6456 pmtx_coef2 = &m->rmtx_coef2;
6457 } else {
6458 mtx_index1 = m->emtx_index1;
6459 mtx_nocoef1 = m->emtx_nocoef1;
6460 mtx_coef1 = m->emtx_coef1;
6461 mtx_index2 = NULL;
6462 mtx_nocoef2 = NULL;
6463 mtx_coef2 = NULL;
6464 pmtx_index2 = &m->emtx_index2;
6465 pmtx_nocoef2 = &m->emtx_nocoef2;
6466 pmtx_coef2 = &m->emtx_coef2;
6467 }
6468
6469 /* Convert the native filter cooeficient representation to */
6470 /* a 2D array we can randomly index. Skip any duplicated */
6471 /* filter cooeficients. */
6472 for (cx = j = jj = 0; j < m->nwav1; j++) { /* For each output wavelength */
6473 if (j >= 40) { /* Assert */
6474 a1logw(p->log,"munki: number of output wavelenths is > 40\n");
6475 return MUNKI_INT_ASSERT;
6476 }
6477
6478 /* For each matrix value */
6479 sx = mtx_index1[j]; /* Starting index */
6480 if (j < (m->nwav1-1) && sx == mtx_index1[j+1]) { /* Skip duplicates + last */
6481 // printf("~1 skipping %d\n",j);
6482 wl_short1 += wl_step1;
6483 nwav1--;
6484 cx += mtx_nocoef1[j];
6485 continue;
6486 }
6487 for (k = 0; k < mtx_nocoef1[j]; k++, cx++, sx++) {
6488 if (k >= 16) { /* Assert */
6489 a1logw(p->log,"munki: number of filter coeefs is > 16\n");
6490 return MUNKI_INT_ASSERT;
6491 }
6492
6493 coeff[jj][k].ix = sx;
6494 coeff[jj][k].we = mtx_coef1[cx];
6495 }
6496 jj++;
6497 }
6498
6499 #ifdef HIGH_RES_PLOT
6500 /* Plot original re-sampling curves */
6501 {
6502 double *xx, *ss;
6503 double **yy;
6504
6505 xx = dvectorz(-1, m->nraw-1); /* X index */
6506 yy = dmatrixz(0, 5, -1, m->nraw-1); /* Curves distributed amongst 5 graphs */
6507
6508 for (i = 0; i < m->nraw; i++)
6509 xx[i] = i;
6510
6511 /* For each output wavelength */
6512 for (j = 0; j < nwav1; j++) {
6513 i = j % 5;
6514
6515 /* For each matrix value */
6516 for (k = 0; k < mtx_nocoef1[j]; k++) {
6517 yy[5][coeff[j][k].ix] += 0.5 * coeff[j][k].we;
6518 yy[i][coeff[j][k].ix] = coeff[j][k].we;
6519 }
6520 }
6521
6522 if (ref)
6523 printf("Original reflection wavelength sampling curves:\n");
6524 else
6525 printf("Original emission wavelength sampling curves:\n");
6526 do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
6527 free_dvector(xx, -1, m->nraw-1);
6528 free_dmatrix(yy, 0, 2, -1, m->nraw-1);
6529 }
6530 #endif /* HIGH_RES_PLOT */
6531
6532 // a1logd(p->log,3,"computing crossover points\n");
6533 /* Compute the crossover points between each filter */
6534 for (i = 0; i < (nwav1-1); i++) {
6535 double den, y1, y2, y3, y4, yn, xn; /* Location of intersection */
6536 double eps = 1e-6; /* Numerical tollerance */
6537 double besty = -1e6;
6538
6539 /* between filter i and i+1, we want to find the two */
6540 /* raw indexes where the weighting values cross over */
6541 /* Do a brute force search to avoid making assumptions */
6542 /* about the raw order. */
6543 for (j = 0; j < (mtx_nocoef1[i]-1); j++) {
6544 for (k = 0; k < (mtx_nocoef1[i+1]-1); k++) {
6545 if (coeff[i][j].ix == coeff[i+1][k].ix
6546 && coeff[i][j+1].ix == coeff[i+1][k+1].ix) {
6547
6548 // a1logd(p->log,3,"got it at %d, %d: %d = %d, %d = %d\n",j,k, coeff[i][j].ix, coeff[i+1][k].ix, coeff[i][j+1].ix, coeff[i+1][k+1].ix);
6549
6550 /* Compute the intersection of the two line segments */
6551 y1 = coeff[i][j].we;
6552 y2 = coeff[i][j+1].we;
6553 y3 = coeff[i+1][k].we;
6554 y4 = coeff[i+1][k+1].we;
6555 // a1logd(p->log,3,"y1 %f, y2 %f, y3 %f, y4 %f\n",y1, y2, y3, y4);
6556 den = -y4 + y3 + y2 - y1;
6557 if (fabs(den) < eps)
6558 continue;
6559 yn = (y2 * y3 - y1 * y4)/den;
6560 xn = (y3 - y1)/den;
6561 if (xn < -eps || xn > (1.0 + eps))
6562 continue;
6563 // a1logd(p->log,3,"den = %f, yn = %f, xn = %f\n",den,yn,xn);
6564 if (yn > besty) {
6565 xp[i+1].wav = XSPECT_WL(wl_short1, wl_long1, nwav1, i + 0.5);
6566 xp[i+1].raw = (1.0 - xn) * coeff[i][j].ix + xn * coeff[i][j+1].ix;
6567 xp[i+1].wei = yn;
6568 besty = yn;
6569 // a1logd(p->log,3,"Intersection %d: wav %f, raw %f, wei %f\n",i+1,xp[i+1].wav,xp[i+1].raw,xp[i+1].wei);
6570 // a1logd(p->log,3,"Found new best y %f\n",yn);
6571 }
6572 // a1logd(p->log,3,"\n");
6573 }
6574 }
6575 }
6576 if (besty < 0.0) { /* Assert */
6577 a1logw(p->log,"munki: failed to locate crossover between resampling filters\n");
6578 return MUNKI_INT_ASSERT;
6579 }
6580 // a1logd(p->log,3,"\n");
6581 }
6582
6583 /* Add the two points for the end filters */
6584 {
6585 double x5, x6, y5, y6; /* Points on intesecting line */
6586 double den, y1, y2, y3, y4, yn, xn; /* Location of intersection */
6587
6588 x5 = xp[1].raw;
6589 y5 = xp[1].wei;
6590 x6 = xp[2].raw;
6591 y6 = xp[2].wei;
6592
6593 /* Search for possible intersection point with first curve */
6594 /* Create equation for line from next two intersection points */
6595 for (j = 0; j < (mtx_nocoef1[0]-1); j++) {
6596 /* Extrapolate line to this segment */
6597 y3 = y5 + (coeff[0][j].ix - x5)/(x6 - x5) * (y6 - y5);
6598 y4 = y5 + (coeff[0][j+1].ix - x5)/(x6 - x5) * (y6 - y5);
6599 /* This segment of curve */
6600 y1 = coeff[0][j].we;
6601 y2 = coeff[0][j+1].we;
6602 if ( (( y1 >= y3 && y2 <= y4) /* Segments overlap */
6603 || ( y1 <= y3 && y2 >= y4))
6604 && (( coeff[0][j].ix < x5 && coeff[0][j].ix < x6
6605 && coeff[0][j+1].ix < x5 && coeff[0][j+1].ix < x6)
6606 || ( coeff[0][j+1].ix > x5 && coeff[0][j+1].ix > x6
6607 && coeff[0][j].ix > x5 && coeff[0][j].ix > x6))) {
6608 break;
6609 }
6610 }
6611 if (j >= mtx_nocoef1[0]) { /* Assert */
6612 a1logw(p->log,"munki: failed to find end crossover\n");
6613 return MUNKI_INT_ASSERT;
6614 }
6615 den = -y4 + y3 + y2 - y1;
6616 yn = (y2 * y3 - y1 * y4)/den;
6617 xn = (y3 - y1)/den;
6618 // a1logd(p->log,3,"den = %f, yn = %f, xn = %f\n",den,yn,xn);
6619 xp[0].wav = XSPECT_WL(wl_short1, wl_long1, nwav1, -0.5);
6620 xp[0].raw = (1.0 - xn) * coeff[0][j].ix + xn * coeff[0][j+1].ix;
6621 xp[0].wei = yn;
6622 // a1logd(p->log,3,"End 0 intersection %d: wav %f, raw %f, wei %f\n",0,xp[0].wav,xp[0].raw,xp[0].wei);
6623 // a1logd(p->log,3,"\n");
6624
6625 x5 = xp[nwav1-2].raw;
6626 y5 = xp[nwav1-2].wei;
6627 x6 = xp[nwav1-1].raw;
6628 y6 = xp[nwav1-1].wei;
6629
6630 // a1logd(p->log,3,"x5 %f, y5 %f, x6 %f, y6 %f\n",x5,y5,x6,y6);
6631 /* Search for possible intersection point with first curve */
6632 /* Create equation for line from next two intersection points */
6633 for (j = 0; j < (mtx_nocoef1[0]-1); j++) {
6634 /* Extrapolate line to this segment */
6635 y3 = y5 + (coeff[nwav1-1][j].ix - x5)/(x6 - x5) * (y6 - y5);
6636 y4 = y5 + (coeff[nwav1-1][j+1].ix - x5)/(x6 - x5) * (y6 - y5);
6637 /* This segment of curve */
6638 y1 = coeff[nwav1-1][j].we;
6639 y2 = coeff[nwav1-1][j+1].we;
6640 if ( (( y1 >= y3 && y2 <= y4) /* Segments overlap */
6641 || ( y1 <= y3 && y2 >= y4))
6642 && (( coeff[nwav1-1][j].ix < x5 && coeff[nwav1-1][j].ix < x6
6643 && coeff[nwav1-1][j+1].ix < x5 && coeff[nwav1-1][j+1].ix < x6)
6644 || ( coeff[nwav1-1][j+1].ix > x5 && coeff[nwav1-1][j+1].ix > x6
6645 && coeff[nwav1-1][j].ix > x5 && coeff[nwav1-1][j].ix > x6))) {
6646 break;
6647 }
6648 }
6649 if (j >= mtx_nocoef1[nwav1-1]) { /* Assert */
6650 a1logw(p->log, "munki: failed to find end crossover\n");
6651 return MUNKI_INT_ASSERT;
6652 }
6653 den = -y4 + y3 + y2 - y1;
6654 yn = (y2 * y3 - y1 * y4)/den;
6655 xn = (y3 - y1)/den;
6656 // a1logd(p->log,3,"den = %f, yn = %f, xn = %f\n",den,yn,xn);
6657 xp[nwav1].wav = XSPECT_WL(wl_short1, wl_long1, nwav1, nwav1-0.5);
6658 xp[nwav1].raw = (1.0 - xn) * coeff[nwav1-1][j].ix + xn * coeff[nwav1-1][j+1].ix;
6659 xp[nwav1].wei = yn;
6660 // a1logd(p->log,3,"End 36 intersection %d: wav %f, raw %f, wei %f\n",nwav1+1,xp[nwav1].wav,xp[nwav1].raw,xp[nwav1].wei);
6661 // a1logd(p->log,3,"\n");
6662 }
6663
6664 #ifdef HIGH_RES_PLOT
6665 /* Plot original re-sampling curves + crossing points */
6666 {
6667 double *xx, *ss;
6668 double **yy;
6669 double *xc, *yc;
6670
6671 xx = dvectorz(-1, m->nraw-1); /* X index */
6672 yy = dmatrixz(0, 5, -1, m->nraw-1); /* Curves distributed amongst 5 graphs */
6673 xc = dvectorz(0, nwav1); /* Crossover X */
6674 yc = dvectorz(0, nwav1); /* Crossover Y */
6675
6676 for (i = 0; i < m->nraw; i++)
6677 xx[i] = i;
6678
6679 /* For each output wavelength */
6680 for (j = 0; j < nwav1; j++) {
6681 i = j % 5;
6682
6683 /* For each matrix value */
6684 for (k = 0; k < mtx_nocoef1[j]; k++) {
6685 yy[5][coeff[j][k].ix] += 0.5 * coeff[j][k].we;
6686 yy[i][coeff[j][k].ix] = coeff[j][k].we;
6687 }
6688 }
6689
6690 /* Crosses at intersection points */
6691 for (i = 0; i <= nwav1; i++) {
6692 xc[i] = xp[i].raw;
6693 yc[i] = xp[i].wei;
6694 }
6695
6696 if (ref)
6697 printf("Original reflection sampling curves + crossover points\n");
6698 else
6699 printf("Original emsission sampling curves + crossover points\n");
6700 do_plot6p(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw, xc, yc, nwav1+1);
6701 free_dvector(xx, -1, m->nraw-1);
6702 free_dmatrix(yy, 0, 2, -1, m->nraw-1);
6703 free_dvector(xc, 0, nwav1);
6704 free_dvector(yc, 0, nwav1);
6705 }
6706 #endif /* HIGH_RES_PLOT */
6707
6708 #ifdef HIGH_RES_DEBUG
6709 /* Check to see if the area of each filter curve is the same */
6710 /* (yep, width times 2 * xover height is close to 1.0, and the */
6711 /* sum of the weightings is exactly 1.0) */
6712 for (i = 0; i < nwav1; i++) {
6713 double area1, area2;
6714 area1 = fabs(xp[i].raw - xp[i+1].raw) * (xp[i].wei + xp[i+1].wei);
6715
6716 area2 = 0.0;
6717 for (j = 0; j < (mtx_nocoef1[i]); j++)
6718 area2 += coeff[i][j].we;
6719
6720 printf("Area of curve %d = %f, %f\n",i,area1, area2);
6721 }
6722 #endif /* HIGH_RES_DEBUG */
6723
6724 /* From our crossover data, create a rspl that maps raw CCD index */
6725 /* value into wavelegth. */
6726 {
6727 co sd[101]; /* Scattered data points */
6728 datai glow, ghigh;
6729 datao vlow, vhigh;
6730 int gres[1];
6731 double avgdev[1];
6732
6733 if ((raw2wav = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) {
6734 a1logd(p->log,3,"munki: creating rspl for high res conversion failed\n");
6735 return MUNKI_INT_NEW_RSPL_FAILED;
6736 }
6737
6738 vlow[0] = 1e6;
6739 vhigh[0] = -1e6;
6740
6741 for (i = 0; i < (nwav1+1); i++) {
6742 sd[i].p[0] = xp[i].raw;
6743 sd[i].v[0] = xp[i].wav;
6744
6745 if (sd[i].v[0] < vlow[0])
6746 vlow[0] = sd[i].v[0];
6747 if (sd[i].v[0] > vhigh[0])
6748 vhigh[0] = sd[i].v[0];
6749 }
6750 glow[0] = 0.0;
6751 ghigh[0] = (double)(m->nraw-1);
6752 gres[0] = m->nraw;
6753 avgdev[0] = 0.0;
6754
6755 raw2wav->fit_rspl(raw2wav, 0, sd, nwav1+1, glow, ghigh, gres, vlow, vhigh, 1.0, avgdev, NULL);
6756 }
6757
6758 #ifdef EXISTING_SHAPE
6759 /* Convert each weighting curves values into normalized values and */
6760 /* accumulate into a single curve. */
6761 /* This probably isn't quite correct - we really need to remove */
6762 /* the effects of the convolution with the CCD cell widths. */
6763 /* perhaps it's closer to a lanczos2 if this were done ? */
6764 {
6765 for (i = 0; i < nwav1; i++) {
6766 double cwl; /* center wavelegth */
6767 double weight = 0.0;
6768
6769 for (j = 0; j < (mtx_nocoef1[i]); j++) {
6770 double w1, w2, cellw;
6771 co pp;
6772
6773 /* Translate CCD cell boundaries index to wavelength */
6774 pp.p[0] = (double)coeff[i][j].ix - 0.5;
6775 raw2wav->interp(raw2wav, &pp);
6776 w1 = pp.v[0];
6777
6778 pp.p[0] = (double)coeff[i][j].ix + 0.5;
6779 raw2wav->interp(raw2wav, &pp);
6780 w2 = pp.v[0];
6781
6782 cellw = fabs(w2 - w1);
6783
6784 cwl = XSPECT_WL(wl_short1, wl_long1, nwav1, i);
6785
6786 /* Translate CCD index to wavelength */
6787 pp.p[0] = (double)coeff[i][j].ix;
6788 raw2wav->interp(raw2wav, &pp);
6789 fshape[ncp].wl = pp.v[0] - cwl;
6790 fshape[ncp].we = coeff[i][j].we / (0.09 * cellw);
6791 ncp++;
6792 }
6793 }
6794
6795 /* Now sort by wavelength */
6796 #define HEAP_COMPARE(A,B) (A.wl < B.wl)
6797 HEAPSORT(munki_fs, fshape, ncp)
6798 #undef HEAP_COMPARE
6799
6800 /* Strip out leading zero's */
6801 for (i = 0; i < ncp; i++) {
6802 if (fshape[i].we != 0.0)
6803 break;
6804 }
6805 if (i > 1 && i < ncp) {
6806 memmove(&fshape[0], &fshape[i-1], sizeof(munki_fs) * (ncp - i + 1));
6807 ncp = ncp - i + 1;
6808 for (i = 0; i < ncp; i++) {
6809 if (fshape[i].we != 0.0)
6810 break;
6811 }
6812 }
6813
6814 #ifdef HIGH_RES_PLOT
6815 /* Plot the shape of the accumulated curve */
6816 {
6817 double *x1 = dvectorz(0, ncp-1);
6818 double *y1 = dvectorz(0, ncp-1);
6819
6820 for (i = 0; i < ncp; i++) {
6821 double x;
6822 x1[i] = fshape[i].wl;
6823 y1[i] = fshape[i].we;
6824 }
6825 if (ref)
6826 printf("Shape of existing reflection sampling curve:\n");
6827 else
6828 printf("Shape of existing emission sampling curve:\n");
6829 do_plot(x1, y1, NULL, NULL, ncp);
6830
6831 free_dvector(x1, 0, ncp-1);
6832 free_dvector(y1, 0, ncp-1);
6833 }
6834 #endif /* HIGH_RES_PLOT */
6835 }
6836 #endif /* EXISTING_SHAPE */
6837
6838 #ifdef HIGH_RES_DEBUG
6839 /* Check that the filter sums to a constant */
6840 {
6841 double x, sum;
6842
6843 for (x = 0.0; x < 10.0; x += 0.2) {
6844 sum = 0;
6845 sum += lin_fshape(fshape, ncp, x - 30.0);
6846 sum += lin_fshape(fshape, ncp, x - 20.0);
6847 sum += lin_fshape(fshape, ncp, x - 10.0);
6848 sum += lin_fshape(fshape, ncp, x - 0.0);
6849 sum += lin_fshape(fshape, ncp, x + 10.0);
6850 sum += lin_fshape(fshape, ncp, x + 20.0);
6851 printf("Offset %f, sum %f\n",x, sum);
6852 }
6853 }
6854 #endif /* HIGH_RES_DEBUG */
6855
6856 {
6857 double fshmax; /* filter shape max wavelength from center */
6858 #define MXNOWL 200 /* Max hires bands */
6859 #define MXNOFC 32
6860 munki_fc coeff2[MXNOWL][MXNOFC]; /* New filter cooefficients */
6861 double twidth;
6862
6863 /* Construct a set of filters that uses more CCD values */
6864 twidth = HIGHRES_WIDTH;
6865
6866 if (m->nwav2 > MXNOWL) { /* Assert */
6867 a1logw(p->log,"High res filter has too many bands\n");
6868 return MUNKI_INT_ASSERT;
6869 }
6870
6871 #ifdef EXISTING_SHAPE /* Else generate filter shape */
6872 /* Cut the filter width by half, to conver from 10nm to 5nm spacing */
6873 for (i = 0; i < ncp; i++)
6874 fshape[i].wl *= twidth/10.0;
6875 fshmax = -fshape[0].wl; /* aximum extent needed around zero */
6876 if (fshape[ncp-1].wl > fshmax)
6877 fshmax = fshape[ncp-1].wl;
6878 #else
6879 /* Use a crude means of determining width */
6880 for (fshmax = 50.0; fshmax >= 0.0; fshmax -= 0.1) {
6881 if (fabs(lanczos2(twidth, fshmax)) > 0.001) {
6882 fshmax += 0.1;
6883 break;
6884 }
6885 }
6886 if (fshmax <= 0.0) {
6887 a1logw(p->log,"munki: fshmax search failed\n");
6888 return MUNKI_INT_ASSERT;
6889 }
6890 #endif
6891 // a1logd(p->log,1,"fshmax = %f\n",fshmax);
6892
6893 #ifdef HIGH_RES_DEBUG
6894 /* Check that the filter sums to a constant */
6895 {
6896 double x, sum;
6897
6898 for (x = 0.0; x < 5.0; x += 0.1) {
6899 sum = 0;
6900 sum += lin_fshape(fshape, ncp, x - 15.0);
6901 sum += lin_fshape(fshape, ncp, x - 10.0);
6902 sum += lin_fshape(fshape, ncp, x - 5.0);
6903 sum += lin_fshape(fshape, ncp, x - 0.0);
6904 sum += lin_fshape(fshape, ncp, x + 5.0);
6905 sum += lin_fshape(fshape, ncp, x + 10.0);
6906 printf("Offset %f, sum %f\n",x, sum);
6907 }
6908 }
6909 #endif /* HIGH_RES_DEBUG */
6910
6911 /* Create all the filters */
6912 if ((*pmtx_nocoef2 = mtx_nocoef2 = (int *)calloc(m->nwav2, sizeof(int))) == NULL) {
6913 a1logd(p->log,1,"munki: malloc mtx_nocoef2 failed!\n");
6914 return MUNKI_INT_MALLOC;
6915 }
6916
6917 /* For all the useful CCD bands */
6918 for (i = 0; i < m->nraw; i++) {
6919 co pp;
6920 double w1, wl, w2;
6921
6922 /* Translate CCD center to to wavelength */
6923 pp.p[0] = (double)i;
6924 raw2wav->interp(raw2wav, &pp);
6925 wl = pp.v[0];
6926
6927 /* Translate CCD cell boundaries index to wavelength */
6928 pp.p[0] = i - 0.5;
6929 raw2wav->interp(raw2wav, &pp);
6930 w1 = pp.v[0];
6931
6932 pp.p[0] = i + 0.5;
6933 raw2wav->interp(raw2wav, &pp);
6934 w2 = pp.v[0];
6935
6936 a1logd(p->log,1,"CCD %d, wl %f - %f\n",i,w1,w2);
6937
6938 /* For each filter */
6939 for (j = 0; j < m->nwav2; j++) {
6940 double cwl, rwl; /* center, relative wavelegth */
6941 double we;
6942
6943 cwl = m->wl_short2 + (double)j * (m->wl_long2 - m->wl_short2)/(m->nwav2-1.0);
6944
6945 if (cwl < min_wl) /* Duplicate below this wl */
6946 cwl = min_wl;
6947
6948 rwl = wl - cwl; /* relative wavelgth to filter */
6949
6950 if (fabs(w1 - cwl) > fshmax && fabs(w2 - cwl) > fshmax)
6951 continue; /* Doesn't fall into this filter */
6952
6953 #ifdef BOX_INTEGRATE
6954 /* Integrate in 0.05 nm increments from filter shape */
6955 {
6956 int nn;
6957 double lw, ll;
6958 #ifdef FAST_HIGH_RES_SETUP
6959 # define FINC 0.2
6960 #else
6961 # define FINC 0.05
6962 #endif
6963 nn = (int)(fabs(w2 - w1)/FINC + 0.5);
6964
6965 lw = w1;
6966 #ifdef EXISTING_SHAPE
6967 ll = lin_fshape(fshape, ncp, w1- cwl);
6968 #else
6969 ll = lanczos2(twidth, w1- cwl);
6970 #endif
6971 we = 0.0;
6972 for (k = 0; k < nn; k++) {
6973 double cw, cl;
6974
6975 #if defined(__APPLE__) && defined(__POWERPC__)
6976 gcc_bug_fix(k);
6977 #endif
6978 cw = w1 + (k+1.0)/(nn +1.0) * (w2 - w1);
6979 #ifdef EXISTING_SHAPE
6980 cl = lin_fshape(fshape, ncp, cw - cwl);
6981 #else
6982 cl = lanczos2(twidth, cw- cwl);
6983 #endif
6984 we += 0.5 * (cl + ll) * (lw - cw);
6985 ll = cl;
6986 lw = cw;
6987 }
6988 }
6989
6990
6991 #else /* Point sample with weighting */
6992
6993 #ifdef EXISTING_SHAPE
6994 we = fabs(w2 - w1) * lin_fshape(fshape, ncp, rwl);
6995 #else
6996 we = fabs(w2 - w1) * lanczos2(twidth, rwl);
6997 #endif
6998
6999 #endif /* Integrate/Point sample */
7000
7001 if (mtx_nocoef2[j] >= MXNOFC) {
7002 a1logw(p->log,"munki: run out of high res filter space\n");
7003 return MUNKI_INT_ASSERT;
7004 }
7005
7006 coeff2[j][mtx_nocoef2[j]].ix = i;
7007 coeff2[j][mtx_nocoef2[j]++].we = we;
7008 a1logd(p->log,1,"filter %d, cwl %f, rwl %f, ix %d, we %f\n",j,cwl,rwl,i,we);
7009 }
7010 }
7011
7012 /* Dump the filter coefficients */
7013 if (p->log->debug >= 1) {
7014
7015 /* For each output wavelength */
7016 for (j = 0; j < m->nwav2; j++) {
7017
7018 a1logd(p->log,1,"filter %d, cwl %f\n",j,XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, j));
7019 /* For each matrix value */
7020 for (k = 0; k < mtx_nocoef2[j]; k++) {
7021 a1logd(p->log,1," CCD %d, we %f\n",coeff2[j][k].ix,coeff2[j][k].we);
7022 }
7023 }
7024 }
7025
7026 #ifdef HIGH_RES_PLOT
7027 /* Plot resampled curves */
7028 {
7029 double *xx, *ss;
7030 double **yy;
7031
7032 xx = dvectorz(0, m->nraw-1); /* X index */
7033 yy = dmatrixz(0, 5, 0, m->nraw-1); /* Curves distributed amongst 5 graphs */
7034
7035 for (i = 0; i < m->nraw; i++)
7036 xx[i] = i;
7037
7038 /* For each output wavelength */
7039 for (j = 0; j < m->nwav2; j++) {
7040 i = j % 5;
7041
7042 /* For each matrix value */
7043 for (k = 0; k < mtx_nocoef2[j]; k++) {
7044 yy[5][coeff2[j][k].ix] += 0.5 * coeff2[j][k].we;
7045 yy[i][coeff2[j][k].ix] = coeff2[j][k].we;
7046 }
7047 }
7048
7049 if (ref)
7050 printf("Hi-Res reflection wavelength sampling curves:\n");
7051 else
7052 printf("Hi-Res emission wavelength sampling curves:\n");
7053 do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
7054 free_dvector(xx, 0, m->nraw-1);
7055 free_dmatrix(yy, 0, 2, 0, m->nraw-1);
7056 }
7057 #endif /* HIGH_RES_PLOT */
7058
7059 /* Convert into runtime format */
7060 {
7061 int xcount;
7062
7063 if ((*pmtx_index2 = mtx_index2 = (int *)calloc(m->nwav2, sizeof(int))) == NULL) {
7064 a1logd(p->log,1,"munki: malloc mtx_index2 failed!\n");
7065 return MUNKI_INT_MALLOC;
7066 }
7067
7068 xcount = 0;
7069 for (j = 0; j < m->nwav2; j++) {
7070 mtx_index2[j] = coeff2[j][0].ix;
7071 xcount += mtx_nocoef2[j];
7072 }
7073
7074 if ((*pmtx_coef2 = mtx_coef2 = (double *)calloc(xcount, sizeof(double))) == NULL) {
7075 a1logd(p->log,1,"munki: malloc mtx_coef2 failed!\n");
7076 return MUNKI_INT_MALLOC;
7077 }
7078
7079 for (i = j = 0; j < m->nwav2; j++)
7080 for (k = 0; k < mtx_nocoef2[j]; k++, i++)
7081 mtx_coef2[i] = coeff2[j][k].we;
7082 }
7083
7084 /* Normalise the filters area in CCD space, while maintaining the */
7085 /* total contribution of each CCD at the target too. */
7086 /* Hmm. This will wreck super-sample. We should fix it */
7087 #ifdef DO_CCDNORM /* Normalise CCD values to original */
7088 {
7089 double x[4], y[4];
7090 double avg[2], max[2];
7091 double ccdsum[2][128]; /* Target weight/actual for each CCD */
7092 double dth[2];
7093
7094 avg[0] = avg[1] = 0.0;
7095 max[0] = max[1] = 0.0;
7096 for (j = 0; j < 128; j++) {
7097 ccdsum[0][j] = 0.0;
7098 ccdsum[1][j] = 0.0;
7099 }
7100
7101 /* Compute the weighting of each CCD value in the normal output */
7102 for (cx = j = 0; j < m->nwav1; j++) { /* For each wavelength */
7103
7104 /* For each matrix value */
7105 sx = mtx_index1[j]; /* Starting index */
7106 if (j < (m->nwav1-2) && sx == mtx_index1[j+1]) {
7107 cx += mtx_nocoef1[j];
7108 continue; /* Skip all duplicate filters */
7109 }
7110 for (k = 0; k < mtx_nocoef1[j]; k++, cx++, sx++) {
7111 ccdsum[0][sx] += mtx_coef1[cx];
7112 //printf("~1 Norm CCD [%d] %f += [%d] %f\n",sx,ccdsum[0][sx],cx, mtx_coef1[cx]);
7113 }
7114 }
7115
7116 /* Compute the weighting of each CCD value in the hires output */
7117 for (cx = j = 0; j < m->nwav2; j++) { /* For each wavelength */
7118
7119 /* For each matrix value */
7120 sx = mtx_index2[j]; /* Starting index */
7121 if (j < (m->nwav2-2) && sx == mtx_index2[j+1]) {
7122 cx += mtx_nocoef2[j];
7123 continue; /* Skip all duplicate filters */
7124 }
7125 for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) {
7126 ccdsum[1][sx] += mtx_coef2[cx];
7127 //printf("~1 HiRes CCD [%d] %f += [%d] %f\n",sx,ccdsum[1][sx],cx, mtx_coef2[cx]);
7128 }
7129 }
7130
7131 #ifdef HIGH_RES_PLOT
7132 /* Plot target CCD values */
7133 {
7134 double xx[128], y1[128], y2[128];
7135
7136 for (i = 0; i < 128; i++) {
7137 xx[i] = i;
7138 y1[i] = ccdsum[0][i];
7139 y2[i] = ccdsum[1][i];
7140 }
7141
7142 printf("Raw target and actual CCD weight sums:\n");
7143 do_plot(xx, y1, y2, NULL, 128);
7144 }
7145 #endif
7146
7147 /* Figure valid range and extrapolate to edges */
7148 dth[0] = 0.0; /* ref */
7149 dth[1] = 0.007; /* hires */
7150
7151 for (k = 0; k < 2; k++) {
7152
7153 for (i = 0; i < 128; i++) {
7154 if (ccdsum[k][i] > max[k])
7155 max[k] = ccdsum[k][i];
7156 }
7157
7158 //printf("~1 max[%d] = %f\n",k, max[k]);
7159 /* Figure out the valid range */
7160 for (i = 64; i >= 0; i--) {
7161 if (ccdsum[k][i] > (0.8 * max[k])) {
7162 x[0] = (double)i;
7163 } else {
7164 break;
7165 }
7166 }
7167 for (i = 64; i < 128; i++) {
7168 if (ccdsum[k][i] > (0.8 * max[k])) {
7169 x[3] = (double)i;
7170 } else {
7171 break;
7172 }
7173 }
7174 /* Space off the last couple of entries */
7175 x[0] += 2.0;
7176 x[3] -= 6.0;
7177 x[1] = floor((2 * x[0] + x[3])/3.0);
7178 x[2] = floor((x[0] + 2 * x[3])/3.0);
7179
7180 for (i = 0; i < 4; i++)
7181 y[i] = ccdsum[k][(int)x[i]];
7182
7183 //printf("~1 extrap nodes %f, %f, %f, %f\n",x[0],x[1],x[2],x[3]);
7184 //printf("~1 extrap value %f, %f, %f, %f\n",y[0],y[1],y[2],y[3]);
7185
7186 for (i = 0; i < 128; i++) {
7187 double xw, yw;
7188
7189 xw = (double)i;
7190
7191 /* Compute interpolated value using Lagrange: */
7192 yw = y[0] * (xw-x[1]) * (xw-x[2]) * (xw-x[3])
7193 /((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3]))
7194 + y[1] * (xw-x[0]) * (xw-x[2]) * (xw-x[3])
7195 /((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3]))
7196 + y[2] * (xw-x[0]) * (xw-x[1]) * (xw-x[3])
7197 /((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3]))
7198 + y[3] * (xw-x[0]) * (xw-x[1]) * (xw-x[2])
7199 /((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2]));
7200
7201 if ((xw < x[0] || xw > x[3])
7202 && fabs(ccdsum[k][i] - yw)/yw > dth[k]) {
7203 ccdsum[k][i] = yw;
7204 }
7205 avg[k] += ccdsum[k][i];
7206 }
7207 avg[k] /= 128.0;
7208 }
7209
7210 #ifdef HIGH_RES_PLOT
7211 /* Plot target CCD values */
7212 {
7213 double xx[129], y1[129], y2[129];
7214
7215 for (i = 0; i < 128; i++) {
7216 xx[i] = i;
7217 y1[i] = ccdsum[0][i]/avg[0];
7218 y2[i] = ccdsum[1][i]/avg[1];
7219 }
7220 xx[i] = i;
7221 y1[i] = 0.0;
7222 y2[i] = 0.0;
7223
7224 printf("Extrap. target and actual CCD weight sums:\n");
7225 do_plot(xx, y1, y2, NULL, 129);
7226 }
7227 #endif
7228
7229 #ifdef DO_CCDNORMAVG /* Just correct by average */
7230 for (cx = j = 0; j < m->nwav2; j++) { /* For each wavelength */
7231
7232 /* For each matrix value */
7233 sx = mtx_index2[j]; /* Starting index */
7234 for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) {
7235 mtx_coef2[cx] *= 10.0/twidth * avg[0]/avg[1];
7236 }
7237 }
7238
7239 #else /* Correct by CCD bin */
7240
7241 /* Correct the weighting of each CCD value in the hires output */
7242 for (i = 0; i < 128; i++) {
7243 ccdsum[1][i] = 10.0/twidth * ccdsum[0][i]/ccdsum[1][i]; /* Correction factor */
7244 }
7245 for (cx = j = 0; j < m->nwav2; j++) { /* For each wavelength */
7246
7247 /* For each matrix value */
7248 sx = mtx_index2[j]; /* Starting index */
7249 for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) {
7250 mtx_coef2[cx] *= ccdsum[1][sx];
7251 }
7252 }
7253 #endif
7254 }
7255 #endif /* DO_CCDNORM */
7256
7257 #ifdef HIGH_RES_PLOT
7258 {
7259 static munki_fc coeff2[MXNOWL][MXNOFC];
7260 double *xx, *ss;
7261 double **yy;
7262
7263 /* Convert the native filter cooeficient representation to */
7264 /* a 2D array we can randomly index. */
7265 for (cx = j = 0; j < m->nwav2; j++) { /* For each output wavelength */
7266 if (j >= MXNOWL) { /* Assert */
7267 a1loge(p->log,1,"munki: number of hires output wavelenths is > %d\n",MXNOWL);
7268 return MUNKI_INT_ASSERT;
7269 }
7270
7271 /* For each matrix value */
7272 sx = mtx_index2[j]; /* Starting index */
7273 for (k = 0; k < mtx_nocoef2[j]; k++, cx++, sx++) {
7274 if (k >= MXNOFC) { /* Assert */
7275 a1loge(p->log,1,"munki: number of hires filter coeefs is > %d\n",MXNOFC);
7276 return MUNKI_INT_ASSERT;
7277 }
7278 coeff2[j][k].ix = sx;
7279 coeff2[j][k].we = mtx_coef2[cx];
7280 }
7281 }
7282
7283 xx = dvectorz(-1, m->nraw-1); /* X index */
7284 yy = dmatrixz(0, 5, -1, m->nraw-1); /* Curves distributed amongst 5 graphs */
7285
7286 for (i = 0; i < m->nraw; i++)
7287 xx[i] = i;
7288
7289 /* For each output wavelength */
7290 for (j = 0; j < m->nwav2; j++) {
7291 i = j % 5;
7292
7293 /* For each matrix value */
7294 for (k = 0; k < mtx_nocoef2[j]; k++) {
7295 yy[5][coeff2[j][k].ix] += 0.5 * coeff2[j][k].we;
7296 yy[i][coeff2[j][k].ix] = coeff2[j][k].we;
7297 }
7298 }
7299
7300 printf("Normalized Hi-Res wavelength sampling curves: %s\n",ref ? "refl" : "emis");
7301 do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], m->nraw);
7302 free_dvector(xx, -1, m->nraw-1);
7303 free_dmatrix(yy, 0, 2, -1, m->nraw-1);
7304 }
7305 #endif /* HIGH_RES_PLOT */
7306 #undef MXNOWL
7307 #undef MXNOFC
7308
7309 /* Basic capability is initialised */
7310 m->hr_inited++;
7311
7312 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
7313 /* If both reflective and emissive samplings have been created, */
7314 /* deal with upsampling the references and calibrations */
7315 if (m->hr_inited == 2) {
7316 #ifdef ONEDSTRAYLIGHTUS
7317 double **slp; /* 2D Array of stray light values */
7318 #endif /* !ONEDSTRAYLIGHTUS */
7319 rspl *trspl; /* Upsample rspl */
7320 cow sd[40 * 40]; /* Scattered data points of existing references */
7321 datai glow, ghigh;
7322 datao vlow, vhigh;
7323 int gres[2];
7324 double avgdev[2];
7325 int ii, jj;
7326 co pp;
7327
7328 /* First the 1D references */
7329 if ((trspl = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) {
7330 a1logd(p->log,3,"munki: creating rspl for high res conversion failed\n");
7331 raw2wav->del(raw2wav);
7332 return MUNKI_INT_NEW_RSPL_FAILED;
7333 }
7334
7335 for (ii = 0; ii < 4; ii++) {
7336 double **ref2, *ref1;
7337
7338 if (ii == 0) {
7339 ref1 = m->white_ref1;
7340 ref2 = &m->white_ref2;
7341 } else if (ii == 1) {
7342 ref1 = m->emis_coef1;
7343 ref2 = &m->emis_coef2;
7344 } else if (ii == 2) {
7345 ref1 = m->amb_coef1;
7346 ref2 = &m->amb_coef2;
7347 } else {
7348 ref1 = m->proj_coef1;
7349 ref2 = &m->proj_coef2;
7350 }
7351
7352 vlow[0] = 1e6;
7353 vhigh[0] = -1e6;
7354
7355 /* Set scattered points */
7356 for (i = 0; i < m->nwav1; i++) {
7357
7358 sd[i].p[0] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
7359 sd[i].v[0] = ref1[i];
7360 sd[i].w = 1.0;
7361
7362 if (sd[i].v[0] < vlow[0])
7363 vlow[0] = sd[i].v[0];
7364 if (sd[i].v[0] > vhigh[0])
7365 vhigh[0] = sd[i].v[0];
7366 }
7367
7368 #ifdef NEVER
7369 /* Add some corrections at short wavelengths */
7370 /* (The combination of the diffraction grating and */
7371 /* LED light source doesn't give us much to work with here.) */
7372 if (ii == 1) { /* Emission */
7373 sd[0].v[0] *= 10.0; /* 380 */
7374 sd[1].v[0] *= 3.0; /* 390 */
7375 sd[2].v[0] *= 1.0; /* 400 */
7376 }
7377
7378 if (ii == 2) { /* Ambient */
7379 sd[0].v[0] *= 5.0; /* 380 */ /* Doesn't help much, because */
7380 sd[1].v[0] *= 2.0; /* 390 */ /* the diffuser absorbs short WL */
7381 sd[2].v[0] *= 1.0; /* 400 */
7382 }
7383
7384 if (ii == 3) { /* Projector */
7385 sd[0].v[0] *= 0.1; /* 380 */
7386 sd[1].v[0] *= 1.0; /* 390 */
7387
7388 sd[i].p[0] = 350.0; /* 350 */
7389 sd[i].v[0] = 0.2 * sd[0].v[0];
7390 sd[i++].w = 1.0;
7391 }
7392 #endif
7393
7394 glow[0] = m->wl_short2;
7395 ghigh[0] = m->wl_long2;
7396 gres[0] = m->nwav2;
7397 avgdev[0] = 0.0;
7398
7399 trspl->fit_rspl_w(trspl, 0, sd, i, glow, ghigh, gres, vlow, vhigh, 5.0, avgdev, NULL);
7400
7401 if ((*ref2 = (double *)calloc(m->nwav2, sizeof(double))) == NULL) {
7402 raw2wav->del(raw2wav);
7403 trspl->del(trspl);
7404 a1logd(p->log,1,"munki: malloc mtx_coef2 failed!\n");
7405 return MUNKI_INT_MALLOC;
7406 }
7407
7408 /* Create upsampled version */
7409 for (i = 0; i < m->nwav2; i++) {
7410 pp.p[0] = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
7411 if (pp.p[0] < min_wl) /* Duplicate below this wl */
7412 pp.p[0] = min_wl;
7413 trspl->interp(trspl, &pp);
7414 if (pp.v[0] < 0.0)
7415 pp.v[0] = 0.0;
7416 (*ref2)[i] = pp.v[0];
7417 }
7418
7419
7420 #ifdef NEVER
7421 /* Add some corrections at short wavelengths */
7422 if (ii == 0) {
7423 /* 376.67 - 470 */
7424 double corr[5][29] = {
7425 { 4.2413, 4.0654, 3.6425, 3.2194, 2.8692, 2.3964,
7426 1.9678, 1.3527, 0.7978, 0.7823, 0.8474, 0.9227,
7427 0.9833, 1.0164, 1.0270, 1.0241, 1.0157, 1.0096,
7428 1.0060, 1.0, 1.0, 1.0, 1.0, 1.0,
7429 1.0, 1.0, 1.0, 1.0, 1.0 },
7430
7431 };
7432
7433 for (i = 0; i < m->nwav2; i++) {
7434 double wl;
7435 int ix;
7436 wl = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
7437 ix = XSPECT_IX(376.6666667, 470.0, 29, wl);
7438
7439 if (ix < 0)
7440 ix = 0;
7441 else if (ix >= 29)
7442 ix = 28;
7443 (*ref2)[i] *= corr[ii][ix];
7444 }
7445
7446 }
7447 #endif
7448
7449 #ifdef HIGH_RES_PLOT
7450 /* Plot original and upsampled reference */
7451 {
7452 double *x1 = dvectorz(0, m->nwav2-1);
7453 double *y1 = dvectorz(0, m->nwav2-1);
7454 double *y2 = dvectorz(0, m->nwav2-1);
7455
7456 for (i = 0; i < m->nwav2; i++) {
7457 double wl = m->wl_short2 + (double)i * (m->wl_long2 - m->wl_short2)/(m->nwav2-1.0);
7458 x1[i] = wl;
7459 y1[i] = (*ref2)[i];
7460 if (wl < m->wl_short1 || wl > m->wl_long1) {
7461 y2[i] = 0.0;
7462 } else {
7463 double x, wl1, wl2;
7464 for (j = 0; j < (m->nwav1-1); j++) {
7465 wl1 = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, j);
7466 wl2 = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, j+1);
7467 if (wl >= wl1 && wl <= wl2)
7468 break;
7469 }
7470 x = (wl - wl1)/(wl2 - wl1);
7471 y2[i] = ref1[j] + (ref1[j+1] - ref1[j]) * x;
7472 }
7473 }
7474 printf("Original and up-sampled ");
7475 if (ii == 0) {
7476 printf("Reflective cal. curve:\n");
7477 } else if (ii == 1) {
7478 printf("Emission cal. curve:\n");
7479 } else if (ii == 2) {
7480 printf("Ambient cal. curve:\n");
7481 } else {
7482 printf("Projector cal. curve:\n");
7483 }
7484 do_plot(x1, y1, y2, NULL, m->nwav2);
7485
7486 free_dvector(x1, 0, m->nwav2-1);
7487 free_dvector(y1, 0, m->nwav2-1);
7488 free_dvector(y2, 0, m->nwav2-1);
7489 }
7490 #endif /* HIGH_RES_PLOT */
7491 }
7492 trspl->del(trspl);
7493
7494 #ifdef ONEDSTRAYLIGHTUS
7495 /* Then the 2D stray light using linear interpolation */
7496 slp = dmatrix(0, m->nwav1-1, 0, m->nwav1-1);
7497
7498 /* Set scattered points */
7499 for (i = 0; i < m->nwav1; i++) { /* Output wavelength */
7500 for (j = 0; j < m->nwav1; j++) { /* Input wavelength */
7501
7502 slp[i][j] = m->straylight1[i][j];
7503
7504 /* Use interpolate/extrapolate for middle points */
7505 if (j == (i-1) || j == i || j == (i+1)) {
7506 int j0, j1;
7507 double w0, w1;
7508 if (j == (i-1)) {
7509 if (j <= 0)
7510 j0 = j+3, j1 = j+4;
7511 else if (j >= (m->nwav1-3))
7512 j0 = j-2, j1 = j-1;
7513 else
7514 j0 = j-1, j1 = j+3;
7515 } else if (j == i) {
7516 if (j <= 1)
7517 j0 = j+2, j1 = j+3;
7518 else if (j >= (m->nwav1-2))
7519 j0 = j-3, j1 = j-2;
7520 else
7521 j0 = j-2, j1 = j+2;
7522 } else if (j == (i+1)) {
7523 if (j <= 2)
7524 j0 = j+1, j1 = j+2;
7525 else if (j >= (m->nwav1-1))
7526 j0 = j-4, j1 = j-3;
7527 else
7528 j0 = j-3, j1 = j+1;
7529 }
7530 w1 = (j - j0)/(j1 - j0);
7531 w0 = 1.0 - w1;
7532 slp[i][j] = w0 * m->straylight1[i][j0]
7533 + w1 * m->straylight1[i][j1];
7534
7535 }
7536 }
7537 }
7538 #else /* !ONEDSTRAYLIGHTUS */
7539 /* Then setup 2D stray light using rspl */
7540 if ((trspl = new_rspl(RSPL_NOFLAGS, 2, 1)) == NULL) {
7541 a1logd(p->log,3,"munki: creating rspl for high res conversion failed\n");
7542 raw2wav->del(raw2wav);
7543 return MUNKI_INT_NEW_RSPL_FAILED;
7544 }
7545
7546 /* Set scattered points */
7547 for (i = 0; i < m->nwav1; i++) { /* Output wavelength */
7548 for (j = 0; j < m->nwav1; j++) { /* Input wavelength */
7549 int ix = i * m->nwav1 + j;
7550
7551 sd[ix].p[0] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
7552 sd[ix].p[1] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, j);
7553 sd[ix].v[0] = m->straylight1[i][j];
7554 sd[ix].w = 1.0;
7555 if (j == (i-1) || j == i || j == (i+1))
7556 sd[ix].w = 0.0;
7557 }
7558 }
7559
7560 glow[0] = m->wl_short2;
7561 glow[1] = m->wl_short2;
7562 ghigh[0] = m->wl_long2;
7563 ghigh[1] = m->wl_long2;
7564 gres[0] = m->nwav2;
7565 gres[1] = m->nwav2;
7566 avgdev[0] = 0.0;
7567 avgdev[1] = 0.0;
7568
7569 trspl->fit_rspl_w(trspl, 0, sd, m->nwav1 * m->nwav1, glow, ghigh, gres, NULL, NULL, 0.5, avgdev, NULL);
7570 #endif /* !ONEDSTRAYLIGHTUS */
7571
7572 m->straylight2 = dmatrixz(0, m->nwav2-1, 0, m->nwav2-1);
7573
7574 /* Create upsampled version */
7575 for (i = 0; i < m->nwav2; i++) { /* Output wavelength */
7576 for (j = 0; j < m->nwav2; j++) { /* Input wavelength */
7577 double p0, p1;
7578 p0 = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
7579 if (p0 < min_wl) /* Duplicate below this wl */
7580 p0 = min_wl;
7581 p1 = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, j);
7582 if (p1 < min_wl) /* Duplicate below this wl */
7583 p1 = min_wl;
7584 #ifdef ONEDSTRAYLIGHTUS
7585 /* Do linear interp with clipping at ends */
7586 {
7587 int x0, x1, y0, y1;
7588 double xx, yy, w0, w1, v0, v1;
7589
7590 xx = (m->nwav1-1.0) * (p0 - m->wl_short1)/(m->wl_long1 - m->wl_short1);
7591 x0 = (int)floor(xx);
7592 if (x0 <= 0)
7593 x0 = 0;
7594 else if (x0 >= (m->nwav1-2))
7595 x0 = m->nwav1-2;
7596 x1 = x0 + 1;
7597 w1 = xx - (double)x0;
7598 w0 = 1.0 - w1;
7599
7600 yy = (m->nwav1-1.0) * (p1 - m->wl_short1)/(m->wl_long1 - m->wl_short1);
7601 y0 = (int)floor(yy);
7602 if (y0 <= 0)
7603 y0 = 0;
7604 else if (y0 >= (m->nwav1-2))
7605 y0 = m->nwav1-2;
7606 y1 = y0 + 1;
7607 v1 = yy - (double)y0;
7608 v0 = 1.0 - v1;
7609
7610 pp.v[0] = w0 * v0 * slp[x0][y0]
7611 + w0 * v1 * slp[x0][y1]
7612 + w1 * v0 * slp[x1][y0]
7613 + w1 * v1 * slp[x1][y1];
7614 }
7615 #else /* !ONEDSTRAYLIGHTUS */
7616 pp.p[0] = p0;
7617 pp.p[1] = p1;
7618 trspl->interp(trspl, &pp);
7619 #endif /* !ONEDSTRAYLIGHTUS */
7620 m->straylight2[i][j] = pp.v[0] * HIGHRES_WIDTH/10.0;
7621 if (m->straylight2[i][j] > 0.0)
7622 m->straylight2[i][j] = 0.0;
7623 }
7624 }
7625
7626 /* Fix primary wavelength weight and neighbors */
7627 for (i = 0; i < m->nwav2; i++) { /* Output wavelength */
7628 double sum;
7629
7630 if (i > 0)
7631 m->straylight2[i][i-1] = 0.0;
7632 m->straylight2[i][i] = 0.0;
7633 if (i < (m->nwav2-1))
7634 m->straylight2[i][i+1] = 0.0;
7635
7636 for (sum = 0.0, j = 0; j < m->nwav2; j++)
7637 sum += m->straylight2[i][j];
7638
7639 m->straylight2[i][i] = 1.0 - sum; /* Total sum should be 1.0 */
7640 }
7641
7642 #ifdef HIGH_RES_PLOT_STRAYL
7643 /* Plot original and upsampled reference */
7644 {
7645 double *x1 = dvectorz(0, m->nwav2-1);
7646 double *y1 = dvectorz(0, m->nwav2-1);
7647 double *y2 = dvectorz(0, m->nwav2-1);
7648
7649 for (i = 0; i < m->nwav2; i++) { /* Output wavelength */
7650 double wli = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, i);
7651 int i1 = XSPECT_IX(m->wl_short1, m->wl_long1, m->nwav1, wli);
7652
7653 for (j = 0; j < m->nwav2; j++) {
7654 double wl = XSPECT_WL(m->wl_short2, m->wl_long2, m->nwav2, j);
7655 x1[j] = wl;
7656 y1[j] = m->straylight2[i][j];
7657 if (y1[j] == 0.0)
7658 y1[j] = -8.0;
7659 else
7660 y1[j] = log10(fabs(y1[j]));
7661 if (wli < m->wl_short1 || wli > m->wl_long1
7662 || wl < m->wl_short1 || wl > m->wl_long1) {
7663 y2[j] = -8.0;
7664 } else {
7665 double x, wl1, wl2;
7666 for (k = 0; k < (m->nwav1-1); k++) {
7667 wl1 = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, k);
7668 wl2 = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, k+1);
7669 if (wl >= wl1 && wl <= wl2)
7670 break;
7671 }
7672 x = (wl - wl1)/(wl2 - wl1);
7673 y2[j] = m->straylight1[i1][k] + (m->straylight1[i1][k+1]
7674 - m->straylight1[i1][k]) * x;
7675 if (y2[j] == 0.0)
7676 y2[j] = -8.0;
7677 else
7678 y2[j] = log10(fabs(y2[j]));
7679 }
7680 }
7681 do_plot(x1, y1, y2, NULL, m->nwav2);
7682 }
7683
7684 free_dvector(x1, 0, m->nwav2-1);
7685 free_dvector(y1, 0, m->nwav2-1);
7686 free_dvector(y2, 0, m->nwav2-1);
7687 }
7688 #endif /* HIGH_RES_PLOT */
7689
7690 #ifdef ONEDSTRAYLIGHTUS
7691 free_dmatrix(slp, 0, m->nwav1-1, 0, m->nwav1-1);
7692 #else /* !ONEDSTRAYLIGHTUS */
7693 trspl->del(trspl);
7694 #endif /* !ONEDSTRAYLIGHTUS */
7695
7696 /* - - - - - - - - - - - - - - - - - - - - - - - - - */
7697 /* Allocate space for per mode calibration reference */
7698 /* and bring high res calibration factors into line */
7699 /* with current standard res. ones */
7700 for (i = 0; i < mk_no_modes; i++) {
7701 munki_state *s = &m->ms[i];
7702
7703 s->cal_factor2 = dvectorz(0, m->nwav2-1);
7704
7705 switch(i) {
7706 case mk_refl_spot:
7707 case mk_refl_scan:
7708 if (s->cal_valid) {
7709 munki_absraw_to_abswav1(p, 1, &s->cal_factor1, &s->white_data);
7710 munki_absraw_to_abswav2(p, 1, &s->cal_factor2, &s->white_data);
7711 munki_compute_white_cal(p, s->cal_factor1, m->white_ref1, s->cal_factor1,
7712 s->cal_factor2, m->white_ref2, s->cal_factor2);
7713 }
7714 break;
7715
7716 case mk_emiss_spot_na:
7717 case mk_emiss_spot:
7718 case mk_emiss_scan:
7719 for (j = 0; j < m->nwav2; j++)
7720 s->cal_factor2[j] = EMIS_SCALE_FACTOR * m->emis_coef2[j];
7721 break;
7722
7723 case mk_tele_spot_na:
7724 case mk_tele_spot:
7725 for (j = 0; j < m->nwav2; j++)
7726 s->cal_factor2[j] = EMIS_SCALE_FACTOR * m->proj_coef2[j];
7727 break;
7728
7729 case mk_amb_spot:
7730 case mk_amb_flash:
7731 if (m->amb_coef1 != NULL) {
7732 for (j = 0; j < m->nwav2; j++)
7733 s->cal_factor2[j] = AMB_SCALE_FACTOR * m->amb_coef2[j];
7734 s->cal_valid = 1;
7735 }
7736 break;
7737 case mk_trans_spot:
7738 case mk_trans_scan:
7739 if (s->cal_valid) {
7740 munki_absraw_to_abswav1(p, 1, &s->cal_factor1, &s->white_data);
7741 munki_absraw_to_abswav2(p, 1, &s->cal_factor2, &s->white_data);
7742 munki_compute_white_cal(p, s->cal_factor1, NULL, s->cal_factor1,
7743 s->cal_factor2, NULL, s->cal_factor2);
7744 }
7745 break;
7746 }
7747 }
7748 }
7749 }
7750
7751 raw2wav->del(raw2wav);
7752
7753 return ev;
7754 }
7755
7756 #endif /* HIGH_RES */
7757
7758
7759 /* return nz if high res is supported */
munki_imp_highres(munki * p)7760 int munki_imp_highres(munki *p) {
7761 #ifdef HIGH_RES
7762 return 1;
7763 #else
7764 return 0;
7765 #endif /* HIGH_RES */
7766 }
7767
7768 /* Set to high resolution mode */
munki_set_highres(munki * p)7769 munki_code munki_set_highres(munki *p) {
7770 int i;
7771 munkiimp *m = (munkiimp *)p->m;
7772 munki_code ev = MUNKI_OK;
7773
7774 #ifdef HIGH_RES
7775 if (m->hr_inited == 0) {
7776 if ((ev = munki_create_hr(p, 1)) != MUNKI_OK) /* Reflective */
7777 return ev;
7778 if ((ev = munki_create_hr(p, 0)) != MUNKI_OK) /* Emissive */
7779 return ev;
7780 }
7781
7782 m->nwav = m->nwav2;
7783 m->wl_short = m->wl_short2;
7784 m->wl_long = m->wl_long2;
7785
7786 m->rmtx_index = m->rmtx_index2;
7787 m->rmtx_nocoef = m->rmtx_nocoef2;
7788 m->rmtx_coef = m->rmtx_coef2;
7789 m->emtx_index = m->emtx_index2;
7790 m->emtx_nocoef = m->emtx_nocoef2;
7791 m->emtx_coef = m->emtx_coef2;
7792 m->white_ref = m->white_ref2;
7793 m->emis_coef = m->emis_coef2;
7794 m->amb_coef = m->amb_coef2;
7795 m->proj_coef = m->proj_coef2;
7796 m->straylight = m->straylight2;
7797
7798 for (i = 0; i < mk_no_modes; i++) {
7799 munki_state *s = &m->ms[i];
7800 s->cal_factor = s->cal_factor2;
7801 }
7802 m->highres = 1;
7803 #else
7804 ev = MUNKI_UNSUPPORTED;
7805 #endif /* HIGH_RES */
7806
7807 return ev;
7808 }
7809
7810 /* Set to standard resolution mode */
munki_set_stdres(munki * p)7811 munki_code munki_set_stdres(munki *p) {
7812 int i;
7813 munkiimp *m = (munkiimp *)p->m;
7814 munki_code ev = MUNKI_OK;
7815
7816 #ifdef HIGH_RES
7817 m->nwav = m->nwav1;
7818 m->wl_short = m->wl_short1;
7819 m->wl_long = m->wl_long1;
7820
7821 m->rmtx_index = m->rmtx_index1;
7822 m->rmtx_nocoef = m->rmtx_nocoef1;
7823 m->rmtx_coef = m->rmtx_coef1;
7824 m->emtx_index = m->emtx_index1;
7825 m->emtx_nocoef = m->emtx_nocoef1;
7826 m->emtx_coef = m->emtx_coef1;
7827 m->white_ref = m->white_ref1;
7828 m->emis_coef = m->emis_coef1;
7829 m->amb_coef = m->amb_coef1;
7830 m->proj_coef = m->proj_coef1;
7831 m->straylight = m->straylight1;
7832
7833 for (i = 0; i < mk_no_modes; i++) {
7834 munki_state *s = &m->ms[i];
7835 s->cal_factor = s->cal_factor1;
7836 }
7837 m->highres = 0;
7838
7839 #else
7840 ev = MUNKI_UNSUPPORTED;
7841 #endif /* HIGH_RES */
7842
7843 return ev;
7844 }
7845
7846 /* Modify the scan consistency tolerance */
munki_set_scan_toll(munki * p,double toll_ratio)7847 munki_code munki_set_scan_toll(munki *p, double toll_ratio) {
7848 munkiimp *m = (munkiimp *)p->m;
7849 munki_code ev = MUNKI_OK;
7850
7851 m->scan_toll_ratio = toll_ratio;
7852
7853 return MUNKI_OK;
7854 }
7855
7856 /* Optical adjustment weights */
7857 static double opt_adj_weights[21] = {
7858 1.4944496665144658e-282, 2.0036175483913455e-070, 1.2554893022685038e+232,
7859 2.3898157055642966e+190, 1.5697625128432372e-076, 6.6912978722191457e+281,
7860 1.2369092402930559e+277, 1.4430907501246712e-153, 3.0017439193018232e+238,
7861 1.2978311824382444e+161, 5.5068703318775818e-311, 7.7791723264455314e-260,
7862 6.4560484084110176e+170, 8.9481529920968425e+165, 1.3565405878488529e-153,
7863 2.0835868791190880e-076, 5.4310198502711138e+241, 4.8689849775675438e+275,
7864 9.2709981544886391e+122, 3.7958270103353899e-153, 7.1366083837501666e-154
7865 };
7866
7867 /* Convert from spectral to XYZ, and transfer to the ipatch array */
munki_conv2XYZ(munki * p,ipatch * vals,int nvals,double ** specrd,instClamping clamp)7868 munki_code munki_conv2XYZ(
7869 munki *p,
7870 ipatch *vals, /* Values to return */
7871 int nvals, /* Number of values */
7872 double **specrd, /* Spectral readings */
7873 instClamping clamp /* Clamp XYZ/Lab to be +ve */
7874 ) {
7875 munkiimp *m = (munkiimp *)p->m;
7876 munki_state *s = &m->ms[m->mmode];
7877 xsp2cie *conv; /* Spectral to XYZ conversion object */
7878 int i, j, k;
7879 int six = 0; /* Starting index */
7880 int nwl = m->nwav; /* Number of wavelegths */
7881 double wl_short = m->wl_short; /* Starting wavelegth */
7882 double sms; /* Weighting */
7883
7884 if (s->emiss)
7885 conv = new_xsp2cie(icxIT_none, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, (icxClamping)clamp);
7886 else
7887 conv = new_xsp2cie(icxIT_D50, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, (icxClamping)clamp);
7888 if (conv == NULL)
7889 return MUNKI_INT_CIECONVFAIL;
7890
7891 a1logd(p->log,3,"munki_conv2XYZ got wl_short %f, wl_long %f, nwav %d\n"
7892 " after skip got wl_short %f, nwl = %d\n",
7893 m->wl_short, m->wl_long, m->nwav, wl_short, nwl);
7894
7895 for (sms = 0.0, i = 1; i < 21; i++)
7896 sms += opt_adj_weights[i];
7897 sms *= opt_adj_weights[0];
7898
7899 for (i = 0; i < nvals; i++) {
7900
7901 vals[i].loc[0] = '\000';
7902 vals[i].mtype = inst_mrt_none;
7903 vals[i].XYZ_v = 0;
7904 vals[i].sp.spec_n = 0;
7905 vals[i].duration = 0.0;
7906
7907 vals[i].sp.spec_n = nwl;
7908 vals[i].sp.spec_wl_short = wl_short;
7909 vals[i].sp.spec_wl_long = m->wl_long;
7910
7911 if (s->emiss) {
7912 for (j = six, k = 0; j < m->nwav; j++, k++) {
7913 vals[i].sp.spec[k] = specrd[i][j] * sms;
7914 }
7915 vals[i].sp.norm = 1.0;
7916
7917 /* Set the XYZ */
7918 conv->convert(conv, vals[i].XYZ, &vals[i].sp);
7919 vals[i].XYZ_v = 1;
7920
7921 if (s->ambient) {
7922 if (s->flash)
7923 vals[i].mtype = inst_mrt_ambient_flash;
7924 else
7925 vals[i].mtype = inst_mrt_ambient;
7926 } else {
7927 if (s->flash)
7928 vals[i].mtype = inst_mrt_emission_flash;
7929 else
7930 vals[i].mtype = inst_mrt_emission;
7931 }
7932
7933 } else {
7934 for (j = six, k = 0; j < m->nwav; j++, k++) {
7935 vals[i].sp.spec[k] = 100.0 * specrd[i][j] * sms;
7936 }
7937 vals[i].sp.norm = 100.0;
7938
7939 /* Set the XYZ */
7940 conv->convert(conv, vals[i].XYZ, &vals[i].sp);
7941 vals[i].XYZ_v = 1;
7942 vals[i].XYZ[0] *= 100.0;
7943 vals[i].XYZ[1] *= 100.0;
7944 vals[i].XYZ[2] *= 100.0;
7945
7946 if (s->trans)
7947 vals[i].mtype = inst_mrt_transmissive;
7948 else
7949 vals[i].mtype = inst_mrt_reflective;
7950 }
7951
7952 /* Don't return spectral if not asked for */
7953 if (!m->spec_en) {
7954 vals[i].sp.spec_n = 0;
7955 }
7956 }
7957
7958 conv->del(conv);
7959
7960 /* Apply any XRGA conversion */
7961 ipatch_convert_xrga(vals, nvals, xcalstd_nonpol, m->target_calstd, m->native_calstd, clamp);
7962
7963
7964 return MUNKI_OK;
7965 }
7966
7967 /* Compute a mode calibration factor given the reading of the white reference. */
7968 /* Return 1 if any of the transmission wavelengths are low. */
munki_compute_white_cal(munki * p,double * cal_factor1,double * white_ref1,double * white_read1,double * cal_factor2,double * white_ref2,double * white_read2)7969 int munki_compute_white_cal(
7970 munki *p,
7971 double *cal_factor1, /* [nwav1] Calibration factor to compute */
7972 double *white_ref1, /* [nwav1] White reference to aim for, NULL for 1.0 */
7973 double *white_read1, /* [nwav1] The white that was read */
7974 double *cal_factor2, /* [nwav2] Calibration factor to compute */
7975 double *white_ref2, /* [nwav2] White reference to aim for, NULL for 1.0 */
7976 double *white_read2 /* [nwav2] The white that was read */
7977 ) {
7978 munkiimp *m = (munkiimp *)p->m;
7979 munki_state *s = &m->ms[m->mmode];
7980 int j, warn = 0;
7981
7982 a1logd(p->log,3,"munki_compute_white_cal called\n");
7983
7984 if (white_ref1 == NULL) { /* transmission white reference */
7985 double avgwh = 0.0;
7986
7987 /* Compute average white reference reading */
7988 for (j = 0; j < m->nwav1; j++)
7989 avgwh += white_read1[j];
7990 avgwh /= (double)m->nwav1;
7991
7992 /* For each wavelength */
7993 for (j = 0; j < m->nwav1; j++) {
7994 /* If reference is < 0.4% of average */
7995 if (white_read1[j]/avgwh < 0.004) {
7996 cal_factor1[j] = 1.0/(0.004 * avgwh);
7997 warn = 1;
7998 } else {
7999 cal_factor1[j] = 1.0/white_read1[j];
8000 }
8001 }
8002
8003 } else { /* Reflection white reference */
8004
8005 /* For each wavelength */
8006 for (j = 0; j < m->nwav1; j++) {
8007 if (white_read1[j] < 1000.0)
8008 cal_factor1[j] = white_ref1[j]/1000.0;
8009 else
8010 cal_factor1[j] = white_ref1[j]/white_read1[j];
8011 }
8012 }
8013
8014 #ifdef HIGH_RES
8015 if (m->hr_inited == 0)
8016 return warn;
8017
8018 if (white_ref2 == NULL) { /* transmission white reference */
8019 double avgwh = 0.0;
8020
8021 /* Compute average white reference reading */
8022 for (j = 0; j < m->nwav2; j++)
8023 avgwh += white_read2[j];
8024 avgwh /= (double)m->nwav2;
8025
8026 /* For each wavelength */
8027 for (j = 0; j < m->nwav2; j++) {
8028 /* If reference is < 0.4% of average */
8029 if (white_read2[j]/avgwh < 0.004) {
8030 cal_factor2[j] = 1.0/(0.004 * avgwh);
8031 warn = 1;
8032 } else {
8033 cal_factor2[j] = 1.0/white_read2[j];
8034 }
8035 }
8036
8037 } else { /* Reflection white reference */
8038
8039 /* For each wavelength */
8040 for (j = 0; j < m->nwav2; j++) {
8041 if (white_read2[j] < 1000.0)
8042 cal_factor2[j] = white_ref2[j]/1000.0;
8043 else
8044 cal_factor2[j] = white_ref2[j]/white_read2[j];
8045 }
8046 }
8047 #endif /* HIGH_RES */
8048 return warn;
8049 }
8050
8051 /* For adaptive mode, compute a new integration time and gain mode */
8052 /* in order to optimise the sensor values. */
munki_optimise_sensor(munki * p,double * pnew_int_time,int * pnew_gain_mode,double cur_int_time,int cur_gain_mode,int permithg,int permitclip,double * targoscale,double scale,double deadtime)8053 munki_code munki_optimise_sensor(
8054 munki *p,
8055 double *pnew_int_time,
8056 int *pnew_gain_mode,
8057 double cur_int_time, /* Current intergration time */
8058 int cur_gain_mode, /* nz if currently high gain */
8059 int permithg, /* nz to permit switching to high gain mode */
8060 int permitclip, /* nz to permit clipping out of range int_time, else error */
8061 double *targoscale, /* Optimising target scale ( <= 1.0) */
8062 /* (May be altered if integration time isn't possible) */
8063 double scale, /* scale needed of current int time to reach optimum */
8064 double deadtime /* Dead integration time (if any) */
8065 ) {
8066 munki_code ev = MUNKI_OK;
8067 munkiimp *m = (munkiimp *)p->m;
8068 munki_state *s = &m->ms[m->mmode];
8069 double new_int_time;
8070 double min_int_time; /* Adjusted min_int_time */
8071 int new_gain_mode;
8072
8073 a1logd(p->log,3,"munki_optimise_sensor called, inttime %f, gain mode %d, scale %f\n",cur_int_time,cur_gain_mode, scale);
8074
8075 min_int_time = m->min_int_time - deadtime;
8076 cur_int_time -= deadtime;
8077
8078 /* Compute new normal gain integration time */
8079 if (cur_gain_mode)
8080 new_int_time = cur_int_time * scale * m->highgain;
8081 else
8082 new_int_time = cur_int_time * scale;
8083 new_gain_mode = 0;
8084
8085 a1logd(p->log,3,"target inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8086
8087 /* Adjust to low light situation by increasing the integration time. */
8088 if (new_int_time > s->targmaxitime) { /* Exceeding target integration time */
8089 if (s->targmaxitime/new_int_time > s->targoscale2) { /* But within range */
8090 /* Compromise sensor target value to maintain targmaxitime */
8091 new_int_time = s->targmaxitime;
8092 a1logd(p->log,3,"Using targmaxitime with compromise sensor target\n");
8093 } else {
8094 /* Target reduced sensor value to give improved measurement time and continuity */
8095 new_int_time *= s->targoscale2;
8096 a1logd(p->log,3,"Using compromse sensor target\n");
8097 }
8098 /* Hmm. It seems not be a good idea to use high gain mode if it compromises */
8099 /* the longer integration time which reduces noise. */
8100 if (s->auto_gain) {
8101 if (new_int_time > m->max_int_time && permithg) {
8102 new_int_time /= m->highgain;
8103 new_gain_mode = 1;
8104 a1logd(p->log,3,"Switching to high gain mode\n");
8105 }
8106 }
8107 }
8108 a1logd(p->log,3,"after low light adjust, inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8109
8110 /* Deal with still low light */
8111 if (new_int_time > m->max_int_time) {
8112 if (permitclip)
8113 new_int_time = m->max_int_time;
8114 else
8115 return MUNKI_RD_LIGHTTOOLOW;
8116 }
8117 a1logd(p->log,3,"after low light clip, inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8118
8119 /* Adjust to high light situation */
8120 if (new_int_time < min_int_time && *targoscale < 1.0) {
8121 *targoscale *= min_int_time/new_int_time;
8122 new_int_time = min_int_time;
8123 }
8124 a1logd(p->log,3,"after high light adjust, targoscale %f, inttime %f, gain mode %d\n",*targoscale, new_int_time,new_gain_mode);
8125
8126 /* Deal with still high light */
8127 if (new_int_time < min_int_time) {
8128 if (permitclip)
8129 new_int_time = min_int_time;
8130 else
8131 return MUNKI_RD_LIGHTTOOHIGH;
8132 }
8133 a1logd(p->log,3,"after high light clip, returning inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8134
8135 new_int_time += deadtime;
8136
8137 a1logd(p->log,3,"munki_optimise_sensor returning inttime %f, gain mode %d\n",new_int_time,new_gain_mode);
8138 if (pnew_int_time != NULL)
8139 *pnew_int_time = new_int_time;
8140
8141 if (pnew_gain_mode != NULL)
8142 *pnew_gain_mode = new_gain_mode;
8143
8144 return MUNKI_OK;
8145 }
8146
8147 /* Compute the number of measurements needed, given the target */
8148 /* time and integration time. Will return 0 if target time is 0 */
munki_comp_nummeas(munki * p,double meas_time,double int_time)8149 int munki_comp_nummeas(
8150 munki *p,
8151 double meas_time,
8152 double int_time
8153 ) {
8154 int nmeas;
8155 if (meas_time <= 0.0)
8156 return 0;
8157 nmeas = (int)floor(meas_time/int_time + 0.5);
8158 if (nmeas < 1)
8159 nmeas = 1;
8160 return nmeas;
8161 }
8162
8163 /* Compute the rounded up number of measurements needed, */
8164 /* given the target time and integration time. */
8165 /* Will return 0 if target time is 0 */
munki_comp_ru_nummeas(munki * p,double meas_time,double int_time)8166 int munki_comp_ru_nummeas(
8167 munki *p,
8168 double meas_time,
8169 double int_time
8170 ) {
8171 int nmeas;
8172 if (meas_time <= 0.0)
8173 return 0;
8174 nmeas = (int)ceil(meas_time/int_time);
8175 return nmeas;
8176 }
8177
8178 /* Convert the dark interpolation data to a useful state */
8179 /* (also allow for interpolating the shielded cell values) */
8180 void
munki_prepare_idark(munki * p)8181 munki_prepare_idark(
8182 munki *p
8183 ) {
8184 munkiimp *m = (munkiimp *)p->m;
8185 munki_state *s = &m->ms[m->mmode];
8186 int i, j;
8187
8188 /* For normal and high gain */
8189 for (i = 0; i < 4; i+=2) {
8190 for (j = -1; j < m->nraw; j++) {
8191 double d01, d1;
8192 d01 = s->idark_data[i+0][j];
8193 d1 = s->idark_data[i+1][j];
8194
8195 /* Compute increment proportional to time */
8196 s->idark_data[i+1][j] = (d1 - d01)/(s->idark_int_time[i+1] - s->idark_int_time[i+0]);
8197
8198 /* Compute base */
8199 s->idark_data[i+0][j] = d01 - s->idark_data[i+1][j] * s->idark_int_time[i+0];;
8200 }
8201 }
8202 }
8203
8204 /* Create the dark reference for the given integration time and gain */
8205 /* by interpolating from the 4 readings prepared earlier */
8206 munki_code
munki_interp_dark(munki * p,double * result,double inttime,int gainmode)8207 munki_interp_dark(
8208 munki *p,
8209 double *result, /* Put result of interpolation here */
8210 double inttime,
8211 int gainmode
8212 ) {
8213 munkiimp *m = (munkiimp *)p->m;
8214 munki_state *s = &m->ms[m->mmode];
8215 int i, j;
8216
8217 if (!s->idark_valid)
8218 return MUNKI_INT_NOTCALIBRATED;
8219
8220 i = 0;
8221 if (s->auto_gain && gainmode)
8222 i = 2;
8223
8224 for (j = -1; j < m->nraw; j++) {
8225 double tt;
8226 tt = s->idark_data[i+0][j] + inttime * s->idark_data[i+1][j];
8227 result[j] = tt;
8228 }
8229 return MUNKI_OK;
8230 }
8231
8232 /* Set the noinitcalib mode */
munki_set_noinitcalib(munki * p,int v,int losecs)8233 void munki_set_noinitcalib(munki *p, int v, int losecs) {
8234 munkiimp *m = (munkiimp *)p->m;
8235 /* Ignore disabling init calib if more than losecs since instrument was open */
8236 a1logd(p->log,3,"set_noinitcalib v = %d, ->lo_secs %d, losecs %d secs\n",v, m->lo_secs,losecs);
8237 if (v && losecs != 0 && m->lo_secs >= losecs) {
8238 a1logd(p->log,3,"initcalib disable ignored because %d >= %d secs\n",m->lo_secs,losecs);
8239 return;
8240 }
8241 m->noinitcalib = v;
8242 }
8243
8244 /* Set the nocalibask mode */
8245 /* Don't ask user for confirmation of calibration */
8246 /* if the instrument is in the correct configuration for it. */
munki_set_nocalibask(munki * p,int v)8247 void munki_set_nocalibask(munki *p, int v) {
8248 munkiimp *m = (munkiimp *)p->m;
8249
8250 m->nocalibask = v;
8251 }
8252
8253 /* Set the trigger config */
munki_set_trig(munki * p,inst_opt_type trig)8254 void munki_set_trig(munki *p, inst_opt_type trig) {
8255 munkiimp *m = (munkiimp *)p->m;
8256 m->trig = trig;
8257 }
8258
8259 /* Return the trigger config */
munki_get_trig(munki * p)8260 inst_opt_type munki_get_trig(munki *p) {
8261 munkiimp *m = (munkiimp *)p->m;
8262 return m->trig;
8263 }
8264
8265 /* Switch thread handler */
munki_switch_thread(void * pp)8266 static int munki_switch_thread(void *pp) {
8267 int nfailed = 0;
8268 munki *p = (munki *)pp;
8269 munkiimp *m = (munkiimp *)p->m;
8270 munki_code rv = MUNKI_OK;
8271 a1logd(p->log,3,"Switch thread started\n");
8272 // for (nfailed = 0;nfailed < 5;)
8273 /* Try indefinitely, in case instrument is put to sleep */
8274 for (;;) {
8275 mk_eve ecode;
8276
8277 rv = munki_waitfor_switch_th(p, &ecode, NULL, SW_THREAD_TIMEOUT);
8278 if (m->th_term) {
8279 m->th_termed = 1;
8280 break;
8281 }
8282 if (rv == MUNKI_INT_BUTTONTIMEOUT) {
8283 nfailed = 0;
8284 continue;
8285 }
8286 if (rv != MUNKI_OK) {
8287 nfailed++;
8288 a1logd(p->log,3,"Switch thread failed with 0x%x\n",rv);
8289 continue;
8290 }
8291 if (ecode == mk_eve_switch_press) {
8292 m->switch_count++;
8293 if (!m->hide_switch && p->eventcallback != NULL) {
8294 p->eventcallback(p->event_cntx, inst_event_switch);
8295 }
8296 } else if (ecode == mk_eve_spos_change) {
8297 #ifdef FILTER_SPOS_EVENTS
8298 /* Signal change to filer thread */
8299 m->spos_msec = msec_time();
8300 m->spos_change++;
8301 #else
8302 if (m->eventcallback != NULL) {
8303 m->eventcallback(p->event_cntx, inst_event_mconf);
8304 }
8305 #endif
8306 }
8307 }
8308 a1logd(p->log,3,"Switch thread returning\n");
8309 return rv;
8310 }
8311
8312 #ifdef FILTER_SPOS_EVENTS
munki_spos_thread(void * pp)8313 static int munki_spos_thread(void *pp) {
8314 munki *p = (munki *)pp;
8315 munkiimp *m = (munkiimp *)p->m;
8316 int change = m->spos_change; /* Current count */
8317
8318 a1logd(p->log,3,"spos thread started\n");
8319
8320 for (;;) {
8321
8322 if (m->spos_th_term) {
8323 m->spos_th_termed = 1;
8324 break;
8325 }
8326
8327 /* Do callback if change has persisted for 1 second */
8328 if (change != m->spos_change
8329 && (msec_time() - m->spos_msec) >= FILTER_TIME) {
8330 change = m->spos_change;
8331 if (p->eventcallback != NULL) {
8332 p->eventcallback(p->event_cntx, inst_event_mconf);
8333 }
8334 }
8335 msec_sleep(100);
8336 }
8337 return 0;
8338 }
8339 #endif
8340
8341
8342 /* ============================================================ */
8343 /* Low level commands */
8344
8345 /* USB Instrument commands */
8346
8347 /* Read from the EEProm */
8348 munki_code
munki_readEEProm(munki * p,unsigned char * buf,int addr,int size)8349 munki_readEEProm(
8350 munki *p,
8351 unsigned char *buf, /* Where to read it to */
8352 int addr, /* Address in EEprom to read from */
8353 int size /* Number of bytes to read (max 65535) */
8354 ) {
8355 munkiimp *m = (munkiimp *)p->m;
8356 int rwbytes; /* Data bytes read or written */
8357 unsigned char pbuf[8]; /* Write EEprom parameters */
8358 int se, rv = MUNKI_OK;
8359
8360 a1logd(p->log,2,"munki_readEEProm: address 0x%x size 0x%x\n",addr,size);
8361
8362 if (size < 0 || addr < 0 || (addr + size) > (m->noeeblocks * m->eeblocksize))
8363 return MUNKI_INT_EEOUTOFRANGE;
8364
8365 int2buf(&pbuf[0], addr);
8366 int2buf(&pbuf[4], size);
8367 se = p->icom->usb_control(p->icom,
8368 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8369 0x81, 0, 0, pbuf, 8, 2.0);
8370
8371 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8372 a1logd(p->log,1,"munki_readEEProm: read failed (1) with ICOM err 0x%x\n",se);
8373 return rv;
8374 }
8375
8376 /* Now read the bytes */
8377 se = p->icom->usb_read(p->icom, NULL, 0x81, buf, size, &rwbytes, 6.0);
8378 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8379 a1logd(p->log,1,"munki_readEEProm: read failed (2) with ICOM err 0x%x\n",se);
8380 return rv;
8381 }
8382
8383 if (rwbytes != size) {
8384 a1logd(p->log,1,"munki_readEEProm: 0x%x bytes, short read error\n",rwbytes);
8385 return MUNKI_HW_EE_SHORTREAD;
8386 }
8387
8388 if (p->log->debug >= 5) {
8389 int i;
8390 char oline[100] = { '\000' }, *bp = oline;
8391 for (i = 0; i < size; i++) {
8392 if ((i % 16) == 0)
8393 bp += sprintf(bp," %04x:",i);
8394 bp += sprintf(bp," %02x",buf[i]);
8395 if ((i+1) >= size || ((i+1) % 16) == 0) {
8396 bp += sprintf(bp,"\n");
8397 a1logd(p->log,5,oline);
8398 bp = oline;
8399 }
8400 }
8401 }
8402
8403 a1logd(p->log,2,"munki_readEEProm: got 0x%x bytes, ICOM err 0x%x\n",rwbytes, se);
8404
8405 return rv;
8406 }
8407
8408
8409
8410 /* Get the firmware parameters */
8411 /* return pointers may be NULL if not needed. */
8412 munki_code
munki_getfirm(munki * p,int * fwrev,int * tickdur,int * minintcount,int * noeeblocks,int * eeblocksize)8413 munki_getfirm(
8414 munki *p,
8415 int *fwrev, /* Return the formware version number as 8.8 */
8416 int *tickdur, /* Tick duration */
8417 int *minintcount, /* Minimum integration tick count */
8418 int *noeeblocks, /* Number of EEPROM blocks */
8419 int *eeblocksize /* Size of each block */
8420 ) {
8421 unsigned char pbuf[24]; /* status bytes read */
8422 int _fwrev_maj, _fwrev_min;
8423 int _tickdur;
8424 int _minintcount;
8425 int _noeeblocks;
8426 int _eeblocksize;
8427 int se, rv = MUNKI_OK;
8428
8429 a1logd(p->log,2,"munki_getfirm:\n");
8430
8431 se = p->icom->usb_control(p->icom,
8432 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8433 0x86, 0, 0, pbuf, 24, 2.0);
8434
8435 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8436 a1logd(p->log,1,"munki_getfirm: failed with ICOM err 0x%x\n",se);
8437 return rv;
8438 }
8439
8440 _fwrev_maj = buf2int(&pbuf[0]);
8441 _fwrev_min = buf2int(&pbuf[4]);
8442 _tickdur = buf2int(&pbuf[8]);
8443 _minintcount = buf2int(&pbuf[12]);
8444 _noeeblocks = buf2int(&pbuf[16]);
8445 _eeblocksize = buf2int(&pbuf[20]);
8446
8447 a1logd(p->log,2,"munki_getfirm: returning fwrev %d.%d, tickdur %d, minint %d, eeblks %d, "
8448 "eeblksz %d ICOM err 0x%x\n", _fwrev_maj, _fwrev_min, _tickdur, _minintcount,
8449 _noeeblocks, _eeblocksize, se);
8450
8451 if (fwrev != NULL) *fwrev = _fwrev_maj * 256 + _fwrev_min ;
8452 if (tickdur != NULL) *tickdur = _tickdur;
8453 if (minintcount != NULL) *minintcount = _minintcount;
8454 if (noeeblocks != NULL) *noeeblocks = _noeeblocks;
8455 if (eeblocksize != NULL) *eeblocksize = _eeblocksize;
8456
8457 return rv;
8458 }
8459
8460 /* Get the Chip ID */
8461 munki_code
munki_getchipid(munki * p,unsigned char chipid[8])8462 munki_getchipid(
8463 munki *p,
8464 unsigned char chipid[8]
8465 ) {
8466 int se, rv = MUNKI_OK;
8467
8468 a1logd(p->log,2,"munki_getchipid: called\n");
8469
8470 se = p->icom->usb_control(p->icom,
8471 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8472 0x8A, 0, 0, chipid, 8, 2.0);
8473
8474 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8475 a1logd(p->log,1,"munki_getchipid: GetChipID failed with ICOM err 0x%x\n",se);
8476 return rv;
8477 }
8478
8479 a1logd(p->log,2," GetChipID returns %02X-%02X%02X%02X%02X%02X%02X%02X ICOM err 0x%x\n",
8480 chipid[0], chipid[1], chipid[2], chipid[3],
8481 chipid[4], chipid[5], chipid[6], chipid[7], se);
8482 return rv;
8483 }
8484
8485 /* Get the Version String */
8486 munki_code
munki_getversionstring(munki * p,char vstring[37])8487 munki_getversionstring(
8488 munki *p,
8489 char vstring[37]
8490 ) {
8491 int se, rv = MUNKI_OK;
8492
8493 a1logd(p->log,2,"munki_getversionstring: called\n");
8494
8495 se = p->icom->usb_control(p->icom,
8496 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8497 0x85, 0, 0, (unsigned char *)vstring, 36, 2.0);
8498
8499 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8500 a1logd(p->log,1,"munki_getversionstring: failed with ICOM err 0x%x\n",se);
8501 return rv;
8502 }
8503
8504 vstring[36] = '\000';
8505
8506 a1logd(p->log,2,"munki_getversionstring: returning '%s' ICOM err 0x%x\n", vstring, se);
8507
8508 return rv;
8509 }
8510
8511 /* Get the measurement state */
8512 /* return pointers may be NULL if not needed. */
8513 munki_code
munki_getmeasstate(munki * p,int * ledtrange,int * ledtemp,int * dutycycle,int * ADfeedback)8514 munki_getmeasstate(
8515 munki *p,
8516 int *ledtrange, /* LED temperature range */
8517 int *ledtemp, /* LED temperature */
8518 int *dutycycle, /* Duty Cycle */
8519 int *ADfeedback /* A/D converter feedback */
8520 ) {
8521 unsigned char pbuf[16]; /* values read */
8522 int _ledtrange;
8523 int _ledtemp;
8524 int _dutycycle;
8525 int _ADfeedback;
8526 int se, rv = MUNKI_OK;
8527
8528 a1logd(p->log,2,"munki_getmeasstate: called\n");
8529
8530 se = p->icom->usb_control(p->icom,
8531 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8532 0x8F, 0, 0, pbuf, 16, 2.0);
8533
8534 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8535 a1logd(p->log,1,"munki_getmeasstate: failed with ICOM err 0x%x\n",se);
8536 return rv;
8537 }
8538
8539 _ledtrange = buf2int(&pbuf[0]);
8540 _ledtemp = buf2int(&pbuf[4]);
8541 _dutycycle = buf2int(&pbuf[8]);
8542 _ADfeedback = buf2int(&pbuf[12]);
8543
8544 a1logd(p->log,2,"munki_getmeasstate: returning LED temp range %d, LED temp %d, "
8545 "Duty Cycle %d, ADFeefback %d, ICOM err 0x%x\n",
8546 _ledtrange, _ledtemp, _dutycycle, _ADfeedback, se);
8547
8548 if (ledtrange != NULL) *ledtrange = _ledtrange;
8549 if (ledtemp != NULL) *ledtemp = _ledtemp;
8550 if (dutycycle != NULL) *dutycycle = _dutycycle;
8551 if (ADfeedback != NULL) *ADfeedback = _ADfeedback;
8552
8553 return rv;
8554 }
8555
8556 /* Get the device status */
8557 /* return pointers may be NULL if not needed. */
8558 munki_code
munki_getstatus(munki * p,mk_spos * spos,mk_but * but)8559 munki_getstatus(
8560 munki *p,
8561 mk_spos *spos, /* Return the sensor position */
8562 mk_but *but /* Return Button state */
8563 ) {
8564 unsigned char pbuf[2]; /* status bytes read */
8565 mk_spos _spos;
8566 mk_but _but;
8567 int se, rv = MUNKI_OK;
8568
8569 a1logd(p->log,2,"munki_getstatus: called\n");
8570
8571 se = p->icom->usb_control(p->icom,
8572 IUSB_ENDPOINT_IN | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8573 0x87, 0, 0, pbuf, 2, 2.0);
8574
8575 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8576 a1logd(p->log,1,"munki_getstatus: failed with ICOM err 0x%x\n",se);
8577 return rv;
8578 }
8579
8580 _spos = (mk_spos)pbuf[0];
8581 _but = (mk_but)pbuf[1];
8582
8583 if (p->log->debug >= 3) {
8584 char sb1[50], sb2[50];
8585 if (_spos == mk_spos_proj)
8586 strcpy(sb1, "Projector");
8587 else if (_spos == mk_spos_surf)
8588 strcpy(sb1, "Surface");
8589 else if (_spos == mk_spos_calib)
8590 strcpy(sb1, "Calibration");
8591 else if (_spos == mk_spos_amb)
8592 strcpy(sb1, "Ambient");
8593 else
8594 sprintf(sb1,"Unknown 0x%x",_spos);
8595 if (_but == mk_but_switch_release)
8596 strcpy(sb2, "Released");
8597 else if (_but == mk_but_switch_press)
8598 strcpy(sb2, "Pressed");
8599 else
8600 sprintf(sb2,"Unknown 0x%x",_but);
8601
8602 a1logd(p->log,3,"munki_getstatus: Sensor pos. %s, Button state %s, ICOM err 0x%x\n",
8603 sb1, sb2, se);
8604 }
8605
8606 if (spos != NULL) *spos = _spos;
8607 if (but != NULL) *but = _but;
8608
8609 return rv;
8610 }
8611
8612 /* Set the indicator LED state (advanced) */
8613 /* NOTE that the instrument seems to turn it off */
8614 /* whenever any other sort of operation occurs. */
8615 munki_code
munki_setindled(munki * p,int p1,int p2,int p3,int p4,int p5)8616 munki_setindled(
8617 munki *p,
8618 int p1, /* On time (msec) */
8619 int p2, /* Off time (msec) */
8620 int p3, /* Transition time (msec) */
8621 int p4, /* Number of pulses, -1 = max */
8622 int p5 /* Ignored ? */
8623 ) {
8624 unsigned char pbuf[20]; /* command bytes written */
8625 int se, rv = MUNKI_OK;
8626
8627 a1logd(p->log,2,"munki_setindled: %d, %d, %d, %d, %d\n",
8628 p1, p2, p3, p4, p5);
8629
8630 int2buf(&pbuf[0], p1); /* On time (msec) */
8631 int2buf(&pbuf[4], p2); /* Off time (msec) */
8632 int2buf(&pbuf[8], p3); /* Transition time (msec) */
8633 int2buf(&pbuf[12], p4); /* Number of pulses, -1 = max */
8634 int2buf(&pbuf[16], p5); /* Unknown */
8635
8636 se = p->icom->usb_control(p->icom,
8637 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8638 0x92, 0, 0, pbuf, 20, 2.0);
8639
8640 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8641 a1logd(p->log,1,"munki_setindled: failed with ICOM err 0x%x\n",se);
8642 return rv;
8643 }
8644
8645 a1logd(p->log,2,"munki_setindled: OK ICOM err 0x%x\n",se);
8646
8647 return rv;
8648 }
8649
8650 /* Trigger a measurement with the given measurement parameters */
8651 munki_code
munki_triggermeasure(munki * p,int intclocks,int nummeas,int measmodeflags,int holdtempduty)8652 munki_triggermeasure(
8653 munki *p,
8654 int intclocks, /* Number of integration clocks */
8655 int nummeas, /* Number of measurements to make */
8656 int measmodeflags, /* Measurement mode flags */
8657 int holdtempduty /* Hold temperature duty cycle */
8658 ) {
8659 munkiimp *m = (munkiimp *)p->m;
8660 unsigned char pbuf[12]; /* command bytes written */
8661 int se, rv = MUNKI_OK;
8662
8663 a1logd(p->log,2,"munki_triggermeasure: lamp %d, scan %d, gain %d, intclks %d, nummeas %d\n",
8664 (measmodeflags & MUNKI_MMF_LAMP) ? 1 : 0,
8665 (measmodeflags & MUNKI_MMF_SCAN) ? 1 : 0,
8666 (measmodeflags & MUNKI_MMF_HIGHGAIN) ? 1 : 0,
8667 intclocks, nummeas);
8668
8669 pbuf[0] = (measmodeflags & MUNKI_MMF_LAMP) ? 1 : 0;
8670 pbuf[1] = (measmodeflags & MUNKI_MMF_SCAN) ? 1 : 0;
8671 pbuf[2] = (measmodeflags & MUNKI_MMF_HIGHGAIN) ? 1 : 0;
8672 pbuf[3] = holdtempduty;
8673 int2buf(&pbuf[4], intclocks);
8674 int2buf(&pbuf[8], nummeas);
8675
8676 m->tr_t1 = m->tr_t2 = m->tr_t3 = m->tr_t4 = m->tr_t5 = m->tr_t6 = m->tr_t7 = 0;
8677 m->tr_t1 = msec_time(); /* Diagnostic */
8678
8679 se = p->icom->usb_control(p->icom,
8680 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8681 0x80, 0, 0, pbuf, 12, 2.0);
8682 m->trigstamp = usec_time();
8683
8684 m->tr_t2 = msec_time(); /* Diagnostic */
8685
8686 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8687 a1logd(p->log,1,"munki_triggermeasure: failed with ICOM err 0x%x\n",se);
8688 return rv;
8689 }
8690
8691 a1logd(p->log,2,"munki_triggermeasure: OK ICOM err 0x%x\n",se);
8692
8693 return rv;
8694 }
8695
8696 /* Read a measurements results. */
8697 /* A buffer full of bytes is returned. */
8698 static munki_code
munki_readmeasurement(munki * p,int inummeas,int scanflag,unsigned char * buf,int bsize,int * nummeas,int calib_measure,int dark_measure)8699 munki_readmeasurement(
8700 munki *p,
8701 int inummeas, /* Initial number of measurements to expect */
8702 int scanflag, /* NZ if in scan mode to continue reading */
8703 unsigned char *buf, /* Where to read it to */
8704 int bsize, /* Bytes available in buffer */
8705 int *nummeas, /* Return number of readings measured */
8706 int calib_measure, /* flag - nz if this is a calibration measurement */
8707 int dark_measure /* flag - nz if this is a dark measurement */
8708 ) {
8709 munkiimp *m = (munkiimp *)p->m;
8710 unsigned char *ibuf = buf; /* Incoming buffer */
8711 int nmeas; /* Number of measurements for this read */
8712 double top, extra; /* Time out period */
8713 int rwbytes; /* Data bytes read or written */
8714 int se, rv = MUNKI_OK;
8715 int treadings = 0;
8716 // int gotshort = 0; /* nz when got a previous short reading */
8717
8718 if ((bsize % (m->nsen * 2)) != 0) {
8719 a1logd(p->log,1,"munki_readmeasurement: got %d bytes, nsen = %d\n",bsize,m->nsen);
8720 return MUNKI_INT_ODDREADBUF;
8721 }
8722
8723 extra = 1.0; /* Extra timeout margin */
8724
8725 #ifdef SINGLE_READ
8726 if (scanflag == 0)
8727 nmeas = inummeas;
8728 else
8729 nmeas = bsize / (m->nsen * 2); /* Use a single large read */
8730 #else
8731 nmeas = inummeas; /* Smaller initial number of measurements */
8732 #endif
8733
8734 top = extra + m->c_inttime * nmeas;
8735
8736 a1logd(p->log,2,"munki_readmeasurement: inummeas %d, scanflag %d, address %p bsize 0x%x, timout %f\n",inummeas, scanflag, buf, bsize, top);
8737
8738 for (;;) {
8739 int size; /* number of bytes to read */
8740
8741 size = (m->nsen * 2) * nmeas;
8742
8743 if (size > bsize) { /* oops, no room for read */
8744 a1logd(p->log,1,"munki_readmeasurement: Buffer was too short for scan\n");
8745 return MUNKI_INT_MEASBUFFTOOSMALL;
8746 }
8747
8748 m->tr_t6 = msec_time(); /* Diagnostic, start of subsequent reads */
8749 if (m->tr_t3 == 0) m->tr_t3 = m->tr_t6; /* Diagnostic, start of first read */
8750
8751 a1logd(p->log,5,"about to call usb_read with %d bytes\n",size);
8752 se = p->icom->usb_read(p->icom, NULL, 0x81, buf, size, &rwbytes, top);
8753
8754 m->tr_t5 = m->tr_t7;
8755 m->tr_t7 = msec_time(); /* Diagnostic, end of subsequent reads */
8756 if (m->tr_t4 == 0) {
8757 m->tr_t5 = m->tr_t2;
8758 m->tr_t4 = m->tr_t7; /* Diagnostic, end of first read */
8759 }
8760
8761 #ifdef NEVER /* Use short + timeout to terminate scan */
8762 if (gotshort != 0 && se == ICOM_TO) { /* We got a timeout after a short read. */
8763 a1logd(p->log,1,"Read timed out in %f secs after getting short read\n"
8764 "(Trig & rd times %d %d %d %d)\n",
8765 top,
8766 m->tr_t2-m->tr_t1, m->tr_t3-m->tr_t2, m->tr_t4-m->tr_t3, m->tr_t6-m->tr_t5);
8767 break; /* We're done */
8768 } else
8769 #endif
8770 if (se == ICOM_SHORT) { /* Expect this to terminate scan reading */
8771 a1logd(p->log,5,"Short read, read %d bytes, asked for %d\n"
8772 "(Trig & rd times %d %d %d %d)\n",
8773 rwbytes,size,
8774 m->tr_t2-m->tr_t1, m->tr_t3-m->tr_t2, m->tr_t4-m->tr_t3, m->tr_t6-m->tr_t5);
8775 } else if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8776 if (m->trig_rv != MUNKI_OK) {
8777 a1logd(p->log,1,"munki_readmeasurement: trigger failed, ICOM err 0x%x\n",m->trig_se);
8778 return m->trig_rv;
8779 }
8780 if (se & ICOM_TO)
8781 a1logd(p->log,1,"munki_readmeasurement: read timed out with top = %f\n",top);
8782
8783 a1logd(p->log,1,"munki_readmeasurement: read failed, bytes read 0x%x, ICOM err 0x%x\n",rwbytes, se);
8784 return rv;
8785 }
8786
8787 /* If we didn't read a multiple of m->nsen * 2, we've got problems */
8788 if ((rwbytes % (m->nsen * 2)) != 0) {
8789 a1logd(p->log,1,"munki_readmeasurement: read %d bytes, nsen %d, odd read error\n",rwbytes, m->nsen);
8790 return MUNKI_HW_ME_ODDREAD;
8791 }
8792
8793 /* Track where we're up to */
8794 bsize -= rwbytes;
8795 buf += rwbytes;
8796 treadings += rwbytes/(m->nsen * 2);
8797
8798 if (scanflag == 0) { /* Not scanning */
8799
8800 /* Expect to read exactly what we asked for */
8801 if (rwbytes != size) {
8802 a1logd(p->log,1,"munki_readmeasurement: unexpected short read, got %d expected %d\n",rwbytes,size);
8803 return MUNKI_HW_ME_SHORTREAD;
8804 }
8805 break; /* And we're done */
8806 }
8807
8808 #ifdef NEVER /* Use short + timeout to terminate scan */
8809 /* We expect to get a short read at the end of a scan, */
8810 /* or we might have the USB transfer truncated by somethinge else. */
8811 /* Note the short read, and keep reading until we get a time out */
8812 if (rwbytes != size) {
8813 gotshort = 1;
8814 } else {
8815 gotshort = 0;
8816 }
8817 #else /* Use short to terminate scan */
8818 /* We're scanning and expect to get a short read at the end of the scan. */
8819 if (rwbytes != size) {
8820 a1logd(p->log,5,"done because read %d bytes != %d\n",rwbytes,size);
8821 break;
8822 }
8823 #endif
8824
8825 if (bsize == 0) { /* oops, no room for more scanning read */
8826 unsigned char tbuf[NSEN_MAX * 2];
8827
8828 /* We need to clean up, so soak up all the data and throw it away */
8829 while ((se = p->icom->usb_read(p->icom, NULL, 0x81, tbuf, m->nsen * 2, &rwbytes, top)) == ICOM_OK)
8830 ;
8831 a1logd(p->log,1,"munki_readmeasurement: buffer was too short for scan\n");
8832 return MUNKI_INT_MEASBUFFTOOSMALL;
8833 }
8834
8835 /* Read a bunch more readings until the read is short or times out */
8836 nmeas = bsize / (m->nsen * 2);
8837 if (nmeas > 64)
8838 nmeas = 64;
8839 top = extra + m->c_inttime * nmeas;
8840 }
8841
8842 /* Must have timed out in initial readings */
8843 if (treadings < inummeas) {
8844 a1logd(p->log,1,"munki_readmeasurement: read failed, bytes read 0x%x, ICOM err 0x%x\n",rwbytes, se);
8845 return MUNKI_RD_SHORTMEAS;
8846 }
8847
8848 if (p->log->debug >= 5) {
8849 int i, size = treadings * m->nsen * 2;
8850 char oline[100] = { '\000' }, *bp = oline;
8851 for (i = 0; i < size; i++) {
8852 if ((i % 16) == 0)
8853 bp += sprintf(bp," %04x:",i);
8854 bp += sprintf(bp," %02x",ibuf[i]);
8855 if ((i+1) >= size || ((i+1) % 16) == 0) {
8856 bp += sprintf(bp,"\n");
8857 a1logd(p->log,5,oline);
8858 bp = oline;
8859 }
8860 }
8861 }
8862
8863 a1logd(p->log,2,"munki_readmeasurement: Read %d readings, ICOM err 0x%x\n"
8864 "(Trig & rd times %d %d %d %d)\n",
8865 treadings, se,
8866 m->tr_t2-m->tr_t1, m->tr_t3-m->tr_t2, m->tr_t4-m->tr_t3, m->tr_t6-m->tr_t5);
8867
8868 if (nummeas != NULL) *nummeas = treadings;
8869
8870 return rv;
8871 }
8872
8873 /* Simulating an event */
munki_simulate_event(munki * p,mk_eve ecode,int timestamp)8874 munki_code munki_simulate_event(munki *p, mk_eve ecode, int timestamp) {
8875 munkiimp *m = (munkiimp *)p->m;
8876 unsigned char pbuf[8]; /* 8 bytes to write */
8877 int se, rv = MUNKI_OK;
8878
8879 a1logd(p->log,2,"munki_simulate_event: 0x%x\n",ecode);
8880
8881 int2buf(&pbuf[0], ecode);
8882 int2buf(&pbuf[4], timestamp); /* msec since munki power up */
8883
8884 se = p->icom->usb_control(p->icom,
8885 IUSB_ENDPOINT_OUT | IUSB_REQ_TYPE_VENDOR | IUSB_REQ_RECIP_DEVICE,
8886 0x8E, 0, 0, pbuf, 8, 2.0);
8887
8888 if ((rv = icoms2munki_err(se)) != MUNKI_OK)
8889 a1logd(p->log,1,"munki_simulate_event: event 0x%x failed with ICOM err 0x%x\n",ecode,se);
8890 else
8891 a1logd(p->log,2,"munki_simulate_event: 0x%x done, ICOM err 0x%x\n",ecode,se);
8892
8893 /* Cancel the I/O in case there is no response*/
8894 msec_sleep(50);
8895 if (m->th_termed == 0) {
8896 a1logd(p->log,1,"munki_simulate_event: terminate switch thread failed, canceling I/O\n");
8897 p->icom->usb_cancel_io(p->icom, &m->sw_cancel);
8898 }
8899
8900 return rv;
8901 }
8902
8903 /* Wait for a reply triggered by an event */
munki_waitfor_switch(munki * p,mk_eve * ecode,int * timest,double top)8904 munki_code munki_waitfor_switch(munki *p, mk_eve *ecode, int *timest, double top) {
8905 int rwbytes; /* Data bytes read */
8906 unsigned char buf[8]; /* Result */
8907 int se, rv = MUNKI_OK;
8908 mk_eve _ecode;
8909 int _timest;
8910
8911 a1logd(p->log,2,"munki_waitfor_switch: Read 8 bytes from switch hit port\n");
8912
8913 /* Now read 8 bytes */
8914 se = p->icom->usb_read(p->icom, NULL, 0x83, buf, 8, &rwbytes, top);
8915
8916 if (se & ICOM_TO) {
8917 a1logd(p->log,1,"munki_waitfor_switch: read 0x%x bytes, timed out\n",rwbytes);
8918 return MUNKI_INT_BUTTONTIMEOUT;
8919 }
8920
8921 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8922 a1logd(p->log,2,"munki_waitfor_switch: read failed with ICOM err 0x%x\n",se);
8923 return rv;
8924 }
8925
8926 if (rwbytes != 8) {
8927 a1logd(p->log,1,"munki_waitfor_switch: read %d bytes, short read error\n",rwbytes);
8928 return MUNKI_HW_EE_SHORTREAD;
8929 }
8930
8931 _ecode = (mk_eve) buf2int(&buf[0]);
8932 _timest = buf2int(&buf[4]);
8933
8934 if (p->log->debug >= 3) {
8935 char sbuf[100];
8936 if (_ecode == mk_eve_none)
8937 strcpy(sbuf, "None");
8938 else if (_ecode == mk_eve_switch_press)
8939 strcpy(sbuf, "Button press");
8940 else if (_ecode == mk_eve_switch_release)
8941 strcpy(sbuf, "Button release");
8942 else if (_ecode == mk_eve_spos_change)
8943 strcpy(sbuf, "Sensor position change");
8944 else
8945 sprintf(sbuf,"Unknown 0x%x",_ecode);
8946
8947 a1logd(p->log,3,"munki_waitfor_switch: Event %s, timestamp %d ICOM err 0x%x\n", sbuf, _timest, se);
8948 }
8949
8950 a1logd(p->log,2,"munki_waitfor_switch: read %d bytes OK\n",rwbytes);
8951
8952 if (ecode != NULL) *ecode = _ecode;
8953 if (timest != NULL) *timest = _timest;
8954
8955 return rv;
8956 }
8957
8958 /* Wait for a reply triggered by a key press or config change (thread version) */
8959 /* Returns MUNKI_OK if the switch has been pressed, */
8960 /* or MUNKI_INT_BUTTONTIMEOUT if */
8961 /* no switch was pressed befor the time expired, */
8962 /* or some other error. */
munki_waitfor_switch_th(munki * p,mk_eve * ecode,int * timest,double top)8963 munki_code munki_waitfor_switch_th(munki *p, mk_eve *ecode, int *timest, double top) {
8964 munkiimp *m = (munkiimp *)p->m;
8965 int rwbytes; /* Data bytes read */
8966 unsigned char buf[8]; /* Result */
8967 int se, rv = MUNKI_OK;
8968 mk_eve _ecode;
8969 int _timest;
8970
8971 a1logd(p->log,2,"munki_waitfor_switch_th: Read 8 bytes from switch hit port\n");
8972
8973 /* Now read 8 bytes */
8974 se = p->icom->usb_read(p->icom, &m->sw_cancel, 0x83, buf, 8, &rwbytes, top);
8975
8976 if (se & ICOM_TO) {
8977 a1logd(p->log,1,"munki_waitfor_switch_th: read 0x%x bytes, timed out\n",rwbytes);
8978 return MUNKI_INT_BUTTONTIMEOUT;
8979 }
8980
8981 if ((rv = icoms2munki_err(se)) != MUNKI_OK) {
8982 a1logd(p->log,2,"munki_waitfor_switch_th: read failed with ICOM err 0x%x\n",se);
8983 return rv;
8984 }
8985
8986 if (rwbytes != 8) {
8987 a1logd(p->log,1,"munki_waitfor_switch_th: read %d bytes, short read error\n",rwbytes);
8988 return MUNKI_HW_EE_SHORTREAD;
8989 }
8990
8991 _ecode = (mk_eve) buf2int(&buf[0]);
8992 _timest = buf2int(&buf[4]); /* msec since munki power up */
8993
8994 if (p->log->debug >= 3) {
8995 char sbuf[100];
8996 if (_ecode == mk_eve_none)
8997 strcpy(sbuf, "None");
8998 else if (_ecode == mk_eve_switch_press)
8999 strcpy(sbuf, "Button press");
9000 else if (_ecode == mk_eve_switch_release)
9001 strcpy(sbuf, "Button release");
9002 else if (_ecode == mk_eve_spos_change)
9003 strcpy(sbuf, "Sensor position change");
9004 else
9005 sprintf(sbuf,"Unknown 0x%x",_ecode);
9006
9007 a1logd(p->log,3,"munki_waitfor_switch_th: Event %s, timestamp %d ICOM err 0x%x\n", sbuf, _timest, se);
9008 }
9009
9010 a1logd(p->log,2,"munki_waitfor_switch_th: read %d bytes OK\n",rwbytes);
9011
9012 if (ecode != NULL) *ecode = _ecode;
9013 if (timest != NULL) *timest = _timest;
9014
9015 return rv;
9016 }
9017
9018
9019 /* ============================================================ */
9020 /* key/value dictionary support for EEProm contents */
9021
9022 /* Fixup values for the window reference */
9023
9024 /* Check values */
9025 static double proj_check[36] = {
9026 0.0,
9027 0.0,
9028 0.0,
9029 0.0,
9030 0.0,
9031 0.0,
9032 0.827859997749328610,
9033 0.849550008773803710,
9034 0.855490028858184810,
9035 0.858709990978240970,
9036 0.861010015010833740,
9037 0.862879991531372070,
9038 0.864109992980957030,
9039 0.864619970321655270,
9040 0.865379989147186280,
9041 0.865629971027374270,
9042 0.865689992904663090,
9043 0.865499973297119140,
9044 0.865499973297119140,
9045 0.865760028362274170,
9046 0.866209983825683590,
9047 0.866630017757415770,
9048 0.867579996585845950,
9049 0.868969976902008060,
9050 0.870270013809204100,
9051 0.871270000934600830,
9052 0.872340023517608640,
9053 0.873269975185394290,
9054 0.873669981956481930,
9055 0.873640000820159910,
9056 0.874390006065368650,
9057 0.873179972171783450,
9058 0.872720003128051760,
9059 0.872640013694763180,
9060 0.873160004615783690,
9061 0.873440027236938480
9062 };
9063
9064
9065 /* Correction values to emission ref. */
9066 static double proj_fix[36] = {
9067 0.639684915542602540,
9068 0.639684915542602540,
9069 0.639684915542602540,
9070 0.812916100025177000,
9071 0.846581041812896730,
9072 0.854855418205261230,
9073 0.859299719333648680,
9074 0.861804306507110600,
9075 0.863713920116424560,
9076 0.865424513816833500,
9077 0.866307735443115230,
9078 0.867028772830963130,
9079 0.867631316184997560,
9080 0.868214190006256100,
9081 0.868206322193145750,
9082 0.868299305438995360,
9083 0.867988884449005130,
9084 0.868103504180908200,
9085 0.868657410144805910,
9086 0.869595944881439210,
9087 0.870542407035827640,
9088 0.871895790100097660,
9089 0.873195052146911620,
9090 0.874702811241149900,
9091 0.876054167747497560,
9092 0.877129673957824710,
9093 0.877931654453277590,
9094 0.877546310424804690,
9095 0.876341819763183590,
9096 0.875181615352630620,
9097 0.875020027160644530,
9098 0.875684559345245360,
9099 0.876559674739837650,
9100 0.876724362373352050,
9101 0.876553714275360110,
9102 0.875786423683166500
9103 };
9104
9105 /* Initialise the calibration from the EEProm contents. */
9106 /* (We're handed a buffer that's been rounded up to an even 32 bits by */
9107 /* padding with zero's) */
munki_parse_eeprom(munki * p,unsigned char * buf,unsigned int len)9108 munki_code munki_parse_eeprom(munki *p, unsigned char *buf, unsigned int len) {
9109 munkiimp *m = (munkiimp *)p->m;
9110 mkdata *d;
9111 int rv = MUNKI_OK;
9112 unsigned int chsum, sum;
9113 int calver, compver; /* Calibration version and compatiblity version */
9114 unsigned char chipid[8]; /* Calibration chip id */
9115 int tint, *tinta; /* Temporary */
9116 double tdouble; /* Temporary */
9117 int i, j;
9118
9119 a1logd(p->log,2,"munki_parse_eeprom: called with %d bytes\n",len);
9120
9121 /* Check the checksum */
9122 chsum = buf2uint(buf+8);
9123 int2buf(buf+8, 0); /* Zero it out */
9124
9125 for (sum = 0, i = 0; i < (len-3); i += 4) {
9126 sum += buf2uint(buf + i);
9127 }
9128
9129
9130
9131 a1logd(p->log,3,"munki_parse_eeprom: cal chsum = 0x%x, should be 0x%x - %s\n",sum,chsum, sum == chsum ? "OK": "BAD");
9132 if (sum != chsum)
9133 return MUNKI_INT_CALBADCHSUM;
9134
9135
9136 /* Create class to handle EEProm parsing */
9137 if ((d = m->data = new_mkdata(p, buf, len)) == NULL)
9138 return MUNKI_INT_CREATE_EEPROM_STORE;
9139
9140 /* Check out the version */
9141 if (d->get_u16_ints(d, &calver, 0, 1) == NULL)
9142 return MUNKI_DATA_RANGE;
9143 if (d->get_u16_ints(d, &compver, 2, 1) == NULL)
9144 return MUNKI_DATA_RANGE;
9145 a1logd(p->log,4,"cal version = %d, compatible with %d\n",calver,compver);
9146
9147 /* We understand versions 3 to 6 */
9148
9149 if (calver < 3 || compver < 3 || compver > 6)
9150 return MUNKI_HW_CALIBVERSION;
9151
9152 /* Choose the version we will treat it as */
9153 if (calver > 6 && compver <= 6)
9154 m->calver = 6;
9155 else
9156 m->calver = calver;
9157 a1logd(p->log,4,"Treating as cal version = %d\n",m->calver);
9158
9159 /* Parse all the calibration info common for vers 3 - 6 */
9160
9161 /* Production number */
9162 if (d->get_32_ints(d, &m->prodno, 12, 1) == NULL)
9163 return MUNKI_DATA_RANGE;
9164 a1logd(p->log,4,"Produnction no = %d\n",m->prodno);
9165
9166 /* Chip HW ID */
9167 if (d->get_8_char(d, (unsigned char *)chipid, 16, 8) == NULL)
9168 return MUNKI_DATA_RANGE;
9169 a1logd(p->log,4,"HW Id = %02x-%02x%02x%02x%02x%02x%02x%02x\n",
9170 chipid[0], chipid[1], chipid[2], chipid[3],
9171 chipid[4], chipid[5], chipid[6], chipid[7]);
9172
9173 /* Check that the chipid matches the calibration */
9174 for (i = 0; i < 8; i++) {
9175 if (chipid[i] != m->chipid[i])
9176 return MUNKI_HW_CALIBMATCH;
9177 }
9178
9179 /* Serial number */
9180 if (d->get_8_asciiz(d, m->serno, 24, 16) == NULL)
9181 return MUNKI_DATA_RANGE;
9182 a1logd(p->log,4,"serial number '%s'\n",m->serno);
9183
9184 /* Underlying calibration information */
9185
9186 m->nsen = 137; /* Sensor bands stored */
9187 m->nraw = 128; /* Raw bands stored */
9188 m->nwav1 = 36; /* Standard res number of cooked spectrum band */
9189 m->wl_short1 = 380.0; /* Standard res short and long wavelengths */
9190 m->wl_long1 = 730.0;
9191
9192 /* Fill this in here too */
9193 m->wl_short2 = HIGHRES_SHORT;
9194 m->wl_long2 = HIGHRES_LONG;
9195 m->nwav2 = (int)((m->wl_long2-m->wl_short2)/HIGHRES_WIDTH + 0.5) + 1;
9196
9197 /* Reflection wavelength calibration information */
9198 /* This is setup assuming 128 raw bands, starting */
9199 /* at offset 6 from the values returned by the hardware. */
9200 if ((m->rmtx_index1 = d->get_32_ints(d, NULL, 40, 36)) == NULL)
9201 return MUNKI_DATA_RANGE;
9202
9203 /* Fake the number of matrix cooeficients for each out wavelength */
9204 if ((m->rmtx_nocoef1 = (int *)malloc(sizeof(int) * 36)) == NULL)
9205 return MUNKI_DATA_MEMORY;
9206 for (i = 0; i < 36; i++)
9207 m->rmtx_nocoef1[i] = 16;
9208
9209 if ((m->rmtx_coef1 = d->get_32_doubles(d, NULL, 184, 36 * 16)) == NULL)
9210 return MUNKI_DATA_RANGE;
9211
9212 if (p->log->debug >= 7) {
9213 a1logd(p->log,7,"Reflectance matrix:\n");
9214 for(i = 0; i < 36; i++) {
9215 a1logd(p->log,7," Wave %d, index %d\n",i, m->rmtx_index1[i]);
9216 for (j = 0; j < 16; j++) {
9217 if (m->rmtx_coef1[i * 16 + j] != 0.0)
9218 a1logd(p->log,7," Wt %d = %f\n",j, m->rmtx_coef1[i * 16 + j]);
9219 }
9220 }
9221 }
9222
9223 /* Emission wavelength calibration information */
9224 if ((m->emtx_index1 = d->get_32_ints(d, NULL, 2488, 36)) == NULL)
9225 return MUNKI_DATA_RANGE;
9226
9227 /* Fake the number of matrix cooeficients for each out wavelength */
9228 if ((m->emtx_nocoef1 = (int *)malloc(sizeof(int) * 36)) == NULL)
9229 return MUNKI_DATA_MEMORY;
9230 for (i = 0; i < 36; i++)
9231 m->emtx_nocoef1[i] = 16;
9232
9233 if ((m->emtx_coef1 = d->get_32_doubles(d, NULL, 2632, 36 * 16)) == NULL)
9234 return MUNKI_DATA_RANGE;
9235
9236 if (p->log->debug >= 7) {
9237 a1logd(p->log,5,"Emmission matrix:\n");
9238 for(i = 0; i < 36; i++) {
9239 a1logd(p->log,7," Wave %d, index %d\n",i, m->emtx_index1[i]);
9240 for (j = 0; j < 16; j++) {
9241 if (m->emtx_coef1[i * 16 + j] != 0.0)
9242 a1logd(p->log,7," Wt %d = %f\n",j, m->emtx_coef1[i * 16 + j]);
9243 }
9244 }
9245 }
9246
9247 /* Linearization */
9248 if ((m->lin0 = d->rget_32_doubles(d, NULL, 4936, 4)) == NULL)
9249 return MUNKI_DATA_RANGE;
9250 m->nlin0 = 4;
9251
9252 if ((m->lin1 = d->rget_32_doubles(d, NULL, 4952, 4)) == NULL)
9253 return MUNKI_DATA_RANGE;
9254 m->nlin1 = 4;
9255
9256 if (p->log->debug >= 3) {
9257 char oline[200] = { '\000' }, *bp = oline;
9258
9259 bp += sprintf(bp,"Normal non-lin =");
9260 for(i = 0; i < m->nlin0; i++)
9261 bp += sprintf(bp," %1.10f",m->lin0[i]);
9262 bp += sprintf(bp,"\n");
9263 a1logd(p->log,2,oline);
9264
9265 bp = oline;
9266 bp += sprintf(bp,"High Gain non-lin =");
9267 for(i = 0; i < m->nlin1; i++)
9268 bp += sprintf(bp," %1.10f",m->lin1[i]);
9269 bp += sprintf(bp,"\n");
9270 a1logd(p->log,2,oline);
9271 }
9272
9273 /* Reflectance reference */
9274 if ((m->white_ref1 = d->get_32_doubles(d, NULL, 4968, 36)) == NULL)
9275 return MUNKI_DATA_RANGE;
9276
9277 /* Emission reference */
9278 if ((m->emis_coef1 = d->get_32_doubles(d, NULL, 5112, 36)) == NULL)
9279 return MUNKI_DATA_RANGE;
9280
9281 /* Ambient reference */
9282 if ((m->amb_coef1 = d->get_32_doubles(d, NULL, 5256, 36)) == NULL)
9283 return MUNKI_DATA_RANGE;
9284
9285 /* Sensor target values */
9286 if (d->get_u16_ints(d, &tint, 5400, 1) == NULL)
9287 return MUNKI_DATA_RANGE;
9288 m->minsval = (double)tint;
9289 if (d->get_u16_ints(d, &tint, 5402, 1) == NULL)
9290 return MUNKI_DATA_RANGE;
9291 m->optsval = (double)tint;
9292 if (d->get_u16_ints(d, &tint, 5404, 1) == NULL)
9293 return MUNKI_DATA_RANGE;
9294 m->maxsval = (double)tint;
9295 if (d->get_u16_ints(d, &tint, 5406, 1) == NULL)
9296 return MUNKI_DATA_RANGE;
9297 m->satlimit = (double)tint;
9298
9299 a1logd(p->log,4,"Sensor targmin %.0f, opt %.0f, max %.0f, sat %.0f\n",
9300 m->minsval,m->optsval,m->maxsval,m->satlimit);
9301
9302 if (d->get_32_doubles(d, &m->cal_int_time, 5408, 1) == NULL)
9303 return MUNKI_DATA_RANGE;
9304 m->cal_int_time *= 1e-3; /* Convert to seconds */
9305
9306 if (d->get_32_ints(d, &tint, 5412, 1) == NULL)
9307 return MUNKI_DATA_RANGE;
9308 m->ledpreheattime = tint * 1e-3; /* Convert to seconds */
9309
9310 if (d->get_32_ints(d, &tint, 5416, 1) == NULL)
9311 return MUNKI_DATA_RANGE;
9312 m->ledwaittime = tint * 1e-3; /* Convert to seconds */
9313
9314 if (d->get_u16_ints(d, &m->ledholdtempdc, 5420, 1) == NULL)
9315 return MUNKI_DATA_RANGE;
9316
9317 a1logd(p->log,4,"Cal int time %f, LED pre-heat %f, Led wait %f, LED hold temp duty cycle %d\n", m->cal_int_time, m->ledpreheattime, m->ledwaittime, m->ledholdtempdc);
9318
9319 if (d->get_u16_ints(d, &tint, 5422, 1) == NULL)
9320 return MUNKI_DATA_RANGE;
9321 m->refinvalidsampt = tint * 1e-3; /* Convert to seconds */
9322
9323 if (d->get_32_ints(d, &tint, 5424, 1) == NULL)
9324 return MUNKI_DATA_RANGE;
9325 m->calscantime = tint * 1e-3; /* Convert to seconds */
9326
9327 a1logd(p->log,4,"Invalid sample time %f, Cal scan time %f\n",
9328 m->refinvalidsampt, m->calscantime);
9329
9330 /* Stray light compensation. Note that 16 bit numbers are signed. */
9331 if ((tinta = d->get_16_ints(d, NULL, 5428, 36 * 36)) == NULL)
9332 return MUNKI_DATA_RANGE;
9333 if (m->calver >= 4) {
9334 if (d->get_32_doubles(d, &tdouble, 8020, 1) == NULL)
9335 return MUNKI_DATA_RANGE;
9336 } else {
9337 tdouble = 0.001; /* Hmm. this is quite different to EEProm value */
9338 }
9339 /* Convert from ints to floats */
9340 m->straylight1 = dmatrixz(0, 35, 0, 35);
9341 for (i = 0; i < 36; i++) {
9342 for (j = 0; j < 36; j++) {
9343 m->straylight1[i][j] = tdouble * tinta[i * 36 + j];
9344 if (i == j)
9345 m->straylight1[i][j] += 1.0;
9346 }
9347 }
9348 free(tinta);
9349
9350 if (p->log->debug >= 7) {
9351 a1logd(p->log,7,"Stray Light matrix:\n");
9352 for(i = 0; i < 36; i++) {
9353 double sum = 0.0;
9354 a1logd(p->log,7," Wave %d, index %d\n",i, m->rmtx_index1[i]);
9355 for (j = 0; j < 36; j++) {
9356 sum += m->straylight1[i][j];
9357 a1logd(p->log,7," Wt %d = %f\n",j, m->straylight1[i][j]);
9358 }
9359 a1logd(p->log,7," Sum = %f\n",sum);
9360 }
9361 }
9362
9363 if (m->calver >= 5) {
9364 /* Projector reference */
9365 if ((m->proj_coef1 = d->get_32_doubles(d, NULL, 8024, 36)) == NULL)
9366 return MUNKI_DATA_RANGE;
9367
9368 /* Apparently this can be faulty though. Check if it is */
9369 for (i = 0; i < 6; i++) {
9370 if (m->proj_coef1[i] != m->proj_coef1[i])
9371 break; /* Not Nan */
9372 }
9373 if (i == 6) { /* First 6 are Nan's */
9374 for (; i < 36; i++) {
9375 if ((m->emis_coef1[i]/m->proj_coef1[i] - proj_check[i]) > 0.001)
9376 break; /* Not less than 0.001 */
9377 }
9378 }
9379 if (i == 36) { /* It's faulty */
9380 free(m->proj_coef1);
9381 m->proj_coef1 = NULL; /* Fall through to fakeup */
9382 }
9383 }
9384
9385 if (m->proj_coef1 == NULL) { /* Fake up a projector reference */
9386 if ((m->proj_coef1 = (double *)malloc(sizeof(double) * 36)) == NULL)
9387 return MUNKI_DATA_MEMORY;
9388 for (i = 0; i < 36; i++) {
9389 m->proj_coef1[i] = m->emis_coef1[i]/proj_fix[i];
9390 }
9391 a1logd(p->log,4,"Faked up projector cal reference\n");
9392 }
9393
9394 if (m->calver >= 6) {
9395 if (d->get_8_ints(d, &m->adctype, 8168, 1) == NULL)
9396 return MUNKI_DATA_RANGE;
9397 } else {
9398 m->adctype = 0;
9399 }
9400
9401 if (p->log->debug >= 7) {
9402 a1logd(p->log,4,"White ref, emission cal, ambient cal, proj cal:\n");
9403 for(i = 0; i < 36; i++) {
9404 a1logd(p->log,7," %d: %f, %f, %f, %f\n",i, m->white_ref1[i], m->emis_coef1[i],
9405 m->amb_coef1[i], m->proj_coef1[i]);
9406 }
9407 }
9408
9409 #ifdef PLOT_RCALCURVE
9410 /* Plot the reflection reference curve */
9411 {
9412 int i;
9413 double xx[36];
9414 double y1[36];
9415
9416 for (i = 0; i < m->nwav1; i++) {
9417 xx[i] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
9418 y1[i] = m->white_ref1[i];
9419 }
9420 printf("Reflection Reference (Black)\n");
9421 do_plot(xx, y1, NULL, NULL, 36);
9422 }
9423 #endif /* PLOT_RCALCURVE */
9424
9425 #ifdef PLOT_ECALCURVES
9426 /* Plot the emission reference curves */
9427 {
9428 int i;
9429 double xx[36];
9430 double y1[36], y2[36], y3[36];
9431
9432 printf("Emission Reference (Black), Ambient (Red), Projector (Green)\n");
9433 for (i = 0; i < m->nwav1; i++) {
9434 xx[i] = XSPECT_WL(m->wl_short1, m->wl_long1, m->nwav1, i);
9435 y1[i] = m->emis_coef1[i];
9436 y2[i] = m->amb_coef1[i];
9437 y3[i] = m->proj_coef1[i];
9438 if (y3[i] > 0.02)
9439 y3[i] = 0.02;
9440 }
9441 do_plot(xx, y1, y2, y3, 36);
9442 }
9443 #endif /* PLOT_ECALCURVES */
9444
9445 /* Default to standard resolution */
9446 m->nwav = m->nwav1;
9447 m->wl_short = m->wl_short1;
9448 m->wl_long = m->wl_long1;
9449
9450 m->rmtx_index = m->rmtx_index1;
9451 m->rmtx_nocoef = m->rmtx_nocoef1;
9452 m->rmtx_coef = m->rmtx_coef1;
9453 m->emtx_index = m->emtx_index1;
9454 m->emtx_nocoef = m->emtx_nocoef1;
9455 m->emtx_coef = m->emtx_coef1;
9456
9457 m->white_ref = m->white_ref1;
9458 m->emis_coef = m->emis_coef1;
9459 m->amb_coef = m->amb_coef1;
9460 m->proj_coef = m->proj_coef1;
9461 m->straylight = m->straylight1;
9462
9463 m->highgain = 1.0/m->lin1[1]; /* Gain is encoded in linearity */
9464 a1logd(p->log,3, "highgain = %f\n",m->highgain);
9465
9466 return rv;
9467 }
9468
9469
9470 /* Return a pointer to an array of chars containing data from 8 bits. */
9471 /* If rv is NULL, the returned value will have been allocated, othewise */
9472 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_8_char(struct _mkdata * d,unsigned char * rv,int off,int count)9473 static unsigned char *mkdata_get_8_char(struct _mkdata *d, unsigned char *rv, int off, int count) {
9474 int i;
9475
9476 if (count <= 0
9477 || off < 0
9478 || (off + count * 1) > d->len)
9479 return NULL;
9480
9481 if (rv == NULL) {
9482 if ((rv = (unsigned char *)malloc(sizeof(int) * count)) == NULL)
9483 return NULL;
9484 }
9485
9486 for (i = 0; i < count; i++, off += 1) {
9487 rv[i] = d->buf[off];
9488 }
9489 return rv;
9490 }
9491
9492 /* Return a pointer to an nul terminated string containing data from 8 bits. */
9493 /* If rv is NULL, the returned value will have been allocated, othewise */
9494 /* the rv will be returned. Return NULL if out of range. */
9495 /* An extra space and a nul terminator will be added to the eeprom data */
mkdata_get_8_asciiz(struct _mkdata * d,char * rv,int off,int count)9496 static char *mkdata_get_8_asciiz(struct _mkdata *d, char *rv, int off, int count) {
9497 int i;
9498
9499 if (count <= 0
9500 || off < 0
9501 || (off + count * 1) > d->len)
9502 return NULL;
9503
9504 if (rv == NULL) {
9505 if ((rv = (char *)malloc(sizeof(int) * (count + 1))) == NULL)
9506 return NULL;
9507 }
9508
9509 for (i = 0; i < count; i++, off += 1) {
9510 rv[i] = (char)d->buf[off];
9511 }
9512 rv[i] = '\000';
9513
9514 return rv;
9515 }
9516
9517 /* Return a pointer to an array of ints containing data from 8 bits. */
9518 /* If rv is NULL, the returned value will have been allocated, othewise */
9519 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_8_ints(struct _mkdata * d,int * rv,int off,int count)9520 static int *mkdata_get_8_ints(struct _mkdata *d, int *rv, int off, int count) {
9521 int i;
9522
9523 if (count <= 0
9524 || off < 0
9525 || (off + count * 1) > d->len)
9526 return NULL;
9527
9528 if (rv == NULL) {
9529 if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9530 return NULL;
9531 }
9532
9533 for (i = 0; i < count; i++, off += 1) {
9534 rv[i] = ((signed char *)d->buf)[off];
9535 }
9536 return rv;
9537 }
9538
9539 /* Return a pointer to an array of ints containing data from unsigned 8 bits. */
9540 /* If rv is NULL, the returned value will have been allocated, othewise */
9541 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_u8_ints(struct _mkdata * d,int * rv,int off,int count)9542 static int *mkdata_get_u8_ints(struct _mkdata *d, int *rv, int off, int count) {
9543 int i;
9544
9545 if (count <= 0
9546 || off < 0
9547 || (off + count * 1) > d->len)
9548 return NULL;
9549
9550 if (rv == NULL) {
9551 if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9552 return NULL;
9553 }
9554
9555 for (i = 0; i < count; i++, off += 1) {
9556 rv[i] = d->buf[off];
9557 }
9558 return rv;
9559 }
9560
9561 /* Return a pointer to an array of ints containing data from 16 bits. */
9562 /* If rv is NULL, the returned value will have been allocated, othewise */
9563 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_16_ints(struct _mkdata * d,int * rv,int off,int count)9564 static int *mkdata_get_16_ints(struct _mkdata *d, int *rv, int off, int count) {
9565 int i;
9566
9567 if (count <= 0
9568 || off < 0
9569 || (off + count * 2) > d->len)
9570 return NULL;
9571
9572 if (rv == NULL) {
9573 if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9574 return NULL;
9575 }
9576
9577 for (i = 0; i < count; i++, off += 2) {
9578 rv[i] = buf2short(d->buf + off);
9579 }
9580 return rv;
9581 }
9582
9583 /* Return a pointer to an array of ints containing data from unsigned 16 bits. */
9584 /* If rv is NULL, the returned value will have been allocated, othewise */
9585 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_u16_ints(struct _mkdata * d,int * rv,int off,int count)9586 static int *mkdata_get_u16_ints(struct _mkdata *d, int *rv, int off, int count) {
9587 int i;
9588
9589 if (count <= 0
9590 || off < 0
9591 || (off + count * 2) > d->len)
9592 return NULL;
9593
9594 if (rv == NULL) {
9595 if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9596 return NULL;
9597 }
9598
9599 for (i = 0; i < count; i++, off += 2) {
9600 rv[i] = buf2ushort(d->buf + off);
9601 }
9602 return rv;
9603 }
9604
9605 /* Return a pointer to an array of ints containing data from 32 bits. */
9606 /* If rv is NULL, the returned value will have been allocated, othewise */
9607 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_32_ints(struct _mkdata * d,int * rv,int off,int count)9608 static int *mkdata_get_32_ints(struct _mkdata *d, int *rv, int off, int count) {
9609 int i;
9610
9611 if (count <= 0
9612 || off < 0
9613 || (off + count * 4) > d->len)
9614 return NULL;
9615
9616 if (rv == NULL) {
9617 if ((rv = (int *)malloc(sizeof(int) * count)) == NULL)
9618 return NULL;
9619 }
9620
9621 for (i = 0; i < count; i++, off += 4) {
9622 rv[i] = buf2int(d->buf + off);
9623 }
9624 return rv;
9625 }
9626
9627 /* Return a pointer to an array of unsigned ints containing data from unsigned 32 bits. */
9628 /* If rv is NULL, the returned value will have been allocated, othewise */
9629 /* the rv will be returned. Return NULL if out of range. */
mkdata_get_u32_uints(struct _mkdata * d,unsigned int * rv,int off,int count)9630 static unsigned int *mkdata_get_u32_uints(struct _mkdata *d, unsigned int *rv, int off, int count) {
9631 int i;
9632
9633 if (count <= 0
9634 || off < 0
9635 || (off + count * 4) > d->len)
9636 return NULL;
9637
9638 if (rv == NULL) {
9639 if ((rv = (unsigned int *)malloc(sizeof(unsigned int) * count)) == NULL)
9640 return NULL;
9641 }
9642
9643 for (i = 0; i < count; i++, off += 4) {
9644 rv[i] = buf2uint(d->buf + off);
9645 }
9646 return rv;
9647 }
9648
9649 /* Return a pointer to an array of doubles containing data from 32 bits. */
9650 /* If rv is NULL, the returned value will have been allocated, othewise */
9651 /* the rv will be returned. Return NULL if out of range or malloc failure. */
mkdata_get_32_doubles(struct _mkdata * d,double * rv,int off,int count)9652 static double *mkdata_get_32_doubles(struct _mkdata *d, double *rv, int off, int count) {
9653 int i;
9654
9655 if (count <= 0
9656 || off < 0
9657 || (off + count * 4) > d->len)
9658 return NULL;
9659
9660 if (rv == NULL) {
9661 if ((rv = (double *)malloc(sizeof(double) * count)) == NULL)
9662 return NULL;
9663 }
9664
9665 for (i = 0; i < count; i++, off += 4) {
9666 unsigned int val;
9667 val = buf2uint(d->buf + off);
9668 rv[i] = IEEE754todouble(val);
9669 }
9670 return rv;
9671 }
9672
9673 /* Return a pointer to an array of doubles containing data from 32 bits, */
9674 /* with the array filled in reverse order. */
9675 /* If rv is NULL, the returned value will have been allocated, othewise */
9676 /* the rv will be returned. Return NULL if out of range or malloc failure. */
mkdata_rget_32_doubles(struct _mkdata * d,double * rv,int off,int count)9677 static double *mkdata_rget_32_doubles(struct _mkdata *d, double *rv, int off, int count) {
9678 int i;
9679
9680 if (count <= 0
9681 || off < 0
9682 || (off + count * 4) > d->len)
9683 return NULL;
9684
9685 if (rv == NULL) {
9686 if ((rv = (double *)malloc(sizeof(double) * count)) == NULL)
9687 return NULL;
9688 }
9689
9690 for (i = count-1; i >= 0; i--, off += 4) {
9691 unsigned int val;
9692 val = buf2uint(d->buf + off);
9693 rv[i] = IEEE754todouble(val);
9694 }
9695 return rv;
9696 }
9697
9698
9699 /* Destroy ourselves */
mkdata_del(mkdata * d)9700 static void mkdata_del(mkdata *d) {
9701 del_a1log(d->log); /* Unref it */
9702 free(d);
9703 }
9704
9705 /* Constructor for mkdata */
new_mkdata(munki * p,unsigned char * buf,int len)9706 mkdata *new_mkdata(munki *p, unsigned char *buf, int len) {
9707 mkdata *d;
9708 if ((d = (mkdata *)calloc(1, sizeof(mkdata))) == NULL) {
9709 a1loge(p->log, 1, "new_mkdata: malloc failed!\n");
9710 return NULL;
9711 }
9712
9713 d->p = p;
9714
9715 d->log = new_a1log_d(p->log); /* Take reference */
9716
9717 d->buf = buf;
9718 d->len = len;
9719
9720 d->get_8_char = mkdata_get_8_char;
9721 d->get_8_asciiz = mkdata_get_8_asciiz;
9722 d->get_8_ints = mkdata_get_8_ints;
9723 d->get_u8_ints = mkdata_get_u8_ints;
9724 d->get_16_ints = mkdata_get_16_ints;
9725 d->get_u16_ints = mkdata_get_u16_ints;
9726 d->get_32_ints = mkdata_get_32_ints;
9727 d->get_u32_uints = mkdata_get_u32_uints;
9728 d->get_32_doubles = mkdata_get_32_doubles;
9729 d->rget_32_doubles = mkdata_rget_32_doubles;
9730
9731 d->del = mkdata_del;
9732
9733 return d;
9734 }
9735
9736 /* ----------------------------------------------------------------- */
9737