IIINSTRUMENT Pipeline Reference Manual 4.4.12
naco_spc_wavecal.c
1/* $Id: naco_spc_wavecal.c,v 1.76 2012-09-06 08:29:10 llundin Exp $
2 *
3 * This file is part of the NACO Pipeline
4 * Copyright (C) 2002,2003 European Southern Observatory
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19 */
20
21/*
22 * $Author: llundin $
23 * $Date: 2012-09-06 08:29:10 $
24 * $Revision: 1.76 $
25 * $Name: not supported by cvs2svn $
26 */
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31
32/*-----------------------------------------------------------------------------
33 Includes
34 -----------------------------------------------------------------------------*/
35
36#include "naco_recipe.h"
37#if 0
38#include "irplib_distortion.h"
39#endif
40
41#include "irplib_wlxcorr.h"
42
43#include "naco_spc.h"
44
45#include "irplib_polynomial.h"
46#include "irplib_wavecal.h"
47#include "irplib_distortion.h"
48
49
50#include <string.h>
51
52/*-----------------------------------------------------------------------------
53 Recipe defines
54 -----------------------------------------------------------------------------*/
55
56#define RECIPE_STRING "naco_spc_wavecal"
57
58/*-----------------------------------------------------------------------------
59 Private Functions prototypes
60 -----------------------------------------------------------------------------*/
61
62static cpl_error_code naco_spc_wavecal_reduce(cpl_imagelist *,
63 cpl_polynomial *,
64 cpl_propertylist *,
65 const char *,
66 const irplib_framelist *,
67 const cpl_table *,
68 const cpl_bivector *,
69 const cpl_parameterlist *);
70
71static cpl_error_code naco_spc_wavecal_get_slitw(const cpl_propertylist *,
72 double *);
73
74
75static cpl_error_code naco_spc_wavecal_1d(cpl_imagelist * self,
76 cpl_propertylist * qclist,
77 const cpl_image * spec2d,
78 const cpl_propertylist * plist,
79 const char * tag,
80 const cpl_polynomial * phdisp,
81 const cpl_bivector * argonlines,
82 const cpl_parameterlist* parlist);
83
84static
85cpl_error_code naco_image_fill_column_from_dispersion(cpl_image *, int, cpl_boolean,
86 const cpl_polynomial *);
87
88static cpl_error_code naco_spc_wavecal_distortion(cpl_image *,
89 cpl_propertylist *,
90 const cpl_parameterlist *);
91
92static cpl_error_code naco_spc_physdisp_fill(cpl_polynomial *, const char *,
93 const cpl_table *);
94
95static cpl_error_code naco_spc_physdisp_transform(cpl_polynomial *, double,
96 double, int, double, double,
97 double, double);
98
99static cpl_error_code naco_spc_wavecal_qc(cpl_propertylist *,
100 cpl_propertylist *,
101 const irplib_framelist *);
102
103static cpl_error_code naco_spc_wavecal_save(cpl_frameset *,
104 const cpl_parameterlist *,
105 const cpl_propertylist *,
106 const cpl_propertylist *,
107 const cpl_imagelist *,
108 const cpl_polynomial *,
109 int, const irplib_framelist *);
110
111static cpl_error_code naco_spc_wavecal_fill_table(cpl_table *,
112 const cpl_polynomial *);
113
114static cpl_error_code naco_spc_wavecal_count_lines(cpl_propertylist *,
115 const cpl_bivector *,
116 double, double);
117static
118cpl_error_code naco_spc_wavecal_interpolate_rejected(cpl_image *,
119 const cpl_polynomial *);
120
121static cpl_error_code naco_spc_wavecal_qc_lines(cpl_propertylist *,
122 const cpl_image *,
123 const cpl_apertures *);
124
125NACO_RECIPE_DEFINE(naco_spc_wavecal,
126 NACO_PARAM_PLOT | NACO_PARAM_FORCE,
127 "Wavelength calibration using arc lamps",
128 RECIPE_STRING
129 " -- NACO spectrocopy wavelength calibration from "
130 "lamp images.\n"
131 "The files listed in the Set Of Frames (sof-file) "
132 "must be tagged:\n"
133 "NACO-raw-file.fits " NACO_SPC_LAMPWAVE_RAW "\n"
134 "NACO-spectrum-model.fits " NACO_SPC_MODEL "\n"
135 "NACO-arc-lines.fits " NACO_SPC_ARGON "\n"
136 "\n"
137 NACO_SPC_MAN_MODESPLIT "\n\n"
138 "Furthermore, each input frame must have a value of "
139 NACO_PFITS_BOOL_LAMP1 " that is false for off-frames and "
140 "true for on-frames.\n"
141 "\n"
142 "Products:\n"
143 NACO_CALIB_ARC_MAP ": Primary HDU with the wavelength map, "
144 "i.e. the pixel values are wavelengths. The first extension "
145 "is a single-row table with the polynomial coefficients of "
146 "the 2D dispersion relation, lambda = P(x, y).\n"
147 NACO_CALIB_ARC_DIFF ": Primary HDU with the difference image "
148 "of the lamp-on and -off image. The first extension is the "
149 "distortion corrected image, were all the arc-lines are "
150 "straight. The dispersion in the distortion corrected image "
151 "is given by the central dispersion, lambda = P(512.5, y).");
152
153
154/*----------------------------------------------------------------------------*/
158/*----------------------------------------------------------------------------*/
159
160/*-----------------------------------------------------------------------------
161 Functions code
162 -----------------------------------------------------------------------------*/
163
164/*----------------------------------------------------------------------------*/
171/*----------------------------------------------------------------------------*/
172static int naco_spc_wavecal(cpl_frameset * framelist,
173 const cpl_parameterlist * parlist)
174{
175 cpl_errorstate cleanstate = cpl_errorstate_get();
176 irplib_framelist * allframes = NULL;
177 irplib_framelist * rawframes = NULL;
178 irplib_framelist * f_one = NULL;
179 const char ** taglist = NULL;
180 cpl_imagelist * lamp_wave = cpl_imagelist_new();
181 cpl_polynomial * disp2d = cpl_polynomial_new(2);
182 cpl_propertylist * qclist = cpl_propertylist_new();
183 cpl_propertylist * paflist = cpl_propertylist_new();
184 const cpl_frame * modelframe;
185 const char * modelfile;
186 cpl_table * modeltab = NULL;
187 const cpl_frame * argonframe;
188 const char * argonfile;
189 cpl_table * argontab = NULL;
190 cpl_bivector * argonlines= NULL;
191 cpl_vector * argonlinex= NULL;
192 cpl_vector * argonliney= NULL;
193 int nargonlines;
194 int nb_good = 0;
195 int nsets;
196 int i;
197
198
199 /* Identify the RAW and CALIB frames in the input frameset */
200 skip_if (naco_dfs_set_groups(framelist));
201
202 allframes = irplib_framelist_cast(framelist);
203 skip_if(allframes == NULL);
204
205 rawframes = irplib_framelist_extract(allframes, NACO_SPC_LAMPWAVE_RAW);
206 skip_if(rawframes == NULL);
207
208 irplib_framelist_empty(allframes);
209
210 /* The parameters of the 1st guess of the dispersion relation */
211 modelframe = cpl_frameset_find_const(framelist, NACO_SPC_MODEL);
212 error_if (modelframe == NULL, CPL_ERROR_DATA_NOT_FOUND, "No input frame "
213 "is tagged %s", NACO_SPC_MODEL);
214
215 modelfile = cpl_frame_get_filename(modelframe);
216 skip_if (modelfile == NULL);
217
218 modeltab = cpl_table_load(modelfile, 1, 0);
219 error_if (modeltab == NULL, cpl_error_get_code(), "Could not "
220 "load the table with the model parameters");
221
222 /* The argon lines */
223 argonframe = cpl_frameset_find_const(framelist, NACO_SPC_ARGON);
224 error_if (argonframe == NULL, CPL_ERROR_DATA_NOT_FOUND, "No input frame "
225 "is tagged %s", NACO_SPC_ARGON);
226
227 argonfile = cpl_frame_get_filename(argonframe);
228 skip_if (argonfile == NULL);
229
230 argontab = cpl_table_load(argonfile, 1, 0);
231 error_if (argontab == NULL, cpl_error_get_code(), "Could not "
232 "load the table with the argon lines");
233
234 /* Wrap a bivector around the argontable */
235 nargonlines = cpl_table_get_nrow(argontab);
236
237 argonlinex = cpl_vector_wrap(nargonlines,
238 cpl_table_get_data_double(argontab,
239 NACO_SPC_LAB_WAVE));
240 skip_if(argonlinex == NULL);
241
242 argonliney = cpl_vector_wrap(nargonlines,
243 cpl_table_get_data_double(argontab,
244 NACO_SPC_LAB_INTENS));
245 skip_if(argonliney == NULL);
246
247#ifdef NACO_SPC_WAVECAL_LOG
248 bug_if(cpl_vector_add_scalar(argonliney, 1.0));
249 skip_if(cpl_vector_logarithm(argonliney, exp(1.0)));
250#endif
251
252 argonlines = cpl_bivector_wrap_vectors(argonlinex, argonliney);
253 bug_if(argonlines == NULL);
254
255 skip_if(irplib_framelist_load_propertylist_all(rawframes, 0, "^("
256 NACO_PFITS_REGEXP_SPCWAVE "|"
257 NACO_PFITS_REGEXP_SPCWAVE_PAF
258 ")$", CPL_FALSE));
259
260 taglist = naco_framelist_set_tag(rawframes, naco_spc_make_tag, &nsets);
261 skip_if(taglist == NULL);
262
263 cpl_msg_info(cpl_func, "Identified %d setting(s) in %d frames",
264 nsets, irplib_framelist_get_size(rawframes));
265
266 /* Extract settings and reduce each of them */
267 for (i=0 ; i < nsets ; i++) {
268 int n_one;
269 cpl_error_code error;
270
271 /* Reduce data set nb i */
272 cpl_msg_info(cpl_func, "Reducing data set %d of %d", i+1, nsets);
273
274 /* Reduce data set nb i */
275 f_one = irplib_framelist_extract(rawframes, taglist[i]);
276
277 bug_if (f_one == NULL);
278
279 n_one = irplib_framelist_get_size(f_one);
280
281 /* Reset the tag */
282 bug_if(irplib_framelist_set_tag_all(f_one, NACO_SPC_LAMPWAVE_RAW));
283
284 cpl_msg_info(cpl_func, "Reducing frame set %d of %d (size=%d) with "
285 "setting: %s", i+1, nsets, n_one, taglist[i]);
286
287 error = naco_spc_wavecal_reduce(lamp_wave, disp2d, qclist, taglist[i],
288 f_one, modeltab, argonlines, parlist);
289
290 /* Save the products */
291 if (error) {
292 if (nsets > 1)
293 irplib_error_recover(cleanstate, "Could not do the wavelength "
294 "calibration for this setting");
295 } else {
296 cpl_errorstate prestate = cpl_errorstate_get();
297
298 skip_if(naco_spc_wavecal_qc(qclist, paflist, f_one));
299
300 /* PRO.CATG */
301 bug_if (cpl_propertylist_append_string(paflist, CPL_DFS_PRO_CATG,
302 NACO_CALIB_ARC_MAP));
303
304 /* modelframe and argonframe will not be modified */
305 /* Cannot skip with frames shared among two framelists */
306 (void)irplib_framelist_set(f_one, (cpl_frame*)modelframe, n_one);
307 (void)irplib_framelist_set(f_one, (cpl_frame*)argonframe, n_one+1);
308 (void)naco_spc_wavecal_save(framelist, parlist, qclist, paflist,
309 lamp_wave, disp2d, i+1, f_one);
310 (void)irplib_framelist_unset(f_one, n_one+1, NULL);
311 (void)irplib_framelist_unset(f_one, n_one, NULL);
312 skip_if(!cpl_errorstate_is_equal(prestate));
313
314 do {
315 cpl_image_delete(cpl_imagelist_unset(lamp_wave, 0));
316 } while (cpl_imagelist_get_size(lamp_wave) > 0);
317
318 nb_good++;
319 }
320 cpl_propertylist_empty(qclist);
321 cpl_propertylist_empty(paflist);
322 irplib_framelist_delete(f_one);
323 f_one = NULL;
324 }
325
326 irplib_ensure(nb_good > 0, CPL_ERROR_DATA_NOT_FOUND,
327 "None of the %d sets could be reduced", nsets);
328
329 end_skip;
330
331 cpl_free(taglist);
332 cpl_imagelist_delete(lamp_wave);
333 cpl_table_delete(modeltab);
334 cpl_bivector_unwrap_vectors(argonlines);
335 (void)cpl_vector_unwrap(argonlinex);
336 (void)cpl_vector_unwrap(argonliney);
337 cpl_table_delete(argontab);
338 irplib_framelist_delete(f_one);
339 irplib_framelist_delete(allframes);
340 irplib_framelist_delete(rawframes);
341 cpl_propertylist_delete(qclist);
342 cpl_propertylist_delete(paflist);
343 cpl_polynomial_delete(disp2d);
344
345 return cpl_error_get_code();
346}
347
348/*----------------------------------------------------------------------------*/
361/*----------------------------------------------------------------------------*/
362static cpl_error_code naco_spc_wavecal_reduce(cpl_imagelist * imglist,
363 cpl_polynomial * disp2d,
364 cpl_propertylist * qclist,
365 const char * tag,
366 const irplib_framelist * framelist,
367 const cpl_table * modeltab,
368 const cpl_bivector * argonlines,
369 const cpl_parameterlist * parlist)
370{
371 cpl_image * self = NULL;
372 cpl_image * corrected = NULL;
373 cpl_imagelist * difflist = cpl_imagelist_new();
374 cpl_polynomial * disp1d = NULL;
375 cpl_polynomial * collapse = cpl_polynomial_new(1);
376 const int nplot = naco_parameterlist_get_int(parlist, RECIPE_STRING,
377 NACO_PARAM_PLOT);
378 const cpl_size deg0 = 0;
379 cpl_polynomial * phdisp = cpl_polynomial_new(1);
380 const cpl_propertylist * plist
381 = irplib_framelist_get_propertylist_const(framelist, 0);
382 const char * specmode
383 = irplib_pfits_get_string(plist, NACO_PFITS_STRING_SPECMODE);
384 const double wlen
385 = irplib_pfits_get_double(plist, NACO_PFITS_DOUBLE_CWLEN);
386 cpl_propertylist * lampkeys = cpl_propertylist_new();
387 cpl_stats * stats = NULL;
388 double adumin, adumax;
389 double mse = 0.0;
390 cpl_vector * center = cpl_vector_new(2);
391 int ny; /* Wavelength resolution */
392
393 bug_if (0);
394 bug_if (imglist == NULL);
395 bug_if (qclist == NULL);
396 bug_if (framelist == NULL);
397 bug_if (modeltab == NULL);
398 bug_if (argonlines == NULL);
399 bug_if (parlist == NULL);
400 bug_if (cpl_imagelist_get_size(imglist));
401
402 /* On-frames have lamp1 on and lamp2 off */
403 bug_if(cpl_propertylist_append_int(lampkeys, NACO_PFITS_BOOL_LAMP1, 1));
404 bug_if(cpl_propertylist_append_int(lampkeys, NACO_PFITS_INT_LAMP2, 0));
405
406 skip_if(naco_imagelist_load_diff(difflist, framelist, lampkeys));
407
408 bug_if(0);
409
410 if (cpl_imagelist_get_size(difflist) > 1) {
411 cpl_msg_warning(cpl_func, "Averaging %d difference images "
412 "into one image for setting %s",
413 (int)cpl_imagelist_get_size(difflist), tag);
414 self = cpl_imagelist_collapse_create(difflist);
415 } else {
416 self = cpl_imagelist_unset(difflist, 0);
417 }
418
419 stats = cpl_stats_new_from_image(self, CPL_STATS_MIN | CPL_STATS_MAX);
420 adumin = cpl_stats_get_min(stats);
421 adumax = cpl_stats_get_max(stats);
422
423 cpl_msg_info(cpl_func, "Difference image for '%s' has ADUs in range: "
424 "%g -> %g", tag, adumin, adumax);
425 if (adumin < 0.0) {
426 cpl_msg_info(cpl_func, "Setting negative ADUs to zero");
427 bug_if(cpl_image_threshold(self, 0.0, FLT_MAX, 0.0, FLT_MAX));
428 }
429
430 if (nplot > 2) {
431 cpl_errorstate prestate = cpl_errorstate_get();
432 cpl_plot_image("", "t 'Difference image'", "", self);
433 if (!cpl_errorstate_is_equal(prestate)) {
434 cpl_errorstate_set(prestate);
435 }
436 }
437
438 corrected = cpl_image_duplicate(self);
439 skip_if(naco_spc_wavecal_distortion(corrected, qclist, parlist));
440
441 ny = cpl_image_get_size_y(self);
442 bug_if (0);
443
444
445 skip_if(naco_spc_physdisp_fill(phdisp, specmode, modeltab));
446
447 cpl_msg_info(cpl_func, "Wavelength range using physical model [micron]: "
448 "%g -> %g",
449 cpl_polynomial_eval_1d(phdisp, 0.5, NULL),
450 cpl_polynomial_eval_1d(phdisp, 0.5 + ny, NULL));
451
452 if (naco_parameterlist_get_bool(parlist, RECIPE_STRING, NACO_PARAM_FORCE)) {
453 /* FIXME: cpl_wlcalib_xc_best_poly() also fails with this */
454 const cpl_size idegree = 0;
455 const double dwlen
456 = cpl_polynomial_eval_1d(phdisp, 0.5*(1+ny), NULL) - wlen;
457 const double newval
458 = cpl_polynomial_get_coeff(phdisp, &idegree) - dwlen;
459
460 bug_if(cpl_polynomial_set_coeff(phdisp, &idegree, newval));
461 }
462
463 cpl_msg_info(cpl_func, "Central Wavelength (model <=> CWLEN) [micron]: "
464 "%g <=> %g", cpl_polynomial_eval_1d(phdisp, 0.5*(1+ny), NULL),
465 wlen);
466
467
468 skip_if(naco_spc_wavecal_1d(imglist, qclist, self, plist, tag, phdisp,
469 argonlines, parlist));
470
471 skip_if(irplib_polynomial_fit_2d_dispersion(disp2d,
472 cpl_imagelist_get(imglist, 0),
473 4, &mse));
474
475 cpl_msg_info(cpl_func, "2D-dispersion with MSE=%g for setting %s", mse, tag);
476 skip_if(cpl_polynomial_dump(disp2d, stdout));
477
478 skip_if(naco_spc_wavecal_interpolate_rejected(cpl_imagelist_get(imglist, 0),
479 disp2d));
480
481 bug_if(cpl_vector_set(center, 0, 0.5*(1+cpl_image_get_size_x(self))));
482 bug_if(cpl_vector_set(center, 1, 0.5*(1+cpl_image_get_size_y(self))));
483
484 bug_if(cpl_propertylist_append_double(qclist, "ESO QC WLEN",
485 cpl_polynomial_eval(disp2d, center)));
486
487 /* Central 1D polynomial */
488 bug_if(cpl_polynomial_set_coeff(collapse, &deg0,
489 0.5 * (1+cpl_image_get_size_x(self))));
490
491 disp1d = cpl_polynomial_extract(disp2d, 0, collapse);
492 cpl_msg_info(cpl_func, "Central 1D-dispersion (at x=%g)",
493 0.5*(1+cpl_image_get_size_x(self)));
494 skip_if(cpl_polynomial_dump(disp1d, stdout));
495
496 end_skip;
497
498 if (cpl_error_get_code()) {
499 cpl_image_delete(self);
500 cpl_image_delete(corrected);
501 } else {
502 cpl_imagelist_set(imglist, self, 1);
503 cpl_imagelist_set(imglist, corrected, 2);
504 }
505
506 cpl_vector_delete(center);
507 cpl_propertylist_delete(lampkeys);
508 cpl_imagelist_delete(difflist);
509 cpl_stats_delete(stats);
510 cpl_polynomial_delete(phdisp);
511 cpl_polynomial_delete(disp1d);
512 cpl_polynomial_delete(collapse);
513
514 return cpl_error_get_code();
515}
516
517
518/*----------------------------------------------------------------------------*/
531/*----------------------------------------------------------------------------*/
532static cpl_error_code naco_spc_wavecal_1d(cpl_imagelist * self,
533 cpl_propertylist * qclist,
534 const cpl_image * spec2d,
535 const cpl_propertylist * plist,
536 const char * tag,
537 const cpl_polynomial * phdisp,
538 const cpl_bivector * argonlines,
539 const cpl_parameterlist* parlist)
540{
541
542 cpl_errorstate prestate = cpl_errorstate_get();
543 const int nx = cpl_image_get_size_x(spec2d); /* Spatial resolution */
544 const int ny = cpl_image_get_size_y(spec2d); /* Wavelength resolution */
545 cpl_vector * vspec1d = NULL;
546 cpl_polynomial * disp = NULL;
547 cpl_polynomial * dispcen = NULL;
548 cpl_polynomial * phshift = cpl_polynomial_duplicate(phdisp);
549 cpl_polynomial * dispdif = cpl_polynomial_new(1);
550 /* Initialize to zero */
551 cpl_vector * linepix
552 = cpl_vector_wrap(cpl_bivector_get_size(argonlines),
553 cpl_calloc(cpl_bivector_get_size(argonlines),
554 sizeof(double)));
555 double slitw = 0.0; /* Fix (false) uninit warning */
556 const double wfwhm = 4.0;
557 double xtrunc;
558 double xc, xcmean, xcstdev;
559 const int nplot = naco_parameterlist_get_int(parlist, RECIPE_STRING,
560 NACO_PARAM_PLOT);
561 const int plotstep = nplot > 0 ? nx / (1<<nplot) : nx + 1;
562 const int degree = cpl_polynomial_get_degree(phdisp);
563 const int fitdeg = degree > 4 ? degree : 4; /* Fit at least 4th degree */
564 cpl_image * imgdisp = cpl_image_new(nx, ny, cpl_image_get_type(spec2d));
565 cpl_vector * vxcall = cpl_vector_new(nx);
566 /* One practical limitation on hshiftmax is that it may not be so big, as
567 to cause phdisp to be evaluated outside its range of monotony. */
568 const int hshiftmax = ny/4;
569 irplib_line_spectrum_model model;
570 const double pixstep = 0.25;
571 const double pixtol = 1e-5; /* 1e-6 leads to more accuracy, is slower */
572 const int istart = nx/2; /* Start on central column */
573 const int maxite = fitdeg * 100;
574 int ispec = istart;
575 double wl2dmin = FLT_MAX;
576 double wl2dmax = 0.0;
577 cpl_boolean isfirst = CPL_TRUE;
578
579 bug_if(0);
580 bug_if(qclist == NULL);
581 bug_if(spec2d == NULL);
582 bug_if(plist == NULL);
583 bug_if(phdisp == NULL);
584 bug_if(argonlines == NULL);
585
586
587 skip_if(naco_spc_wavecal_get_slitw(plist, &slitw));
588
589 cpl_msg_info(cpl_func, "Slitwidth [pixel]: %g", slitw);
590
591 xtrunc = 0.5 * slitw + 5.0 * wfwhm * CPL_MATH_SIG_FWHM;
592
593 memset(&model, 0, sizeof(model));
594 model.wslit = slitw;
595 model.wfwhm = wfwhm;
596 model.xtrunc = xtrunc;
597 model.lines = argonlines;
598 model.linepix = linepix;
599 model.cost = 0;
600
601 vspec1d = cpl_vector_new_from_image_column(spec2d, ispec);
602#ifdef NACO_SPC_WAVECAL_LOG
603 bug_if(cpl_vector_add_scalar(vspec1d, 1.0));
604 skip_if(cpl_vector_logarithm(vspec1d, exp(1.0)));
605#endif
606
607 if (nplot > 0) {
608 char * title = cpl_sprintf("t 'Uncalibrated 1D-spectrum "
609 "using %s' w linespoints", tag);
610 cpl_vector * vphys = cpl_vector_new(ny);
611 cpl_bivector * bspec1d = cpl_bivector_wrap_vectors(vphys, vspec1d);
612
613 (void)cpl_vector_fill_polynomial(vphys, phdisp, 1.0, 1.0);
614
615
616 cpl_plot_bivector("set grid;set xlabel 'Wavelength [micron]';"
617 "set ylabel 'Intensity [ADU]';", title, "",
618 bspec1d);
619 cpl_free(title);
620 cpl_vector_delete(vphys);
621 cpl_bivector_unwrap_vectors(bspec1d);
622 if (!cpl_errorstate_is_equal(prestate)) {
623 cpl_errorstate_set(prestate);
624 }
625 }
626
627 skip_if(irplib_polynomial_shift_1d_from_correlation(phshift, vspec1d,
628 (void*)&model,
629 irplib_vector_fill_line_spectrum,
630 hshiftmax, nplot > 0,
631 NULL));
632
633 if (nplot > 0) {
634 bug_if(irplib_plot_spectrum_and_model(vspec1d, phshift, (void*)&model,
635 irplib_vector_fill_line_spectrum));
636 }
637
638 bug_if(cpl_polynomial_subtract(dispdif, phshift, phdisp));
639
640 cpl_msg_info(cpl_func, "Changes to model polynomial by XC is of degree %d",
641 (int)cpl_polynomial_get_degree(dispdif));
642 skip_if(cpl_polynomial_dump(dispdif, stdout));
643
644 disp = cpl_polynomial_duplicate(phshift);
645 /* In the unlikely event that the calibration fails on the central column */
646 dispcen = cpl_polynomial_duplicate(disp);
647
648 /* Right half starting from central column */
649 for (; ispec <= nx; ispec++) {
650 const cpl_size prevcost = model.cost;
651
652 if (!isfirst) {
653 cpl_vector_delete(vspec1d);
654 vspec1d = cpl_vector_new_from_image_column(spec2d, ispec);
655#ifdef NACO_SPC_WAVECAL_LOG
656 bug_if(cpl_vector_add_scalar(vspec1d, 1.0));
657 skip_if(cpl_vector_logarithm(vspec1d, exp(1.0)));
658#endif
659 }
660
661 xc = 0.0;
662 if (irplib_polynomial_find_1d_from_correlation
663 (disp, fitdeg, vspec1d, (void *)&model,
664 irplib_vector_fill_line_spectrum, pixtol, pixstep, 2, maxite,
665 &xc)) {
666 irplib_error_recover(prestate, "Could not calibrate column %d of "
667 "%d", ispec, nx);
668 cpl_polynomial_copy(disp, dispcen);
669 xc = 0.0;
670 } else {
671 double wl2d = cpl_polynomial_eval_1d(disp, 0.5, NULL);
672
673 if (wl2d < wl2dmin) wl2dmin = wl2d;
674
675 wl2d = cpl_polynomial_eval_1d(disp, ny + 0.5, NULL);
676 if (wl2d > wl2dmax) wl2dmax = wl2d;
677
678 if (ispec % plotstep == 0) {
679 bug_if(irplib_plot_spectrum_and_model
680 (vspec1d, disp,
681 (void*)&model,
682 irplib_vector_fill_line_spectrum));
683 }
684#ifdef IRPLIB_SPC_DUMP
685 /* Need irplib_wavecal.c rev. 1.12 through 1.15 */
686 if (ispec == istart) {
687 irplib_polynomial_tabulate(disp, vspec1d, (void *)&model,
688 irplib_vector_fill_line_spectrum,
689 50, 0.1);
690 }
691#endif
692 }
693
694 bug_if(cpl_vector_set(vxcall, ispec-1, xc));
695
696 cpl_msg_info(cpl_func, "XC(%d): %g (cost=%u of %u)", ispec, xc,
697 (unsigned)(model.cost-prevcost), (unsigned)model.cost);
698
699 bug_if(naco_image_fill_column_from_dispersion(imgdisp, ispec, xc <= 0.0,
700 disp));
701
702 if (isfirst) {
703 double cdisp;
704 const double cwl = cpl_polynomial_eval_1d(disp, 0.5*ny + 0.5,
705 &cdisp);
706
707 isfirst = CPL_FALSE;
708 cpl_msg_info(cpl_func, "Center of setting %s has range %g -> %g "
709 "-> %g [um], center dispersion %g [nm/pixel] and "
710 "dispersion (of degree %d):", tag,
711 cpl_polynomial_eval_1d(disp, 0.5, NULL),
712 cwl,
713 cpl_polynomial_eval_1d(disp, ny + 0.5, NULL),
714 1e3*cdisp,
715 (int)cpl_polynomial_get_degree(disp));
716 skip_if(cpl_polynomial_dump(disp, stdout));
717
718 cpl_polynomial_copy(dispcen, disp);
719
720 bug_if(cpl_polynomial_subtract(dispdif, disp, phshift));
721
722 cpl_msg_info(cpl_func, "Changes to model polynomial by search is of"
723 " degree %d", (int)cpl_polynomial_get_degree(dispdif));
724 skip_if(cpl_polynomial_dump(dispdif, stdout));
725
726 bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISP XCORR",
727 xc));
728 }
729 }
730
731 /* Left half, restarting from central column */
732 cpl_polynomial_copy(disp, dispcen);
733
734 for (ispec = istart-1; ispec > 0; ispec--) {
735 const cpl_size prevcost = model.cost;
736
737 cpl_vector_delete(vspec1d);
738 vspec1d = cpl_vector_new_from_image_column(spec2d, ispec);
739#ifdef NACO_SPC_WAVECAL_LOG
740 bug_if(cpl_vector_add_scalar(vspec1d, 1.0));
741 skip_if(cpl_vector_logarithm(vspec1d, exp(1.0)));
742#endif
743
744 xc = 0.0;
745 if (irplib_polynomial_find_1d_from_correlation
746 (disp, fitdeg, vspec1d, (void *)&model,
747 irplib_vector_fill_line_spectrum, pixtol, pixstep, 2, maxite, &xc)) {
748 if (ispec == 1 && wl2dmin >= wl2dmax) {
749 error_if (0, cpl_error_get_code(),
750 "None of the columns could be calibrated");
751 }
752
753 irplib_error_recover(prestate, "Could not calibrate column %d of "
754 "%d", ispec, nx);
755 cpl_polynomial_copy(disp, dispcen);
756 } else {
757 double wl2d = cpl_polynomial_eval_1d(disp, 0.5, NULL);
758
759 if (wl2d < wl2dmin) wl2dmin = wl2d;
760
761 wl2d = cpl_polynomial_eval_1d(disp, ny + 0.5, NULL);
762 if (wl2d > wl2dmax) wl2dmax = wl2d;
763
764 if (ispec % plotstep == 0) {
765 bug_if(irplib_plot_spectrum_and_model
766 (vspec1d, disp,
767 (void*)&model,
768 irplib_vector_fill_line_spectrum));
769 }
770 }
771
772 bug_if(cpl_vector_set(vxcall, ispec-1, xc));
773
774 cpl_msg_info(cpl_func, "XC(%d): %g (cost=%u of %u)", ispec, xc,
775 (unsigned)(model.cost-prevcost), (unsigned)model.cost);
776
777 bug_if(naco_image_fill_column_from_dispersion(imgdisp, ispec, xc <= 0.0,
778 disp));
779 }
780
781 if (nplot > 0) {
782 cpl_plot_vector("set grid;", "t 'XC over spatial range' w linespoints",
783 "", vxcall);
784 }
785
786 xcmean = cpl_vector_get_mean(vxcall);
787 xcstdev = cpl_vector_get_stdev(vxcall);
788
789 bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISP XCORR MEAN",
790 xcmean));
791 bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISP XCORR STDEV",
792 xcstdev));
793
794 cpl_msg_info(cpl_func,"Cross-correlation mean and stdev for setting %s: %g "
795 "%g", tag, xcmean, xcstdev);
796 cpl_msg_info(cpl_func, "Total fitting cost: %u", (unsigned)model.cost);
797
798 skip_if(naco_spc_wavecal_count_lines(qclist, argonlines, wl2dmin, wl2dmax));
799
800 end_skip;
801
802 if (cpl_error_get_code()) {
803 cpl_image_delete(imgdisp);
804 } else {
805 cpl_imagelist_set(self, imgdisp, 0);
806 }
807
808 cpl_vector_delete(linepix);
809 cpl_vector_delete(vxcall);
810 cpl_polynomial_delete(dispdif);
811 cpl_polynomial_delete(disp);
812 cpl_polynomial_delete(dispcen);
813 cpl_polynomial_delete(phshift);
814 cpl_vector_delete(vspec1d);
815
816 return cpl_error_get_code();
817}
818
819
820/*----------------------------------------------------------------------------*/
829/*----------------------------------------------------------------------------*/
830static cpl_error_code naco_spc_physdisp_fill(cpl_polynomial * self,
831 const char * mode,
832 const cpl_table * modeltab)
833{
834
835 int imode;
836 const int nrows = cpl_table_get_nrow(modeltab);
837 const char ** smode = cpl_table_get_data_string_const(modeltab,
838 NACO_SPC_LAB_MODE);
839
840 bug_if(self == NULL);
841 bug_if(mode == NULL);
842 bug_if(modeltab == NULL);
843 bug_if(cpl_polynomial_get_dimension(self) != 1);
844
845 skip_if (smode == NULL);
846
847 for (imode = 0; imode < nrows; imode++) {
848
849 skip_if(smode[imode] == NULL);
850
851 if (!strcmp(mode, smode[imode])) break;
852 }
853
854 error_if (imode == nrows, CPL_ERROR_UNSUPPORTED_MODE, "Unsupported value "
855 "'%s' for " NACO_PFITS_STRING_SPECMODE, mode);
856
857 cpl_msg_info(cpl_func, "Finding dispersion relation for spectrum mode "
858 "%d/%d: %s ", imode, nrows, mode);
859
860 skip_if(naco_spc_physdisp_transform
861 (self,
862 cpl_table_get_double(modeltab, NACO_SPC_LAB_XMIN, imode, NULL),
863 cpl_table_get_double(modeltab, NACO_SPC_LAB_XMAX, imode, NULL),
864 cpl_table_get_int (modeltab, NACO_SPC_LAB_ORDER, imode, NULL),
865 cpl_table_get_double(modeltab, NACO_SPC_LAB_C1, imode, NULL),
866 cpl_table_get_double(modeltab, NACO_SPC_LAB_C2, imode, NULL),
867 cpl_table_get_double(modeltab, NACO_SPC_LAB_C3, imode, NULL),
868 cpl_table_get_double(modeltab, NACO_SPC_LAB_C4, imode, NULL)));
869
870#ifdef NACO_SPC_WAVECAL_S54_3_SH
871 if (!strcmp("S54_3_SH", mode)) {
872 double p0 = 1.36983;
873 double p1 = 0.000165591;
874 double p2 = 2.86676e-07;
875 cpl_size degree = 0;
876
877 bug_if(cpl_polynomial_set_coeff(self, &degree, p0));
878 degree++;
879 bug_if(cpl_polynomial_set_coeff(self, &degree, p1));
880 degree++;
881 bug_if(cpl_polynomial_set_coeff(self, &degree, p2));
882 degree++;
883 bug_if(cpl_polynomial_set_coeff(self, &degree, 0.0));
884 degree++;
885 bug_if(cpl_polynomial_set_coeff(self, &degree, 0.0));
886
887 cpl_msg_warning(cpl_func, "Changing phdisp to simple fit:");
888 skip_if(cpl_polynomial_dump(self, stdout));
889 }
890#endif
891
892 end_skip;
893
894 return cpl_error_get_code();
895}
896
897
898/*----------------------------------------------------------------------------*/
913/*----------------------------------------------------------------------------*/
914static cpl_error_code naco_spc_physdisp_transform(cpl_polynomial * self,
915 double xmin, double xmax,
916 int fit_order,
917 double c1, double c2,
918 double c3, double c4)
919{
920
921 const double alpha = 2.0/(xmax - xmin);
922 const double beta = -(xmax + xmin) / (xmax - xmin);
923 double value;
924 double lambdamin1, lambdamax1;
925 double lambdamin2, lambdamax2;
926 cpl_size degree;
927
928 bug_if(self == NULL);
929 bug_if(cpl_polynomial_get_dimension(self) != 1);
930
931 /* The specifics of the model may later become user definable */
932 skip_if(xmin >= xmax);
933
934 value = 1e-4 * (c1 - 0.5 * c3);
935 degree = 0;
936 bug_if(cpl_polynomial_set_coeff(self, &degree, value));
937
938 value = 1e-4 * (c2 - 1.5 * c4);
939 degree = 1;
940 bug_if(cpl_polynomial_set_coeff(self, &degree, value));
941
942 if (fit_order > 2) {
943 value = 1e-4 * 1.5 * c3;
944 degree = 2;
945 bug_if(cpl_polynomial_set_coeff(self, &degree, value));
946
947 if (fit_order > 3) {
948 value = 1e-4 * 2.5 * c4;
949 degree = 3;
950 bug_if(cpl_polynomial_set_coeff(self, &degree, value));
951
952 skip_if(fit_order > 4);
953 }
954 } else {
955 skip_if(fit_order < 2);
956 }
957
958
959 lambdamin1 = cpl_polynomial_eval_1d(self, -1.0, NULL);
960 lambdamax1 = cpl_polynomial_eval_1d(self, 1.0, NULL);
961
962
963 /* Now transform the polynomial from the domain [-1;1] to [xmin;xmax],
964 n = (2 * x - (xmax + xmin) / (xmax - xmin),
965 n = x * alpha + beta */
966
967 bug_if(cpl_polynomial_shift_1d(self, 0, beta));
968
969 degree = 1;
970 value = cpl_polynomial_get_coeff(self, &degree) * alpha;
971 bug_if(cpl_polynomial_set_coeff(self, &degree, value));
972
973 if (fit_order > 2) {
974 degree = 2;
975 value = cpl_polynomial_get_coeff(self, &degree) * alpha * alpha;
976 bug_if(cpl_polynomial_set_coeff(self, &degree, value));
977
978 if (fit_order > 3) {
979 degree = 3;
980 value = cpl_polynomial_get_coeff(self, &degree) * alpha * alpha
981 * alpha;
982 bug_if(cpl_polynomial_set_coeff(self, &degree, value));
983 }
984 }
985
986 lambdamin2 = cpl_polynomial_eval_1d(self, xmin, NULL);
987 lambdamax2 = cpl_polynomial_eval_1d(self, xmax, NULL);
988
989 skip_if(cpl_polynomial_get_degree(self) != fit_order - 1);
990
991 skip_if(cpl_polynomial_dump(self, stdout));
992
993 cpl_msg_debug(cpl_func, "Interpolation minimum=%g: %g (%g)", xmin,
994 lambdamin1, lambdamin2-lambdamin1);
995 cpl_msg_debug(cpl_func, "Interpolation maximum=%g: %g (%g)", xmax,
996 lambdamax1, lambdamax2-lambdamax1);
997
998 end_skip;
999
1000 return cpl_error_get_code();
1001}
1002
1003
1004/*----------------------------------------------------------------------------*/
1014/*----------------------------------------------------------------------------*/
1015static cpl_error_code naco_spc_wavecal_get_slitw(const cpl_propertylist * self,
1016 double * pslitw)
1017{
1018 const char * sslitw;
1019 int nvals;
1020 unsigned uslitw;
1021 double pixscale;
1022
1023
1024 bug_if(self == NULL);
1025 bug_if(pslitw == NULL);
1026
1027 sslitw = irplib_pfits_get_string(self, NACO_PFITS_STRING_SLITNAME);
1028 skip_if(sslitw == NULL);
1029
1030 nvals = sscanf(sslitw, "Slit_%u", &uslitw);
1031
1032 error_if(nvals != 1, CPL_ERROR_UNSUPPORTED_MODE, "Unsupported value of '"
1033 NACO_PFITS_STRING_SLITNAME ": %s", sslitw);
1034
1035 pixscale = irplib_pfits_get_double(self, NACO_PFITS_DOUBLE_PIXSCALE);
1036 skip_if(0);
1037 error_if(pixscale <= 0.0, CPL_ERROR_ILLEGAL_INPUT, "Non-positive value of '"
1038 NACO_PFITS_DOUBLE_PIXSCALE ": %g", pixscale);
1039
1040 *pslitw = (double)uslitw/(1000.0*pixscale); /* Convert from mas to pixel */
1041
1042 end_skip;
1043
1044 return cpl_error_get_code();
1045}
1046
1047
1048/*----------------------------------------------------------------------------*/
1058/*----------------------------------------------------------------------------*/
1059static cpl_error_code
1060naco_image_fill_column_from_dispersion(cpl_image * self,
1061 int ispec, cpl_boolean is_bad,
1062 const cpl_polynomial * disp)
1063{
1064
1065 const int ny = cpl_image_get_size_y(self);
1066 int i;
1067
1068 bug_if(self == NULL);
1069 bug_if(disp == NULL);
1070 bug_if(cpl_polynomial_get_dimension(disp) != 1);
1071 bug_if(cpl_polynomial_get_degree(disp) < 1);
1072
1073 for (i = 1; i <= ny; i++) {
1074 const double value = cpl_polynomial_eval_1d(disp, (double)i, NULL);
1075 cpl_image_set(self, ispec, i, value);
1076 if (is_bad) cpl_image_reject(self, ispec, i);
1077 }
1078
1079 end_skip;
1080
1081 return cpl_error_get_code();
1082}
1083
1084
1085/*----------------------------------------------------------------------------*/
1094/*----------------------------------------------------------------------------*/
1095static
1096cpl_error_code naco_spc_wavecal_distortion(cpl_image * self,
1097 cpl_propertylist * qclist,
1098 const cpl_parameterlist * parlist)
1099{
1100
1101 const int fitdeg = 2;
1102 cpl_image * copy = NULL;
1103 const int nx = cpl_image_get_size_x(self);
1104 const int ny = cpl_image_get_size_y(self);
1105 cpl_apertures * lines = NULL;
1106 cpl_polynomial* distortion = NULL;
1107 cpl_polynomial* yid2d = cpl_polynomial_new(2);
1108 cpl_vector * profile = cpl_vector_new(CPL_KERNEL_DEF_SAMPLES);
1109 cpl_size power[] = {0, 1};
1110 const int nplot = naco_parameterlist_get_int(parlist, RECIPE_STRING,
1111 NACO_PARAM_PLOT);
1112 cpl_polynomial* center = cpl_polynomial_new(1);
1113 cpl_polynomial* dist1d = NULL;
1114 cpl_vector * dist1dfix = NULL;
1115 const cpl_size i0 = 0;
1116 const cpl_size i1 = 1;
1117 const double xcent = 0.5*(nx + 1);
1118 const double ycent = 0.5*(ny + 1);
1119
1120 bug_if(0);
1121 bug_if(self == NULL);
1122 bug_if(qclist == NULL);
1123 bug_if(parlist == NULL);
1124
1125
1126 /* Distortion correction supports only vertical lines */
1127 bug_if(cpl_image_turn(self, 1));
1128
1129 distortion = irplib_distortion_estimate(self, 1, 1, nx, ny,
1130 CPL_FALSE, 1e8,
1131 33,
1132 0.33, fitdeg, &lines);
1133
1134 error_if(distortion == NULL, cpl_error_get_code(), "Curvature estimation "
1135 "failed");
1136
1137 if (cpl_msg_get_level() <= CPL_MSG_DEBUG) {
1138 skip_if(cpl_polynomial_dump(distortion, stdout));
1139 cpl_apertures_dump(lines, stdout);
1140 }
1141
1142 skip_if(naco_spc_wavecal_qc_lines(qclist, self, lines));
1143
1144 /* Create the y-identity 2D-polynomial */
1145 bug_if(cpl_polynomial_set_coeff(yid2d, power, 1.0));
1146
1147 /* Fill the kernel */
1148 bug_if(cpl_vector_fill_kernel_profile(profile, CPL_KERNEL_DEFAULT,
1149 CPL_KERNEL_DEF_WIDTH));
1150
1151 /* Apply the distortion correction */
1152 copy = cpl_image_duplicate(self); /* Needed, so self can keep result */
1153 error_if (cpl_image_warp_polynomial(self, copy, distortion, yid2d, profile,
1154 CPL_KERNEL_DEF_WIDTH, profile,
1155 CPL_KERNEL_DEF_WIDTH),
1156 cpl_error_get_code(), "Distortion correction failed");
1157
1158 /* Rotate distortion corrected image back */
1159 bug_if(cpl_image_turn(self, -1));
1160
1161 if (nplot > 1) {
1162 cpl_errorstate prestate = cpl_errorstate_get();
1163 cpl_plot_image("", "t 'Distortion corrected image'", "", self);
1164 if (!cpl_errorstate_is_equal(prestate)) {
1165 cpl_errorstate_set(prestate);
1166 }
1167
1168 /* Rotate distortion corrected image back */
1169 (void)cpl_image_turn(copy, -1);
1170
1171 (void)cpl_image_subtract(copy, self);
1172
1173 cpl_plot_image("", "t 'Distortion correction'", "", copy);
1174 if (!cpl_errorstate_is_equal(prestate)) {
1175 cpl_errorstate_set(prestate);
1176 }
1177
1178 }
1179
1180 skip_if(cpl_polynomial_dump(distortion, stdout));
1181
1182 /* center is a zero-degree polynomial, p(x) = (nx+1)/2 */
1183 bug_if(cpl_polynomial_set_coeff(center, &i0, xcent));
1184
1185 dist1d = cpl_polynomial_extract(distortion, 1, center);
1186
1187 /* Reuse center-polynimial: P(y) = y */
1188 bug_if(cpl_polynomial_set_coeff(center, &i1, 1.0));
1189 bug_if(cpl_polynomial_set_coeff(center, &i0, 0.0));
1190
1191 /* The deviation from the perfect center polynomial */
1192 bug_if(cpl_polynomial_subtract(center, dist1d, center));
1193
1194 if (cpl_polynomial_get_degree(center) > 0) {
1195 const cpl_size ndist1d = cpl_polynomial_get_degree(dist1d);
1196 cpl_size dist1dnreal;
1197
1198 cpl_msg_info(cpl_func, "On the center column (x=%g) the distortion poly"
1199 "nomial should be P(y)=y, its deviation from that has deg"
1200 "ree %d:", xcent, (int)cpl_polynomial_get_degree(center));
1201 skip_if(cpl_polynomial_dump(center, stdout));
1202
1203 if (ndist1d > 0) {
1204 cpl_errorstate prestate = cpl_errorstate_get();
1205 dist1dfix = cpl_vector_new(ndist1d);
1206 if (irplib_polynomial_solve_1d_all(dist1d, dist1dfix,
1207 &dist1dnreal)) {
1208 dist1dnreal = 0;
1209 irplib_error_recover(prestate, "Could not compute fix-points for "
1210 "%d-degree polynomial", (int)ndist1d);
1211 }
1212 } else {
1213 dist1dnreal = 0;
1214 }
1215
1216 if (dist1dnreal > 0) {
1217 cpl_vector * dist1dfixreal = dist1dnreal == ndist1d ? dist1dfix
1218 : cpl_vector_wrap(dist1dnreal, cpl_vector_get_data(dist1dfix));
1219 cpl_msg_info(cpl_func, "The distortion correction has %d fix-"
1220 "point(s) on the center column:", (int)dist1dnreal);
1221 cpl_vector_dump(dist1dfixreal, stdout);
1222 if (dist1dfixreal != dist1dfix)
1223 (void)cpl_vector_unwrap(dist1dfixreal);
1224 } else if (cpl_polynomial_get_coeff(dist1d, &i0) != 0.0) {
1225 /* Should not reach this point */
1226 cpl_msg_info(cpl_func, "The distortion correction has "
1227 "no fix-points on the center column");
1228 } else {
1229 /* Should not reach this point */
1230 cpl_msg_info(cpl_func, "The distortion correction has "
1231 "no fix-points on the center column");
1232 }
1233
1234 cpl_msg_info(cpl_func, "The distortion correction moves the detector "
1235 "center at (%g,%g) by (%g,%g)", xcent, ycent, 0.0,
1236 cpl_polynomial_eval_1d(dist1d, ycent, NULL)-ycent);
1237 } else if (cpl_polynomial_get_coeff(center, &i0) != 0.0) {
1238 cpl_msg_info(cpl_func, "The distortion correction has no fix-points "
1239 "on the center column, their Y-offset are [pixel]: %g",
1240 cpl_polynomial_get_coeff(center, &i0));
1241 } else {
1242 cpl_msg_info(cpl_func, "The distortion correction has all points "
1243 "on the center column (at x=%g) as fix-points", xcent);
1244 }
1245
1246
1247 power[0] = power[1] = 0;
1248 bug_if(cpl_propertylist_append_double(qclist, "ESO QC DIST1",
1249 cpl_polynomial_get_coeff(distortion,
1250 power)));
1251 power[0] = 1;
1252 bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISTY",
1253 cpl_polynomial_get_coeff(distortion,
1254 power)));
1255 power[0] = 2;
1256 bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISTYY",
1257 cpl_polynomial_get_coeff(distortion,
1258 power)));
1259
1260 power[0] = power[1] = 1;
1261 bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISTXY",
1262 cpl_polynomial_get_coeff(distortion,
1263 power)));
1264 power[0] = 0;
1265 bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISTX",
1266 cpl_polynomial_get_coeff(distortion,
1267 power)));
1268 power[1] = 2;
1269 bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISTXX",
1270 cpl_polynomial_get_coeff(distortion,
1271 power)));
1272
1273 end_skip;
1274
1275 /* lines and distortion are rotated :-( */
1276
1277 cpl_image_delete(copy);
1278 cpl_apertures_delete(lines);
1279 cpl_vector_delete(profile);
1280 cpl_vector_delete(dist1dfix);
1281 cpl_polynomial_delete(distortion);
1282 cpl_polynomial_delete(yid2d);
1283 cpl_polynomial_delete(center);
1284 cpl_polynomial_delete(dist1d);
1285
1286 return cpl_error_get_code();
1287}
1288
1289/*----------------------------------------------------------------------------*/
1297/*----------------------------------------------------------------------------*/
1298static cpl_error_code naco_spc_wavecal_qc(cpl_propertylist * qclist,
1299 cpl_propertylist * paflist,
1300 const irplib_framelist * rawframes)
1301{
1302
1303 const cpl_propertylist * reflist
1304 = irplib_framelist_get_propertylist_const(rawframes, 0);
1305 const char pafcopy[] = "^(" NACO_PFITS_REGEXP_SPCWAVE_PAF ")$";
1306
1307
1308 bug_if (0);
1309
1310
1311 /* THE PAF FILE FOR QC PARAMETERS */
1312 skip_if (cpl_propertylist_copy_property_regexp(paflist, reflist, pafcopy,
1313 0));
1314 skip_if (cpl_propertylist_append(paflist, qclist));
1315
1316 bug_if (cpl_propertylist_copy_property_regexp(qclist, reflist, "^("
1317 IRPLIB_PFITS_REGEXP_RECAL_LAMP
1318 ")$", 0));
1319 end_skip;
1320
1321 return cpl_error_get_code();
1322}
1323
1324/*----------------------------------------------------------------------------*/
1336/*----------------------------------------------------------------------------*/
1337static cpl_error_code naco_spc_wavecal_fill_table(cpl_table * self,
1338 const cpl_polynomial * disp2d)
1339{
1340
1341 const int degree = cpl_polynomial_get_degree(disp2d);
1342 const char * lunit = "micron";
1343 int i, j;
1344
1345 bug_if (self == NULL);
1346 bug_if (cpl_polynomial_get_dimension(disp2d) != 2);
1347 bug_if (degree < 1);
1348
1349 bug_if(cpl_table_set_size(self, 1));
1350
1351 for (i=0; i <= degree; i++) {
1352 for (j = 0; j <= i; j++) {
1353 const cpl_size powers[2] = {i-j, j};
1354 const double value = cpl_polynomial_get_coeff(disp2d, powers);
1355 char * label = cpl_sprintf("DISP2D_%d_%d", i-j, j);
1356 char * unit = i > 1 ? cpl_sprintf("%s/pixel^%d", lunit, i)
1357 : cpl_sprintf(i ? "%s/pixel" : "%s", lunit);
1358
1359 cpl_table_new_column(self, label, CPL_TYPE_DOUBLE);
1360 cpl_table_set_column_unit(self, label, unit);
1361 cpl_table_set_double(self, label, 0, value);
1362
1363 cpl_free(label);
1364 cpl_free(unit);
1365 bug_if(0);
1366 }
1367 }
1368
1369 end_skip;
1370
1371 return cpl_error_get_code();
1372}
1373
1374/*----------------------------------------------------------------------------*/
1387/*----------------------------------------------------------------------------*/
1388static cpl_error_code naco_spc_wavecal_save(cpl_frameset * set_tot,
1389 const cpl_parameterlist * parlist,
1390 const cpl_propertylist * qclist,
1391 const cpl_propertylist * paflist,
1392 const cpl_imagelist * lamp_wave,
1393 const cpl_polynomial * disp2d,
1394 int set_nb,
1395 const irplib_framelist * rawframes)
1396{
1397 cpl_frameset * proframes = irplib_frameset_cast(rawframes);
1398 cpl_table * table2d = cpl_table_new(1);
1399 char * filename = NULL;
1400 cpl_propertylist * xtlist = cpl_propertylist_new();
1401
1402 bug_if(cpl_propertylist_append_string(xtlist, "EXTNAME",
1403 "Wavelength calibration"));
1404
1405 /* The wavelength map */
1406 filename = cpl_sprintf(RECIPE_STRING "_set%02d" CPL_DFS_FITS, set_nb);
1407 skip_if (irplib_dfs_save_image(set_tot, parlist, proframes,
1408 cpl_imagelist_get_const(lamp_wave, 0),
1409 CPL_BPP_IEEE_FLOAT, RECIPE_STRING,
1410 NACO_CALIB_ARC_MAP, qclist, NULL,
1411 naco_pipe_id, filename));
1412
1413 bug_if(naco_spc_wavecal_fill_table(table2d, disp2d));
1414
1415 skip_if(cpl_table_save(table2d, NULL, xtlist, filename, CPL_IO_EXTEND));
1416
1417 /* The difference image with the arc exposure */
1418 cpl_free(filename);
1419 filename = cpl_sprintf(RECIPE_STRING "_set%02d_diff" CPL_DFS_FITS, set_nb);
1420 skip_if (irplib_dfs_save_image(set_tot, parlist, proframes,
1421 cpl_imagelist_get_const(lamp_wave, 1),
1422 CPL_BPP_IEEE_FLOAT, RECIPE_STRING,
1423 NACO_CALIB_ARC_DIFF, qclist, NULL,
1424 naco_pipe_id, filename));
1425
1426 bug_if(cpl_propertylist_set_string(xtlist, "EXTNAME",
1427 "Difference Image"));
1428 skip_if (cpl_image_save(cpl_imagelist_get_const(lamp_wave, 2), filename,
1429 CPL_BPP_IEEE_FLOAT, xtlist, CPL_IO_EXTEND));
1430
1431#ifdef NACO_SAVE_PAF
1432 cpl_free(filename);
1433 filename = cpl_sprintf(RECIPE_STRING "_set%02d" CPL_DFS_PAF, set_nb);
1434 skip_if (cpl_dfs_save_paf("NACO", RECIPE_STRING, paflist, filename));
1435#else
1436 bug_if(paflist == NULL);
1437#endif
1438
1439 end_skip;
1440
1441 cpl_table_delete(table2d);
1442 cpl_free(filename);
1443 cpl_frameset_delete(proframes);
1444 cpl_propertylist_delete(xtlist);
1445
1446 return cpl_error_get_code();
1447
1448}
1449
1450
1451/*----------------------------------------------------------------------------*/
1460/*----------------------------------------------------------------------------*/
1461static cpl_error_code naco_spc_wavecal_count_lines(cpl_propertylist * self,
1462 const cpl_bivector * lines,
1463 double wlmin,
1464 double wlmax)
1465{
1466
1467 const cpl_vector * xlines = cpl_bivector_get_x_const(lines);
1468 const double * dxlines = cpl_vector_get_data_const(xlines);
1469 int minline, maxline;
1470 int clines;
1471
1472
1473 bug_if (self == NULL);
1474 bug_if (lines == NULL);
1475
1476 bug_if (wlmin < 0.0);
1477 bug_if (wlmax < wlmin);
1478
1479 /* Find the 1st line */
1480 minline = cpl_vector_find(xlines, wlmin);
1481
1482 /* The first line must be at least at wlmin */
1483 if (dxlines[minline] < wlmin) minline++;
1484
1485 /* Find the last line */
1486 maxline = cpl_vector_find(xlines, wlmax);
1487
1488 /* The last line must be at most at wlmax */
1489 if (dxlines[maxline] > wlmax) maxline--;
1490
1491 clines = maxline >= minline ? maxline - minline : 0;
1492
1493 bug_if(cpl_propertylist_append_int(self, "ESO QC DISP NUMCAT", clines));
1494
1495 end_skip;
1496
1497 return cpl_error_get_code();
1498}
1499
1500/*----------------------------------------------------------------------------*/
1508/*----------------------------------------------------------------------------*/
1509static cpl_error_code
1510naco_spc_wavecal_interpolate_rejected(cpl_image * self,
1511 const cpl_polynomial * disp2d)
1512{
1513
1514 const int nz = cpl_image_count_rejected(self);
1515 const int nx = cpl_image_get_size_x(self);
1516 const int ny = cpl_image_get_size_y(self);
1517 double power[2];
1518 cpl_vector * vpower = cpl_vector_wrap(2, power);
1519 int i, j;
1520 int k = nz;
1521
1522 bug_if(self == NULL);
1523 bug_if(disp2d == NULL);
1524
1525 if (nz > 0) cpl_msg_info(cpl_func, "Interpolating %d poorly calibrated "
1526 "pixels in the wavelength map", nz);
1527
1528 for (i = 1; i <= nx && k > 0; i++) {
1529 for (j = 1; j <= ny && k > 0; j++) {
1530 if (cpl_image_is_rejected(self, i, j)) {
1531 power[0] = (double)i;
1532 power[1] = (double)j;
1533 cpl_image_set(self, i, j, cpl_polynomial_eval(disp2d, vpower));
1534 cpl_image_reject(self, i, j); /* Flagged as interpolated */
1535 k--;
1536 }
1537 }
1538 }
1539
1540 end_skip;
1541
1542 cpl_vector_unwrap(vpower);
1543
1544 return cpl_error_get_code();
1545}
1546
1547
1548/*----------------------------------------------------------------------------*/
1557/*----------------------------------------------------------------------------*/
1558static cpl_error_code naco_spc_wavecal_qc_lines(cpl_propertylist * self,
1559 const cpl_image * spec2d,
1560 const cpl_apertures * lines)
1561{
1562
1563 const int nlines = cpl_apertures_get_size(lines);
1564 const int ny = cpl_image_get_size_y(spec2d);
1565 const double ycen = 0.5 * (ny + 1);
1566 int i, igood;
1567 char * label = NULL;
1568 cpl_vector * vmedian = cpl_vector_new(nlines);
1569 double median;
1570
1571 bug_if(self == NULL);
1572 bug_if(spec2d == NULL);
1573 bug_if(lines == NULL);
1574
1575 bug_if(cpl_propertylist_append_int(self, "ESO QC ARCS NUM", nlines));
1576
1577 igood = 0;
1578 for(i = 1; i <= nlines; i++) {
1579 cpl_errorstate prestate = cpl_errorstate_get();
1580 const double flux = cpl_apertures_get_flux(lines, i);
1581 const double xcen = cpl_apertures_get_centroid_x(lines, i);
1582 double fwhm_x, fwhm_y;
1583
1584 if (cpl_image_get_fwhm(spec2d, xcen, ycen, &fwhm_x, &fwhm_y)) {
1585 irplib_error_recover(prestate, "Could not compute the FWHM for "
1586 "aperture %d of %d (with xcentroid=%g, flux=%g",
1587 i, nlines, xcen, flux);
1588 fwhm_x = -1.0;
1589 }
1590
1591 if (fwhm_x > 0.0) {
1592 cpl_vector_set(vmedian, igood++, fwhm_x);
1593 }
1594
1595 cpl_free(label);
1596 label = cpl_sprintf("ESO QC ARCS%d XPOS", i);
1597 cpl_propertylist_append_double(self, label, xcen);
1598
1599 cpl_free(label);
1600 label = cpl_sprintf("ESO QC ARCS%d FWHM", i);
1601
1602 cpl_propertylist_append_double(self, label, fwhm_x);
1603
1604 cpl_free(label);
1605 label = cpl_sprintf("ESO QC ARCS%d FLUX", i);
1606
1607 cpl_propertylist_append_double(self, label, flux);
1608 }
1609 bug_if(0);
1610
1611 bug_if(cpl_propertylist_append_int(self, "ESO QC ARCS NUMGOOD", igood));
1612
1613 if (igood > 0) {
1614 bug_if(cpl_vector_set_size(vmedian, igood));
1615 median = cpl_vector_get_median(vmedian);
1616 } else {
1617 median = -1.0;
1618 }
1619
1620 bug_if(cpl_propertylist_append_double(self, "ESO QC FWHM MED", median));
1621
1622 end_skip;
1623
1624 cpl_vector_delete(vmedian);
1625 cpl_free(label);
1626
1627 return cpl_error_get_code();
1628}
int naco_dfs_set_groups(cpl_frameset *set)
Set the group as RAW or CALIB in a frameset.
Definition: naco_dfs.c:62
int naco_parameterlist_get_int(const cpl_parameterlist *self, const char *recipe, naco_parameter bitmask)
Retrieve the value of a NACO integer parameter.
cpl_boolean naco_parameterlist_get_bool(const cpl_parameterlist *self, const char *recipe, naco_parameter bitmask)
Retrieve the value of a NACO boolean parameter.
char * naco_spc_make_tag(const cpl_frame *self, const cpl_propertylist *plist, int dummy)
Create a string suitable for frame comparison in spectroscopy.
Definition: naco_spc.c:407
cpl_error_code naco_imagelist_load_diff(cpl_imagelist *self, const irplib_framelist *onofflist, const cpl_propertylist *onoffkeys)
Fill the list of difference images from on/off frames.
Definition: naco_spc.c:482
const char ** naco_framelist_set_tag(irplib_framelist *self, char *(*pftag)(const cpl_frame *, const cpl_propertylist *, int), int *pntags)
Retag a framelist according to the given tagging function.
Definition: naco_utils.c:176