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