IIINSTRUMENT Pipeline Reference Manual  4.4.11
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 
62 static 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 
71 static cpl_error_code naco_spc_wavecal_get_slitw(const cpl_propertylist *,
72  double *);
73 
74 
75 static 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 
84 static
85 cpl_error_code naco_image_fill_column_from_dispersion(cpl_image *, int, cpl_boolean,
86  const cpl_polynomial *);
87 
88 static cpl_error_code naco_spc_wavecal_distortion(cpl_image *,
89  cpl_propertylist *,
90  const cpl_parameterlist *);
91 
92 static cpl_error_code naco_spc_physdisp_fill(cpl_polynomial *, const char *,
93  const cpl_table *);
94 
95 static cpl_error_code naco_spc_physdisp_transform(cpl_polynomial *, double,
96  double, int, double, double,
97  double, double);
98 
99 static cpl_error_code naco_spc_wavecal_qc(cpl_propertylist *,
100  cpl_propertylist *,
101  const irplib_framelist *);
102 
103 static 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 
111 static cpl_error_code naco_spc_wavecal_fill_table(cpl_table *,
112  const cpl_polynomial *);
113 
114 static cpl_error_code naco_spc_wavecal_count_lines(cpl_propertylist *,
115  const cpl_bivector *,
116  double, double);
117 static
118 cpl_error_code naco_spc_wavecal_interpolate_rejected(cpl_image *,
119  const cpl_polynomial *);
120 
121 static cpl_error_code naco_spc_wavecal_qc_lines(cpl_propertylist *,
122  const cpl_image *,
123  const cpl_apertures *);
124 
125 NACO_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 /*----------------------------------------------------------------------------*/
172 static 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 /*----------------------------------------------------------------------------*/
362 static 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 /*----------------------------------------------------------------------------*/
532 static 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 /*----------------------------------------------------------------------------*/
830 static 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 /*----------------------------------------------------------------------------*/
914 static 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 /*----------------------------------------------------------------------------*/
1015 static 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 /*----------------------------------------------------------------------------*/
1059 static cpl_error_code
1060 naco_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 /*----------------------------------------------------------------------------*/
1095 static
1096 cpl_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 /*----------------------------------------------------------------------------*/
1298 static 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 /*----------------------------------------------------------------------------*/
1337 static 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 /*----------------------------------------------------------------------------*/
1388 static 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 /*----------------------------------------------------------------------------*/
1461 static 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 /*----------------------------------------------------------------------------*/
1509 static cpl_error_code
1510 naco_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 /*----------------------------------------------------------------------------*/
1558 static 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