FORS Pipeline Reference Manual  4.12.5
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 (message) 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 (message) 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  int nslits;
440  double mean_rms;
441  double *xpos;
442  double mxpos;
443  int narc;
444  int nlines;
445  int rebin;
446  double *line;
447  double *fiterror = NULL;
448  int *fitlines = NULL;
449  int nx, ny;
450  int first_row, last_row;
451  int ylow, yhig;
452  double reference;
453  int i;
454 
455  char *instrume = NULL;
456 
457 
458  cpl_msg_set_indentation(2);
459 
460  /*
461  * Get configuration parameters
462  */
463 
464  cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
465  cpl_msg_indent_more();
466 
467  if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
468  fors_wave_calib_lss_exit("Too many in input: GRISM_TABLE");
469 
470  grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
471 
472  dispersion = dfs_get_parameter_double(parlist,
473  "fors.fors_wave_calib_lss.dispersion", grism_table);
474 
475  if (dispersion <= 0.0)
476  fors_wave_calib_lss_exit("Invalid spectral dispersion value");
477 
478  peakdetection = dfs_get_parameter_double(parlist,
479  "fors.fors_wave_calib_lss.peakdetection", grism_table);
480  if (peakdetection <= 0.0)
481  fors_wave_calib_lss_exit("Invalid peak detection level");
482 
483  wdegree = dfs_get_parameter_int(parlist,
484  "fors.fors_wave_calib_lss.wdegree", grism_table);
485 
486  if (wdegree < 1)
487  fors_wave_calib_lss_exit("Invalid polynomial degree");
488 
489  if (wdegree > 5)
490  fors_wave_calib_lss_exit("Max allowed polynomial degree is 5");
491 
492  wradius = dfs_get_parameter_int(parlist,
493  "fors.fors_wave_calib_lss.wradius", NULL);
494 
495  if (wradius < 0)
496  fors_wave_calib_lss_exit("Invalid search radius");
497 
498  wreject = dfs_get_parameter_double(parlist,
499  "fors.fors_wave_calib_lss.wreject", NULL);
500 
501  if (wreject <= 0.0)
502  fors_wave_calib_lss_exit("Invalid rejection threshold");
503 
504  wmode = dfs_get_parameter_int(parlist,
505  "fors.fors_wave_calib_lss.wmode", NULL);
506 
507  if (wmode < 0 || wmode > 2)
508  fors_wave_calib_lss_exit("Invalid interpolation mode");
509 
510  wcolumn = dfs_get_parameter_string(parlist,
511  "fors.fors_wave_calib_lss.wcolumn", NULL);
512 
513  startwavelength = dfs_get_parameter_double(parlist,
514  "fors.fors_wave_calib_lss.startwavelength", grism_table);
515  if (startwavelength > 1.0)
516  if (startwavelength < 3000.0 || startwavelength > 13000.0)
517  fors_wave_calib_lss_exit("Invalid wavelength");
518 
519  endwavelength = dfs_get_parameter_double(parlist,
520  "fors.fors_wave_calib_lss.endwavelength", grism_table);
521  if (endwavelength > 1.0) {
522  if (endwavelength < 3000.0 || endwavelength > 13000.0)
523  fors_wave_calib_lss_exit("Invalid wavelength");
524  if (startwavelength < 1.0)
525  fors_wave_calib_lss_exit("Invalid wavelength interval");
526  }
527 
528  if (startwavelength > 1.0)
529  if (endwavelength - startwavelength <= 0.0)
530  fors_wave_calib_lss_exit("Invalid wavelength interval");
531 
532  cpl_table_delete(grism_table); grism_table = NULL;
533 
534  if (cpl_error_get_code())
535  fors_wave_calib_lss_exit("Failure reading the configuration "
536  "parameters");
537 
538 
539  cpl_msg_indent_less();
540  cpl_msg_info(recipe, "Check input set-of-frames:");
541  cpl_msg_indent_more();
542 
543  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
544  fors_wave_calib_lss_exit("Missing required input: MASTER_LINECAT");
545 
546  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
547  fors_wave_calib_lss_exit("Too many in input: MASTER_LINECAT");
548 
549  mxu = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MXU");
550  mos = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MOS");
551  lss = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_LSS");
552 
553  narc = mxu + mos + lss;
554 
555  if (narc == 0) {
556  fors_wave_calib_lss_exit("Missing input long-slit arc lamp frame");
557  }
558  if (narc > 1) {
559  cpl_msg_error(recipe, "Too many input arc lamp frames (%d > 1)", narc);
560  fors_wave_calib_lss_exit(NULL);
561  }
562 
563  if (mxu) {
564  arc_tag = "LAMP_UNBIAS_MXU";
565  slit_location_tag = "SLIT_LOCATION_MXU";
566  reduced_lamp_tag = "REDUCED_LAMP_MXU";
567  disp_residuals_tag = "DISP_RESIDUALS_MXU";
568  disp_coeff_tag = "DISP_COEFF_MXU";
569  wavelength_map_tag = "WAVELENGTH_MAP_MXU";
570  spectral_resolution_tag = "SPECTRAL_RESOLUTION_MXU";
571  }
572  else if (mos) {
573  arc_tag = "LAMP_UNBIAS_MOS";
574  slit_location_tag = "SLIT_LOCATION_MOS";
575  reduced_lamp_tag = "REDUCED_LAMP_MOS";
576  disp_residuals_tag = "DISP_RESIDUALS_MOS";
577  disp_coeff_tag = "DISP_COEFF_MOS";
578  wavelength_map_tag = "WAVELENGTH_MAP_MOS";
579  spectral_resolution_tag = "SPECTRAL_RESOLUTION_MOS";
580  }
581  else if (lss) {
582  arc_tag = "LAMP_UNBIAS_LSS";
583  slit_location_tag = "SLIT_LOCATION_LSS";
584  reduced_lamp_tag = "REDUCED_LAMP_LSS";
585  disp_residuals_tag = "DISP_RESIDUALS_LSS";
586  disp_coeff_tag = "DISP_COEFF_LSS";
587  wavelength_map_tag = "WAVELENGTH_MAP_LSS";
588  spectral_resolution_tag = "SPECTRAL_RESOLUTION_LSS";
589  }
590 
591 
592  if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
593  fors_wave_calib_lss_exit("Input frames are not from the same grism");
594 
595  if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
596  fors_wave_calib_lss_exit("Input frames are not from the same filter");
597 
598  if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
599  fors_wave_calib_lss_exit("Input frames are not from the same chip");
600 
601 
602  /*
603  * Get the reference wavelength and the rebin factor along the
604  * dispersion direction from the arc lamp exposure
605  */
606 
607  header = dfs_load_header(frameset, arc_tag, 0);
608 
609  if (header == NULL)
610  fors_wave_calib_lss_exit("Cannot load arc lamp header");
611 
612  instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
613  if (instrume == NULL)
614  fors_wave_calib_lss_exit("Missing keyword INSTRUME in arc lamp header");
615 
616  if (instrume[4] == '1')
617  snprintf(version, 80, "%s/%s", "fors1", VERSION);
618  if (instrume[4] == '2')
619  snprintf(version, 80, "%s/%s", "fors2", VERSION);
620 
621  reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
622 
623  if (cpl_error_get_code() != CPL_ERROR_NONE)
624  fors_wave_calib_lss_exit("Missing keyword ESO INS GRIS1 WLEN "
625  "in arc lamp frame header");
626 
627  if (reference < 3000.0) /* Perhaps in nanometers... */
628  reference *= 10;
629 
630  if (reference < 3000.0 || reference > 13000.0) {
631  cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
632  "keyword ESO INS GRIS1 WLEN in arc lamp frame header",
633  reference);
634  fors_wave_calib_lss_exit(NULL);
635  }
636 
637  cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
638 
639  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
640 
641  if (cpl_error_get_code() != CPL_ERROR_NONE)
642  fors_wave_calib_lss_exit("Missing keyword ESO DET WIN1 BINX "
643  "in arc lamp frame header");
644 
645  if (rebin != 1) {
646  dispersion *= rebin;
647  cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
648  "working dispersion used is %f A/pixel", rebin,
649  dispersion);
650  }
651 
652 
653  if (mos || mxu) {
654 
655  int nslits_out_det = 0;
656 
657  /*
658  * Check if all slits have the same X offset. If not, this is the
659  * wrong recipe...
660  */
661 
662  if (mos)
663  maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
664  else
665  maskslits = mos_load_slits_fors_mxu(header);
666 
667  treat_as_lss = fors_mos_is_lss_like(maskslits, nslits_out_det);
668 
669  cpl_table_delete(maskslits); maskslits = NULL;
670 
671  if (!treat_as_lss)
672  fors_wave_calib_lss_exit("Input data are not long-slit data");
673  }
674 
675 
676  cpl_msg_indent_less();
677  cpl_msg_info(recipe, "Load arc lamp exposure...");
678  cpl_msg_indent_more();
679 
680  spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0);
681 
682  if (spectra == NULL)
683  fors_wave_calib_lss_exit("Cannot load arc lamp exposure");
684 
685 
686  cpl_msg_indent_less();
687  cpl_msg_info(recipe, "Load input line catalog...");
688  cpl_msg_indent_more();
689 
690  wavelengths = dfs_load_table(frameset, "MASTER_LINECAT", 1);
691 
692  if (wavelengths == NULL)
693  fors_wave_calib_lss_exit("Cannot load line catalog");
694 
695 
696  /*
697  * Cast the wavelengths into a (double precision) CPL vector
698  */
699 
700  nlines = cpl_table_get_nrow(wavelengths);
701 
702  if (nlines == 0)
703  fors_wave_calib_lss_exit("Empty input line catalog");
704 
705  if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
706  cpl_msg_error(recipe, "Missing column %s in input line catalog table",
707  wcolumn);
708  fors_wave_calib_lss_exit(NULL);
709  }
710 
711  line = cpl_malloc(nlines * sizeof(double));
712 
713  for (i = 0; i < nlines; i++)
714  line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
715 
716  cpl_table_delete(wavelengths); wavelengths = NULL;
717 
718  lines = cpl_vector_wrap(nlines, line);
719 
720 
721  cpl_msg_indent_less();
722  cpl_msg_info(recipe, "Perform wavelength calibration...");
723  cpl_msg_indent_more();
724 
725  nx = cpl_image_get_size_x(spectra);
726  ny = cpl_image_get_size_y(spectra);
727 
728  wavemap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
729  idscoeff_all = cpl_table_new(ny);
730 
731  if (mos_saturation_process(spectra))
732  fors_wave_calib_lss_exit("Cannot process saturation");
733 
734  if (mos_subtract_background(spectra))
735  fors_wave_calib_lss_exit("Cannot subtract the background");
736 
737  rectified = mos_wavelength_calibration_raw(spectra, lines, dispersion,
738  peakdetection, wradius,
739  wdegree, wreject, reference,
740  &startwavelength,
741  &endwavelength, NULL,
742  NULL, idscoeff_all, wavemap,
743  NULL, NULL, NULL, NULL);
744 
745  if (rectified == NULL)
746  fors_wave_calib_lss_exit("Wavelength calibration failure.");
747 
748  cpl_image_delete(rectified); rectified = NULL;
749 
750  first_row = 0;
751  while (!cpl_table_is_valid(idscoeff_all, "c0", first_row))
752  first_row++;
753 
754  last_row = ny - 1;
755  while (!cpl_table_is_valid(idscoeff_all, "c0", last_row))
756  last_row--;
757 
758  ylow = first_row + 1;
759  yhig = last_row + 1;
760 
761  dummy = cpl_image_extract(spectra, 1, ylow, nx, yhig);
762  cpl_image_delete(spectra); spectra = dummy;
763 
764  ny = cpl_image_get_size_y(spectra);
765 
766  residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
767 
768  fiterror = cpl_calloc(ny, sizeof(double));
769  fitlines = cpl_calloc(ny, sizeof(int));
770  idscoeff = cpl_table_new(ny);
771 
772  if (mos_saturation_process(spectra))
773  fors_wave_calib_lss_exit("Cannot process saturation");
774 
775  if (mos_subtract_background(spectra))
776  fors_wave_calib_lss_exit("Cannot subtract the background");
777 
778  rectified = mos_wavelength_calibration_raw(spectra, lines, dispersion,
779  peakdetection, wradius,
780  wdegree, wreject, reference,
781  &startwavelength,
782  &endwavelength, fitlines,
783  fiterror, idscoeff, NULL,
784  residual, NULL, NULL, NULL);
785 
786  if (rectified == NULL)
787  fors_wave_calib_lss_exit("Wavelength calibration failure.");
788 
789  /*
790  * A dummy slit locations table
791  */
792 
793  slits = cpl_table_new(1);
794  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
795  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
796  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
797  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
798  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
799  cpl_table_new_column(slits, "position", CPL_TYPE_INT);
800  cpl_table_new_column(slits, "length", CPL_TYPE_INT);
801  cpl_table_set_column_unit(slits, "xtop", "pixel");
802  cpl_table_set_column_unit(slits, "ytop", "pixel");
803  cpl_table_set_column_unit(slits, "xbottom", "pixel");
804  cpl_table_set_column_unit(slits, "ybottom", "pixel");
805  cpl_table_set_column_unit(slits, "position", "pixel");
806  cpl_table_set_column_unit(slits, "length", "pixel");
807  cpl_table_set_int(slits, "slit_id", 0, 0);
808  cpl_table_set_double(slits, "xtop", 0, 0);
809  cpl_table_set_double(slits, "ytop", 0, last_row);
810  cpl_table_set_double(slits, "xbottom", 0, 0);
811  cpl_table_set_double(slits, "ybottom", 0, first_row);
812  cpl_table_set_int(slits, "position", 0, 0);
813  cpl_table_set_int(slits, "length", 0, ny);
814 
815  if (dfs_save_table(frameset, slits, slit_location_tag, NULL,
816  parlist, recipe, version))
817  fors_wave_calib_lss_exit(NULL);
818 
819  cpl_table_delete(slits); slits = NULL;
820 
821  if (wmode) {
822  cpl_image_delete(rectified); rectified = NULL;
823  cpl_image_delete(wavemap); wavemap = NULL;
824 
825  /*
826  * Wavemap is intentionally NULL in the next two calls
827  */
828 
829  mos_interpolate_wavecalib(idscoeff, wavemap, wmode, 2);
830  mos_interpolate_wavecalib(idscoeff_all, wavemap, wmode, 2);
831 
832  wavemap = mos_map_idscoeff(idscoeff_all, nx, reference,
833  startwavelength, endwavelength);
834  rectified = mos_wavelength_calibration(spectra, reference,
835  startwavelength,
836  endwavelength, dispersion,
837  idscoeff, 0);
838  }
839 
840  cpl_table_delete(idscoeff_all); idscoeff_all = NULL;
841 
842  cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
843  cpl_table_set_column_unit(idscoeff, "error", "pixel");
844  cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
845 
846  for (i = 0; i < ny; i++)
847  if (!cpl_table_is_valid(idscoeff, "c0", i))
848  cpl_table_set_invalid(idscoeff, "error", i);
849 
850  cpl_msg_info(recipe, "Valid solutions found: %d out of %d rows",
851  ny - cpl_table_count_invalid(idscoeff, "c0"), ny);
852 
853  cpl_image_delete(spectra); spectra = NULL;
854 
855  mean_rms = mos_distortions_rms(rectified, lines, startwavelength,
856  dispersion, 6, 0);
857 
858  cpl_msg_info(recipe, "Mean residual: %f pixel", mean_rms);
859 
860  mean_rms = cpl_table_get_column_mean(idscoeff, "error");
861 
862  cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
863  mean_rms, mean_rms * dispersion);
864 
865  restab = mos_resolution_table(rectified, startwavelength, dispersion,
866  60000, lines);
867 
868  if (restab) {
869  cpl_msg_info(recipe, "Mean spectral resolution: %.2f",
870  cpl_table_get_column_mean(restab, "resolution"));
871  cpl_msg_info(recipe,
872  "Mean reference lines FWHM: %.2f +/- %.2f pixel",
873  cpl_table_get_column_mean(restab, "fwhm") / dispersion,
874  cpl_table_get_column_mean(restab, "fwhm_rms") / dispersion);
875 
876  if (dfs_save_table(frameset, restab, spectral_resolution_tag,
877  NULL, parlist, recipe, version))
878  fors_wave_calib_lss_exit(NULL);
879 
880  cpl_table_delete(restab); restab = NULL;
881 
882  }
883  else
884  fors_wave_calib_lss_exit("Cannot compute the spectral "
885  "resolution table");
886 
887  cpl_vector_delete(lines); lines = NULL;
888 
889 
890  /*
891  * Save rectified arc lamp spectrum to disk
892  */
893 
894  save_header = cpl_propertylist_new();
895  cpl_propertylist_update_double(save_header, "CRPIX1", 1.0);
896  cpl_propertylist_update_double(save_header, "CRPIX2", 1.0);
897  cpl_propertylist_update_double(save_header, "CRVAL1",
898  startwavelength + dispersion/2);
899  cpl_propertylist_update_double(save_header, "CRVAL2", 1.0);
900  /* cpl_propertylist_update_double(save_header, "CDELT1", dispersion);
901  cpl_propertylist_update_double(save_header, "CDELT2", 1.0); */
902  cpl_propertylist_update_double(save_header, "CD1_1", dispersion);
903  cpl_propertylist_update_double(save_header, "CD1_2", 0.0);
904  cpl_propertylist_update_double(save_header, "CD2_1", 0.0);
905  cpl_propertylist_update_double(save_header, "CD2_2", 1.0);
906  cpl_propertylist_update_string(save_header, "CTYPE1", "LINEAR");
907  cpl_propertylist_update_string(save_header, "CTYPE2", "PIXEL");
908  cpl_propertylist_update_int(save_header, "ESO PRO DATANCOM", 1);
909 
910  if (dfs_save_image(frameset, rectified, reduced_lamp_tag, save_header,
911  parlist, recipe, version))
912  fors_wave_calib_lss_exit(NULL);
913 
914  cpl_image_delete(rectified); rectified = NULL;
915  cpl_propertylist_delete(save_header); save_header = NULL;
916 
917  if (dfs_save_table(frameset, idscoeff, disp_coeff_tag, NULL,
918  parlist, recipe, version))
919  fors_wave_calib_lss_exit(NULL);
920 
921  cpl_table_delete(idscoeff); idscoeff = NULL;
922 
923  if (dfs_save_image(frameset, wavemap, wavelength_map_tag, header,
924  parlist, recipe, version))
925  fors_wave_calib_lss_exit(NULL);
926 
927  cpl_image_delete(wavemap); wavemap = NULL;
928  cpl_propertylist_delete(header); header = NULL;
929  header = cpl_propertylist_new();
930 
931  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
932  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
933  /* cpl_propertylist_update_double(header, "CDELT2", 1.0); */
934  cpl_propertylist_update_double(header, "CD1_1", 1.0);
935  cpl_propertylist_update_double(header, "CD1_2", 0.0);
936  cpl_propertylist_update_double(header, "CD2_1", 0.0);
937  cpl_propertylist_update_double(header, "CD2_2", 1.0);
938  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
939  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
940 
941  if (dfs_save_image(frameset, residual, disp_residuals_tag, header,
942  parlist, recipe, version))
943  fors_wave_calib_lss_exit(NULL);
944 
945  cpl_image_delete(residual); residual = NULL;
946  cpl_propertylist_delete(header); header = NULL;
947 
948  return 0;
949 }
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:845
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:587
cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, cpl_image *wavemap, int mode, int degree)
Interpolate LSS wavelength calibration.
Definition: moses.c:3061
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:5221
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:10754
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:951
cpl_table * mos_load_slits_fors_mxu(cpl_propertylist *header)
Create slit location table from FITS header of FORS2-MXU data.
Definition: moses.c:14561
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:9412
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:14801
cpl_error_code mos_subtract_background(cpl_image *image)
Subtract the background.
Definition: moses.c:16079
int dfs_equal_keyword(cpl_frameset *frameset, const char *keyword)
Saving table data of given category.
Definition: fors_dfs.c:1685
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:10976
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:1447
cpl_table * dfs_load_table(cpl_frameset *frameset, const char *category, int ext)
Loading table data of given category.
Definition: fors_dfs.c:901
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:392
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:1574
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:14401
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:489
cpl_error_code mos_saturation_process(cpl_image *image)
Process saturation.
Definition: moses.c:16013