FORS Pipeline Reference Manual  5.0.9
fors_wave_calib.c
1 /* $Id: fors_wave_calib.c,v 1.9 2013-08-20 17:02:58 cgarcia Exp $
2  *
3  * This file is part of the FORS Data Reduction Pipeline
4  * Copyright (C) 2002-2010 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 02110-1301 USA
19  */
20 
21 /*
22  * $Author: cgarcia $
23  * $Date: 2013-08-20 17:02:58 $
24  * $Revision: 1.9 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <math.h>
33 #include <cpl.h>
34 #include <moses.h>
35 #include <fors_dfs.h>
36 
37 static int fors_wave_calib_create(cpl_plugin *);
38 static int fors_wave_calib_exec(cpl_plugin *);
39 static int fors_wave_calib_destroy(cpl_plugin *);
40 static int fors_wave_calib(cpl_parameterlist *, cpl_frameset *);
41 
42 static char fors_wave_calib_description[] =
43 "This recipe is used to wavelength calibrate MOS/MXU slit spectra contained\n"
44 "in the rectified arc lamp exposure produced with recipe fors_extract_slits.\n"
45 "A pattern-matching algorithm is applied as in recipe fors_detect_spectra.\n"
46 "The input spatial map is used in the production of the wavelength map.\n"
47 "\n"
48 "Use recipe fors_wave_calib_lss for LSS data, or for MOS/MXU data where all\n"
49 "slits have the same offset. For more details on this data reduction strategy\n"
50 "please refer to the FORS Pipeline User's Manual.\n"
51 "\n"
52 "Note that specifying an input GRISM_TABLE will set some of the recipe\n"
53 "configuration parameters to default values valid for a particular grism.\n"
54 "\n"
55 "In the table below the MXU acronym can be alternatively read as MOS.\n\n"
56 "Input files:\n\n"
57 " DO category: Type: Explanation: Required:\n"
58 " SPATIAL_MAP_MXU Calib Spatial map Y\n"
59 " RECTIFIED_LAMP_MXU Calib Rectified arc exposure Y\n"
60 " SLIT_LOCATION_MXU Calib Slit location table Y\n"
61 " CURV_COEFF_MXU Calib Spectral curvature Y\n"
62 " MASTER_LINECAT Calib Line catalog Y\n"
63 " GRISM_TABLE Calib Grism table .\n\n"
64 "Output files:\n\n"
65 " DO category: Data type: Explanation:\n"
66 " REDUCED_LAMP_MXU FITS image Calibrated arc lamp exposure\n"
67 " DISP_COEFF_MXU FITS table Inverse dispersion coefficients\n"
68 " DISP_RESIDUALS_MXU FITS image Image of modeling residuals\n"
69 " WAVELENGTH_MAP_MXU FITS image Wavelengths mapped on CCD\n"
70 " SPECTRAL_RESOLUTION_MXU FITS table Spectral resolution table\n\n";
71 
72 #define fors_wave_calib_exit(message) \
73 { \
74 if ((const char *)message != NULL) cpl_msg_error(recipe, message); \
75 cpl_image_delete(spectra); \
76 cpl_image_delete(spatial); \
77 cpl_image_delete(rainbow); \
78 cpl_image_delete(residual); \
79 cpl_image_delete(rectified); \
80 cpl_image_delete(wavemap); \
81 cpl_table_delete(grism_table); \
82 cpl_table_delete(wavelengths); \
83 cpl_table_delete(maskslits); \
84 cpl_table_delete(idscoeff); \
85 cpl_table_delete(restab); \
86 cpl_table_delete(slits); \
87 cpl_table_delete(polytraces); \
88 cpl_vector_delete(lines); \
89 cpl_propertylist_delete(header); \
90 cpl_propertylist_delete(save_header); \
91 cpl_msg_indent_less(); \
92 return -1; \
93 }
94 
95 #define fors_wave_calib_exit_memcheck(message) \
96 { \
97 if ((const char *)message != NULL) cpl_msg_info(recipe, message); \
98 printf("free spectra (%p)\n", spectra); \
99 cpl_image_delete(spectra); \
100 printf("free spatial (%p)\n", spatial); \
101 cpl_image_delete(spatial); \
102 printf("free rainbow (%p)\n", rainbow); \
103 cpl_image_delete(rainbow); \
104 printf("free residual (%p)\n", residual); \
105 cpl_image_delete(residual); \
106 printf("free rectified (%p)\n", rectified); \
107 cpl_image_delete(rectified); \
108 printf("free wavemap (%p)\n", wavemap); \
109 cpl_image_delete(wavemap); \
110 printf("free grism_table (%p)\n", grism_table); \
111 cpl_table_delete(grism_table); \
112 printf("free wavelengths (%p)\n", wavelengths); \
113 cpl_table_delete(wavelengths); \
114 printf("free maskslits (%p)\n", maskslits); \
115 cpl_table_delete(maskslits); \
116 printf("free idscoeff (%p)\n", idscoeff); \
117 cpl_table_delete(idscoeff); \
118 printf("free restab (%p)\n", restab); \
119 cpl_table_delete(restab); \
120 printf("free slits (%p)\n", slits); \
121 cpl_table_delete(slits); \
122 printf("free polytraces (%p)\n", polytraces); \
123 cpl_table_delete(polytraces); \
124 printf("free lines (%p)\n", lines); \
125 cpl_vector_delete(lines); \
126 printf("free header (%p)\n", header); \
127 cpl_propertylist_delete(header); \
128 printf("free save_header (%p)\n", save_header); \
129 cpl_propertylist_delete(save_header); \
130 cpl_msg_indent_less(); \
131 return 0; \
132 }
133 
134 
146 int cpl_plugin_get_info(cpl_pluginlist *list)
147 {
148  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
149  cpl_plugin *plugin = &recipe->interface;
150 
151  cpl_plugin_init(plugin,
152  CPL_PLUGIN_API,
153  FORS_BINARY_VERSION,
154  CPL_PLUGIN_TYPE_RECIPE,
155  "fors_wave_calib",
156  "Derive dispersion relation from rectified arc lamp frame",
157  fors_wave_calib_description,
158  "Carlo Izzo",
159  PACKAGE_BUGREPORT,
160  "This file is currently part of the FORS Instrument Pipeline\n"
161  "Copyright (C) 2002-2010 European Southern Observatory\n\n"
162  "This program is free software; you can redistribute it and/or modify\n"
163  "it under the terms of the GNU General Public License as published by\n"
164  "the Free Software Foundation; either version 2 of the License, or\n"
165  "(at your option) any later version.\n\n"
166  "This program is distributed in the hope that it will be useful,\n"
167  "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
168  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
169  "GNU General Public License for more details.\n\n"
170  "You should have received a copy of the GNU General Public License\n"
171  "along with this program; if not, write to the Free Software Foundation,\n"
172  "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n",
173  fors_wave_calib_create,
174  fors_wave_calib_exec,
175  fors_wave_calib_destroy);
176 
177  cpl_pluginlist_append(list, plugin);
178 
179  return 0;
180 }
181 
182 
193 static int fors_wave_calib_create(cpl_plugin *plugin)
194 {
195  cpl_recipe *recipe;
196  cpl_parameter *p;
197 
198  /*
199  * Check that the plugin is part of a valid recipe
200  */
201 
202  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
203  recipe = (cpl_recipe *)plugin;
204  else
205  return -1;
206 
207  /*
208  * Create the (empty) parameters list in the cpl_recipe object
209  */
210 
211  recipe->parameters = cpl_parameterlist_new();
212 
213  /*
214  * Dispersion
215  */
216 
217  p = cpl_parameter_new_value("fors.fors_wave_calib.dispersion",
218  CPL_TYPE_DOUBLE,
219  "Expected spectral dispersion (Angstrom/pixel)",
220  "fors.fors_wave_calib",
221  0.0);
222  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
223  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
224  cpl_parameterlist_append(recipe->parameters, p);
225 
226  /*
227  * Peak detection level
228  */
229 
230  p = cpl_parameter_new_value("fors.fors_wave_calib.peakdetection",
231  CPL_TYPE_DOUBLE,
232  "Initial peak detection threshold (ADU)",
233  "fors.fors_wave_calib",
234  0.0);
235  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "peakdetection");
236  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
237  cpl_parameterlist_append(recipe->parameters, p);
238 
239  /*
240  * Degree of wavelength calibration polynomial
241  */
242 
243  p = cpl_parameter_new_value("fors.fors_wave_calib.wdegree",
244  CPL_TYPE_INT,
245  "Degree of wavelength calibration polynomial",
246  "fors.fors_wave_calib",
247  0);
248  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wdegree");
249  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
250  cpl_parameterlist_append(recipe->parameters, p);
251 
252  /*
253  * Reference lines search radius
254  */
255 
256  p = cpl_parameter_new_value("fors.fors_wave_calib.wradius",
257  CPL_TYPE_INT,
258  "Search radius if iterating pattern-matching "
259  "with first-guess method",
260  "fors.fors_wave_calib",
261  4);
262  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wradius");
263  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
264  cpl_parameterlist_append(recipe->parameters, p);
265 
266  /*
267  * Rejection threshold in dispersion relation polynomial fitting
268  */
269 
270  p = cpl_parameter_new_value("fors.fors_wave_calib.wreject",
271  CPL_TYPE_DOUBLE,
272  "Rejection threshold in dispersion "
273  "relation fit (pixel)",
274  "fors.fors_wave_calib",
275  0.7);
276  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wreject");
277  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
278  cpl_parameterlist_append(recipe->parameters, p);
279 
280  /*
281  * Line catalog table column containing the reference wavelengths
282  */
283 
284  p = cpl_parameter_new_value("fors.fors_wave_calib.wcolumn",
285  CPL_TYPE_STRING,
286  "Name of line catalog table column "
287  "with wavelengths",
288  "fors.fors_wave_calib",
289  "WLEN");
290  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
291  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
292  cpl_parameterlist_append(recipe->parameters, p);
293 
294  /*
295  * Start wavelength for spectral extraction
296  */
297 
298  p = cpl_parameter_new_value("fors.fors_wave_calib.startwavelength",
299  CPL_TYPE_DOUBLE,
300  "Start wavelength in spectral extraction",
301  "fors.fors_wave_calib",
302  0.0);
303  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
304  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
305  cpl_parameterlist_append(recipe->parameters, p);
306 
307  /*
308  * End wavelength for spectral extraction
309  */
310 
311  p = cpl_parameter_new_value("fors.fors_wave_calib.endwavelength",
312  CPL_TYPE_DOUBLE,
313  "End wavelength in spectral extraction",
314  "fors.fors_wave_calib",
315  0.0);
316  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
317  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
318  cpl_parameterlist_append(recipe->parameters, p);
319 
320  return 0;
321 }
322 
323 
332 static int fors_wave_calib_exec(cpl_plugin *plugin)
333 {
334  cpl_recipe *recipe;
335 
336  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
337  recipe = (cpl_recipe *)plugin;
338  else
339  return -1;
340 
341  return fors_wave_calib(recipe->parameters, recipe->frames);
342 }
343 
344 
353 static int fors_wave_calib_destroy(cpl_plugin *plugin)
354 {
355  cpl_recipe *recipe;
356 
357  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
358  recipe = (cpl_recipe *)plugin;
359  else
360  return -1;
361 
362  cpl_parameterlist_delete(recipe->parameters);
363 
364  return 0;
365 }
366 
367 
377 static int fors_wave_calib(cpl_parameterlist *parlist,
378  cpl_frameset *frameset)
379 {
380 
381  const char *recipe = "fors_wave_calib";
382 
383 
384  /*
385  * Input parameters
386  */
387 
388  double dispersion;
389  double peakdetection;
390  int wdegree;
391  int wradius;
392  double wreject;
393  const char *wcolumn;
394  double startwavelength;
395  double endwavelength;
396 
397  /*
398  * CPL objects
399  */
400 
401  cpl_image *spectra = NULL;
402  cpl_image *spatial = NULL;
403  cpl_image *rectified = NULL;
404  cpl_image *wavemap = NULL;
405  cpl_image *rainbow = NULL;
406  cpl_image *residual = NULL;
407  cpl_image *bimage = NULL;
408  cpl_table *grism_table = NULL;
409  cpl_table *wavelengths = NULL;
410  cpl_table *slits = NULL;
411  cpl_table *polytraces = NULL;
412  cpl_table *idscoeff = NULL;
413  cpl_table *maskslits = NULL;
414  cpl_table *restab = NULL;
415  cpl_vector *lines = NULL;
416  cpl_propertylist *header = NULL;
417  cpl_propertylist *save_header = NULL;
418 
419  /*
420  * Auxiliary variables
421  */
422 
423  char version[80];
424  const char *arc_tag;
425  const char *reduced_lamp_tag;
426  const char *wavelength_map_tag;
427  const char *spatial_map_tag;
428  const char *disp_residuals_tag;
429  const char *disp_coeff_tag;
430  const char *curv_coeff_tag;
431  const char *slit_location_tag;
432  const char *spectral_resolution_tag;
433  int lamp_mxu;
434  int lamp_mos;
435  int mos;
436  int treat_as_lss = 0;
437  double mean_rms;
438  double mxpos;
439  int narc, nref;
440  int nlines;
441  int rebin;
442  double *line;
443  double *fiterror = NULL;
444  int *fitlines = NULL;
445  int nx, ny;
446  double reference;
447  int i;
448  int nslits_out_det = 0;
449 
450 
451  char *instrume = NULL;
452 
453 
454  cpl_msg_set_indentation(2);
455 
456  /*
457  * Get configuration parameters
458  */
459 
460  cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
461  cpl_msg_indent_more();
462 
463  if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
464  fors_wave_calib_exit("Too many in input: GRISM_TABLE");
465 
466  grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
467 
468  dispersion = dfs_get_parameter_double(parlist,
469  "fors.fors_wave_calib.dispersion", grism_table);
470 
471  if (dispersion <= 0.0)
472  fors_wave_calib_exit("Invalid spectral dispersion value");
473 
474  peakdetection = dfs_get_parameter_double(parlist,
475  "fors.fors_wave_calib.peakdetection", grism_table);
476  if (peakdetection <= 0.0)
477  fors_wave_calib_exit("Invalid peak detection level");
478 
479  wdegree = dfs_get_parameter_int(parlist,
480  "fors.fors_wave_calib.wdegree", grism_table);
481 
482  if (wdegree < 1)
483  fors_wave_calib_exit("Invalid polynomial degree");
484 
485  if (wdegree > 5)
486  fors_wave_calib_exit("Max allowed polynomial degree is 5");
487 
488  wradius = dfs_get_parameter_int(parlist,
489  "fors.fors_wave_calib.wradius", NULL);
490 
491  if (wradius < 0)
492  fors_wave_calib_exit("Invalid search radius");
493 
494  wreject = dfs_get_parameter_double(parlist,
495  "fors.fors_wave_calib.wreject", NULL);
496 
497  if (wreject <= 0.0)
498  fors_wave_calib_exit("Invalid rejection threshold");
499 
500  wcolumn = dfs_get_parameter_string(parlist,
501  "fors.fors_wave_calib.wcolumn", NULL);
502 
503  startwavelength = dfs_get_parameter_double(parlist,
504  "fors.fors_wave_calib.startwavelength", grism_table);
505  if (startwavelength > 1.0)
506  if (startwavelength < 3000.0 || startwavelength > 13000.0)
507  fors_wave_calib_exit("Invalid wavelength");
508 
509  endwavelength = dfs_get_parameter_double(parlist,
510  "fors.fors_wave_calib.endwavelength", grism_table);
511  if (endwavelength > 1.0) {
512  if (endwavelength < 3000.0 || endwavelength > 13000.0)
513  fors_wave_calib_exit("Invalid wavelength");
514  if (startwavelength < 1.0)
515  fors_wave_calib_exit("Invalid wavelength interval");
516  }
517 
518  if (startwavelength > 1.0)
519  if (endwavelength - startwavelength <= 0.0)
520  fors_wave_calib_exit("Invalid wavelength interval");
521 
522  cpl_table_delete(grism_table); grism_table = NULL;
523 
524  if (cpl_error_get_code())
525  fors_wave_calib_exit("Failure reading the configuration parameters");
526 
527 
528  cpl_msg_indent_less();
529  cpl_msg_info(recipe, "Check input set-of-frames:");
530  cpl_msg_indent_more();
531 
532  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
533  fors_wave_calib_exit("Missing required input: MASTER_LINECAT");
534 
535  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
536  fors_wave_calib_exit("Too many in input: MASTER_LINECAT");
537 
538  lamp_mxu = cpl_frameset_count_tags(frameset, "RECTIFIED_LAMP_MXU");
539  lamp_mos = cpl_frameset_count_tags(frameset, "RECTIFIED_LAMP_MOS");
540 
541  narc = lamp_mxu + lamp_mos;
542 
543  if (narc == 0) {
544  fors_wave_calib_exit("Missing input rectified arc lamp frame");
545  }
546  if (narc > 1) {
547  cpl_msg_error(recipe, "Too many input rectified arc lamp frames "
548  "(%d > 1)", narc);
549  fors_wave_calib_exit(NULL);
550  }
551 
552  mos = 0;
553 
554  if (lamp_mxu) {
555  arc_tag = "RECTIFIED_LAMP_MXU";
556  slit_location_tag = "SLIT_LOCATION_MXU";
557  spatial_map_tag = "SPATIAL_MAP_MXU";
558  reduced_lamp_tag = "REDUCED_LAMP_MXU";
559  disp_residuals_tag = "DISP_RESIDUALS_MXU";
560  disp_coeff_tag = "DISP_COEFF_MXU";
561  curv_coeff_tag = "CURV_COEFF_MXU";
562  wavelength_map_tag = "WAVELENGTH_MAP_MXU";
563  spectral_resolution_tag = "SPECTRAL_RESOLUTION_MXU";
564  }
565  else if (lamp_mos) {
566  mos = 1;
567  arc_tag = "RECTIFIED_LAMP_MOS";
568  slit_location_tag = "SLIT_LOCATION_MOS";
569  spatial_map_tag = "SPATIAL_MAP_MOS";
570  reduced_lamp_tag = "REDUCED_LAMP_MOS";
571  disp_residuals_tag = "DISP_RESIDUALS_MOS";
572  disp_coeff_tag = "DISP_COEFF_MOS";
573  curv_coeff_tag = "CURV_COEFF_MXU";
574  wavelength_map_tag = "WAVELENGTH_MAP_MOS";
575  spectral_resolution_tag = "SPECTRAL_RESOLUTION_MOS";
576  }
577 
578 
579  nref = cpl_frameset_count_tags(frameset, spatial_map_tag);
580 
581  if (nref == 0) {
582  fors_wave_calib_exit("Missing input spatial map");
583  }
584  if (nref > 1) {
585  cpl_msg_error(recipe, "Too many input spatial maps (%d > 1)", nref);
586  fors_wave_calib_exit(NULL);
587  }
588 
589  nref = cpl_frameset_count_tags(frameset, slit_location_tag);
590 
591  if (nref == 0) {
592  fors_wave_calib_exit("Missing input slit location table");
593  }
594  if (nref > 1) {
595  cpl_msg_error(recipe, "Too many input slit location tables (%d > 1)",
596  nref);
597  fors_wave_calib_exit(NULL);
598  }
599 
600  nref = cpl_frameset_count_tags(frameset, curv_coeff_tag);
601 
602  if (nref == 0) {
603  fors_wave_calib_exit("Missing input spectral curvature table");
604  }
605  if (nref > 1) {
606  cpl_msg_error(recipe, "Too many input spectral curvature tables "
607  "(%d > 1)", nref);
608  fors_wave_calib_exit(NULL);
609  }
610 
611 
612  if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
613  cpl_msg_warning(cpl_func,"Input frames are not from the same grism");
614 
615  if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
616  cpl_msg_warning(cpl_func,"Input frames are not from the same filter");
617 
618  if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
619  cpl_msg_warning(cpl_func,"Input frames are not from the same chip");
620 
621 
622  /*
623  * Get the reference wavelength and the rebin factor along the
624  * dispersion direction from the arc lamp exposure
625  */
626 
627  header = dfs_load_header(frameset, spatial_map_tag, 0);
628 
629  if (header == NULL)
630  fors_wave_calib_exit("Cannot load arc lamp header");
631 
632  instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
633  if (instrume == NULL)
634  fors_wave_calib_exit("Missing keyword INSTRUME in arc lamp header");
635 
636  if (instrume[4] == '1')
637  snprintf(version, 80, "%s/%s", "fors1", VERSION);
638  if (instrume[4] == '2')
639  snprintf(version, 80, "%s/%s", "fors2", VERSION);
640 
641  reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
642 
643  if (cpl_error_get_code() != CPL_ERROR_NONE)
644  fors_wave_calib_exit("Missing keyword ESO INS GRIS1 WLEN "
645  "in arc lamp frame header");
646 
647  if (reference < 3000.0) /* Perhaps in nanometers... */
648  reference *= 10;
649 
650  if (reference < 3000.0 || reference > 13000.0) {
651  cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
652  "keyword ESO INS GRIS1 WLEN in arc lamp frame header",
653  reference);
654  fors_wave_calib_exit(NULL);
655  }
656 
657  cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
658 
659  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
660 
661  if (cpl_error_get_code() != CPL_ERROR_NONE)
662  fors_wave_calib_exit("Missing keyword ESO DET WIN1 BINX "
663  "in arc lamp frame header");
664 
665  if (rebin != 1) {
666  dispersion *= rebin;
667  cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
668  "working dispersion used is %f A/pixel", rebin,
669  dispersion);
670  }
671 
672 
673  cpl_msg_info(recipe, "Produce mask slit position table...");
674 
675  if (mos)
676  maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
677  else
678  maskslits = mos_load_slits_fors_mxu(header);
679 
680 
681  /*
682  * Check if all slits have the same X offset: in such case,
683  * treat the observation as a long-slit one!
684  */
685 
686  treat_as_lss = fors_mos_is_lss_like(maskslits, nslits_out_det);
687 
688  cpl_table_delete(maskslits); maskslits = NULL;
689 
690  if (treat_as_lss) {
691  cpl_msg_error(recipe, "All slits have the same offset: %.2f mm\n"
692  "The LSS data reduction strategy must be applied: "
693  "please use recipe fors_wave_calib_lss", mxpos);
694  fors_wave_calib_exit(NULL);
695  }
696 
697 
698  cpl_msg_indent_less();
699  cpl_msg_info(recipe, "Load arc lamp exposure...");
700  cpl_msg_indent_more();
701 
702  spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0);
703 
704  if (spectra == NULL)
705  fors_wave_calib_exit("Cannot load arc lamp exposure");
706 
707 
708  cpl_msg_indent_less();
709  cpl_msg_info(recipe, "Load input line catalog...");
710  cpl_msg_indent_more();
711 
712  wavelengths = dfs_load_table(frameset, "MASTER_LINECAT", 1);
713 
714  if (wavelengths == NULL)
715  fors_wave_calib_exit("Cannot load line catalog");
716 
717 
718  /*
719  * Cast the wavelengths into a (double precision) CPL vector
720  */
721 
722  nlines = cpl_table_get_nrow(wavelengths);
723 
724  if (nlines == 0)
725  fors_wave_calib_exit("Empty input line catalog");
726 
727  if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
728  cpl_msg_error(recipe, "Missing column %s in input line catalog table",
729  wcolumn);
730  fors_wave_calib_exit(NULL);
731  }
732 
733  line = cpl_malloc(nlines * sizeof(double));
734 
735  for (i = 0; i < nlines; i++)
736  line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
737 
738  cpl_table_delete(wavelengths); wavelengths = NULL;
739 
740  lines = cpl_vector_wrap(nlines, line);
741 
742 
743  cpl_msg_indent_less();
744  cpl_msg_info(recipe, "Load slit location table...");
745  cpl_msg_indent_more();
746 
747  slits = dfs_load_table(frameset, slit_location_tag, 1);
748 
749  if (slits == NULL)
750  fors_wave_calib_exit("Cannot load slit location table");
751 
752 
753  cpl_msg_indent_less();
754  cpl_msg_info(recipe, "Subtract background from input arc exposure...");
755  cpl_msg_indent_more();
756 
757  bimage = mos_arc_background(spectra, 15, 15);
758  cpl_image_subtract(spectra, bimage);
759  cpl_image_delete(bimage);
760 
761 
762  cpl_msg_indent_less();
763  cpl_msg_info(recipe, "Perform wavelength calibration...");
764  cpl_msg_indent_more();
765 
766  nx = cpl_image_get_size_x(spectra);
767  ny = cpl_image_get_size_y(spectra);
768 
769  idscoeff = cpl_table_new(ny);
770  rainbow = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
771  residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
772  fiterror = cpl_calloc(ny, sizeof(double));
773  fitlines = cpl_calloc(ny, sizeof(int));
774 
775  rectified = mos_wavelength_calibration_final(spectra, slits, lines,
776  dispersion, peakdetection,
777  wradius, wdegree, wreject,
778  reference, &startwavelength,
779  &endwavelength, fitlines,
780  fiterror, idscoeff, rainbow,
781  residual, NULL, NULL);
782 
783  cpl_image_delete(spectra); spectra = NULL;
784 
785  if (rectified == NULL)
786  fors_wave_calib_exit("Wavelength calibration failure");
787 
788  save_header = cpl_propertylist_new();
789  cpl_propertylist_update_double(save_header, "CRPIX1", 1.0);
790  cpl_propertylist_update_double(save_header, "CRPIX2", 1.0);
791  cpl_propertylist_update_double(save_header, "CRVAL1",
792  startwavelength + dispersion/2);
793  cpl_propertylist_update_double(save_header, "CRVAL2", 1.0);
794  /* cpl_propertylist_update_double(save_header, "CDELT1", dispersion);
795  cpl_propertylist_update_double(save_header, "CDELT2", 1.0); */
796  cpl_propertylist_update_double(save_header, "CD1_1", dispersion);
797  cpl_propertylist_update_double(save_header, "CD1_2", 0.0);
798  cpl_propertylist_update_double(save_header, "CD2_1", 0.0);
799  cpl_propertylist_update_double(save_header, "CD2_2", 1.0);
800  cpl_propertylist_update_string(save_header, "CTYPE1", "LINEAR");
801  cpl_propertylist_update_string(save_header, "CTYPE2", "PIXEL");
802  cpl_propertylist_update_int(save_header, "ESO PRO DATANCOM", 1);
803 
804  if (dfs_save_image(frameset, rectified, reduced_lamp_tag, save_header,
805  parlist, recipe, version))
806  fors_wave_calib_exit(NULL);
807 
808  cpl_propertylist_delete(save_header); save_header = NULL;
809  save_header = cpl_propertylist_new();
810 
811  cpl_propertylist_update_double(save_header, "CRPIX2", 1.0);
812  cpl_propertylist_update_double(save_header, "CRVAL2", 1.0);
813  /* cpl_propertylist_update_double(save_header, "CDELT2", 1.0); */
814  cpl_propertylist_update_double(save_header, "CD1_1", 1.0);
815  cpl_propertylist_update_double(save_header, "CD1_2", 0.0);
816  cpl_propertylist_update_double(save_header, "CD2_1", 0.0);
817  cpl_propertylist_update_double(save_header, "CD2_2", 1.0);
818  cpl_propertylist_update_string(save_header, "CTYPE1", "LINEAR");
819  cpl_propertylist_update_string(save_header, "CTYPE2", "PIXEL");
820 
821  if (dfs_save_image(frameset, residual, disp_residuals_tag, save_header,
822  parlist, recipe, version))
823  fors_wave_calib_exit(NULL);
824 
825  cpl_propertylist_delete(save_header); save_header = NULL;
826  cpl_image_delete(residual); residual = NULL;
827 
828  cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
829  cpl_table_set_column_unit(idscoeff, "error", "pixel");
830  cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
831 
832  for (i = 0; i < ny; i++)
833  if (!cpl_table_is_valid(idscoeff, "c0", i))
834  cpl_table_set_invalid(idscoeff, "error", i);
835 
836  if (dfs_save_table(frameset, idscoeff, disp_coeff_tag, NULL,
837  parlist, recipe, version))
838  fors_wave_calib_exit(NULL);
839 
840  mean_rms = mos_distortions_rms(rectified, lines, startwavelength,
841  dispersion, 6, 0);
842 
843  cpl_msg_info(recipe, "Mean residual: %f pixel", mean_rms);
844 
845  mean_rms = cpl_table_get_column_mean(idscoeff, "error");
846 
847  cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
848  mean_rms, mean_rms * dispersion);
849 
850  cpl_table_delete(idscoeff); idscoeff = NULL;
851 
852  /*
853  * Create resolution table
854  */
855 
856  restab = mos_resolution_table(rectified, startwavelength, dispersion,
857  60000, lines);
858 
859  cpl_vector_delete(lines); lines = NULL;
860  cpl_image_delete(rectified); rectified = NULL;
861 
862  if (restab) {
863  cpl_msg_info(recipe, "Mean spectral resolution: %.2f",
864  cpl_table_get_column_mean(restab, "resolution"));
865  cpl_msg_info(recipe, "Mean reference lines FWHM: %.2f +/- %.2f pixel",
866  cpl_table_get_column_mean(restab, "fwhm") / dispersion,
867  cpl_table_get_column_mean(restab, "fwhm_rms") / dispersion);
868 
869  if (dfs_save_table(frameset, restab, spectral_resolution_tag, NULL,
870  parlist, recipe, version))
871  fors_wave_calib_exit(NULL);
872 
873  cpl_table_delete(restab); restab = NULL;
874  }
875  else
876  fors_wave_calib_exit("Cannot compute the spectral resolution table");
877 
878 
879  cpl_msg_indent_less();
880  cpl_msg_info(recipe, "Load spatial map image...");
881  cpl_msg_indent_more();
882 
883  spatial = dfs_load_image(frameset, spatial_map_tag, CPL_TYPE_FLOAT, 0, 0);
884 
885  if (spatial == NULL)
886  fors_wave_calib_exit("Cannot load spatial map");
887 
888  cpl_msg_indent_less();
889  cpl_msg_info(recipe, "Load spectral curvature table...");
890  cpl_msg_indent_more();
891 
892  polytraces = dfs_load_table(frameset, curv_coeff_tag, 1);
893 
894  if (polytraces == NULL)
895  fors_wave_calib_exit("Cannot load spectral curvature table");
896 
897  wavemap = mos_map_wavelengths(spatial, rainbow, slits, polytraces,
898  reference, startwavelength, endwavelength,
899  dispersion);
900 
901  cpl_image_delete(rainbow); rainbow = NULL;
902  cpl_image_delete(spatial); spatial = NULL;
903  cpl_table_delete(slits); slits = NULL;
904  cpl_table_delete(polytraces); polytraces = NULL;
905 
906  if (dfs_save_image(frameset, wavemap, wavelength_map_tag, header,
907  parlist, recipe, version))
908  fors_wave_calib_exit(NULL);
909 
910  cpl_image_delete(wavemap); wavemap = NULL;
911  cpl_propertylist_delete(header); header = NULL;
912 
913  return 0;
914 }
int cpl_plugin_get_info(cpl_pluginlist *list)
Build the list of available plugins, for this module.
Definition: fors_bias.c:62
cpl_image * dfs_load_image(cpl_frameset *frameset, const char *category, cpl_type type, int ext, int calib)
Loading image data of given category.
Definition: fors_dfs.c:860
const char * dfs_get_parameter_string(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe string parameter value.
Definition: fors_dfs.c:602
double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, double wavestart, double dispersion, int radius, int highres)
Estimate the spectral distortion modeling goodness.
Definition: moses.c:11047
cpl_propertylist * dfs_load_header(cpl_frameset *frameset, const char *category, int ext)
Loading header associated to data of given category.
Definition: fors_dfs.c:964
cpl_table * mos_load_slits_fors_mxu(cpl_propertylist *header)
Create slit location table from FITS header of FORS2-MXU data.
Definition: moses.c:14858
cpl_image * mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion)
Remapping of spatially rectified wavelengths to original CCD pixels.
Definition: moses.c:11380
cpl_table * mos_load_slits_fors_mos(cpl_propertylist *header, int *nslits_out_det)
Create slit location table from FITS header of FORS1/2 MOS data.
Definition: moses.c:15098
int dfs_equal_keyword(cpl_frameset *frameset, const char *keyword)
Saving table data of given category.
Definition: fors_dfs.c:1683
int dfs_save_image(cpl_frameset *frameset, const cpl_image *image, const char *category, cpl_propertylist *header, const cpl_parameterlist *parlist, const char *recipename, const char *version)
Saving image data of given category.
Definition: fors_dfs.c:1451
cpl_table * dfs_load_table(cpl_frameset *frameset, const char *category, int ext)
Loading table data of given category.
Definition: fors_dfs.c:916
cpl_image * mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits, cpl_vector *lines, double dispersion, float level, int sradius, int order, double reject, double refwave, double *wavestart, double *waveend, int *nlines, double *error, cpl_table *idscoeff, cpl_image *calibration, cpl_image *residuals, cpl_table *restable, cpl_table *detected_lines)
Derive wavelength calibration from a rectified arc lamp or sky exposure.
Definition: moses.c:8954
int dfs_get_parameter_int(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe integer parameter value.
Definition: fors_dfs.c:407
int dfs_save_table(cpl_frameset *frameset, const cpl_table *table, const char *category, cpl_propertylist *header, const cpl_parameterlist *parlist, const char *recipename, const char *version)
Saving table data of given category.
Definition: fors_dfs.c:1575
Definition: list.c:74
cpl_table * mos_resolution_table(cpl_image *image, double startwave, double dispersion, int saturation, cpl_vector *lines)
Compute mean spectral resolution at a given arc lamp line.
Definition: moses.c:14698
double dfs_get_parameter_double(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe double parameter value.
Definition: fors_dfs.c:504
cpl_image * mos_arc_background(cpl_image *image, int msize, int fsize)
Background determination on emission line spectrum (arc)
Definition: moses.c:3969