FORS Pipeline Reference Manual  5.0.9
fors_detect_spectra.c
1 /* $Id: fors_detect_spectra.c,v 1.11 2013-10-09 15:59:38 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-10-09 15:59:38 $
24  * $Revision: 1.11 $
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_detect_spectra_create(cpl_plugin *);
38 static int fors_detect_spectra_exec(cpl_plugin *);
39 static int fors_detect_spectra_destroy(cpl_plugin *);
40 static int fors_detect_spectra(cpl_parameterlist *, cpl_frameset *);
41 
42 static char fors_detect_spectra_description[] =
43 "This recipe is used to detect and locate MOS/MXU slit spectra on the CCD,\n"
44 "applying a pattern-matching algorithm. The input spectral exposure must\n"
45 "contain spectra with the dispersion direction approximately horizontal,\n"
46 "with blue on the left and red on the right. Use recipe fors_wave_calib_lss\n"
47 "for LSS data, or for MOS/MXU data where all slits have the same offset.\n"
48 "\n"
49 "The rows of the input spectral exposure are processed separately, one\n"
50 "by one. First, the background continuum is removed. Second, a list of\n"
51 "positions of reference lines candidates is created. Only peaks above a\n"
52 "given threshold (specified by the parameter --peakdetection) are selected.\n"
53 "Third, the pattern-matching task selects from the found peaks the ones\n"
54 "corresponding to the reference lines, listed in the input line catalog,\n"
55 "associating them to the appropriate wavelengths. The ensuing polynomial\n"
56 "fit is used to locate the central wavelength of the applied grism along\n"
57 "each image row. The contributions from all rows form an image of the\n"
58 "location of all spectra, that can be used as a starting point for the\n"
59 "proper modeling of the optical and spectral distortions. For more details\n"
60 "on this reduction strategy please refer to the FORS Pipeline User's Manual.\n"
61 "\n"
62 "Note that specifying an input GRISM_TABLE will set some of the recipe\n"
63 "configuration parameters to default values valid for a particular grism.\n"
64 "Again, see the pipeline manual for more details.\n"
65 "\n"
66 "In the table below the MXU acronym can be alternatively read as MOS.\n\n"
67 "Input files:\n\n"
68 " DO category: Type: Explanation: Required:\n"
69 " LAMP_UNBIAS_MXU Calib Bias subtracted arc Y\n"
70 " MASTER_LINECAT Calib Line catalog Y\n"
71 " GRISM_TABLE Calib Grism table .\n\n"
72 "Output files:\n\n"
73 " DO category: Data type: Explanation:\n"
74 " SLIT_MAP_MXU FITS image Map of central wavelength on CCD\n"
75 " SLIT_LOCATION_DETECT_MXU FITS table Slits positions on CCD\n"
76 " SPECTRA_DETECTION_MXU FITS image Check of preliminary detection\n\n";
77 
78 #define fors_detect_spectra_exit(message) \
79 { \
80 if ((const char *)message != NULL) cpl_msg_error(recipe, message); \
81 cpl_image_delete(spectra); \
82 cpl_image_delete(checkwave); \
83 cpl_image_delete(refimage); \
84 cpl_mask_delete(refmask); \
85 cpl_table_delete(grism_table); \
86 cpl_table_delete(wavelengths); \
87 cpl_table_delete(maskslits); \
88 cpl_table_delete(slits); \
89 cpl_vector_delete(lines); \
90 cpl_propertylist_delete(header); \
91 cpl_msg_indent_less(); \
92 return -1; \
93 }
94 
95 #define fors_detect_spectra_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 checkwave (%p)\n", checkwave); \
101 cpl_image_delete(checkwave); \
102 printf("free refimage (%p)\n", refimage); \
103 cpl_image_delete(refimage); \
104 printf("free refmask (%p)\n", refmask); \
105 cpl_mask_delete(refmask); \
106 printf("free grism_table (%p)\n", grism_table); \
107 cpl_table_delete(grism_table); \
108 printf("free wavelengths (%p)\n", wavelengths); \
109 cpl_table_delete(wavelengths); \
110 printf("free maskslits (%p)\n", maskslits); \
111 cpl_table_delete(maskslits); \
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 cpl_msg_indent_less(); \
119 return 0; \
120 }
121 
122 
134 int cpl_plugin_get_info(cpl_pluginlist *list)
135 {
136  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
137  cpl_plugin *plugin = &recipe->interface;
138 
139  cpl_plugin_init(plugin,
140  CPL_PLUGIN_API,
141  FORS_BINARY_VERSION,
142  CPL_PLUGIN_TYPE_RECIPE,
143  "fors_detect_spectra",
144  "Detect MOS/MXU spectra on CCD",
145  fors_detect_spectra_description,
146  "Carlo Izzo",
147  PACKAGE_BUGREPORT,
148  "This file is currently part of the FORS Instrument Pipeline\n"
149  "Copyright (C) 2002-2010 European Southern Observatory\n\n"
150  "This program is free software; you can redistribute it and/or modify\n"
151  "it under the terms of the GNU General Public License as published by\n"
152  "the Free Software Foundation; either version 2 of the License, or\n"
153  "(at your option) any later version.\n\n"
154  "This program is distributed in the hope that it will be useful,\n"
155  "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
156  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
157  "GNU General Public License for more details.\n\n"
158  "You should have received a copy of the GNU General Public License\n"
159  "along with this program; if not, write to the Free Software Foundation,\n"
160  "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n",
161  fors_detect_spectra_create,
162  fors_detect_spectra_exec,
163  fors_detect_spectra_destroy);
164 
165  cpl_pluginlist_append(list, plugin);
166 
167  return 0;
168 }
169 
170 
181 static int fors_detect_spectra_create(cpl_plugin *plugin)
182 {
183  cpl_recipe *recipe;
184  cpl_parameter *p;
185 
186  /*
187  * Check that the plugin is part of a valid recipe
188  */
189 
190  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
191  recipe = (cpl_recipe *)plugin;
192  else
193  return -1;
194 
195  /*
196  * Create the (empty) parameters list in the cpl_recipe object
197  */
198 
199  recipe->parameters = cpl_parameterlist_new();
200 
201  /*
202  * Dispersion
203  */
204 
205  p = cpl_parameter_new_value("fors.fors_detect_spectra.dispersion",
206  CPL_TYPE_DOUBLE,
207  "Expected spectral dispersion (Angstrom/pixel)",
208  "fors.fors_detect_spectra",
209  0.0);
210  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
211  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
212  cpl_parameterlist_append(recipe->parameters, p);
213 
214  /*
215  * Peak detection level
216  */
217 
218  p = cpl_parameter_new_value("fors.fors_detect_spectra.peakdetection",
219  CPL_TYPE_DOUBLE,
220  "Initial peak detection threshold (ADU)",
221  "fors.fors_detect_spectra",
222  0.0);
223  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "peakdetection");
224  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
225  cpl_parameterlist_append(recipe->parameters, p);
226 
227  /*
228  * Degree of wavelength calibration polynomial
229  */
230 
231  p = cpl_parameter_new_value("fors.fors_detect_spectra.wdegree",
232  CPL_TYPE_INT,
233  "Degree of wavelength calibration polynomial",
234  "fors.fors_detect_spectra",
235  0);
236  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wdegree");
237  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
238  cpl_parameterlist_append(recipe->parameters, p);
239 
240  /*
241  * Reference lines search radius
242  */
243 
244  p = cpl_parameter_new_value("fors.fors_detect_spectra.wradius",
245  CPL_TYPE_INT,
246  "Search radius if iterating pattern-matching "
247  "with first-guess method",
248  "fors.fors_detect_spectra",
249  4);
250  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wradius");
251  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
252  cpl_parameterlist_append(recipe->parameters, p);
253 
254  /*
255  * Rejection threshold in dispersion relation polynomial fitting
256  */
257 
258  p = cpl_parameter_new_value("fors.fors_detect_spectra.wreject",
259  CPL_TYPE_DOUBLE,
260  "Rejection threshold in dispersion "
261  "relation fit (pixel)",
262  "fors.fors_detect_spectra",
263  0.7);
264  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wreject");
265  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
266  cpl_parameterlist_append(recipe->parameters, p);
267 
268  /*
269  * Line catalog table column containing the reference wavelengths
270  */
271 
272  p = cpl_parameter_new_value("fors.fors_detect_spectra.wcolumn",
273  CPL_TYPE_STRING,
274  "Name of line catalog table column "
275  "with wavelengths",
276  "fors.fors_detect_spectra",
277  "WLEN");
278  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
279  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
280  cpl_parameterlist_append(recipe->parameters, p);
281 
282  /*
283  * Start wavelength for spectral extraction
284  */
285 
286  p = cpl_parameter_new_value("fors.fors_detect_spectra.startwavelength",
287  CPL_TYPE_DOUBLE,
288  "Start wavelength in spectral extraction",
289  "fors.fors_detect_spectra",
290  0.0);
291  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
292  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
293  cpl_parameterlist_append(recipe->parameters, p);
294 
295  /*
296  * End wavelength for spectral extraction
297  */
298 
299  p = cpl_parameter_new_value("fors.fors_detect_spectra.endwavelength",
300  CPL_TYPE_DOUBLE,
301  "End wavelength in spectral extraction",
302  "fors.fors_detect_spectra",
303  0.0);
304  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
305  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
306  cpl_parameterlist_append(recipe->parameters, p);
307 
308  /*
309  * Try slit identification
310  */
311 
312  p = cpl_parameter_new_value("fors.fors_detect_spectra.slit_ident",
313  CPL_TYPE_BOOL,
314  "Attempt slit identification for MOS or MXU",
315  "fors.fors_detect_spectra",
316  TRUE);
317  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "slit_ident");
318  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
319  cpl_parameterlist_append(recipe->parameters, p);
320 
321 
322  return 0;
323 }
324 
325 
334 static int fors_detect_spectra_exec(cpl_plugin *plugin)
335 {
336  cpl_recipe *recipe;
337 
338  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
339  recipe = (cpl_recipe *)plugin;
340  else
341  return -1;
342 
343  return fors_detect_spectra(recipe->parameters, recipe->frames);
344 }
345 
346 
355 static int fors_detect_spectra_destroy(cpl_plugin *plugin)
356 {
357  cpl_recipe *recipe;
358 
359  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
360  recipe = (cpl_recipe *)plugin;
361  else
362  return -1;
363 
364  cpl_parameterlist_delete(recipe->parameters);
365 
366  return 0;
367 }
368 
369 
379 static int fors_detect_spectra(cpl_parameterlist *parlist,
380  cpl_frameset *frameset)
381 {
382 
383  const char *recipe = "fors_detect_spectra";
384 
385 
386  /*
387  * Input parameters
388  */
389 
390  double dispersion;
391  double peakdetection;
392  int wdegree;
393  int wradius;
394  double wreject;
395  const char *wcolumn;
396  double startwavelength;
397  double endwavelength;
398  int slit_ident;
399 
400  /*
401  * CPL objects
402  */
403 
404  cpl_image *spectra = NULL;
405  cpl_image *checkwave = NULL;
406  cpl_image *refimage = NULL;
407  cpl_mask *refmask = NULL;
408  cpl_table *grism_table = NULL;
409  cpl_table *wavelengths = NULL;
410  cpl_table *slits = NULL;
411  cpl_table *positions = NULL;
412  cpl_table *maskslits = NULL;
413  cpl_vector *lines = NULL;
414  cpl_propertylist *header = NULL;
415 
416  /*
417  * Auxiliary variables
418  */
419 
420  char version[80];
421  const char *arc_tag;
422  const char *spectra_detection_tag;
423  const char *slit_map_tag;
424  const char *slit_location_tag;
425  int lamp_mxu;
426  int lamp_mos;
427  int lamp_lss;
428  int mos;
429  int treat_as_lss = 0;
430  int nslits;
431  double mxpos;
432  int narc;
433  int nlines;
434  int rebin;
435  double *line;
436  int nx, ny;
437  double reference;
438  int i;
439  int nslits_out_det = 0;
440 
441  char *instrume = NULL;
442 
443 
444  cpl_msg_set_indentation(2);
445 
446  /*
447  * Get configuration parameters
448  */
449 
450  cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
451  cpl_msg_indent_more();
452 
453  if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
454  fors_detect_spectra_exit("Too many in input: GRISM_TABLE");
455 
456  grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
457 
458  dispersion = dfs_get_parameter_double(parlist,
459  "fors.fors_detect_spectra.dispersion", grism_table);
460 
461  if (dispersion <= 0.0)
462  fors_detect_spectra_exit("Invalid spectral dispersion value");
463 
464  peakdetection = dfs_get_parameter_double(parlist,
465  "fors.fors_detect_spectra.peakdetection", grism_table);
466  if (peakdetection <= 0.0)
467  fors_detect_spectra_exit("Invalid peak detection level");
468 
469  wdegree = dfs_get_parameter_int(parlist,
470  "fors.fors_detect_spectra.wdegree", grism_table);
471 
472  if (wdegree < 1)
473  fors_detect_spectra_exit("Invalid polynomial degree");
474 
475  if (wdegree > 5)
476  fors_detect_spectra_exit("Max allowed polynomial degree is 5");
477 
478  wradius = dfs_get_parameter_int(parlist,
479  "fors.fors_detect_spectra.wradius", NULL);
480 
481  if (wradius < 0)
482  fors_detect_spectra_exit("Invalid search radius");
483 
484  wreject = dfs_get_parameter_double(parlist,
485  "fors.fors_detect_spectra.wreject", NULL);
486 
487  if (wreject <= 0.0)
488  fors_detect_spectra_exit("Invalid rejection threshold");
489 
490  wcolumn = dfs_get_parameter_string(parlist,
491  "fors.fors_detect_spectra.wcolumn", NULL);
492 
493  startwavelength = dfs_get_parameter_double(parlist,
494  "fors.fors_detect_spectra.startwavelength", grism_table);
495  if (startwavelength > 1.0)
496  if (startwavelength < 3000.0 || startwavelength > 13000.0)
497  fors_detect_spectra_exit("Invalid wavelength");
498 
499  endwavelength = dfs_get_parameter_double(parlist,
500  "fors.fors_detect_spectra.endwavelength", grism_table);
501  if (endwavelength > 1.0) {
502  if (endwavelength < 3000.0 || endwavelength > 13000.0)
503  fors_detect_spectra_exit("Invalid wavelength");
504  if (startwavelength < 1.0)
505  fors_detect_spectra_exit("Invalid wavelength interval");
506  }
507 
508  if (startwavelength > 1.0)
509  if (endwavelength - startwavelength <= 0.0)
510  fors_detect_spectra_exit("Invalid wavelength interval");
511 
512  slit_ident = dfs_get_parameter_bool(parlist,
513  "fors.fors_detect_spectra.slit_ident", NULL);
514 
515  cpl_table_delete(grism_table); grism_table = NULL;
516 
517  if (cpl_error_get_code())
518  fors_detect_spectra_exit("Failure reading the "
519  "configuration parameters");
520 
521 
522  cpl_msg_indent_less();
523  cpl_msg_info(recipe, "Check input set-of-frames:");
524  cpl_msg_indent_more();
525 
526  narc = lamp_mxu = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MXU");
527  narc += lamp_mos = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MOS");
528  narc += lamp_lss = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_LSS");
529 
530  if (narc == 0) {
531  fors_detect_spectra_exit("Missing input arc lamp frame");
532  }
533  if (narc > 1) {
534  cpl_msg_error(recipe, "Too many input arc lamp frames (%d > 1)", narc);
535  fors_detect_spectra_exit(NULL);
536  }
537 
538  mos = 0;
539 
540  if (lamp_mxu) {
541  arc_tag = "LAMP_UNBIAS_MXU";
542  spectra_detection_tag = "SPECTRA_DETECTION_MXU";
543  slit_map_tag = "SLIT_MAP_MXU";
544  slit_location_tag = "SLIT_LOCATION_DETECT_MXU";
545  }
546  else if (lamp_mos) {
547  mos = 1;
548  arc_tag = "LAMP_UNBIAS_MOS";
549  spectra_detection_tag = "SPECTRA_DETECTION_MOS";
550  slit_map_tag = "SLIT_MAP_MOS";
551  slit_location_tag = "SLIT_LOCATION_DETECT_MOS";
552  }
553  else if (lamp_lss) {
554  fors_detect_spectra_exit("Use recipe fors_wave_calib_lss "
555  "for LSS data reduction");
556  }
557 
558 
559  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
560  fors_detect_spectra_exit("Missing required input: MASTER_LINECAT");
561 
562  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
563  fors_detect_spectra_exit("Too many in input: MASTER_LINECAT");
564 
565  if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
566  cpl_msg_warning(cpl_func,"Input frames are not from the same grism");
567 
568  if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
569  cpl_msg_warning(cpl_func,"Input frames are not from the same filter");
570 
571  if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
572  cpl_msg_warning(cpl_func,"Input frames are not from the same chip");
573 
574 
575  /*
576  * Get the reference wavelength and the rebin factor along the
577  * dispersion direction from the arc lamp exposure
578  */
579 
580  header = dfs_load_header(frameset, arc_tag, 0);
581 
582  if (header == NULL)
583  fors_detect_spectra_exit("Cannot load arc lamp header");
584 
585  instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
586  if (instrume == NULL)
587  fors_detect_spectra_exit("Missing keyword INSTRUME in arc lamp header");
588 
589  if (instrume[4] == '1')
590  snprintf(version, 80, "%s/%s", "fors1", VERSION);
591  if (instrume[4] == '2')
592  snprintf(version, 80, "%s/%s", "fors2", VERSION);
593 
594  reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
595 
596  if (cpl_error_get_code() != CPL_ERROR_NONE)
597  fors_detect_spectra_exit("Missing keyword ESO INS GRIS1 WLEN "
598  "in arc lamp frame header");
599 
600  if (reference < 3000.0) /* Perhaps in nanometers... */
601  reference *= 10;
602 
603  if (reference < 3000.0 || reference > 13000.0) {
604  cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
605  "keyword ESO INS GRIS1 WLEN in arc lamp frame header",
606  reference);
607  fors_detect_spectra_exit(NULL);
608  }
609 
610  cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
611 
612  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
613 
614  if (cpl_error_get_code() != CPL_ERROR_NONE)
615  fors_detect_spectra_exit("Missing keyword ESO DET WIN1 BINX "
616  "in arc lamp frame header");
617 
618  if (rebin != 1) {
619  dispersion *= rebin;
620  cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
621  "working dispersion used is %f A/pixel", rebin,
622  dispersion);
623  }
624 
625 
626  cpl_msg_info(recipe, "Produce mask slit position table...");
627 
628  if (mos)
629  maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
630  else
631  maskslits = mos_load_slits_fors_mxu(header);
632 
633  cpl_propertylist_delete(header); header = NULL;
634 
635 
636  /*
637  * Check if all slits have the same X offset: in such case,
638  * treat the observation as a long-slit one!
639  */
640 
641  treat_as_lss = fors_mos_is_lss_like(maskslits, nslits_out_det);
642 
643  if (treat_as_lss) {
644  cpl_msg_error(recipe, "All slits have the same offset: %.2f mm\n"
645  "The LSS data reduction strategy must be applied: "
646  "please use recipe fors_wave_calib_lss", mxpos);
647  fors_detect_spectra_exit(NULL);
648  }
649 
650  if (slit_ident == 0) {
651  cpl_table_delete(maskslits); maskslits = NULL;
652  }
653 
654 
655  cpl_msg_indent_less();
656  cpl_msg_info(recipe, "Load arc lamp exposure...");
657  cpl_msg_indent_more();
658 
659  spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0);
660 
661  if (spectra == NULL)
662  fors_detect_spectra_exit("Cannot load arc lamp exposure");
663 
664 
665  cpl_msg_indent_less();
666  cpl_msg_info(recipe, "Load input line catalog...");
667  cpl_msg_indent_more();
668 
669  wavelengths = dfs_load_table(frameset, "MASTER_LINECAT", 1);
670 
671  if (wavelengths == NULL)
672  fors_detect_spectra_exit("Cannot load line catalog");
673 
674 
675  /*
676  * Cast the wavelengths into a (double precision) CPL vector
677  */
678 
679  nlines = cpl_table_get_nrow(wavelengths);
680 
681  if (nlines == 0)
682  fors_detect_spectra_exit("Empty input line catalog");
683 
684  if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
685  cpl_msg_error(recipe, "Missing column %s in input line catalog table",
686  wcolumn);
687  fors_detect_spectra_exit(NULL);
688  }
689 
690  line = cpl_malloc(nlines * sizeof(double));
691 
692  for (i = 0; i < nlines; i++)
693  line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
694 
695  cpl_table_delete(wavelengths); wavelengths = NULL;
696 
697  lines = cpl_vector_wrap(nlines, line);
698 
699 
700  cpl_msg_indent_less();
701  cpl_msg_info(recipe, "Detecting spectra on CCD...");
702  cpl_msg_indent_more();
703 
704  nx = cpl_image_get_size_x(spectra);
705  ny = cpl_image_get_size_y(spectra);
706 
707  refmask = cpl_mask_new(nx, ny);
708 
709  if (mos_saturation_process(spectra))
710  fors_detect_spectra_exit("Cannot process saturation");
711 
712  if (mos_subtract_background(spectra))
713  fors_detect_spectra_exit("Cannot subtract the background");
714 
715  checkwave = mos_wavelength_calibration_raw(spectra, lines, dispersion,
716  peakdetection, wradius,
717  wdegree, wreject, reference,
718  &startwavelength, &endwavelength,
719  NULL, NULL, NULL, NULL, NULL,
720  NULL, refmask, NULL);
721 
722  cpl_image_delete(spectra); spectra = NULL;
723  cpl_vector_delete(lines); lines = NULL;
724 
725  if (checkwave == NULL)
726  fors_detect_spectra_exit("Wavelength calibration failure.");
727 
728 
729  /*
730  * Save check image to disk
731  */
732 
733  header = cpl_propertylist_new();
734  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
735  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
736  cpl_propertylist_update_double(header, "CRVAL1",
737  startwavelength + dispersion/2);
738  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
739  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
740  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
741  cpl_propertylist_update_double(header, "CD1_1", dispersion);
742  cpl_propertylist_update_double(header, "CD1_2", 0.0);
743  cpl_propertylist_update_double(header, "CD2_1", 0.0);
744  cpl_propertylist_update_double(header, "CD2_2", 1.0);
745  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
746  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
747 
748  if (dfs_save_image(frameset, checkwave, spectra_detection_tag, header,
749  parlist, recipe, version))
750  fors_detect_spectra_exit(NULL);
751 
752  cpl_image_delete(checkwave); checkwave = NULL;
753  cpl_propertylist_delete(header); header = NULL;
754 
755 
756  cpl_msg_info(recipe, "Locate slits at reference wavelength on CCD...");
757  slits = mos_locate_spectra(refmask);
758 
759  if (!slits) {
760  cpl_msg_error(cpl_error_get_where(), "%s", cpl_error_get_message());
761  fors_detect_spectra_exit("No slits could be detected!");
762  }
763 
764  refimage = cpl_image_new_from_mask(refmask);
765  cpl_mask_delete(refmask); refmask = NULL;
766 
767  header = dfs_load_header(frameset, arc_tag, 0);
768  if (dfs_save_image(frameset, refimage, slit_map_tag, header,
769  parlist, recipe, version))
770  fors_detect_spectra_exit(NULL);
771  cpl_propertylist_delete(header); header = NULL;
772 
773  cpl_image_delete(refimage); refimage = NULL;
774 
775  if (slit_ident) {
776 
777  /*
778  * Attempt slit identification: this recipe may continue even
779  * in case of failed identification (i.e., the position table is
780  * not produced, but an error is not set). In case of failure,
781  * the spectra would be still extracted, even if they would not
782  * be associated to slits on the mask.
783  *
784  * The reason for making the slit identification an user option
785  * (via the parameter slit_ident) is to offer the possibility
786  * to avoid identifications that are only apparently successful,
787  * as it would happen in the case of an incorrect slit description
788  * in the data header.
789  */
790 
791  cpl_msg_indent_less();
792  cpl_msg_info(recipe, "Attempt slit identification (optional)...");
793  cpl_msg_indent_more();
794 
795  positions = mos_identify_slits(slits, maskslits, NULL);
796  cpl_table_delete(maskslits); maskslits = NULL;
797 
798  if (positions) {
799  cpl_table_delete(slits);
800  slits = positions;
801 
802  /*
803  * Eliminate slits which are _entirely_ outside the CCD
804  */
805 
806  cpl_table_and_selected_double(slits,
807  "ybottom", CPL_GREATER_THAN, ny-1);
808  cpl_table_or_selected_double(slits,
809  "ytop", CPL_LESS_THAN, 0);
810  cpl_table_erase_selected(slits);
811 
812  nslits = cpl_table_get_nrow(slits);
813 
814  if (nslits == 0)
815  fors_detect_spectra_exit("No slits found on the CCD");
816 
817  cpl_msg_info(recipe, "%d slits are entirely or partially "
818  "contained in CCD", nslits);
819 
820  }
821  else {
822  slit_ident = 0;
823  cpl_msg_info(recipe, "Global distortion model cannot be computed");
824  if (cpl_error_get_code() != CPL_ERROR_NONE) {
825  fors_detect_spectra_exit(NULL);
826  }
827  }
828  }
829 
830  if (dfs_save_table(frameset, slits, slit_location_tag, NULL,
831  parlist, recipe, version))
832  fors_detect_spectra_exit(NULL);
833 
834  cpl_table_delete(slits); slits = NULL;
835 
836  return 0;
837 }
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_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
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_table * mos_identify_slits(cpl_table *slits, cpl_table *maskslits, cpl_table *global)
Identify slits listed in a slit location table.
Definition: moses.c:6459
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_get_parameter_bool(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe boolean parameter value.
Definition: fors_dfs.c:701
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
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_locate_spectra(cpl_mask *mask)
Find the location of detected spectra on the CCD.
Definition: moses.c:6193
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