CRIRES Pipeline Reference Manual  2.3.15
crires_util_wlcalib.c
1 /* $Id: crires_util_wlcalib.c,v 1.69 2012-09-20 12:06:34 yjung Exp $
2  *
3  * This file is part of the crires 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */
20 
21 /*
22  * $Author: yjung $
23  * $Date: 2012-09-20 12:06:34 $
24  * $Revision: 1.69 $
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 <locale.h>
37 #include "crires_recipe.h"
38 
39 #include "irplib_wlxcorr.h"
40 #include "irplib_utils.h"
41 
42 #include "crires_wlcalib.h"
43 #include "crires_wlestimate.h"
44 
45 /*-----------------------------------------------------------------------------
46  Define
47  -----------------------------------------------------------------------------*/
48 
49 #define RECIPE_STRING "crires_util_wlcalib"
50 
51 /*-----------------------------------------------------------------------------
52  Functions prototypes
53  -----------------------------------------------------------------------------*/
54 
55 static int crires_util_wlcalib_save(const cpl_imagelist *, const cpl_table **,
56  const cpl_table **, const cpl_parameterlist *, cpl_frameset *) ;
57 
58 static char crires_util_wlcalib_description[] =
59 "This recipe accepts 2 parameters:\n"
60 "First parameter: the extracted spectrum in pixels.\n"
61 " (PRO TYPE = "CRIRES_PROTYPE_SPEC_PIX")\n"
62 "Second parameter: the table with the lines catalog.\n"
63 " (PRO TYPE = "CRIRES_PROTYPE_CATALOG")\n"
64 "\n"
65 "This recipe produces 2 files:\n"
66 "First product: the image with the wavelength values.\n"
67 " (PRO TYPE = "CRIRES_PROTYPE_WL_MAP")\n"
68 "Second product: the table with the wavelength polynomial coefficients.\n"
69 " (PRO TYPE = "CRIRES_PROTYPE_WL_POLY")\n" ;
70 
71 CRIRES_RECIPE_DEFINE(crires_util_wlcalib,
72  CRIRES_PARAM_WAVES |
73  CRIRES_PARAM_DISPLAY |
74  CRIRES_PARAM_WL_LOG |
75  CRIRES_PARAM_WL_NOLIMIT |
76  CRIRES_PARAM_WL_ERROR |
77  CRIRES_PARAM_XC_LIMIT |
78  CRIRES_PARAM_WL_NBSAMPLES |
79  CRIRES_PARAM_DEGREE |
80  CRIRES_PARAM_WL_CLEAN |
81  CRIRES_PARAM_LINES |
82  CRIRES_PARAM_FIRST_PLOTTED |
83  CRIRES_PARAM_LAST_PLOTTED,
84  "Wavelength calibration of a spectrum",
85  crires_util_wlcalib_description) ;
86 
87 /*-----------------------------------------------------------------------------
88  Static variables
89  -----------------------------------------------------------------------------*/
90 
91 static struct {
92  /* Inputs */
93  double wstart[CRIRES_NB_DETECTORS] ;
94  double wstop[CRIRES_NB_DETECTORS] ;
95  int display ;
96  int degree ;
97  int wl_nolimit ;
98  int wl_log ;
99  double wl_err ;
100  int wl_nsamples ;
101  int wl_clean ;
102  double wl_xclimit ;
103  int use_ppm ;
104  int slitw ;
105  int fwhm ;
106  const char * lines ;
107  int first_plotted_line ;
108  int last_plotted_line ;
109  /* Output */
110  int win_mode ;
111  crires_illum_period period ;
112  double qc_wlcent[CRIRES_NB_DETECTORS] ;
113  double qc_wldisp[CRIRES_NB_DETECTORS] ;
114  double qc_wlxc[CRIRES_NB_DETECTORS] ;
115 } crires_util_wlcalib_config ;
116 
117 /*-----------------------------------------------------------------------------
118  Functions code
119  -----------------------------------------------------------------------------*/
120 
121 /*----------------------------------------------------------------------------*/
128 /*----------------------------------------------------------------------------*/
129 static int crires_util_wlcalib(
130  cpl_frameset * frameset,
131  const cpl_parameterlist * parlist)
132 {
133  cpl_table * ext_spec ;
134  const char * fname ;
135  cpl_propertylist * plist ;
136  double wmin, wmax ;
137  cpl_vector * spec ;
138  double * pspec ;
139  cpl_polynomial * phdisp ;
140  cpl_polynomial * poly_sol ;
141  cpl_matrix * samppos ;
142  cpl_size mindeg, maxdeg ;
143  cpl_bivector * lines_biv ;
144  cpl_table * cat ;
145  double val ;
146  cpl_table * real_sol ;
147  int nrows, spec_pos ;
148  cpl_bivector * cat_biv ;
149  double * cat_biv_x ;
150  double * cat_biv_y ;
151  const char * sval ;
152  cpl_frame * fr ;
153  cpl_polynomial * disp_poly ;
154  cpl_table * wl_infos[CRIRES_NB_DETECTORS] ;
155  cpl_table * wl_tab[CRIRES_NB_DETECTORS] ;
156  cpl_imagelist * wl_map ;
157  int i, j ;
158 
159  /* Needed for sscanf() */
160  setlocale(LC_NUMERIC, "C");
161 
162  /* Initialise */
163  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
164  crires_util_wlcalib_config.qc_wlcent[i] = -1.0 ;
165  crires_util_wlcalib_config.qc_wldisp[i] = -1.0 ;
166  crires_util_wlcalib_config.qc_wlxc[i] = -1.0 ;
167  wl_tab[i] = NULL ;
168  wl_infos[i] = NULL ;
169  }
170  crires_util_wlcalib_config.lines = NULL ;
171  crires_util_wlcalib_config.use_ppm = 0 ;
172  crires_util_wlcalib_config.slitw = 2 ;
173  crires_util_wlcalib_config.fwhm = 2 ;
174 
175  /* Retrieve input parameters */
176  sval = crires_parameterlist_get_string(parlist, RECIPE_STRING,
177  CRIRES_PARAM_WAVES) ;
178  if (sscanf(sval, "%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg",
179  &crires_util_wlcalib_config.wstart[0],
180  &crires_util_wlcalib_config.wstop[0],
181  &crires_util_wlcalib_config.wstart[1],
182  &crires_util_wlcalib_config.wstop[1],
183  &crires_util_wlcalib_config.wstart[2],
184  &crires_util_wlcalib_config.wstop[2],
185  &crires_util_wlcalib_config.wstart[3],
186  &crires_util_wlcalib_config.wstop[3])!=2*CRIRES_NB_DETECTORS){
187  return -1 ;
188  }
189  crires_util_wlcalib_config.display = crires_parameterlist_get_int(parlist,
190  RECIPE_STRING, CRIRES_PARAM_DISPLAY) ;
191  crires_util_wlcalib_config.degree = crires_parameterlist_get_int(parlist,
192  RECIPE_STRING, CRIRES_PARAM_DEGREE) ;
193  crires_util_wlcalib_config.wl_log = crires_parameterlist_get_bool(parlist,
194  RECIPE_STRING, CRIRES_PARAM_WL_LOG) ;
195  crires_util_wlcalib_config.wl_nolimit = crires_parameterlist_get_bool(
196  parlist, RECIPE_STRING, CRIRES_PARAM_WL_NOLIMIT) ;
197  crires_util_wlcalib_config.wl_err = crires_parameterlist_get_double(parlist,
198  RECIPE_STRING, CRIRES_PARAM_WL_ERROR) ;
199  crires_util_wlcalib_config.wl_xclimit = crires_parameterlist_get_double(
200  parlist, RECIPE_STRING, CRIRES_PARAM_XC_LIMIT) ;
201  crires_util_wlcalib_config.wl_nsamples = crires_parameterlist_get_int(
202  parlist, RECIPE_STRING, CRIRES_PARAM_WL_NBSAMPLES) ;
203  crires_util_wlcalib_config.wl_clean = crires_parameterlist_get_bool(parlist,
204  RECIPE_STRING, CRIRES_PARAM_WL_CLEAN) ;
205  crires_util_wlcalib_config.lines = crires_parameterlist_get_string(parlist,
206  RECIPE_STRING, CRIRES_PARAM_LINES) ;
207  crires_util_wlcalib_config.first_plotted_line=crires_parameterlist_get_int(
208  parlist, RECIPE_STRING, CRIRES_PARAM_FIRST_PLOTTED) ;
209  crires_util_wlcalib_config.last_plotted_line=crires_parameterlist_get_int(
210  parlist, RECIPE_STRING, CRIRES_PARAM_LAST_PLOTTED) ;
211 
212  /* Identify the RAW and CALIB frames in the input frameset */
213  if (crires_dfs_set_groups(frameset, NULL)) {
214  cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
215  return -1 ;
216  }
217 
218  /* Check that we have 2 files in input */
219  if (cpl_frameset_get_size(frameset) != 2) {
220  cpl_msg_error(__func__, "Expects 2 files in input") ;
221  return -1 ;
222  }
223 
224  /* Get the detector illumination period */
225  fr = cpl_frameset_get_position(frameset, 0);
226  crires_util_wlcalib_config.period =
227  crires_get_detector_illum_period(cpl_frame_get_filename(fr)) ;
228  if (crires_util_wlcalib_config.period == CRIRES_ILLUM_UNKNOWN) {
229  cpl_msg_error(__func__,
230  "Cannot determine the detector illumination period") ;
231  return -1 ;
232  }
233 
234  /* Windowing mode ? */
235  if ((plist=cpl_propertylist_load(cpl_frame_get_filename(fr), 0)) == NULL)
236  return -1 ;
237  sval = crires_pfits_get_ncorrs(plist) ;
238  if (!strcmp(sval, "FowlerNsampGRstWin")) {
239  crires_util_wlcalib_config.period = CRIRES_ILLUM_FULL_DETECTOR ;
240  crires_util_wlcalib_config.win_mode = 1 ;
241  } else {
242  crires_util_wlcalib_config.win_mode = 0 ;
243  }
244  cpl_propertylist_delete(plist) ;
245 
246  /* Display the Detector illumination */
247  crires_display_detector_illum(crires_util_wlcalib_config.period) ;
248 
249  /* Load the second file */
250  cpl_msg_info(__func__, "Second frame validity check") ;
251  cpl_msg_indent_more() ;
252  fr = cpl_frameset_get_position(frameset, 1);
253  cat = crires_load_table_check(cpl_frame_get_filename(fr), 1,
254  CRIRES_PROTYPE_CATALOG, -1, -1, 0) ;
255  if (cat == NULL) {
256  cpl_msg_error(__func__, "Second frame is incorrect") ;
257  cpl_msg_indent_less() ;
258  return -1 ;
259  }
260  cpl_msg_indent_less() ;
261 
262  /* Create the catalog spectrum in a bivector */
263  nrows = cpl_table_get_nrow(cat) ;
264  cat_biv = cpl_bivector_new(nrows) ;
265  cat_biv_x = cpl_bivector_get_x_data(cat_biv) ;
266  cat_biv_y = cpl_bivector_get_y_data(cat_biv) ;
267  for (i=0 ; i<nrows ; i++) {
268  cat_biv_x[i] = cpl_table_get(cat, CRIRES_COL_WAVELENGTH, i, NULL) ;
269  val = cpl_table_get(cat, CRIRES_COL_EMISSION, i, NULL) ;
270  if (crires_util_wlcalib_config.wl_log && val > 0)
271  cat_biv_y[i] = log10(val) ;
272  else
273  cat_biv_y[i] = val ;
274  }
275  cpl_table_delete(cat) ;
276 
277  /* Get the raw file name */
278  fr = cpl_frameset_get_position(frameset, 0);
279  fname = cpl_frame_get_filename(fr) ;
280 
281  /* Get the Minimum and Maximum wavelengths */
282  if (crires_util_wlcalib_config.wl_nolimit == 0) {
283  plist = cpl_propertylist_load(fname, 0) ;
284  wmin = crires_pfits_get_wlen_min(plist) ;
285  wmax = crires_pfits_get_wlen_max(plist) ;
286  cpl_propertylist_delete(plist) ;
287  if (cpl_error_get_code()) {
288  wmin = wmax = -1.0 ;
289  cpl_error_reset() ;
290  }
291  } else {
292  wmin = wmax = -1.0 ;
293  }
294 
295  /* Loop on the spectra to calibrate */
296  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
297  /* Skip some detectors in windowing mode */
298  if ((i==0 || i==CRIRES_NB_DETECTORS-1) &&
299  (crires_util_wlcalib_config.win_mode == 1)) {
300  continue ;
301  }
302 
303  cpl_msg_info(__func__, "Wavelength calibration for chip %d", i+1) ;
304  /* Load the first file in table */
305  cpl_msg_info(__func__, "Load the extracted table") ;
306  cpl_msg_indent_more() ;
307  if ((ext_spec=crires_load_table_check(fname, i+1,
308  CRIRES_PROTYPE_SPEC_PIX, -1, -1, 0)) == NULL) {
309  cpl_msg_warning(__func__, "Empty extension") ;
310  cpl_msg_indent_less() ;
311  continue ;
312  }
313  cpl_msg_indent_less() ;
314 
315  /* Get the Wavlength estimation */
316  cpl_msg_info(__func__, "Wavelength estimation") ;
317  cpl_msg_indent_more() ;
318  if ((phdisp = crires_wlestimate_compute(
319  crires_util_wlcalib_config.wstart[i],
320  crires_util_wlcalib_config.wstop[i])) == NULL) {
321  if ((phdisp = crires_wlestimate_get(fname, i+1)) == NULL) {
322  cpl_msg_error(__func__, "Cannot get the wavelength estimate") ;
323  cpl_msg_indent_less() ;
324  cpl_table_delete(ext_spec) ;
325  continue ;
326  }
327  }
328  cpl_msg_indent_less() ;
329 
330  /* Convert to vector */
331  nrows = cpl_table_get_nrow(ext_spec) ;
332  spec = cpl_vector_new(nrows) ;
333  pspec = cpl_vector_get_data(spec) ;
334  for (j=0 ; j<nrows ; j++) {
335  pspec[j] = cpl_table_get(ext_spec, CRIRES_COL_EXTRACT_INT_RECT, j,
336  NULL);
337  }
338  cpl_table_delete(ext_spec) ;
339 
340  /* Wavelength calibration */
341  cpl_msg_info(__func__, "Wavelength calibration computation") ;
342  cpl_msg_indent_more() ;
343  if ((disp_poly = crires_wlcalib_engine(spec, cat_biv, phdisp,
344  crires_util_wlcalib_config.slitw,
345  crires_util_wlcalib_config.fwhm,
346  crires_util_wlcalib_config.degree,
347  wmin, wmax,
348  crires_util_wlcalib_config.wl_err,
349  crires_util_wlcalib_config.wl_nsamples,
350  crires_util_wlcalib_config.wl_clean,
351  crires_util_wlcalib_config.wl_xclimit,
352  crires_util_wlcalib_config.use_ppm,
353  (i+1==crires_util_wlcalib_config.display),
354  crires_util_wlcalib_config.first_plotted_line,
355  crires_util_wlcalib_config.last_plotted_line,
356  &((crires_util_wlcalib_config.qc_wlxc)[i]),
357  NULL)) == NULL) {
358  cpl_msg_error(__func__,
359  "Wavelength calibration failed - use estimate") ;
360  disp_poly = cpl_polynomial_duplicate(phdisp) ;
361  crires_util_wlcalib_config.qc_wlxc[i] = -1.0 ;
362  }
363  cpl_msg_indent_less() ;
364 
365  /* Compute the QC parameters */
366  crires_util_wlcalib_config.qc_wlcent[i] =
367  cpl_polynomial_eval_1d(disp_poly, (double)512, NULL) ;
368  crires_util_wlcalib_config.qc_wldisp[i] =
369  (cpl_polynomial_eval_1d(disp_poly, (double)1024, NULL) -
370  cpl_polynomial_eval_1d(disp_poly, (double)1, NULL)) / 1023 ;
371 
372  /* Plot the result */
373  if (crires_util_wlcalib_config.display==i+1) {
374  /* Compute the solution from the passed associated lines if passed*/
375  poly_sol = NULL ;
376  if (crires_util_wlcalib_config.lines != NULL &&
377  crires_util_wlcalib_config.lines[0] != (char)0) {
378  lines_biv =
379  cpl_bivector_read((char*)crires_util_wlcalib_config.lines);
380  if ((lines_biv != NULL) &&
381  (cpl_bivector_get_size(lines_biv)>crires_util_wlcalib_config.degree)) {
382  poly_sol = cpl_polynomial_new(1);
383  samppos = cpl_matrix_wrap(1,
384  cpl_bivector_get_size(lines_biv),
385  cpl_bivector_get_x_data(lines_biv)) ;
386  mindeg = 1 ;
387  maxdeg = (cpl_size)crires_util_wlcalib_config.degree ;
388  cpl_polynomial_fit(poly_sol,
389  samppos, NULL,
390  cpl_bivector_get_y(lines_biv),
391  NULL, CPL_FALSE,
392  &mindeg,
393  &maxdeg) ;
394  cpl_matrix_unwrap(samppos) ;
395  }
396  if (lines_biv != NULL) cpl_bivector_delete(lines_biv) ;
397  }
398  irplib_wlxcorr_plot_solution(phdisp, disp_poly, poly_sol, 1, 1024);
399  if (poly_sol != NULL) {
400  real_sol = irplib_wlxcorr_gen_spc_table(spec, cat_biv,
401  crires_util_wlcalib_config.slitw,
402  crires_util_wlcalib_config.fwhm,
403  phdisp, poly_sol) ;
404  irplib_wlxcorr_plot_spc_table(real_sol, "Real",
405  crires_util_wlcalib_config.first_plotted_line,
406  crires_util_wlcalib_config.last_plotted_line) ;
407  cpl_table_delete(real_sol) ;
408  }
409  if (poly_sol != NULL) cpl_polynomial_delete(poly_sol) ;
410  }
411 
412  /* Compute the products */
413  wl_infos[i] = irplib_wlxcorr_gen_spc_table(spec, cat_biv,
414  crires_util_wlcalib_config.slitw,
415  crires_util_wlcalib_config.fwhm,
416  phdisp, disp_poly) ;
417 
418  /* Free */
419  cpl_vector_delete(spec) ;
420  cpl_polynomial_delete(phdisp) ;
421 
422  /* Create the table */
423  if (crires_util_wlcalib_config.qc_wlxc[i] > 0)
424  spec_pos = crires_get_y_spec_position(fname, i+1) ;
425  else
426  spec_pos = -1 ;
427  wl_tab[i] = crires_wlcalib_gen_wltab_one(
428  (const cpl_polynomial *)disp_poly,
429  spec_pos,
430  crires_util_wlcalib_config.qc_wlxc[i]) ;
431  cpl_polynomial_delete(disp_poly) ;
432  }
433  cpl_bivector_delete(cat_biv) ;
434 
435  /* Reconstruct chips in windowing mode using detector 2 */
436  if (crires_util_wlcalib_config.win_mode == 1) {
437  if (wl_tab[1] != NULL) {
438  wl_tab[0] = cpl_table_duplicate(wl_tab[1]) ;
439  cpl_table_set_size(wl_tab[0], 0) ;
440  wl_tab[CRIRES_NB_DETECTORS-1] = cpl_table_duplicate(wl_tab[0]) ;
441  }
442  if (wl_infos[1] != NULL) {
443  wl_infos[0] = cpl_table_duplicate(wl_infos[1]) ;
444  cpl_table_set_size(wl_infos[0], 0) ;
445  wl_infos[CRIRES_NB_DETECTORS-1] = cpl_table_duplicate(wl_infos[0]) ;
446  }
447  }
448 
449  /* Create the Map */
450  if ((wl_map = crires_wlcalib_gen_wlmap((const cpl_table **)wl_tab))
451  == NULL) {
452  cpl_msg_error(__func__, "Cannot create WL Map") ;
453  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
454  if (wl_tab[i] != NULL) cpl_table_delete(wl_tab[i]) ;
455  if (wl_infos[i] != NULL) cpl_table_delete(wl_infos[i]) ;
456  }
457  return -1 ;
458  }
459 
460  /* Save the result */
461  cpl_msg_info(__func__, "Save the products") ;
462  cpl_msg_indent_more() ;
463  if (crires_util_wlcalib_save(wl_map, (const cpl_table **)wl_tab,
464  (const cpl_table **)wl_infos, parlist, frameset) == -1) {
465  cpl_msg_error(__func__, "Cannot save products");
466  cpl_msg_indent_less() ;
467  cpl_imagelist_delete(wl_map) ;
468  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
469  if (wl_tab[i] != NULL) cpl_table_delete(wl_tab[i]) ;
470  if (wl_infos[i] != NULL) cpl_table_delete(wl_infos[i]) ;
471  }
472  return -1 ;
473  }
474  cpl_msg_indent_less() ;
475  cpl_imagelist_delete(wl_map) ;
476  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
477  if (wl_tab[i] != NULL) cpl_table_delete(wl_tab[i]) ;
478  if (wl_infos[i] != NULL) cpl_table_delete(wl_infos[i]) ;
479  }
480 
481  /* Return */
482  if (cpl_error_get_code()) return -1 ;
483  else return 0 ;
484 }
485 
486 /*----------------------------------------------------------------------------*/
496 /*----------------------------------------------------------------------------*/
497 static int crires_util_wlcalib_save(
498  const cpl_imagelist * ilist,
499  const cpl_table ** tab,
500  const cpl_table ** xctab,
501  const cpl_parameterlist * parlist,
502  cpl_frameset * set)
503 {
504  cpl_propertylist ** qclists ;
505  const cpl_frame * ref_frame ;
506  cpl_propertylist * inputlist ;
507  const char * recipe_name = "crires_util_wlcalib" ;
508  int i ;
509 
510  /* Get the reference frame */
511  ref_frame = irplib_frameset_get_first_from_group(set, CPL_FRAME_GROUP_RAW) ;
512 
513  /* Create the QC lists */
514  qclists = cpl_malloc(CRIRES_NB_DETECTORS * sizeof(cpl_propertylist*)) ;
515  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
516  qclists[i] = cpl_propertylist_new() ;
517  cpl_propertylist_append_double(qclists[i], "ESO QC CENTWL",
518  crires_util_wlcalib_config.qc_wlcent[i]) ;
519  cpl_propertylist_append_double(qclists[i], "ESO QC DISPWL",
520  crires_util_wlcalib_config.qc_wldisp[i]) ;
521  cpl_propertylist_append_double(qclists[i], "ESO QC XCORR",
522  crires_util_wlcalib_config.qc_wlxc[i]) ;
523 
524  /* Propagate some keywords from input raw frame extensions */
525  inputlist = cpl_propertylist_load_regexp(
526  cpl_frame_get_filename(ref_frame), i+1,
527  CRIRES_HEADER_EXT_FORWARD, 0) ;
528  cpl_propertylist_copy_property_regexp(qclists[i], inputlist,
529  CRIRES_HEADER_EXT_FORWARD, 0) ;
530  cpl_propertylist_delete(inputlist) ;
531  }
532 
533  /* Write the image */
534  crires_image_save(set,
535  parlist,
536  set,
537  ilist,
538  recipe_name,
539  CRIRES_WL_MAP_IMA,
540  CRIRES_PROTYPE_WL_MAP,
541  crires_util_wlcalib_config.period,
542  NULL,
543  (const cpl_propertylist **)qclists,
544  PACKAGE "/" PACKAGE_VERSION,
545  "crires_util_wlcalib_ima.fits") ;
546 
547  /* Write the table */
548  crires_table_save(set,
549  parlist,
550  set,
551  tab,
552  recipe_name,
553  CRIRES_CALPRO_WAVE,
554  CRIRES_PROTYPE_WL_POLY,
555  NULL,
556  (const cpl_propertylist **)qclists,
557  PACKAGE "/" PACKAGE_VERSION,
558  "crires_util_wlcalib_tab.fits") ;
559 
560  if (xctab[0] != NULL) {
561  /* Write the table */
562  crires_table_save(set,
563  parlist,
564  set,
565  xctab,
566  recipe_name,
567  CRIRES_WL_XCORR_TAB,
568  CRIRES_PROTYPE_XCORR,
569  NULL,
570  (const cpl_propertylist **)qclists,
571  PACKAGE "/" PACKAGE_VERSION,
572  "crires_util_wlcalib_xctab.fits") ;
573  }
574 
575  /* Free and return */
576  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
577  cpl_propertylist_delete(qclists[i]) ;
578  }
579  cpl_free(qclists) ;
580  return 0;
581 }