CRIRES Pipeline Reference Manual  2.3.15
crires_spec_wavecal.c
1 /* $Id: crires_spec_wavecal.c,v 1.75 2012-10-09 08:18:01 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-10-09 08:18:01 $
24  * $Revision: 1.75 $
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 "crires_combine.h"
40 #include "crires_wlcalib.h"
41 #include "crires_extract.h"
42 #include "crires_model_kernel.h"
43 #include "irplib_utils.h"
44 
45 /*-----------------------------------------------------------------------------
46  Define
47  -----------------------------------------------------------------------------*/
48 
49 #define RECIPE_STRING "crires_spec_wavecal"
50 
51 /*-----------------------------------------------------------------------------
52  Functions prototypes
53  -----------------------------------------------------------------------------*/
54 
55 static int crires_spec_wavecal_save(const cpl_imagelist *,
56  const cpl_imagelist *, const cpl_table **, const cpl_parameterlist *,
57  cpl_frameset *) ;
58 
59 static char crires_spec_wavecal_description[] =
60 "crires_spec_wavecal -- Wavelength calibration\n"
61 "The files listed in the Set Of Frames (sof-file) must be tagged:\n"
62 "raw-file.fits "CRIRES_SPEC_WAVECAL_SKY_RAW" or\n"
63 "raw-file.fits "CRIRES_SPEC_WAVECAL_LAMP_RAW" or\n"
64 "raw-file.fits "CRIRES_SPEC_WAVECAL_ABS_RAW" or\n"
65 "flat-file.fits "CRIRES_CALPRO_FLAT" or\n"
66 "bpm-file.fits "CRIRES_CALPRO_BPM" or\n"
67 "dark-file.fits "CRIRES_CALPRO_DARK" or\n"
68 "detlin-file.fits "CRIRES_CALPRO_COEFFS_CUBE" or\n"
69 "catalog-file.fits "CRIRES_CALPRO_THAR_CAT" or\n"
70 "catalog-file.fits "CRIRES_CALPRO_OH_CAT" or\n"
71 "catalog-file.fits "CRIRES_CALPRO_HITRAN_CAT" or\n"
72 "ypos-file.fits "CRIRES_CALPRO_THAR_POS" or\n"
73 "model-config-file.fits "CRIRES_CALPRO_MODEL_CONFIG".\n"
74 "\n"
75 "This recipe produces 3 files:\n"
76 "First product: the image with the wavelength values.\n"
77 " (PRO TYPE = "CRIRES_PROTYPE_WL_MAP")\n"
78 "Second product: the image with the wavelength values from the model.\n"
79 " (PRO TYPE = "CRIRES_PROTYPE_WL_MAP")\n"
80 "Third product: the table with the wavelength polynomial coefficients.\n"
81 " (PRO TYPE = "CRIRES_PROTYPE_WL_POLY")\n" ;
82 
83 CRIRES_RECIPE_DEFINE(crires_spec_wavecal,
84  CRIRES_PARAM_WAVES |
85  CRIRES_PARAM_DISPLAY |
86  CRIRES_PARAM_WL_LOG |
87  CRIRES_PARAM_WL_NOLIMIT |
88  CRIRES_PARAM_WL_ERROR |
89  CRIRES_PARAM_XC_LIMIT |
90  CRIRES_PARAM_WL_NBSAMPLES |
91  CRIRES_PARAM_Y_POS_CHIP1 |
92  CRIRES_PARAM_Y_POS_CHIP2 |
93  CRIRES_PARAM_Y_POS_CHIP3 |
94  CRIRES_PARAM_Y_POS_CHIP4 |
95  CRIRES_PARAM_Y_WIDTH |
96  CRIRES_PARAM_DEGREE |
97  CRIRES_PARAM_WL_CLEAN,
98  "Wavelength calibration",
99  crires_spec_wavecal_description) ;
100 
101 /*-----------------------------------------------------------------------------
102  Static variables
103  -----------------------------------------------------------------------------*/
104 
105 static struct {
106  /* Inputs */
107  double wstart[CRIRES_NB_DETECTORS] ;
108  double wstop[CRIRES_NB_DETECTORS] ;
109  int wl_nolimit ;
110  int wl_log ;
111  const char * wl_ypos_c1 ;
112  const char * wl_ypos_c2 ;
113  const char * wl_ypos_c3 ;
114  const char * wl_ypos_c4 ;
115  int wl_width ;
116  double wl_fwhm ;
117  double wl_slitw ;
118  int wl_degree ;
119  double wl_err ;
120  int wl_samples ;
121  int wl_clean ;
122  double wl_xclimit ;
123  int wl_ppm ;
124  int display ;
125  /* Outputs */
126  crires_illum_period period ;
127  int mode ;
128  double qc_wlxc[CRIRES_NB_DETECTORS] ;
129  double qc_wlcent[CRIRES_NB_DETECTORS] ;
130  double qc_wldisp[CRIRES_NB_DETECTORS] ;
131  double qc_lines_flux[CRIRES_NB_DETECTORS] ;
132  double qc_fwhm[CRIRES_NB_DETECTORS] ;
133  double qc_rpower[CRIRES_NB_DETECTORS] ;
134 } crires_spec_wavecal_config ;
135 
136 /*-----------------------------------------------------------------------------
137  Functions code
138  -----------------------------------------------------------------------------*/
139 
140 /*----------------------------------------------------------------------------*/
147 /*----------------------------------------------------------------------------*/
148 static int crires_spec_wavecal(
149  cpl_frameset * frameset,
150  const cpl_parameterlist * parlist)
151 {
152  const char * sval ;
153  const char * wl_ypos ;
154  cpl_frameset * rawframes ;
155  const char * fname ;
156  const char * flat ;
157  const char * dark ;
158  const char * bpm ;
159  const char * detlin ;
160  const char * thar_cat ;
161  const char * n2o_cat ;
162  const char * oh_cat ;
163  const char * hitran_cat ;
164  const char * cfg_model ;
165  const char * thar_positions ;
166  cpl_propertylist * plist ;
167  double wmin, wmax ;
168  cpl_table * wave_tab[CRIRES_NB_DETECTORS] ;
169  cpl_imagelist * wl_map ;
170  cpl_imagelist * wl_map_model ;
171  cpl_vector * wave_ypos ;
172  int pix ;
173  int i, j ;
174 
175  /* Needed for sscanf() */
176  setlocale(LC_NUMERIC, "C");
177 
178  /* Initialise */
179  rawframes = NULL ;
180  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
181  crires_spec_wavecal_config.qc_wlxc[i] = -1.0 ;
182  crires_spec_wavecal_config.qc_wlcent[i] = -1.0 ;
183  crires_spec_wavecal_config.qc_wldisp[i] = -1.0 ;
184  crires_spec_wavecal_config.qc_lines_flux[i] = -1.0 ;
185  crires_spec_wavecal_config.qc_fwhm[i] = -1.0 ;
186  crires_spec_wavecal_config.qc_rpower[i] = -1.0 ;
187  }
188  crires_spec_wavecal_config.wl_ppm = 0 ;
189  crires_spec_wavecal_config.wl_slitw = 2.0 ;
190  crires_spec_wavecal_config.wl_fwhm = 2.0 ;
191 
192  /* Retrieve input parameters */
193 
194  sval = crires_parameterlist_get_string(parlist, RECIPE_STRING,
195  CRIRES_PARAM_WAVES) ;
196  if (sscanf(sval, "%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg",
197  &crires_spec_wavecal_config.wstart[0],
198  &crires_spec_wavecal_config.wstop[0],
199  &crires_spec_wavecal_config.wstart[1],
200  &crires_spec_wavecal_config.wstop[1],
201  &crires_spec_wavecal_config.wstart[2],
202  &crires_spec_wavecal_config.wstop[2],
203  &crires_spec_wavecal_config.wstart[3],
204  &crires_spec_wavecal_config.wstop[3])!=2*CRIRES_NB_DETECTORS){
205  return -1 ;
206  }
207  crires_spec_wavecal_config.display = crires_parameterlist_get_int(parlist,
208  RECIPE_STRING, CRIRES_PARAM_DISPLAY) ;
209  crires_spec_wavecal_config.wl_log = crires_parameterlist_get_bool(parlist,
210  RECIPE_STRING, CRIRES_PARAM_WL_LOG) ;
211  crires_spec_wavecal_config.wl_nolimit = crires_parameterlist_get_bool(
212  parlist, RECIPE_STRING, CRIRES_PARAM_WL_NOLIMIT) ;
213  crires_spec_wavecal_config.wl_degree = crires_parameterlist_get_int(parlist,
214  RECIPE_STRING, CRIRES_PARAM_DEGREE) ;
215  crires_spec_wavecal_config.wl_err = crires_parameterlist_get_double(parlist,
216  RECIPE_STRING, CRIRES_PARAM_WL_ERROR) ;
217  crires_spec_wavecal_config.wl_xclimit = crires_parameterlist_get_double(
218  parlist, RECIPE_STRING, CRIRES_PARAM_XC_LIMIT) ;
219  crires_spec_wavecal_config.wl_ypos_c1=crires_parameterlist_get_string(
220  parlist, RECIPE_STRING, CRIRES_PARAM_Y_POS_CHIP1) ;
221  crires_spec_wavecal_config.wl_ypos_c2=crires_parameterlist_get_string(
222  parlist, RECIPE_STRING, CRIRES_PARAM_Y_POS_CHIP2) ;
223  crires_spec_wavecal_config.wl_ypos_c3=crires_parameterlist_get_string(
224  parlist, RECIPE_STRING, CRIRES_PARAM_Y_POS_CHIP3) ;
225  crires_spec_wavecal_config.wl_ypos_c4=crires_parameterlist_get_string(
226  parlist, RECIPE_STRING, CRIRES_PARAM_Y_POS_CHIP4) ;
227  crires_spec_wavecal_config.wl_width= crires_parameterlist_get_int(parlist,
228  RECIPE_STRING, CRIRES_PARAM_Y_WIDTH) ;
229  crires_spec_wavecal_config.wl_samples = crires_parameterlist_get_int(
230  parlist, RECIPE_STRING, CRIRES_PARAM_WL_NBSAMPLES) ;
231  crires_spec_wavecal_config.wl_clean = crires_parameterlist_get_bool(parlist,
232  RECIPE_STRING, CRIRES_PARAM_WL_CLEAN) ;
233 
234  /* Identify the RAW and CALIB frames in the input frameset */
235  if (crires_dfs_set_groups(frameset, "crires_spec_wavecal")) {
236  cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
237  return -1 ;
238  }
239 
240  /* Retrieve calibration data */
241  flat = crires_extract_filename(frameset, CRIRES_CALPRO_FLAT) ;
242  dark = crires_extract_filename(frameset, CRIRES_CALPRO_DARK) ;
243  bpm = crires_extract_filename(frameset, CRIRES_CALPRO_BPM) ;
244  detlin = crires_extract_filename(frameset, CRIRES_CALPRO_COEFFS_CUBE) ;
245  thar_cat = crires_extract_filename(frameset, CRIRES_CALPRO_THAR_CAT) ;
246  n2o_cat = crires_extract_filename(frameset, CRIRES_CALPRO_N2O_CAT) ;
247  oh_cat = crires_extract_filename(frameset, CRIRES_CALPRO_OH_CAT) ;
248  hitran_cat = crires_extract_filename(frameset, CRIRES_CALPRO_HITRAN_CAT) ;
249  cfg_model = crires_extract_filename(frameset, CRIRES_CALPRO_MODEL_CONFIG) ;
250  thar_positions = crires_extract_filename(frameset, CRIRES_CALPRO_THAR_POS) ;
251 
252  /* Retrieve raw frames */
253  if ((rawframes = crires_extract_frameset(frameset,
254  CRIRES_SPEC_WAVECAL_SKY_RAW)) != NULL) {
255  crires_spec_wavecal_config.mode = 1 ;
256  } else if ((rawframes = crires_extract_frameset(frameset,
257  CRIRES_SPEC_WAVECAL_LAMP_RAW)) != NULL) {
258  crires_spec_wavecal_config.mode = 2 ;
259  } else if ((rawframes = crires_extract_frameset(frameset,
260  CRIRES_SPEC_WAVECAL_ABS_RAW)) != NULL) {
261  crires_spec_wavecal_config.mode = 3 ;
262  } else {
263  cpl_msg_error(__func__, "No raw frame in input") ;
264  return -1 ;
265  }
266 
267  /* Get the detector illumination period */
268  crires_spec_wavecal_config.period =
269  crires_get_detector_illum_period(
270  cpl_frame_get_filename(cpl_frameset_get_position(rawframes, 0))) ;
271  if (crires_spec_wavecal_config.period == CRIRES_ILLUM_UNKNOWN) {
272  cpl_msg_error(__func__,
273  "Cannot determine the detector illumination period") ;
274  cpl_frameset_delete(rawframes) ;
275  return -1 ;
276  } else {
277  crires_display_detector_illum(crires_spec_wavecal_config.period) ;
278  }
279 
280  /* Reduce the first raw frame */
281  fname = cpl_frame_get_filename(cpl_frameset_get_position(rawframes,0)) ;
282 
283  /* Get the Minimum and Maximum wavelengths */
284  if (crires_spec_wavecal_config.wl_nolimit == 0) {
285  plist = cpl_propertylist_load(fname, 0) ;
286  wmin = crires_pfits_get_wlen_min(plist) ;
287  wmax = crires_pfits_get_wlen_max(plist) ;
288  cpl_propertylist_delete(plist) ;
289  if (cpl_error_get_code()) {
290  wmin = wmax = -1.0 ;
291  cpl_error_reset() ;
292  }
293  } else {
294  wmin = wmax = -1.0 ;
295  }
296 
297  /* Wavelength calibration */
298  cpl_msg_info(__func__, "Apply the Wavelength Calibration") ;
299  cpl_msg_indent_more() ;
300  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
301  cpl_msg_info(__func__, "Calibrate chip number %d", i+1) ;
302  cpl_msg_indent_more() ;
303 
304  /* Where Compute the wavelength from ? */
305  wl_ypos = "" ;
306  if (i+1 == 1) wl_ypos = crires_spec_wavecal_config.wl_ypos_c1 ;
307  if (i+1 == 2) wl_ypos = crires_spec_wavecal_config.wl_ypos_c2 ;
308  if (i+1 == 3) wl_ypos = crires_spec_wavecal_config.wl_ypos_c3 ;
309  if (i+1 == 4) wl_ypos = crires_spec_wavecal_config.wl_ypos_c4 ;
310 
311  /* Try to parse the user specified positions */
312  if (!strcmp(wl_ypos, "")) {
313  wave_ypos = NULL ;
314  } else {
315  cpl_msg_info(__func__,
316  "Use the Y positions provided on the command line") ;
317  if ((wave_ypos = crires_parse_y_positions(wl_ypos)) == NULL) {
318  cpl_msg_warning(__func__,
319  "Cannot parse the y_positions value : %s", wl_ypos) ;
320  }
321  }
322 
323  /* Otherwise use passed calibration file */
324  if (wave_ypos == NULL && thar_positions != NULL) {
325  cpl_msg_info(__func__,
326  "Use the Y positions provided in the FITS file") ;
327  if ((wave_ypos = crires_read_y_positions(thar_positions,
328  i+1)) == NULL) {
329  cpl_msg_warning(__func__,
330  "Cannot read the Y positions from file %s",
331  thar_positions) ;
332  }
333  }
334 
335  /* Try to detect the positions */
336  if (wave_ypos == NULL) {
337  cpl_msg_info(__func__,
338  "Try an automatic detection of the Y positions") ;
339  wave_ypos = crires_wlcalib_detect_wave_ypos(fname,
340  crires_spec_wavecal_config.period, i+1);
341  }
342 
343  /* Check the Mode */
344  if (crires_spec_wavecal_config.mode == 1) {
345  /* Calibrate from the sky */
346  cpl_msg_info(__func__,"Get the calibration from the oh/hitran sky");
347  wave_tab[i] = crires_wlcalib_sky(fname,
348  crires_spec_wavecal_config.period,
349  oh_cat, hitran_cat, crires_spec_wavecal_config.wl_log,
350  flat, dark, bpm, detlin,
351  crires_spec_wavecal_config.wstart[i],
352  crires_spec_wavecal_config.wstop[i],
353  wmin, wmax, i+1,
354  wave_ypos,
355  crires_spec_wavecal_config.wl_width,
356  crires_spec_wavecal_config.wl_degree,
357  crires_spec_wavecal_config.wl_slitw,
358  crires_spec_wavecal_config.wl_fwhm,
359  crires_spec_wavecal_config.wl_err,
360  crires_spec_wavecal_config.wl_samples,
361  crires_spec_wavecal_config.wl_clean,
362  crires_spec_wavecal_config.wl_xclimit,
363  crires_spec_wavecal_config.wl_ppm,
364  (i+1==crires_spec_wavecal_config.display)) ;
365  } else if (crires_spec_wavecal_config.mode == 2) {
366  /* Calibrate from the lamp */
367  cpl_msg_info(__func__, "Get the calibration from the thar lamp") ;
368  wave_tab[i] = crires_wlcalib_lamp(fname,
369  crires_spec_wavecal_config.period,
370  thar_cat, crires_spec_wavecal_config.wl_log,
371  flat, dark, bpm, detlin,
372  crires_spec_wavecal_config.wstart[i],
373  crires_spec_wavecal_config.wstop[i],
374  wmin, wmax, i+1,
375  wave_ypos,
376  crires_spec_wavecal_config.wl_width,
377  crires_spec_wavecal_config.wl_degree,
378  crires_spec_wavecal_config.wl_slitw,
379  crires_spec_wavecal_config.wl_fwhm,
380  crires_spec_wavecal_config.wl_err,
381  crires_spec_wavecal_config.wl_samples,
382  crires_spec_wavecal_config.wl_clean,
383  crires_spec_wavecal_config.wl_xclimit,
384  crires_spec_wavecal_config.wl_ppm,
385  (i+1==crires_spec_wavecal_config.display),
386  &(crires_spec_wavecal_config.qc_lines_flux[i]),
387  &(crires_spec_wavecal_config.qc_fwhm[i]),
388  &(crires_spec_wavecal_config.qc_rpower[i])) ;
389  } else if (crires_spec_wavecal_config.mode == 3) {
390  /* Calibrate from the gas cell */
391  cpl_msg_info(__func__, "Get the calibration from the n2o gas cell");
392  wave_tab[i] = crires_wlcalib_lamp(fname,
393  crires_spec_wavecal_config.period,
394  n2o_cat, crires_spec_wavecal_config.wl_log,
395  flat, dark, bpm, detlin,
396  crires_spec_wavecal_config.wstart[i],
397  crires_spec_wavecal_config.wstop[i],
398  wmin, wmax, i+1,
399  wave_ypos,
400  crires_spec_wavecal_config.wl_width,
401  crires_spec_wavecal_config.wl_degree,
402  crires_spec_wavecal_config.wl_slitw,
403  crires_spec_wavecal_config.wl_fwhm,
404  crires_spec_wavecal_config.wl_err,
405  crires_spec_wavecal_config.wl_samples,
406  crires_spec_wavecal_config.wl_clean,
407  crires_spec_wavecal_config.wl_xclimit,
408  crires_spec_wavecal_config.wl_ppm,
409  (i+1==crires_spec_wavecal_config.display),
410  &(crires_spec_wavecal_config.qc_lines_flux[i]),
411  &(crires_spec_wavecal_config.qc_fwhm[i]),
412  &(crires_spec_wavecal_config.qc_rpower[i])) ;
413  }
414  cpl_msg_indent_less() ;
415  cpl_vector_delete(wave_ypos) ;
416  }
417  cpl_msg_indent_less() ;
418 
419  /* Create the wave map */
420  if ((wl_map = crires_wlcalib_gen_wlmap((const cpl_table **)wave_tab))
421  == NULL) {
422  cpl_msg_error(__func__, "Cannot compute the Wavelength Map") ;
423  cpl_frameset_delete(rawframes) ;
424  for (j=0 ; j<CRIRES_NB_DETECTORS ; j++) {
425  if (wave_tab[j] != NULL) cpl_table_delete(wave_tab[j]);
426  }
427  return -1 ;
428  }
429 
430  /* Compute the QC parameters */
431  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
432  crires_spec_wavecal_config.qc_wlcent[i] =
433  cpl_image_get(cpl_imagelist_get(wl_map, i),
434  512, 256, &pix) ;
435  crires_spec_wavecal_config.qc_wldisp[i] =
436  ((cpl_image_get(cpl_imagelist_get(wl_map, i), 1024,
437  256, &pix)) -
438  (cpl_image_get(cpl_imagelist_get(wl_map, i), 1,
439  256, &pix)))
440  / 1023 ;
441  crires_spec_wavecal_config.qc_wlxc[i] =
442  crires_wlcalib_get_better_xc(wave_tab[i]) ;
443  }
444 
445  /* Get the wl map from the model */
446  if ((cfg_model != NULL) && (!crires_model_off()) &&
447  (crires_model_config_check(cfg_model, fname) == 0)) {
448  cpl_msg_info(__func__, "Call the model to get the wavelength map") ;
449  cpl_msg_indent_more() ;
450  wl_map_model = crires_model_wavpix(fname, cfg_model, -1) ;
451  if (wl_map_model == NULL) {
452  cpl_msg_warning(__func__, "Model function returns NULL") ;
453  cpl_error_reset() ;
454  }
455  cpl_msg_indent_less() ;
456  } else {
457  wl_map_model = NULL ;
458  }
459  cpl_frameset_delete(rawframes) ;
460 
461  /* Save the product */
462  cpl_msg_info(__func__, "Save the product") ;
463  cpl_msg_indent_more() ;
464  if (crires_spec_wavecal_save(wl_map, wl_map_model,
465  (const cpl_table **)wave_tab, parlist, frameset)) {
466  cpl_msg_error(__func__, "Cannot save the product") ;
467  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++)
468  if (wave_tab[i] != NULL) cpl_table_delete(wave_tab[i]);
469  cpl_imagelist_delete(wl_map) ;
470  if (wl_map_model) cpl_imagelist_delete(wl_map_model) ;
471  cpl_msg_indent_less() ;
472  return -1 ;
473  }
474  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++)
475  if (wave_tab[i] != NULL) cpl_table_delete(wave_tab[i]);
476  cpl_imagelist_delete(wl_map) ;
477  if (wl_map_model) cpl_imagelist_delete(wl_map_model) ;
478  cpl_msg_indent_less() ;
479 
480  /* Return */
481  if (cpl_error_get_code()) return -1 ;
482  else return 0 ;
483 }
484 
485 /*----------------------------------------------------------------------------*/
495 /*----------------------------------------------------------------------------*/
496 static int crires_spec_wavecal_save(
497  const cpl_imagelist * ilist,
498  const cpl_imagelist * ilist_model,
499  const cpl_table ** wl_tab,
500  const cpl_parameterlist * parlist,
501  cpl_frameset * set)
502 {
503  cpl_propertylist ** qclists ;
504  const cpl_frame * ref_frame ;
505  cpl_propertylist * inputlist ;
506  const char * recipe_name = "crires_spec_wavecal" ;
507  int i ;
508 
509  /* Get the reference frame */
510  ref_frame = irplib_frameset_get_first_from_group(set, CPL_FRAME_GROUP_RAW) ;
511 
512  /* Create the QC lists */
513  qclists = cpl_malloc(CRIRES_NB_DETECTORS * sizeof(cpl_propertylist*)) ;
514  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
515  qclists[i] = cpl_propertylist_new() ;
516  cpl_propertylist_append_double(qclists[i], "ESO QC CENTWL",
517  crires_spec_wavecal_config.qc_wlcent[i]) ;
518  cpl_propertylist_append_double(qclists[i], "ESO QC DISPWL",
519  crires_spec_wavecal_config.qc_wldisp[i]) ;
520  cpl_propertylist_append_double(qclists[i], "ESO QC XCORR",
521  crires_spec_wavecal_config.qc_wlxc[i]) ;
522  cpl_propertylist_append_double(qclists[i], "ESO QC LINES FLUX",
523  crires_spec_wavecal_config.qc_lines_flux[i]) ;
524  cpl_propertylist_append_double(qclists[i], "ESO QC FWHM MED",
525  crires_spec_wavecal_config.qc_fwhm[i]) ;
526  cpl_propertylist_append_double(qclists[i], "ESO QC RESOL MED",
527  crires_spec_wavecal_config.qc_rpower[i]) ;
528 
529  /* Propagate some keywords from input raw frame extensions */
530  inputlist = cpl_propertylist_load_regexp(
531  cpl_frame_get_filename(ref_frame), i+1,
532  CRIRES_HEADER_EXT_FORWARD, 0) ;
533  cpl_propertylist_copy_property_regexp(qclists[i], inputlist,
534  CRIRES_HEADER_EXT_FORWARD, 0) ;
535  cpl_propertylist_delete(inputlist) ;
536  }
537 
538  /* Write the image */
539  crires_image_save(set,
540  parlist,
541  set,
542  ilist,
543  recipe_name,
544  CRIRES_WL_MAP_IMA,
545  CRIRES_PROTYPE_WL_MAP,
546  crires_spec_wavecal_config.period,
547  NULL,
548  (const cpl_propertylist **)qclists,
549  PACKAGE "/" PACKAGE_VERSION,
550  "crires_spec_wavecal_ima.fits") ;
551 
552  if (ilist_model != NULL) {
553  /* Write the image */
554  crires_image_save(set,
555  parlist,
556  set,
557  ilist_model,
558  recipe_name,
559  CRIRES_WL_MAP_MODEL_IMA,
560  CRIRES_PROTYPE_WL_MAP,
561  crires_spec_wavecal_config.period,
562  NULL,
563  (const cpl_propertylist **)qclists,
564  PACKAGE "/" PACKAGE_VERSION,
565  "crires_spec_wavecal_ima_model.fits") ;
566  }
567 
568  /* Write the table */
569  crires_table_save(set,
570  parlist,
571  set,
572  wl_tab,
573  recipe_name,
574  CRIRES_CALPRO_WAVE,
575  CRIRES_PROTYPE_WL_POLY,
576  NULL,
577  (const cpl_propertylist **)qclists,
578  PACKAGE "/" PACKAGE_VERSION,
579  "crires_spec_wavecal_tab.fits") ;
580 
581  /* Free and return */
582  for (i=0 ; i<CRIRES_NB_DETECTORS ; i++) {
583  cpl_propertylist_delete(qclists[i]) ;
584  }
585  cpl_free(qclists) ;
586  return 0;
587 }