FORS Pipeline Reference Manual  5.0.9
fors_wave_calib_lss.c
1 /* $Id: fors_wave_calib_lss.c,v 1.10 2013-09-09 12:25:33 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-09-09 12:25:33 $
24  * $Revision: 1.10 $
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_lss_create(cpl_plugin *);
38 static int fors_wave_calib_lss_exec(cpl_plugin *);
39 static int fors_wave_calib_lss_destroy(cpl_plugin *);
40 static int fors_wave_calib_lss(cpl_parameterlist *, cpl_frameset *);
41 
42 static char fors_wave_calib_lss_description[] =
43 "This recipe is used to wavelength calibrate one long slit spectrum, i.e.,\n"
44 "a FORS spectral obtained either in LSS mode or in MOS/MXU mode with all\n"
45 "slits at the same offset. A pattern-matching algorithm is applied as in\n"
46 "recipe fors_detect_spectra. For more details on this data reduction\n"
47 "strategy please refer to the FORS Pipeline User's Manual.\n"
48 "\n"
49 "Note that specifying an input GRISM_TABLE will set some of the recipe\n"
50 "configuration parameters to default values valid for a particular grism.\n"
51 "\n"
52 "In the table below the LSS acronym can be alternatively read as MOS or\n"
53 "MXU.\n\n"
54 "Input files:\n\n"
55 " DO category: Type: Explanation: Required:\n"
56 " LAMP_UNBIAS_LSS Calib Arc lamp exposure Y\n"
57 " MASTER_LINECAT Calib Line catalog Y\n"
58 " GRISM_TABLE Calib Grism table .\n\n"
59 "Output files:\n\n"
60 " DO category: Data type: Explanation:\n"
61 " REDUCED_LAMP_LSS FITS image Calibrated arc lamp exposure\n"
62 " DISP_COEFF_LSS FITS table Inverse dispersion coefficients\n"
63 " DISP_RESIDUALS_LSS FITS image Image of modeling residuals\n"
64 " WAVELENGTH_MAP_LSS FITS image Wavelengths mapped on CCD\n"
65 " SLIT_LOCATION_LSS FITS image Background subtracted arc frame\n"
66 " SPECTRAL_RESOLUTION_LSS FITS table Spectral resolution table\n\n";
67 
68 #define fors_wave_calib_lss_exit(message) \
69 { \
70 if ((const char *)message != NULL) cpl_msg_error(recipe, message); \
71 cpl_image_delete(spectra); \
72 cpl_image_delete(residual); \
73 cpl_image_delete(rectified); \
74 cpl_image_delete(wavemap); \
75 cpl_table_delete(grism_table); \
76 cpl_table_delete(wavelengths); \
77 cpl_table_delete(maskslits); \
78 cpl_table_delete(idscoeff); \
79 cpl_table_delete(idscoeff_all); \
80 cpl_table_delete(restab); \
81 cpl_table_delete(slits); \
82 cpl_vector_delete(lines); \
83 cpl_propertylist_delete(header); \
84 cpl_propertylist_delete(save_header); \
85 cpl_msg_indent_less(); \
86 return -1; \
87 }
88 
89 #define fors_wave_calib_lss_exit_memcheck(message) \
90 { \
91 if ((const char *)message != NULL) cpl_msg_info(recipe, message); \
92 printf("free spectra (%p)\n", spectra); \
93 cpl_image_delete(spectra); \
94 printf("free residual (%p)\n", residual); \
95 cpl_image_delete(residual); \
96 printf("free rectified (%p)\n", rectified); \
97 cpl_image_delete(rectified); \
98 printf("free wavemap (%p)\n", wavemap); \
99 cpl_image_delete(wavemap); \
100 printf("free grism_table (%p)\n", grism_table); \
101 cpl_table_delete(grism_table); \
102 printf("free wavelengths (%p)\n", wavelengths); \
103 cpl_table_delete(wavelengths); \
104 printf("free maskslits (%p)\n", maskslits); \
105 cpl_table_delete(maskslits); \
106 printf("free idscoeff (%p)\n", idscoeff); \
107 cpl_table_delete(idscoeff); \
108 printf("free idscoeff_all (%p)\n", idscoeff_all); \
109 cpl_table_delete(idscoeff_all); \
110 printf("free restab (%p)\n", restab); \
111 cpl_table_delete(restab); \
112 printf("free slits (%p)\n", slits); \
113 cpl_table_delete(slits); \
114 printf("free lines (%p)\n", lines); \
115 cpl_vector_delete(lines); \
116 printf("free header (%p)\n", header); \
117 cpl_propertylist_delete(header); \
118 printf("free save_header (%p)\n", save_header); \
119 cpl_propertylist_delete(save_header); \
120 cpl_msg_indent_less(); \
121 return 0; \
122 }
123 
124 
136 int cpl_plugin_get_info(cpl_pluginlist *list)
137 {
138  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
139  cpl_plugin *plugin = &recipe->interface;
140 
141  cpl_plugin_init(plugin,
142  CPL_PLUGIN_API,
143  FORS_BINARY_VERSION,
144  CPL_PLUGIN_TYPE_RECIPE,
145  "fors_wave_calib_lss",
146  "Derive dispersion relation from long-slit arc lamp frame",
147  fors_wave_calib_lss_description,
148  "Carlo Izzo",
149  PACKAGE_BUGREPORT,
150  "This file is currently part of the FORS Instrument Pipeline\n"
151  "Copyright (C) 2002-2010 European Southern Observatory\n\n"
152  "This program is free software; you can redistribute it and/or modify\n"
153  "it under the terms of the GNU General Public License as published by\n"
154  "the Free Software Foundation; either version 2 of the License, or\n"
155  "(at your option) any later version.\n\n"
156  "This program is distributed in the hope that it will be useful,\n"
157  "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
158  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
159  "GNU General Public License for more details.\n\n"
160  "You should have received a copy of the GNU General Public License\n"
161  "along with this program; if not, write to the Free Software Foundation,\n"
162  "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n",
163  fors_wave_calib_lss_create,
164  fors_wave_calib_lss_exec,
165  fors_wave_calib_lss_destroy);
166 
167  cpl_pluginlist_append(list, plugin);
168 
169  return 0;
170 }
171 
172 
183 static int fors_wave_calib_lss_create(cpl_plugin *plugin)
184 {
185  cpl_recipe *recipe;
186  cpl_parameter *p;
187 
188  /*
189  * Check that the plugin is part of a valid recipe
190  */
191 
192  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
193  recipe = (cpl_recipe *)plugin;
194  else
195  return -1;
196 
197  /*
198  * Create the (empty) parameters list in the cpl_recipe object
199  */
200 
201  recipe->parameters = cpl_parameterlist_new();
202 
203  /*
204  * Dispersion
205  */
206 
207  p = cpl_parameter_new_value("fors.fors_wave_calib_lss.dispersion",
208  CPL_TYPE_DOUBLE,
209  "Expected spectral dispersion (Angstrom/pixel)",
210  "fors.fors_wave_calib_lss",
211  0.0);
212  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
213  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
214  cpl_parameterlist_append(recipe->parameters, p);
215 
216  /*
217  * Peak detection level
218  */
219 
220  p = cpl_parameter_new_value("fors.fors_wave_calib_lss.peakdetection",
221  CPL_TYPE_DOUBLE,
222  "Initial peak detection threshold (ADU)",
223  "fors.fors_wave_calib_lss",
224  0.0);
225  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "peakdetection");
226  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
227  cpl_parameterlist_append(recipe->parameters, p);
228 
229  /*
230  * Degree of wavelength calibration polynomial
231  */
232 
233  p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wdegree",
234  CPL_TYPE_INT,
235  "Degree of wavelength calibration polynomial",
236  "fors.fors_wave_calib_lss",
237  0);
238  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wdegree");
239  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
240  cpl_parameterlist_append(recipe->parameters, p);
241 
242  /*
243  * Reference lines search radius
244  */
245 
246  p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wradius",
247  CPL_TYPE_INT,
248  "Search radius if iterating pattern-matching "
249  "with first-guess method",
250  "fors.fors_wave_calib_lss",
251  4);
252  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wradius");
253  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
254  cpl_parameterlist_append(recipe->parameters, p);
255 
256  /*
257  * Rejection threshold in dispersion relation polynomial fitting
258  */
259 
260  p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wreject",
261  CPL_TYPE_DOUBLE,
262  "Rejection threshold in dispersion "
263  "relation fit (pixel)",
264  "fors.fors_wave_calib_lss",
265  0.7);
266  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wreject");
267  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
268  cpl_parameterlist_append(recipe->parameters, p);
269 
270  /*
271  * Line catalog table column containing the reference wavelengths
272  */
273 
274  p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wcolumn",
275  CPL_TYPE_STRING,
276  "Name of line catalog table column "
277  "with wavelengths",
278  "fors.fors_wave_calib_lss",
279  "WLEN");
280  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
281  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
282  cpl_parameterlist_append(recipe->parameters, p);
283 
284  /*
285  * Start wavelength for spectral extraction
286  */
287 
288  p = cpl_parameter_new_value("fors.fors_wave_calib_lss.startwavelength",
289  CPL_TYPE_DOUBLE,
290  "Start wavelength in spectral extraction",
291  "fors.fors_wave_calib_lss",
292  0.0);
293  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
294  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
295  cpl_parameterlist_append(recipe->parameters, p);
296 
297  /*
298  * End wavelength for spectral extraction
299  */
300 
301  p = cpl_parameter_new_value("fors.fors_wave_calib_lss.endwavelength",
302  CPL_TYPE_DOUBLE,
303  "End wavelength in spectral extraction",
304  "fors.fors_wave_calib_lss",
305  0.0);
306  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
307  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
308  cpl_parameterlist_append(recipe->parameters, p);
309 
310  /*
311  * Wavelength solution interpolation
312  */
313 
314  p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wmode",
315  CPL_TYPE_INT,
316  "Interpolation mode of wavelength solution "
317  "(0 = no interpolation, 1 = fill gaps, "
318  "2 = global model)",
319  "fors.fors_wave_calib_lss",
320  2);
321  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wmode");
322  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
323  cpl_parameterlist_append(recipe->parameters, p);
324 
325  return 0;
326 }
327 
328 
337 static int fors_wave_calib_lss_exec(cpl_plugin *plugin)
338 {
339  cpl_recipe *recipe;
340 
341  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
342  recipe = (cpl_recipe *)plugin;
343  else
344  return -1;
345 
346  return fors_wave_calib_lss(recipe->parameters, recipe->frames);
347 }
348 
349 
358 static int fors_wave_calib_lss_destroy(cpl_plugin *plugin)
359 {
360  cpl_recipe *recipe;
361 
362  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
363  recipe = (cpl_recipe *)plugin;
364  else
365  return -1;
366 
367  cpl_parameterlist_delete(recipe->parameters);
368 
369  return 0;
370 }
371 
372 
382 static int fors_wave_calib_lss(cpl_parameterlist *parlist,
383  cpl_frameset *frameset)
384 {
385 
386  const char *recipe = "fors_wave_calib_lss";
387 
388 
389  /*
390  * Input parameters
391  */
392 
393  double dispersion;
394  double peakdetection;
395  int wdegree;
396  int wradius;
397  double wreject;
398  int wmode;
399  const char *wcolumn;
400  double startwavelength;
401  double endwavelength;
402 
403  /*
404  * CPL objects
405  */
406 
407  cpl_image *spectra = NULL;
408  cpl_image *rectified = NULL;
409  cpl_image *wavemap = NULL;
410  cpl_image *residual = NULL;
411  cpl_image *dummy = NULL;
412  cpl_table *grism_table = NULL;
413  cpl_table *wavelengths = NULL;
414  cpl_table *slits = NULL;
415  cpl_table *idscoeff = NULL;
416  cpl_table *idscoeff_all = NULL;
417  cpl_table *maskslits = NULL;
418  cpl_table *restab = NULL;
419  cpl_vector *lines = NULL;
420  cpl_propertylist *header = NULL;
421  cpl_propertylist *save_header = NULL;
422 
423  /*
424  * Auxiliary variables
425  */
426 
427  char version[80];
428  const char *arc_tag;
429  const char *reduced_lamp_tag;
430  const char *wavelength_map_tag;
431  const char *disp_residuals_tag;
432  const char *disp_coeff_tag;
433  const char *slit_location_tag;
434  const char *spectral_resolution_tag;
435  int mxu;
436  int mos;
437  int lss;
438  int treat_as_lss = 0;
439  double mean_rms;
440  int narc;
441  int nlines;
442  int rebin;
443  double *line;
444  double *fiterror = NULL;
445  int *fitlines = NULL;
446  int nx, ny;
447  int first_row, last_row;
448  int ylow, yhig;
449  double reference;
450  int i;
451 
452  char *instrume = NULL;
453 
454 
455  cpl_msg_set_indentation(2);
456 
457  /*
458  * Get configuration parameters
459  */
460 
461  cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
462  cpl_msg_indent_more();
463 
464  if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
465  fors_wave_calib_lss_exit("Too many in input: GRISM_TABLE");
466 
467  grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
468 
469  dispersion = dfs_get_parameter_double(parlist,
470  "fors.fors_wave_calib_lss.dispersion", grism_table);
471 
472  if (dispersion <= 0.0)
473  fors_wave_calib_lss_exit("Invalid spectral dispersion value");
474 
475  peakdetection = dfs_get_parameter_double(parlist,
476  "fors.fors_wave_calib_lss.peakdetection", grism_table);
477  if (peakdetection <= 0.0)
478  fors_wave_calib_lss_exit("Invalid peak detection level");
479 
480  wdegree = dfs_get_parameter_int(parlist,
481  "fors.fors_wave_calib_lss.wdegree", grism_table);
482 
483  if (wdegree < 1)
484  fors_wave_calib_lss_exit("Invalid polynomial degree");
485 
486  if (wdegree > 5)
487  fors_wave_calib_lss_exit("Max allowed polynomial degree is 5");
488 
489  wradius = dfs_get_parameter_int(parlist,
490  "fors.fors_wave_calib_lss.wradius", NULL);
491 
492  if (wradius < 0)
493  fors_wave_calib_lss_exit("Invalid search radius");
494 
495  wreject = dfs_get_parameter_double(parlist,
496  "fors.fors_wave_calib_lss.wreject", NULL);
497 
498  if (wreject <= 0.0)
499  fors_wave_calib_lss_exit("Invalid rejection threshold");
500 
501  wmode = dfs_get_parameter_int(parlist,
502  "fors.fors_wave_calib_lss.wmode", NULL);
503 
504  if (wmode < 0 || wmode > 2)
505  fors_wave_calib_lss_exit("Invalid interpolation mode");
506 
507  wcolumn = dfs_get_parameter_string(parlist,
508  "fors.fors_wave_calib_lss.wcolumn", NULL);
509 
510  startwavelength = dfs_get_parameter_double(parlist,
511  "fors.fors_wave_calib_lss.startwavelength", grism_table);
512  if (startwavelength > 1.0)
513  if (startwavelength < 3000.0 || startwavelength > 13000.0)
514  fors_wave_calib_lss_exit("Invalid wavelength");
515 
516  endwavelength = dfs_get_parameter_double(parlist,
517  "fors.fors_wave_calib_lss.endwavelength", grism_table);
518  if (endwavelength > 1.0) {
519  if (endwavelength < 3000.0 || endwavelength > 13000.0)
520  fors_wave_calib_lss_exit("Invalid wavelength");
521  if (startwavelength < 1.0)
522  fors_wave_calib_lss_exit("Invalid wavelength interval");
523  }
524 
525  if (startwavelength > 1.0)
526  if (endwavelength - startwavelength <= 0.0)
527  fors_wave_calib_lss_exit("Invalid wavelength interval");
528 
529  cpl_table_delete(grism_table); grism_table = NULL;
530 
531  if (cpl_error_get_code())
532  fors_wave_calib_lss_exit("Failure reading the configuration "
533  "parameters");
534 
535 
536  cpl_msg_indent_less();
537  cpl_msg_info(recipe, "Check input set-of-frames:");
538  cpl_msg_indent_more();
539 
540  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
541  fors_wave_calib_lss_exit("Missing required input: MASTER_LINECAT");
542 
543  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
544  fors_wave_calib_lss_exit("Too many in input: MASTER_LINECAT");
545 
546  mxu = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MXU");
547  mos = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MOS");
548  lss = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_LSS");
549 
550  narc = mxu + mos + lss;
551 
552  if (narc == 0) {
553  fors_wave_calib_lss_exit("Missing input long-slit arc lamp frame");
554  }
555  if (narc > 1) {
556  cpl_msg_error(recipe, "Too many input arc lamp frames (%d > 1)", narc);
557  fors_wave_calib_lss_exit(NULL);
558  }
559 
560  if (mxu) {
561  arc_tag = "LAMP_UNBIAS_MXU";
562  slit_location_tag = "SLIT_LOCATION_MXU";
563  reduced_lamp_tag = "REDUCED_LAMP_MXU";
564  disp_residuals_tag = "DISP_RESIDUALS_MXU";
565  disp_coeff_tag = "DISP_COEFF_MXU";
566  wavelength_map_tag = "WAVELENGTH_MAP_MXU";
567  spectral_resolution_tag = "SPECTRAL_RESOLUTION_MXU";
568  }
569  else if (mos) {
570  arc_tag = "LAMP_UNBIAS_MOS";
571  slit_location_tag = "SLIT_LOCATION_MOS";
572  reduced_lamp_tag = "REDUCED_LAMP_MOS";
573  disp_residuals_tag = "DISP_RESIDUALS_MOS";
574  disp_coeff_tag = "DISP_COEFF_MOS";
575  wavelength_map_tag = "WAVELENGTH_MAP_MOS";
576  spectral_resolution_tag = "SPECTRAL_RESOLUTION_MOS";
577  }
578  else if (lss) {
579  arc_tag = "LAMP_UNBIAS_LSS";
580  slit_location_tag = "SLIT_LOCATION_LSS";
581  reduced_lamp_tag = "REDUCED_LAMP_LSS";
582  disp_residuals_tag = "DISP_RESIDUALS_LSS";
583  disp_coeff_tag = "DISP_COEFF_LSS";
584  wavelength_map_tag = "WAVELENGTH_MAP_LSS";
585  spectral_resolution_tag = "SPECTRAL_RESOLUTION_LSS";
586  }
587 
588 
589  if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
590  cpl_msg_warning(cpl_func,"Input frames are not from the same grism");
591 
592  if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
593  cpl_msg_warning(cpl_func,"Input frames are not from the same filter");
594 
595  if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
596  cpl_msg_warning(cpl_func,"Input frames are not from the same chip");
597 
598 
599  /*
600  * Get the reference wavelength and the rebin factor along the
601  * dispersion direction from the arc lamp exposure
602  */
603 
604  header = dfs_load_header(frameset, arc_tag, 0);
605 
606  if (header == NULL)
607  fors_wave_calib_lss_exit("Cannot load arc lamp header");
608 
609  instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
610  if (instrume == NULL)
611  fors_wave_calib_lss_exit("Missing keyword INSTRUME in arc lamp header");
612 
613  if (instrume[4] == '1')
614  snprintf(version, 80, "%s/%s", "fors1", VERSION);
615  if (instrume[4] == '2')
616  snprintf(version, 80, "%s/%s", "fors2", VERSION);
617 
618  reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
619 
620  if (cpl_error_get_code() != CPL_ERROR_NONE)
621  fors_wave_calib_lss_exit("Missing keyword ESO INS GRIS1 WLEN "
622  "in arc lamp frame header");
623 
624  if (reference < 3000.0) /* Perhaps in nanometers... */
625  reference *= 10;
626 
627  if (reference < 3000.0 || reference > 13000.0) {
628  cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
629  "keyword ESO INS GRIS1 WLEN in arc lamp frame header",
630  reference);
631  fors_wave_calib_lss_exit(NULL);
632  }
633 
634  cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
635 
636  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
637 
638  if (cpl_error_get_code() != CPL_ERROR_NONE)
639  fors_wave_calib_lss_exit("Missing keyword ESO DET WIN1 BINX "
640  "in arc lamp frame header");
641 
642  if (rebin != 1) {
643  dispersion *= rebin;
644  cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
645  "working dispersion used is %f A/pixel", rebin,
646  dispersion);
647  }
648 
649 
650  if (mos || mxu) {
651 
652  int nslits_out_det = 0;
653 
654  /*
655  * Check if all slits have the same X offset. If not, this is the
656  * wrong recipe...
657  */
658 
659  if (mos)
660  maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
661  else
662  maskslits = mos_load_slits_fors_mxu(header);
663 
664  treat_as_lss = fors_mos_is_lss_like(maskslits, nslits_out_det);
665 
666  cpl_table_delete(maskslits); maskslits = NULL;
667 
668  if (!treat_as_lss)
669  fors_wave_calib_lss_exit("Input data are not long-slit data");
670  }
671 
672 
673  cpl_msg_indent_less();
674  cpl_msg_info(recipe, "Load arc lamp exposure...");
675  cpl_msg_indent_more();
676 
677  spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0);
678 
679  if (spectra == NULL)
680  fors_wave_calib_lss_exit("Cannot load arc lamp exposure");
681 
682 
683  cpl_msg_indent_less();
684  cpl_msg_info(recipe, "Load input line catalog...");
685  cpl_msg_indent_more();
686 
687  wavelengths = dfs_load_table(frameset, "MASTER_LINECAT", 1);
688 
689  if (wavelengths == NULL)
690  fors_wave_calib_lss_exit("Cannot load line catalog");
691 
692 
693  /*
694  * Cast the wavelengths into a (double precision) CPL vector
695  */
696 
697  nlines = cpl_table_get_nrow(wavelengths);
698 
699  if (nlines == 0)
700  fors_wave_calib_lss_exit("Empty input line catalog");
701 
702  if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
703  cpl_msg_error(recipe, "Missing column %s in input line catalog table",
704  wcolumn);
705  fors_wave_calib_lss_exit(NULL);
706  }
707 
708  line = cpl_malloc(nlines * sizeof(double));
709 
710  for (i = 0; i < nlines; i++)
711  line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
712 
713  cpl_table_delete(wavelengths); wavelengths = NULL;
714 
715  lines = cpl_vector_wrap(nlines, line);
716 
717 
718  cpl_msg_indent_less();
719  cpl_msg_info(recipe, "Perform wavelength calibration...");
720  cpl_msg_indent_more();
721 
722  nx = cpl_image_get_size_x(spectra);
723  ny = cpl_image_get_size_y(spectra);
724 
725  wavemap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
726  idscoeff_all = cpl_table_new(ny);
727 
728  if (mos_saturation_process(spectra))
729  fors_wave_calib_lss_exit("Cannot process saturation");
730 
731  if (mos_subtract_background(spectra))
732  fors_wave_calib_lss_exit("Cannot subtract the background");
733 
734  rectified = mos_wavelength_calibration_raw(spectra, lines, dispersion,
735  peakdetection, wradius,
736  wdegree, wreject, reference,
737  &startwavelength,
738  &endwavelength, NULL,
739  NULL, idscoeff_all, wavemap,
740  NULL, NULL, NULL, NULL);
741 
742  if (rectified == NULL)
743  fors_wave_calib_lss_exit("Wavelength calibration failure.");
744 
745  cpl_image_delete(rectified); rectified = NULL;
746 
747  first_row = 0;
748  while (!cpl_table_is_valid(idscoeff_all, "c0", first_row))
749  first_row++;
750 
751  last_row = ny - 1;
752  while (!cpl_table_is_valid(idscoeff_all, "c0", last_row))
753  last_row--;
754 
755  ylow = first_row + 1;
756  yhig = last_row + 1;
757 
758  dummy = cpl_image_extract(spectra, 1, ylow, nx, yhig);
759  cpl_image_delete(spectra); spectra = dummy;
760 
761  ny = cpl_image_get_size_y(spectra);
762 
763  residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
764 
765  fiterror = cpl_calloc(ny, sizeof(double));
766  fitlines = cpl_calloc(ny, sizeof(int));
767  idscoeff = cpl_table_new(ny);
768 
769  if (mos_saturation_process(spectra))
770  fors_wave_calib_lss_exit("Cannot process saturation");
771 
772  if (mos_subtract_background(spectra))
773  fors_wave_calib_lss_exit("Cannot subtract the background");
774 
775  rectified = mos_wavelength_calibration_raw(spectra, lines, dispersion,
776  peakdetection, wradius,
777  wdegree, wreject, reference,
778  &startwavelength,
779  &endwavelength, fitlines,
780  fiterror, idscoeff, NULL,
781  residual, NULL, NULL, NULL);
782 
783  if (rectified == NULL)
784  fors_wave_calib_lss_exit("Wavelength calibration failure.");
785 
786  /*
787  * A dummy slit locations table
788  */
789 
790  slits = cpl_table_new(1);
791  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
792  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
793  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
794  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
795  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
796  cpl_table_new_column(slits, "position", CPL_TYPE_INT);
797  cpl_table_new_column(slits, "length", CPL_TYPE_INT);
798  cpl_table_set_column_unit(slits, "xtop", "pixel");
799  cpl_table_set_column_unit(slits, "ytop", "pixel");
800  cpl_table_set_column_unit(slits, "xbottom", "pixel");
801  cpl_table_set_column_unit(slits, "ybottom", "pixel");
802  cpl_table_set_column_unit(slits, "position", "pixel");
803  cpl_table_set_column_unit(slits, "length", "pixel");
804  cpl_table_set_int(slits, "slit_id", 0, 0);
805  cpl_table_set_double(slits, "xtop", 0, 0);
806  cpl_table_set_double(slits, "ytop", 0, last_row);
807  cpl_table_set_double(slits, "xbottom", 0, 0);
808  cpl_table_set_double(slits, "ybottom", 0, first_row);
809  cpl_table_set_int(slits, "position", 0, 0);
810  cpl_table_set_int(slits, "length", 0, ny);
811 
812  if (dfs_save_table(frameset, slits, slit_location_tag, NULL,
813  parlist, recipe, version))
814  fors_wave_calib_lss_exit(NULL);
815 
816  cpl_table_delete(slits); slits = NULL;
817 
818  if (wmode) {
819  cpl_image_delete(rectified); rectified = NULL;
820  cpl_image_delete(wavemap); wavemap = NULL;
821 
822  /*
823  * Wavemap is intentionally NULL in the next two calls
824  */
825 
826  mos_interpolate_wavecalib(idscoeff, wavemap, wmode, 2);
827  mos_interpolate_wavecalib(idscoeff_all, wavemap, wmode, 2);
828 
829  wavemap = mos_map_idscoeff(idscoeff_all, nx, reference,
830  startwavelength, endwavelength);
831  rectified = mos_wavelength_calibration(spectra, reference,
832  startwavelength,
833  endwavelength, dispersion,
834  idscoeff, 0);
835  }
836 
837  cpl_table_delete(idscoeff_all); idscoeff_all = NULL;
838 
839  cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
840  cpl_table_set_column_unit(idscoeff, "error", "pixel");
841  cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
842 
843  for (i = 0; i < ny; i++)
844  if (!cpl_table_is_valid(idscoeff, "c0", i))
845  cpl_table_set_invalid(idscoeff, "error", i);
846 
847  cpl_msg_info(recipe, "Valid solutions found: %d out of %d rows",
848  ny - cpl_table_count_invalid(idscoeff, "c0"), ny);
849 
850  cpl_image_delete(spectra); spectra = NULL;
851 
852  mean_rms = mos_distortions_rms(rectified, lines, startwavelength,
853  dispersion, 6, 0);
854 
855  cpl_msg_info(recipe, "Mean residual: %f pixel", mean_rms);
856 
857  mean_rms = cpl_table_get_column_mean(idscoeff, "error");
858 
859  cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
860  mean_rms, mean_rms * dispersion);
861 
862  restab = mos_resolution_table(rectified, startwavelength, dispersion,
863  60000, lines);
864 
865  if (restab) {
866  cpl_msg_info(recipe, "Mean spectral resolution: %.2f",
867  cpl_table_get_column_mean(restab, "resolution"));
868  cpl_msg_info(recipe,
869  "Mean reference lines FWHM: %.2f +/- %.2f pixel",
870  cpl_table_get_column_mean(restab, "fwhm") / dispersion,
871  cpl_table_get_column_mean(restab, "fwhm_rms") / dispersion);
872 
873  if (dfs_save_table(frameset, restab, spectral_resolution_tag,
874  NULL, parlist, recipe, version))
875  fors_wave_calib_lss_exit(NULL);
876 
877  cpl_table_delete(restab); restab = NULL;
878 
879  }
880  else
881  fors_wave_calib_lss_exit("Cannot compute the spectral "
882  "resolution table");
883 
884  cpl_vector_delete(lines); lines = NULL;
885 
886 
887  /*
888  * Save rectified arc lamp spectrum to disk
889  */
890 
891  save_header = cpl_propertylist_new();
892  cpl_propertylist_update_double(save_header, "CRPIX1", 1.0);
893  cpl_propertylist_update_double(save_header, "CRPIX2", 1.0);
894  cpl_propertylist_update_double(save_header, "CRVAL1",
895  startwavelength + dispersion/2);
896  cpl_propertylist_update_double(save_header, "CRVAL2", 1.0);
897  /* cpl_propertylist_update_double(save_header, "CDELT1", dispersion);
898  cpl_propertylist_update_double(save_header, "CDELT2", 1.0); */
899  cpl_propertylist_update_double(save_header, "CD1_1", dispersion);
900  cpl_propertylist_update_double(save_header, "CD1_2", 0.0);
901  cpl_propertylist_update_double(save_header, "CD2_1", 0.0);
902  cpl_propertylist_update_double(save_header, "CD2_2", 1.0);
903  cpl_propertylist_update_string(save_header, "CTYPE1", "LINEAR");
904  cpl_propertylist_update_string(save_header, "CTYPE2", "PIXEL");
905  cpl_propertylist_update_int(save_header, "ESO PRO DATANCOM", 1);
906 
907  if (dfs_save_image(frameset, rectified, reduced_lamp_tag, save_header,
908  parlist, recipe, version))
909  fors_wave_calib_lss_exit(NULL);
910 
911  cpl_image_delete(rectified); rectified = NULL;
912  cpl_propertylist_delete(save_header); save_header = NULL;
913 
914  if (dfs_save_table(frameset, idscoeff, disp_coeff_tag, NULL,
915  parlist, recipe, version))
916  fors_wave_calib_lss_exit(NULL);
917 
918  cpl_table_delete(idscoeff); idscoeff = NULL;
919 
920  if (dfs_save_image(frameset, wavemap, wavelength_map_tag, header,
921  parlist, recipe, version))
922  fors_wave_calib_lss_exit(NULL);
923 
924  cpl_image_delete(wavemap); wavemap = NULL;
925  cpl_propertylist_delete(header); header = NULL;
926  header = cpl_propertylist_new();
927 
928  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
929  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
930  /* cpl_propertylist_update_double(header, "CDELT2", 1.0); */
931  cpl_propertylist_update_double(header, "CD1_1", 1.0);
932  cpl_propertylist_update_double(header, "CD1_2", 0.0);
933  cpl_propertylist_update_double(header, "CD2_1", 0.0);
934  cpl_propertylist_update_double(header, "CD2_2", 1.0);
935  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
936  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
937 
938  if (dfs_save_image(frameset, residual, disp_residuals_tag, header,
939  parlist, recipe, version))
940  fors_wave_calib_lss_exit(NULL);
941 
942  cpl_image_delete(residual); residual = NULL;
943  cpl_propertylist_delete(header); header = NULL;
944 
945  return 0;
946 }
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
cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, cpl_image *wavemap, int mode, int degree)
Interpolate LSS wavelength calibration.
Definition: moses.c:3104
cpl_image * mos_wavelength_calibration_raw(const cpl_image *image, 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_mask *refmask, cpl_table *detected_lines)
Derive wavelength calibration from a raw arc lamp or sky exposure.
Definition: moses.c:5492
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_wavelength_calibration(cpl_image *image, double refwave, double firstLambda, double lastLambda, double dispersion, cpl_table *idscoeff, int flux)
Remap at constant wavelength step an image of rectified scientific spectra.
Definition: moses.c:9697
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
cpl_error_code mos_subtract_background(cpl_image *image)
Subtract the background.
Definition: moses.c:16378
int dfs_equal_keyword(cpl_frameset *frameset, const char *keyword)
Saving table data of given category.
Definition: fors_dfs.c:1683
cpl_image * mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference, double blue, double red)
Create a wavelengths map from an IDS coefficients table.
Definition: moses.c:11269
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
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_error_code mos_saturation_process(cpl_image *image)
Process saturation.
Definition: moses.c:16310