FORS Pipeline Reference Manual  5.0.9
fors_pmos_science.c
1 /* $Id: fors_pmos_science.c,v 1.65 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.65 $
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 <string.h>
34 #include <ctype.h>
35 #include <assert.h>
36 
37 #include <cpl.h>
38 #include <moses.h>
39 #include <fors_dfs.h>
40 #include <fors_utils.h>
41 #include <fors_qc.h>
42 
43 static int fors_pmos_science_create(cpl_plugin *);
44 static int fors_pmos_science_exec(cpl_plugin *);
45 static int fors_pmos_science_destroy(cpl_plugin *);
46 static int fors_pmos_science(cpl_parameterlist *, cpl_frameset *);
47 
48 static float * fors_check_angles(cpl_frameset *, int, const char *, int *);
49 static int
50 fors_find_angle_pos(float * angles, int nangles, float angle);
51 
52 static char fors_pmos_science_description[] =
53 "This recipe is used to reduce scientific spectra using the extraction\n"
54 "mask and the products created by the recipe fors_mpol_calib. The spectra\n"
55 "are bias subtracted, flat fielded (if a normalised flat field is specified)\n"
56 "and remapped eliminating the optical distortions. The wavelength calibration\n"
57 "can be optionally upgraded using a number of sky lines: if no sky lines\n"
58 "catalog of wavelengths is specified, an internal one is used instead.\n"
59 "If the alignment to the sky lines is performed, the input dispersion\n"
60 "coefficients table is upgraded and saved to disk, and a new CCD wavelengths\n"
61 "map is created.\n"
62 "This recipe accepts both FORS1 and FORS2 frames. A grism table (typically\n"
63 "depending on the instrument mode, and in particular on the grism used)\n"
64 "may also be specified: this table contains a default recipe parameter\n"
65 "setting to control the way spectra are extracted for a specific instrument\n"
66 "mode, as it is used for automatic run of the pipeline on Paranal and in\n"
67 "Garching. If this table is specified, it will modify the default recipe\n"
68 "parameter setting, with the exception of those parameters which have been\n"
69 "explicitly modifyed on the command line. If a grism table is not specified,\n"
70 "the input recipe parameters values will always be read from the command\n"
71 "line, or from an esorex configuration file if present, or from their\n"
72 "generic default values (that are rarely meaningful).\n"
73 "Either a scientific or a standard star exposure can be specified in input.\n"
74 "The acronym SCI on products should be read STD in case of standard stars\n"
75 "observations.\n\n"
76 "Input files:\n\n"
77 " DO category: Type: Explanation: Required:\n"
78 " SCIENCE_PMOS Raw Scientific exposure Y\n"
79 " or STANDARD_PMOS Raw Standard star exposure Y\n"
80 " MASTER_BIAS Calib Master bias Y\n"
81 " GRISM_TABLE Calib Grism table .\n"
82 " MASTER_SKYLINECAT Calib Sky lines catalog .\n"
83 " MASTER_NORM_FLAT_PMOS Calib Normalised flat field .\n"
84 " DISP_COEFF_PMOS Calib Inverse dispersion Y\n"
85 " CURV_COEFF_PMOS Calib Spectral curvature Y\n"
86 " SLIT_LOCATION_PMOS Calib Slits positions table Y\n"
87 " RETARDER_WAVEPLATE_CHROMATISM Calib Chromatism correction .\n"
88 " STD_PMOS_TABLE Calib Linear pol. of std stars .\n"
89 "\n"
90 "Output files:\n\n"
91 " DO category: Data type: Explanation:\n"
92 " REDUCED_SCI_PMOS FITS image Extracted scientific spectra\n"
93 " REDUCED_SKY_SCI_PMOS FITS image Extracted sky spectra\n"
94 " REDUCED_ERROR_SCI_PMOS FITS image Errors on extracted spectra\n"
95 " REDUCED_X_SCI_PMOS FITS image X Stokes parameter (and L)\n"
96 " REDUCED_ERROR_X_SCI_PMOS FITS image Error on X Stokes parameter\n"
97 " REDUCED_NUL_X_SCI_PMOS FITS image Null parameter for X\n"
98 " REDUCED_ANGLE_SCI_PMOS FITS image Direction of linear polarization\n"
99 " REDUCED_ERROR_ANGLE_SCI_PMOS FITS image Error on polarization direction\n"
100 " UNMAPPED_SCI_PMOS FITS image Sky subtracted scientific spectra\n"
101 " MAPPED_SCI_PMOS FITS image Rectified scientific spectra\n"
102 " MAPPED_ALL_SCI_PMOS FITS image Rectified science spectra with sky\n"
103 " MAPPED_SKY_SCI_PMOS FITS image Rectified sky spectra\n"
104 " UNMAPPED_SKY_SCI_PMOS FITS image Sky on CCD\n"
105 " OBJECT_TABLE_SCI_PMOS FITS table Positions of detected objects\n"
106 " OBJECT_TABLE_POL_SCI_PMOS FITS table Positions of real objects\n"
107 "\n"
108 " Only if the sky-alignment of the wavelength solution is requested:\n"
109 " DISP_COEFF_SCI_PMOS FITS table Upgraded dispersion coefficients\n"
110 " WAVELENGTH_MAP_SCI_PMOS FITS image Upgraded wavelength map\n\n";
111 
112 #define fors_pmos_science_exit(message, nscience) \
113 { \
114 if ((const char *)message != NULL) cpl_msg_error(recipe, message); \
115 if(reduceds != NULL) { \
116  for (j = 0; j < nscience; j++) \
117  cpl_image_delete(reduceds[j]); \
118 } \
119 if(rerrors != NULL) { \
120  for (j = 0; j < nscience; j++) \
121  cpl_image_delete(rerrors[j]); \
122 } \
123 if(slitss != NULL) { \
124  for (j = 0; j < nscience; j++) \
125  cpl_table_delete(slitss[j]); \
126 } \
127 if(mappeds != NULL) { \
128  for (j = 0; j < nscience; j++) \
129  cpl_image_delete(mappeds[j]); \
130 } \
131 if(skylocalmaps != NULL) { \
132  for (j = 0; j < nscience; j++) \
133  cpl_image_delete(skylocalmaps[j]); \
134 } \
135 cpl_free(reduceds); \
136 cpl_free(rerrors); \
137 cpl_free(slitss); \
138 cpl_free(mappeds); \
139 cpl_free(skylocalmaps); \
140 cpl_free(instrume); \
141 cpl_image_delete(dummy); \
142 cpl_image_delete(mapped_sky); \
143 cpl_image_delete(mapped_cleaned); \
144 cpl_image_delete(skymap); \
145 cpl_image_delete(smapped); \
146 cpl_table_delete(offsets); \
147 cpl_table_delete(sky); \
148 cpl_image_delete(bias); \
149 cpl_image_delete(spectra); \
150 cpl_image_delete(coordinate); \
151 cpl_image_delete(norm_flat); \
152 cpl_image_delete(rainbow); \
153 cpl_image_delete(rectified); \
154 cpl_image_delete(wavemap); \
155 cpl_propertylist_delete(header); \
156 cpl_propertylist_delete(save_header); \
157 cpl_table_delete(grism_table); \
158 cpl_table_delete(idscoeff); \
159 cpl_table_delete(maskslits); \
160 cpl_table_delete(overscans); \
161 cpl_table_delete(polytraces); \
162 cpl_table_delete(wavelengths); \
163 cpl_table_delete(mask_science); \
164 cpl_table_delete(mask_arc); \
165 cpl_table_delete(mask_flat); \
166 cpl_vector_delete(lines); \
167 cpl_msg_indent_less(); \
168 return -1; \
169 }
170 
171 
172 #define fors_pmos_science_exit_memcheck(message) \
173 { \
174 if ((const char *)message != NULL) cpl_msg_info(recipe, message); \
175 cpl_free(instrume); \
176 cpl_image_delete(dummy); \
177 cpl_image_delete(mapped_cleaned); \
178 cpl_image_delete(mapped_sky); \
179 cpl_image_delete(skymap); \
180 cpl_image_delete(smapped); \
181 cpl_table_delete(offsets); \
182 cpl_table_delete(sky); \
183 cpl_image_delete(bias); \
184 cpl_image_delete(spectra); \
185 cpl_image_delete(coordinate); \
186 cpl_image_delete(norm_flat); \
187 cpl_image_delete(rainbow); \
188 cpl_image_delete(rectified); \
189 cpl_image_delete(wavemap); \
190 cpl_propertylist_delete(header); \
191 cpl_propertylist_delete(save_header); \
192 cpl_table_delete(grism_table); \
193 cpl_table_delete(idscoeff); \
194 cpl_table_delete(maskslits); \
195 cpl_table_delete(overscans); \
196 cpl_table_delete(polytraces); \
197 cpl_table_delete(wavelengths); \
198 cpl_table_delete(mask_science); \
199 cpl_table_delete(mask_arc); \
200 cpl_table_delete(mask_flat); \
201 cpl_vector_delete(lines); \
202 cpl_msg_indent_less(); \
203 return 0; \
204 }
205 
206 
218 int cpl_plugin_get_info(cpl_pluginlist *list)
219 {
220  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
221  cpl_plugin *plugin = &recipe->interface;
222 
223  cpl_plugin_init(plugin,
224  CPL_PLUGIN_API,
225  FORS_BINARY_VERSION,
226  CPL_PLUGIN_TYPE_RECIPE,
227  "fors_pmos_science",
228  "Extraction of scientific spectra",
229  fors_pmos_science_description,
230  "Carlo Izzo",
231  PACKAGE_BUGREPORT,
232  "This file is currently part of the FORS Instrument Pipeline\n"
233  "Copyright (C) 2002-2010 European Southern Observatory\n\n"
234  "This program is free software; you can redistribute it and/or modify\n"
235  "it under the terms of the GNU General Public License as published by\n"
236  "the Free Software Foundation; either version 2 of the License, or\n"
237  "(at your option) any later version.\n\n"
238  "This program is distributed in the hope that it will be useful,\n"
239  "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
240  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
241  "GNU General Public License for more details.\n\n"
242  "You should have received a copy of the GNU General Public License\n"
243  "along with this program; if not, write to the Free Software Foundation,\n"
244  "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n",
245  fors_pmos_science_create,
246  fors_pmos_science_exec,
247  fors_pmos_science_destroy);
248 
249  cpl_pluginlist_append(list, plugin);
250 
251  return 0;
252 }
253 
254 
265 static int fors_pmos_science_create(cpl_plugin *plugin)
266 {
267  cpl_recipe *recipe;
268  cpl_parameter *p;
269 
270 
271  /*
272  * Check that the plugin is part of a valid recipe
273  */
274 
275  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
276  recipe = (cpl_recipe *)plugin;
277  else
278  return -1;
279 
280  /*
281  * Create the parameters list in the cpl_recipe object
282  */
283 
284  recipe->parameters = cpl_parameterlist_new();
285 
286 
287  /*
288  * Dispersion
289  */
290 
291  p = cpl_parameter_new_value("fors.fors_pmos_science.dispersion",
292  CPL_TYPE_DOUBLE,
293  "Expected spectral dispersion (Angstrom/pixel)",
294  "fors.fors_pmos_science",
295  0.0);
296  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
297  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
298  cpl_parameterlist_append(recipe->parameters, p);
299 
300  /*
301  * Rebin
302  */
303 
304  p = cpl_parameter_new_value("fors.fors_pmos_science.rebin",
305  CPL_TYPE_INT,
306  "Rebin (pixel)",
307  "fors.fors_pmos_science",
308  1);
309  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rebin");
310  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
311  cpl_parameterlist_append(recipe->parameters, p);
312 
313  /*
314  * Sky lines alignment
315  */
316 
317  p = cpl_parameter_new_value("fors.fors_pmos_science.skyalign",
318  CPL_TYPE_INT,
319  "Polynomial order for sky lines alignment, "
320  "or -1 to avoid alignment",
321  "fors.fors_pmos_science",
322  0);
323  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skyalign");
324  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
325  cpl_parameterlist_append(recipe->parameters, p);
326 
327  /*
328  * Line catalog table column containing the sky reference wavelengths
329  */
330 
331  p = cpl_parameter_new_value("fors.fors_pmos_science.wcolumn",
332  CPL_TYPE_STRING,
333  "Name of sky line catalog table column "
334  "with wavelengths",
335  "fors.fors_pmos_science",
336  "WLEN");
337  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
338  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
339  cpl_parameterlist_append(recipe->parameters, p);
340 
341  /*
342  * Start wavelength for spectral extraction
343  */
344 
345  p = cpl_parameter_new_value("fors.fors_pmos_science.startwavelength",
346  CPL_TYPE_DOUBLE,
347  "Start wavelength in spectral extraction",
348  "fors.fors_pmos_science",
349  0.0);
350  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
351  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
352  cpl_parameterlist_append(recipe->parameters, p);
353 
354  /*
355  * End wavelength for spectral extraction
356  */
357 
358  p = cpl_parameter_new_value("fors.fors_pmos_science.endwavelength",
359  CPL_TYPE_DOUBLE,
360  "End wavelength in spectral extraction",
361  "fors.fors_pmos_science",
362  0.0);
363  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
364  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
365  cpl_parameterlist_append(recipe->parameters, p);
366 
367  /*
368  * Flux conservation
369  */
370 
371  p = cpl_parameter_new_value("fors.fors_pmos_science.flux",
372  CPL_TYPE_BOOL,
373  "Apply flux conservation",
374  "fors.fors_pmos_science",
375  TRUE);
376  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
377  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
378  cpl_parameterlist_append(recipe->parameters, p);
379 
380  /*
381  * Apply flat field
382  */
383 
384  p = cpl_parameter_new_value("fors.fors_pmos_science.flatfield",
385  CPL_TYPE_BOOL,
386  "Apply flat field",
387  "fors.fors_pmos_science",
388  TRUE);
389  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flatfield");
390  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
391  cpl_parameterlist_append(recipe->parameters, p);
392 
393  /*
394  * Median sky subtraction method
395  */
396 
397  p = cpl_parameter_new_value("fors.fors_pmos_science.skymedian",
398  CPL_TYPE_BOOL,
399  "Sky subtraction from extracted slit spectra",
400  "fors.fors_pmos_science",
401  FALSE);
402  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymedian");
403  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
404  cpl_parameterlist_append(recipe->parameters, p);
405 
406  /*
407  * Local sky subtraction on CCD spectra
408  */
409 
410  p = cpl_parameter_new_value("fors.fors_pmos_science.skylocal",
411  CPL_TYPE_BOOL,
412  "Sky subtraction from CCD slit spectra",
413  "fors.fors_pmos_science",
414  TRUE);
415  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skylocal");
416  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
417  cpl_parameterlist_append(recipe->parameters, p);
418 
419  /*
420  * Cosmic rays removal
421  */
422 
423  p = cpl_parameter_new_value("fors.fors_pmos_science.cosmics",
424  CPL_TYPE_BOOL,
425  "Eliminate cosmic rays hits (only if local "
426  "sky subtraction is also requested)",
427  "fors.fors_pmos_science",
428  FALSE);
429  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cosmics");
430  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
431  cpl_parameterlist_append(recipe->parameters, p);
432 
433  /*
434  * Slit margin
435  */
436 
437  p = cpl_parameter_new_value("fors.fors_pmos_science.slit_margin",
438  CPL_TYPE_INT,
439  "Number of pixels to exclude at each slit "
440  "in object detection and extraction",
441  "fors.fors_pmos_science",
442  3);
443  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "slit_margin");
444  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
445  cpl_parameterlist_append(recipe->parameters, p);
446 
447  /*
448  * Extraction radius
449  */
450 
451  p = cpl_parameter_new_value("fors.fors_pmos_science.ext_radius",
452  CPL_TYPE_INT,
453  "Maximum extraction radius for detected "
454  "objects (pixel)",
455  "fors.fors_pmos_science",
456  12);
457  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_radius");
458  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
459  cpl_parameterlist_append(recipe->parameters, p);
460 
461  /*
462  * Contamination radius
463  */
464 
465  p = cpl_parameter_new_value("fors.fors_pmos_science.cont_radius",
466  CPL_TYPE_INT,
467  "Minimum distance at which two objects "
468  "of equal luminosity do not contaminate "
469  "each other (pixel)",
470  "fors.fors_pmos_science",
471  0);
472  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cont_radius");
473  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
474  cpl_parameterlist_append(recipe->parameters, p);
475 
476  /*
477  * Object extraction method
478  */
479 
480  p = cpl_parameter_new_value("fors.fors_pmos_science.ext_mode",
481  CPL_TYPE_INT,
482  "Object extraction method: 0 = aperture, "
483  "1 = Horne optimal extraction",
484  "fors.fors_pmos_science",
485  1);
486  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_mode");
487  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
488  cpl_parameterlist_append(recipe->parameters, p);
489 
490  /*
491  * Tolerance in object matching
492  */
493 
494  p = cpl_parameter_new_value("fors.fors_pmos_science.match_tolerance",
495  CPL_TYPE_DOUBLE,
496  "Tolerance for matching spectra from the "
497  "same object at different angles and beams "
498  "(pixel)",
499  "fors.fors_pmos_science",
500  5.0);
501  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "match_tolerance");
502  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
503  cpl_parameterlist_append(recipe->parameters, p);
504 
505  /*
506  * Normalise output by exposure time
507  */
508 
509  p = cpl_parameter_new_value("fors.fors_pmos_science.time_normalise",
510  CPL_TYPE_BOOL,
511  "Normalise output spectra by the exposure time",
512  "fors.fors_pmos_science",
513  TRUE);
514  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "time_normalise");
515  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
516  cpl_parameterlist_append(recipe->parameters, p);
517 
518  /*
519  * Apply chromatism correction to polarization angle
520  */
521 
522  p = cpl_parameter_new_value("fors.fors_pmos_science.chromatism",
523  CPL_TYPE_BOOL,
524  "Chromatism correction to polarization angles",
525  "fors.fors_pmos_science",
526  TRUE);
527  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "chromatism");
528  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
529  cpl_parameterlist_append(recipe->parameters, p);
530 
531  /*
532  * Rotation correction for linear polarisation
533  */
534 
535  p = cpl_parameter_new_value("fors.fors_pmos_science.wollaston",
536  CPL_TYPE_BOOL,
537  "Wollaston mounting (FORS2 only): true = 0 degrees "
538  "(ord. beam on top, extr. beam on bottom), "
539  "false = 180 degrees (beams are reversed), for FORS1 "
540  "is frozen to true",
541  "fors.fors_pmos_science",
542  TRUE);
543  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wollaston");
544  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
545  cpl_parameterlist_append(recipe->parameters, p);
546 
547  /*
548  * Create check products
549  */
550 
551  p = cpl_parameter_new_value("fors.fors_pmos_science.check",
552  CPL_TYPE_BOOL,
553  "Create intermediate products",
554  "fors.fors_pmos_science",
555  FALSE);
556  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "check");
557  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
558  cpl_parameterlist_append(recipe->parameters, p);
559 
560  /*
561  * Computation of QC1 parameters
562  */
563 
564  p = cpl_parameter_new_value("fors.fors_pmos_science.qc",
565  CPL_TYPE_BOOL,
566  "Compute QC1 parameters",
567  "fors.fors_pmos_science",
568  TRUE);
569  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "qc");
570  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
571  cpl_parameterlist_append(recipe->parameters, p);
572 
573  return 0;
574 }
575 
576 
585 static int fors_pmos_science_exec(cpl_plugin *plugin)
586 {
587  cpl_recipe *recipe;
588 
589  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
590  recipe = (cpl_recipe *)plugin;
591  else
592  return -1;
593 
594  return fors_pmos_science(recipe->parameters, recipe->frames);
595 }
596 
597 
606 static int fors_pmos_science_destroy(cpl_plugin *plugin)
607 {
608  cpl_recipe *recipe;
609 
610  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
611  recipe = (cpl_recipe *)plugin;
612  else
613  return -1;
614 
615  cpl_parameterlist_delete(recipe->parameters);
616 
617  return 0;
618 }
619 
620 
630 static int fors_pmos_science(cpl_parameterlist *parlist, cpl_frameset *frameset)
631 {
632 
633  const char *recipe = "fors_pmos_science";
634 
635 
636  /*
637  * Input parameters
638  */
639 
640  double dispersion;
641  int group;
642  int skyalign;
643  const char *wcolumn;
644  double startwavelength;
645  double endwavelength;
646  int flux;
647  int flatfield;
648  int skylocal;
649  int skymedian;
650  int chromatism;
651  double wollaston;
652  int cosmics;
653  int slit_margin;
654  int ext_radius;
655  int cont_radius;
656  int ext_mode;
657  double tolerance;
658  int time_normalise;
659  int check;
660  int qc;
661 
662  /*
663  * CPL objects
664  */
665 
666  cpl_image **images;
667 
668  cpl_image **reduceds = NULL;
669  cpl_image **rerrors = NULL;
670  cpl_table **slitss = NULL;
671  cpl_image **mappeds = NULL;
672  cpl_image **skylocalmaps = NULL;
673 
674  int nobjects = 0;
675 
676  cpl_image *bias = NULL;
677  cpl_image *norm_flat = NULL;
678  cpl_image *spectra = NULL;
679  cpl_image *rectified = NULL;
680  cpl_image *coordinate = NULL;
681  cpl_image *rainbow = NULL;
682  cpl_image *mapped = NULL;
683  cpl_image *mapped_sky = NULL;
684  cpl_image *mapped_cleaned = NULL;
685  cpl_image *smapped = NULL;
686  cpl_image *wavemap = NULL;
687  cpl_image *skymap = NULL;
688  cpl_image *skylocalmap = NULL;
689  cpl_image *dummy = NULL;
690 
691  cpl_table *grism_table = NULL;
692  cpl_table *overscans = NULL;
693  cpl_table *wavelengths = NULL;
694  cpl_table *idscoeff = NULL;
695  cpl_table *slits = NULL;
696  cpl_table *origslits = NULL;
697  cpl_table *maskslits = NULL;
698  cpl_table *mask_science = NULL;
699  cpl_table *mask_arc = NULL;
700  cpl_table *mask_flat = NULL;
701  cpl_table *polytraces = NULL;
702  cpl_table *offsets = NULL;
703  cpl_table *sky = NULL;
704 
705  cpl_vector *lines = NULL;
706 
707  cpl_propertylist *header = NULL;
708  cpl_propertylist *save_header = NULL;
709 
710  /*
711  * Auxiliary variables
712  */
713 
714  char version[80];
715  char *instrume = NULL;
716  const char *science_tag;
717  const char *master_norm_flat_tag;
718  const char *disp_coeff_tag;
719  const char *disp_coeff_sky_tag;
720  const char *wavelength_map_sky_tag;
721  const char *curv_coeff_tag;
722  const char *slit_location_tag;
723  const char *reduced_science_tag;
724  const char *reduced_sky_tag;
725  const char *reduced_error_tag;
726  const char *mapped_science_tag;
727  const char *unmapped_science_tag;
728  const char *mapped_science_sky_tag;
729  const char *mapped_sky_tag;
730  const char *unmapped_sky_tag;
731  const char *object_table_tag;
732  const char *object_table_pol_tag;
733  const char *skylines_offsets_tag;
734  const char *reduced_q_tag;
735  const char *reduced_u_tag;
736  const char *reduced_v_tag;
737  const char *reduced_l_tag;
738  const char *reduced_i_tag;
739  const char *reduced_error_q_tag;
740  const char *reduced_error_u_tag;
741  const char *reduced_error_v_tag;
742  const char *reduced_error_l_tag;
743  const char *reduced_error_i_tag;
744  const char *reduced_nul_q_tag;
745  const char *reduced_nul_u_tag;
746  const char *reduced_nul_v_tag;
747  const char *reduced_angle_tag;
748  const char *reduced_error_angle_tag;
749  const char *chrom_table_tag = "RETARDER_WAVEPLATE_CHROMATISM";
750  const char *std_pmos_table_tag = "STD_PMOS_TABLE";
751  float *angles = NULL;
752  int pmos, circ;
753  int nscience = 0;
754  double alltime;
755  double mean_rms;
756  int nlines;
757  int rebin;
758  double *line;
759  int nx = 0, ny;
760  int ccd_xsize, ccd_ysize;
761  double reference;
762  double gain;
763  double ron;
764  double ra, dec;
765  char filter;
766  double qc_angle;
767  double qc_angle_err;
768  double qc_pl;
769  double qc_pl_err;
770  int standard;
771  int polarised;
772  int highres;
773  int i, j;
774 
775  int *nobjs_per_slit;
776  int nslits;
777 
778  int bagoo = 0;
779  double blevel = 0.0;
780  int doit = 0; // montecarlo simulation
781  int conta = 0; // Bagoo, conta gli oggetti con S/N > s2n
782  int bright = 0; // Bagoo, marca un oggetto con S/N > s2n
783  int nslits_out_det = 0;
784 
785 
786  cpl_error_code error;
787 
788  snprintf(version, 80, "%s-%s", PACKAGE, PACKAGE_VERSION);
789 
790  if (bagoo) {
791  char *montecarlo = getenv("MONTECARLO");
792 
793  if (montecarlo) {
794  doit = atoi(montecarlo);
795  }
796  }
797 
798  cpl_msg_set_indentation(2);
799 
800  fors_dfs_set_groups(frameset);
801 
802 
803  /*
804  * Get configuration parameters
805  */
806 
807  cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
808  cpl_msg_indent_more();
809 
810  if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
811  fors_pmos_science_exit("Too many in input: GRISM_TABLE", nscience);
812 
813  grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
814 
815  dispersion = dfs_get_parameter_double(parlist,
816  "fors.fors_pmos_science.dispersion", grism_table);
817 
818  if (dispersion <= 0.0)
819  fors_pmos_science_exit("Invalid spectral dispersion", nscience);
820 
821  group = dfs_get_parameter_int(parlist,
822  "fors.fors_pmos_science.rebin", NULL);
823 
824  if (group < 1)
825  fors_pmos_science_exit("Invalid rebin factor", nscience);
826 
827  skyalign = dfs_get_parameter_int(parlist,
828  "fors.fors_pmos_science.skyalign", NULL);
829 
830  if (skyalign > 2)
831  fors_pmos_science_exit("Max polynomial degree for sky alignment is 2", nscience);
832 
833  wcolumn = dfs_get_parameter_string(parlist,
834  "fors.fors_pmos_science.wcolumn", NULL);
835 
836  startwavelength = dfs_get_parameter_double(parlist,
837  "fors.fors_pmos_science.startwavelength", grism_table);
838  if (startwavelength < 3000.0 || startwavelength > 13000.0)
839  fors_pmos_science_exit("Invalid wavelength", nscience);
840 
841  endwavelength = dfs_get_parameter_double(parlist,
842  "fors.fors_pmos_science.endwavelength", grism_table);
843  if (endwavelength < 3000.0 || endwavelength > 13000.0)
844  fors_pmos_science_exit("Invalid wavelength", nscience);
845 
846  if (endwavelength - startwavelength <= 0.0)
847  fors_pmos_science_exit("Invalid wavelength interval", nscience);
848 
849  flux = dfs_get_parameter_bool(parlist, "fors.fors_pmos_science.flux", NULL);
850 
851  flatfield = dfs_get_parameter_bool(parlist,
852  "fors.fors_pmos_science.flatfield",
853  NULL);
854 
855  skylocal = dfs_get_parameter_bool(parlist,
856  "fors.fors_pmos_science.skylocal",
857  NULL);
858  skymedian = dfs_get_parameter_bool(parlist,
859  "fors.fors_pmos_science.skymedian",
860  NULL);
861 
862  chromatism = dfs_get_parameter_bool(parlist,
863  "fors.fors_pmos_science.chromatism",
864  NULL);
865 
866  wollaston = dfs_get_parameter_bool(parlist,
867  "fors.fors_pmos_science.wollaston",
868  NULL);
869 
870  wollaston = wollaston ? 0 : 1;
871 
872  if (skylocal && skymedian)
873  fors_pmos_science_exit("Cannot apply sky subtraction both on "
874  "extracted and non-extracted spectra", nscience);
875 
876  cosmics = dfs_get_parameter_bool(parlist,
877  "fors.fors_pmos_science.cosmics", NULL);
878 
879  if (cosmics)
880  if (!skylocal)
881  fors_pmos_science_exit("Cosmic rays correction requires "
882  "skylocal=true", nscience);
883 
884  slit_margin = dfs_get_parameter_int(parlist,
885  "fors.fors_pmos_science.slit_margin",
886  NULL);
887  if (slit_margin < 0)
888  fors_pmos_science_exit("Value must be zero or positive", nscience);
889 
890  ext_radius = dfs_get_parameter_int(parlist,
891  "fors.fors_pmos_science.ext_radius",
892  NULL);
893  if (ext_radius < 0)
894  fors_pmos_science_exit("Value must be zero or positive", nscience);
895 
896  cont_radius = dfs_get_parameter_int(parlist,
897  "fors.fors_pmos_science.cont_radius",
898  NULL);
899  if (cont_radius < 0)
900  fors_pmos_science_exit("Value must be zero or positive", nscience);
901 
902  ext_mode = dfs_get_parameter_int(parlist, "fors.fors_pmos_science.ext_mode",
903  NULL);
904  if (ext_mode < 0 || ext_mode > 1)
905  fors_pmos_science_exit("Invalid object extraction mode", nscience);
906 
907  tolerance = dfs_get_parameter_double(parlist,
908  "fors.fors_pmos_science.match_tolerance", NULL);
909  if (tolerance <= 0.0)
910  fors_pmos_science_exit("Invalid object match tolerance", nscience);
911 
912  time_normalise = dfs_get_parameter_bool(parlist,
913  "fors.fors_pmos_science.time_normalise", NULL);
914 
915  check = dfs_get_parameter_bool(parlist,
916  "fors.fors_pmos_science.check", NULL);
917 
918  qc = dfs_get_parameter_bool(parlist, "fors.fors_pmos_science.qc", NULL);
919 
920  cpl_table_delete(grism_table); grism_table = NULL;
921 
922  if (cpl_error_get_code())
923  fors_pmos_science_exit("Failure getting the configuration parameters",nscience);
924 
925 
926  /*
927  * Check input set-of-frames
928  */
929 
930  cpl_msg_indent_less();
931  cpl_msg_info(recipe, "Check input set-of-frames:");
932  cpl_msg_indent_more();
933 
934  {
935  cpl_frameset *subframeset = cpl_frameset_duplicate(frameset);
936  cpl_frameset_erase(subframeset, "MASTER_BIAS");
937 
938  if (!dfs_equal_keyword(subframeset, "ESO INS GRIS1 ID"))
939  cpl_msg_warning(cpl_func,"Input frames are not from the same grism");
940 
941  if (!dfs_equal_keyword(subframeset, "ESO INS FILT1 ID"))
942  cpl_msg_warning(cpl_func,"Input frames are not from the same filter");
943 
944  if (!dfs_equal_keyword(subframeset, "ESO DET CHIP1 ID"))
945  cpl_msg_warning(cpl_func,"Input frames are not from the same chip");
946 
947  cpl_frameset_delete(subframeset);
948  }
949 
950  standard = 0;
951  pmos = cpl_frameset_count_tags(frameset, "SCIENCE_PMOS");
952 
953  if (pmos == 0) {
954  pmos = cpl_frameset_count_tags(frameset, "STANDARD_PMOS");
955  standard = 1;
956  }
957 
958  if (pmos == 0)
959  fors_pmos_science_exit("Missing input scientific frame", nscience);
960 
961  angles = fors_check_angles(frameset, pmos,
962  standard ? "STANDARD_PMOS" : "SCIENCE_PMOS",
963  &circ);
964  if (angles == NULL)
965  fors_pmos_science_exit("Polarization angles could not be read", nscience);
966 
967  if (circ)
968  chromatism = 0; /* Chromatism correction unrequired for
969  circular polarimetry */
970 
971 
972  nscience = pmos;
973 
974  reduceds = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
975  rerrors = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
976  slitss = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
977  mappeds = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
978  skylocalmaps = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
979 
980  if (pmos) {
981  cpl_msg_info(recipe, "PMOS data found");
982  if (standard) {
983  science_tag = "STANDARD_PMOS";
984  reduced_science_tag = "REDUCED_STD_PMOS";
985  unmapped_science_tag = "UNMAPPED_STD_PMOS";
986  mapped_science_tag = "MAPPED_STD_PMOS";
987  mapped_science_sky_tag = "MAPPED_ALL_STD_PMOS";
988  skylines_offsets_tag = "SKY_SHIFTS_SLIT_STD_PMOS";
989  wavelength_map_sky_tag = "WAVELENGTH_MAP_STD_PMOS";
990  disp_coeff_sky_tag = "DISP_COEFF_STD_PMOS";
991  mapped_sky_tag = "MAPPED_SKY_STD_PMOS";
992  unmapped_sky_tag = "UNMAPPED_SKY_STD_PMOS";
993  object_table_tag = "OBJECT_TABLE_STD_PMOS";
994  object_table_pol_tag = "OBJECT_TABLE_POL_STD_PMOS";
995  reduced_sky_tag = "REDUCED_SKY_STD_PMOS";
996  reduced_error_tag = "REDUCED_ERROR_STD_PMOS";
997  reduced_q_tag = "REDUCED_Q_STD_PMOS";
998  reduced_u_tag = "REDUCED_U_STD_PMOS";
999  reduced_v_tag = "REDUCED_V_STD_PMOS";
1000  reduced_l_tag = "REDUCED_L_STD_PMOS";
1001  reduced_i_tag = "REDUCED_I_STD_PMOS";
1002  reduced_error_q_tag = "REDUCED_ERROR_Q_STD_PMOS";
1003  reduced_error_u_tag = "REDUCED_ERROR_U_STD_PMOS";
1004  reduced_error_v_tag = "REDUCED_ERROR_V_STD_PMOS";
1005  reduced_error_l_tag = "REDUCED_ERROR_L_STD_PMOS";
1006  reduced_error_i_tag = "REDUCED_ERROR_I_STD_PMOS";
1007  reduced_nul_q_tag = "REDUCED_NUL_Q_STD_PMOS";
1008  reduced_nul_u_tag = "REDUCED_NUL_U_STD_PMOS";
1009  reduced_nul_v_tag = "REDUCED_NUL_V_STD_PMOS";
1010  reduced_angle_tag = "REDUCED_ANGLE_STD_PMOS";
1011  reduced_error_angle_tag = "REDUCED_ERROR_ANGLE_STD_PMOS";
1012  }
1013  else {
1014  science_tag = "SCIENCE_PMOS";
1015  reduced_science_tag = "REDUCED_SCI_PMOS";
1016  unmapped_science_tag = "UNMAPPED_SCI_PMOS";
1017  mapped_science_tag = "MAPPED_SCI_PMOS";
1018  mapped_science_sky_tag = "MAPPED_ALL_SCI_PMOS";
1019  skylines_offsets_tag = "SKY_SHIFTS_SLIT_SCI_PMOS";
1020  wavelength_map_sky_tag = "WAVELENGTH_MAP_SCI_PMOS";
1021  disp_coeff_sky_tag = "DISP_COEFF_SCI_PMOS";
1022  mapped_sky_tag = "MAPPED_SKY_SCI_PMOS";
1023  unmapped_sky_tag = "UNMAPPED_SKY_SCI_PMOS";
1024  object_table_tag = "OBJECT_TABLE_SCI_PMOS";
1025  object_table_pol_tag = "OBJECT_TABLE_POL_SCI_PMOS";
1026  reduced_sky_tag = "REDUCED_SKY_SCI_PMOS";
1027  reduced_error_tag = "REDUCED_ERROR_SCI_PMOS";
1028  reduced_q_tag = "REDUCED_Q_SCI_PMOS";
1029  reduced_u_tag = "REDUCED_U_SCI_PMOS";
1030  reduced_v_tag = "REDUCED_V_SCI_PMOS";
1031  reduced_l_tag = "REDUCED_L_SCI_PMOS";
1032  reduced_i_tag = "REDUCED_I_SCI_PMOS";
1033  reduced_error_q_tag = "REDUCED_ERROR_Q_SCI_PMOS";
1034  reduced_error_u_tag = "REDUCED_ERROR_U_SCI_PMOS";
1035  reduced_error_v_tag = "REDUCED_ERROR_V_SCI_PMOS";
1036  reduced_error_l_tag = "REDUCED_ERROR_L_SCI_PMOS";
1037  reduced_error_i_tag = "REDUCED_ERROR_I_SCI_PMOS";
1038  reduced_nul_q_tag = "REDUCED_NUL_Q_SCI_PMOS";
1039  reduced_nul_u_tag = "REDUCED_NUL_U_SCI_PMOS";
1040  reduced_nul_v_tag = "REDUCED_NUL_V_SCI_PMOS";
1041  reduced_angle_tag = "REDUCED_ANGLE_SCI_PMOS";
1042  reduced_error_angle_tag = "REDUCED_ERROR_ANGLE_SCI_PMOS";
1043  }
1044 
1045  master_norm_flat_tag = "MASTER_NORM_FLAT_PMOS";
1046  disp_coeff_tag = "DISP_COEFF_PMOS";
1047  curv_coeff_tag = "CURV_COEFF_PMOS";
1048  slit_location_tag = "SLIT_LOCATION_PMOS";
1049 
1050  if (!cpl_frameset_count_tags(frameset, master_norm_flat_tag)) {
1051  master_norm_flat_tag = "MASTER_NORM_FLAT_LONG_PMOS";
1052  disp_coeff_tag = "DISP_COEFF_LONG_PMOS";
1053  slit_location_tag = "SLIT_LOCATION_LONG_PMOS";
1054  }
1055  }
1056 
1057  if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") == 0)
1058  fors_pmos_science_exit("Missing required input: MASTER_BIAS", nscience);
1059 
1060  if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") > 1)
1061  fors_pmos_science_exit("Too many in input: MASTER_BIAS", nscience);
1062 
1063  if (skyalign >= 0)
1064  if (cpl_frameset_count_tags(frameset, "MASTER_SKYLINECAT") > 1)
1065  fors_pmos_science_exit("Too many in input: MASTER_SKYLINECAT", nscience);
1066 
1067  if (cpl_frameset_count_tags(frameset, disp_coeff_tag) == 0) {
1068  cpl_msg_error(recipe, "Missing required input: %s", disp_coeff_tag);
1069  fors_pmos_science_exit(NULL, nscience);
1070  }
1071 
1072  if (cpl_frameset_count_tags(frameset, disp_coeff_tag) > 1) {
1073  cpl_msg_error(recipe, "Too many in input: %s", disp_coeff_tag);
1074  fors_pmos_science_exit(NULL, nscience);
1075  }
1076 
1077  if (cpl_frameset_count_tags(frameset, slit_location_tag) == 0) {
1078  cpl_msg_error(recipe, "Missing required input: %s",
1079  slit_location_tag);
1080  fors_pmos_science_exit(NULL, nscience);
1081  }
1082 
1083  if (cpl_frameset_count_tags(frameset, slit_location_tag) > 1) {
1084  cpl_msg_error(recipe, "Too many in input: %s", slit_location_tag);
1085  fors_pmos_science_exit(NULL, nscience);
1086  }
1087 
1088  if (chromatism) {
1089  if (cpl_frameset_count_tags(frameset, chrom_table_tag) == 0) {
1090  cpl_msg_error(recipe, "Missing required input: %s",
1091  chrom_table_tag);
1092  fors_pmos_science_exit(NULL, nscience);
1093  }
1094 
1095  if (cpl_frameset_count_tags(frameset, chrom_table_tag) > 1) {
1096  cpl_msg_error(recipe, "Too many in input: %s", chrom_table_tag);
1097  fors_pmos_science_exit(NULL, nscience);
1098  }
1099  }
1100 
1101  if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) > 1) {
1102  if (flatfield) {
1103  cpl_msg_error(recipe, "Too many in input: %s",
1104  master_norm_flat_tag);
1105  fors_pmos_science_exit(NULL, nscience);
1106  }
1107  else {
1108  cpl_msg_warning(recipe, "%s in input are ignored, "
1109  "since flat field correction was not requested",
1110  master_norm_flat_tag);
1111  }
1112  }
1113 
1114  if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 1) {
1115  if (!flatfield) {
1116  cpl_msg_warning(recipe, "%s in input is ignored, "
1117  "since flat field correction was not requested",
1118  master_norm_flat_tag);
1119  }
1120  }
1121 
1122  if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 0) {
1123  if (flatfield) {
1124  cpl_msg_error(recipe, "Flat field correction was requested, "
1125  "but no %s are found in input",
1126  master_norm_flat_tag);
1127  fors_pmos_science_exit(NULL, nscience);
1128  }
1129  }
1130 
1131  if (standard) {
1132  if (cpl_frameset_count_tags(frameset, std_pmos_table_tag) > 1) {
1133  cpl_msg_error(recipe, "Too many in input: %s", std_pmos_table_tag);
1134  fors_pmos_science_exit(NULL, nscience);
1135  }
1136 
1137  if (qc) {
1138  if (cpl_frameset_count_tags(frameset, std_pmos_table_tag) == 0) {
1139  cpl_msg_error(recipe, "QC computation was requested, but no "
1140  "%s is found in input", std_pmos_table_tag);
1141  fors_pmos_science_exit(NULL, nscience);
1142  }
1143  }
1144  }
1145 
1146  cpl_msg_indent_less();
1147 
1148 
1149  /*
1150  * Get the reference wavelength and the rebin factor along the
1151  * dispersion direction from a scientific exposure
1152  */
1153 
1154  header = dfs_load_header(frameset, science_tag, 0);
1155 
1156  if (header == NULL)
1157  fors_pmos_science_exit("Cannot load scientific frame header", nscience);
1158 
1159  instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
1160  if (instrume == NULL)
1161  fors_pmos_science_exit("Missing keyword INSTRUME in scientific header", nscience);
1162  instrume = cpl_strdup(instrume);
1163 
1164  if (instrume[4] == '1')
1165  snprintf(version, 80, "%s/%s", "fors1", VERSION);
1166  if (instrume[4] == '2')
1167  snprintf(version, 80, "%s/%s", "fors2", VERSION);
1168 
1169  reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
1170 
1171  if (cpl_error_get_code() != CPL_ERROR_NONE)
1172  fors_pmos_science_exit("Missing keyword ESO INS GRIS1 WLEN in scientific "
1173  "frame header", nscience);
1174 
1175  if (reference < 3000.0) /* Perhaps in nanometers... */
1176  reference *= 10;
1177 
1178  if (reference < 3000.0 || reference > 13000.0) {
1179  cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
1180  "keyword ESO INS GRIS1 WLEN in scientific frame header",
1181  reference);
1182  fors_pmos_science_exit(NULL, nscience);
1183  }
1184 
1185  cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
1186 
1187  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
1188 
1189  if (cpl_error_get_code() != CPL_ERROR_NONE)
1190  fors_pmos_science_exit("Missing keyword ESO DET WIN1 BINX in "
1191  "scientific frame header", nscience);
1192 
1193  if (rebin != 1) {
1194  dispersion *= rebin;
1195  cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
1196  "spectral dispersion used is %f A/pixel", rebin,
1197  dispersion);
1198  ext_radius /= rebin;
1199  cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
1200  "extraction radius used is %d pixel", rebin,
1201  ext_radius);
1202  }
1203 
1204  gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
1205 
1206  if (cpl_error_get_code() != CPL_ERROR_NONE)
1207  fors_pmos_science_exit("Missing keyword ESO DET OUT1 CONAD in "
1208  "scientific frame header", nscience);
1209 
1210  cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
1211 
1212  ron = cpl_propertylist_get_double(header, "ESO DET OUT1 RON");
1213 
1214  if (cpl_error_get_code() != CPL_ERROR_NONE)
1215  fors_pmos_science_exit("Missing keyword ESO DET OUT1 RON in "
1216  "scientific frame header", nscience);
1217 
1218  ron /= gain; /* Convert from electrons to ADU */
1219 
1220  cpl_msg_info(recipe, "The read-out-noise is: %.2f ADU", ron);
1221 
1222  if (cpl_frameset_count_tags(frameset, curv_coeff_tag) == 0) {
1223  cpl_msg_error(recipe, "Missing required input: %s", curv_coeff_tag);
1224  fors_pmos_science_exit(NULL, nscience);
1225  }
1226 
1227  if (cpl_frameset_count_tags(frameset, curv_coeff_tag) > 1) {
1228  cpl_msg_error(recipe, "Too many in input: %s", curv_coeff_tag);
1229  fors_pmos_science_exit(NULL, nscience);
1230  }
1231 
1232  cpl_msg_info(recipe, "Load normalised flat field (if present)...");
1233  cpl_msg_indent_more();
1234 
1235  if (flatfield) {
1236  norm_flat = dfs_load_image(frameset, master_norm_flat_tag,
1237  CPL_TYPE_FLOAT, 0, 1);
1238  }
1239 
1240  if (skyalign >= 0) {
1241 
1242  cpl_msg_indent_less();
1243  cpl_msg_info(recipe, "Load input sky line catalog...");
1244  cpl_msg_indent_more();
1245 
1246  wavelengths = dfs_load_table(frameset, "MASTER_SKYLINECAT", 1);
1247 
1248  if (wavelengths) {
1249  /*
1250  * Cast the wavelengths into a (double precision) CPL vector
1251  */
1252 
1253  nlines = cpl_table_get_nrow(wavelengths);
1254 
1255  if (nlines == 0)
1256  fors_pmos_science_exit("Empty input sky line catalog", nscience);
1257 
1258  if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
1259  cpl_msg_error(recipe, "Missing column %s in input line "
1260  "catalog table", wcolumn);
1261  fors_pmos_science_exit(NULL, nscience);
1262  }
1263 
1264  line = cpl_malloc(nlines * sizeof(double));
1265 
1266  for (i = 0; i < nlines; i++)
1267  line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
1268 
1269  cpl_table_delete(wavelengths); wavelengths = NULL;
1270 
1271  lines = cpl_vector_wrap(nlines, line);
1272  }
1273  else {
1274  cpl_msg_info(recipe, "No sky line catalog found in input - fine!");
1275  }
1276  }
1277 
1278  /*
1279  * Keep a table of slit positions according to science, in order to
1280  * check its consistency with those from arc and flat.
1281  */
1282 
1283  mask_science = mos_load_slits_fors_mos(header, &nslits_out_det);
1284 
1285  cpl_propertylist_delete(header); header = NULL;
1286 
1287  cpl_table_name_column(mask_science, "xtop", "science");
1288 
1289  /*
1290  * Load the wavelength calibration table
1291  */
1292 
1293  idscoeff = dfs_load_table(frameset, disp_coeff_tag, 1);
1294 
1295  if (idscoeff == NULL)
1296  fors_pmos_science_exit("Cannot load wavelength calibration table", nscience);
1297 
1298  /*
1299  * Keep a table of slit positions according to arc, in order to
1300  * check its consistency with those from science and flat.
1301  */
1302 
1303  header = dfs_load_header(frameset, disp_coeff_tag, 0);
1304 
1305  mask_arc = mos_load_slits_fors_mos(header, &nslits_out_det);
1306 
1307  cpl_propertylist_delete(header); header = NULL;
1308 
1309  if (cpl_table_move_column(mask_science, "xtop", mask_arc)) {
1310  cpl_error_reset();
1311  cpl_msg_warning(recipe,
1312  "Slit configuration of science and arc differs!");
1313  cpl_table_delete(mask_arc); mask_arc = NULL;
1314  goto skip;
1315  }
1316  cpl_table_name_column(mask_science, "xtop", "arc");
1317  cpl_table_delete(mask_arc); mask_arc = NULL;
1318 
1319  if (norm_flat) {
1320 
1321  /*
1322  * Keep a table of slit positions according to arc, in order to
1323  * check its consistency with those from science and flat.
1324  */
1325 
1326  header = dfs_load_header(frameset, master_norm_flat_tag, 0);
1327 
1328  mask_flat = mos_load_slits_fors_mos(header, &nslits_out_det);
1329 
1330  cpl_propertylist_delete(header); header = NULL;
1331 
1332  if (cpl_table_move_column(mask_science, "xtop", mask_flat)) {
1333  cpl_error_reset();
1334  cpl_msg_warning(recipe,
1335  "Slit configuration of science and flat differs!");
1336  cpl_table_delete(mask_flat); mask_flat = NULL;
1337  goto skip;
1338  }
1339  cpl_table_name_column(mask_science, "xtop", "flat");
1340  cpl_table_delete(mask_flat); mask_flat = NULL;
1341  }
1342 
1343  cpl_table_duplicate_column(mask_science, "diff", mask_science, "science");
1344  cpl_table_subtract_columns(mask_science, "diff", "arc");
1345  cpl_table_abs_column(mask_science, "diff");
1346 
1347  if (cpl_table_get_column_max(mask_science, "diff") > 0.01) {
1348  cpl_msg_warning(recipe,
1349  "Slit configuration of science and arc differs!");
1350  goto skip;
1351  }
1352 
1353  if (norm_flat) {
1354  cpl_table_erase_column(mask_science, "diff");
1355 
1356  cpl_table_duplicate_column(mask_science, "diff",
1357  mask_science, "science");
1358  cpl_table_subtract_columns(mask_science, "diff", "flat");
1359  cpl_table_abs_column(mask_science, "diff");
1360 
1361  if (cpl_table_get_column_max(mask_science, "diff") > 0.01) {
1362  cpl_msg_warning(recipe,
1363  "Slit configuration of science and flat differs!");
1364  goto skip;
1365  }
1366  }
1367 
1368 skip:
1369 
1370  cpl_table_delete(mask_science); mask_science = NULL;
1371 
1372  for (j = 0; j < nscience; j++) {
1373  int k;
1374 
1375  cpl_msg_indent_less();
1376  cpl_msg_info(recipe, "Processing scientific exposure of angle %.2f "
1377  "(%d out of %d) ...",
1378  angles[j], j + 1, nscience);
1379  cpl_msg_indent_more();
1380 
1381  cpl_msg_info(recipe, "Load scientific exposure...");
1382  cpl_msg_indent_more();
1383 
1384 
1385  /*
1386  * FIXME: Horrible workaround to avoid the problem because of the
1387  * multiple encapsulation of cpl_frameset_find() in different
1388  * loading functions
1389  */
1390 
1391  header = dfs_load_header(frameset, science_tag, 0);
1392 
1393  for (k = 0; k < j; k ++) {
1394  cpl_propertylist_delete(header);
1395  header = dfs_load_header(frameset, NULL, 0);
1396  }
1397 
1398  spectra = dfs_load_image(frameset, science_tag, CPL_TYPE_FLOAT, 0, 0);
1399 
1400  for (k = 0; k < j; k ++) {
1401  cpl_image_delete(spectra);
1402  spectra = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
1403  }
1404 
1405  if (spectra == NULL)
1406  fors_pmos_science_exit("Cannot load scientific frame", nscience);
1407 
1408  if (header == NULL)
1409  fors_pmos_science_exit("Cannot load scientific frame header", nscience);
1410 
1411  alltime = cpl_propertylist_get_double(header, "EXPTIME");
1412 
1413  if (cpl_error_get_code() != CPL_ERROR_NONE)
1414  fors_pmos_science_exit("Missing keyword EXPTIME in scientific "
1415  "frame header", nscience);
1416 
1417  cpl_msg_info(recipe, "Scientific frame exposure time: %.2f s",
1418  alltime);
1419 
1420  ra = cpl_propertylist_get_double(header, "RA");
1421  dec = cpl_propertylist_get_double(header, "DEC");
1422 
1423  if (cpl_error_get_code() != CPL_ERROR_NONE)
1424  fors_pmos_science_exit("Missing keywords RA and DEC in scientific "
1425  "frame header", nscience);
1426 
1427  /* Leave the header on for the next step... */
1428 
1429  cpl_msg_indent_less();
1430 
1431  /*
1432  * Remove the master bias
1433  */
1434 
1435  cpl_msg_info(recipe, "Remove the master bias...");
1436 
1437  bias = dfs_load_image(frameset, "MASTER_BIAS", CPL_TYPE_FLOAT, 0, 1);
1438 
1439  if (bias == NULL)
1440  fors_pmos_science_exit("Cannot load master bias", nscience);
1441 
1442  if (doit) {
1443  if (j == 0)
1444  blevel = cpl_image_get_mean(bias);
1445  mos_randomise_image(spectra, ron, gain, blevel);
1446  }
1447 
1448  overscans = mos_load_overscans_fors(header);
1449 
1450  dummy = mos_remove_bias(spectra, bias, overscans);
1451  cpl_image_delete(spectra); spectra = dummy; dummy = NULL;
1452  cpl_image_delete(bias); bias = NULL;
1453  cpl_table_delete(overscans); overscans = NULL;
1454 
1455  if (spectra == NULL)
1456  fors_pmos_science_exit("Cannot remove bias from scientific frame", nscience);
1457 
1458  ccd_xsize = nx = cpl_image_get_size_x(spectra);
1459  ccd_ysize = ny = cpl_image_get_size_y(spectra);
1460 
1461  if (flatfield) {
1462 
1463  if (norm_flat) {
1464  cpl_msg_info(recipe, "Apply flat field correction...");
1465  if (cpl_image_divide(spectra, norm_flat) != CPL_ERROR_NONE) {
1466  cpl_msg_error(recipe,
1467  "Failure of flat field correction: %s",
1468  cpl_error_get_message());
1469  fors_pmos_science_exit(NULL, nscience);
1470  }
1471  }
1472  else {
1473  cpl_msg_error(recipe, "Cannot load input %s for flat field "
1474  "correction", master_norm_flat_tag);
1475  fors_pmos_science_exit(NULL, nscience);
1476  }
1477 
1478  }
1479 
1480  /*
1481  * Load the spectral curvature table
1482  */
1483 
1484  polytraces = dfs_load_table(frameset, curv_coeff_tag, 1);
1485  if (polytraces == NULL)
1486  fors_pmos_science_exit("Cannot load spectral curvature table", nscience);
1487 
1488  /*
1489  * Load the slit location table
1490  */
1491 
1492  slits = dfs_load_table(frameset, slit_location_tag, 1);
1493  if (slits == NULL)
1494  fors_pmos_science_exit("Cannot load slits location table", nscience);
1495 
1496  cpl_msg_info(recipe, "Processing scientific spectra...");
1497 
1498  /*
1499  * This one will also generate the spatial map from the spectral
1500  * curvature table (in the case of multislit data)
1501  */
1502 
1503  coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
1504 
1505  smapped = mos_spatial_calibration(spectra, slits, polytraces,
1506  reference, startwavelength,
1507  endwavelength, dispersion,
1508  flux, coordinate);
1509 
1510  /*
1511  * Generate a rectified wavelength map from the wavelength calibration
1512  * table
1513  */
1514 
1515  rainbow = mos_map_idscoeff(idscoeff, nx, reference, startwavelength,
1516  endwavelength);
1517 
1518  if (dispersion > 1.0)
1519  highres = 0;
1520  else
1521  highres = 1;
1522 
1523  if (skyalign >= 0) {
1524  if (skyalign) {
1525  cpl_msg_info(recipe,
1526  "Align wavelength solution to reference skylines "
1527  "applying %d order residual fit...", skyalign);
1528  }
1529  else {
1530  cpl_msg_info(recipe, "Align wavelength solution to reference "
1531  "skylines applying median offset...");
1532  }
1533 
1534  if (!j) {
1535  offsets = mos_wavelength_align(smapped, slits, reference,
1536  startwavelength, endwavelength,
1537  idscoeff, lines, highres,
1538  skyalign, rainbow, 4);
1539  if (offsets) {
1540  if (standard)
1541  cpl_msg_warning(recipe, "Alignment of the wavelength "
1542  "solution to reference sky lines may "
1543  "be unreliable in this case!");
1544 
1545  if (dfs_save_table(frameset, offsets, skylines_offsets_tag,
1546  NULL, parlist, recipe, version)) {
1547  fors_pmos_science_exit(NULL, nscience);
1548  }
1549 
1550  } else {
1551  cpl_msg_warning(recipe, "Alignment of the wavelength "
1552  "solution to reference sky lines could "
1553  "not be done!");
1554  skyalign = -1;
1555  }
1556  }
1557 
1558 
1559  }
1560 
1561  wavemap = mos_map_wavelengths(coordinate, rainbow, slits,
1562  polytraces, reference,
1563  startwavelength, endwavelength,
1564  dispersion);
1565 
1566 
1567  cpl_image_delete(rainbow); rainbow = NULL;
1568  cpl_image_delete(coordinate); coordinate = NULL;
1569 
1570  /*
1571  * Here the wavelength calibrated slit spectra are created. This frame
1572  * contains sky_science.
1573  */
1574 
1575  mapped_sky = mos_wavelength_calibration(smapped, reference,
1576  startwavelength, endwavelength,
1577  dispersion, idscoeff, flux);
1578 
1579  if (!j) {
1580  cpl_msg_indent_less();
1581  cpl_msg_info(recipe,
1582  "Check applied wavelength against skylines...");
1583  cpl_msg_indent_more();
1584 
1585  mean_rms = mos_distortions_rms(mapped_sky, lines, startwavelength,
1586  dispersion, 6, highres);
1587 
1588 
1589  cpl_msg_info(recipe, "Mean residual: %f", mean_rms);
1590 
1591  mean_rms = cpl_table_get_column_mean(idscoeff, "error");
1592 
1593  cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
1594  mean_rms, mean_rms * dispersion);
1595  }
1596 
1597  save_header = cpl_propertylist_duplicate(header);
1598 
1599  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
1600  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
1601  cpl_propertylist_update_double(header, "CRVAL1",
1602  startwavelength + dispersion/2);
1603  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
1604  cpl_propertylist_update_double(header, "CD1_1", dispersion);
1605  cpl_propertylist_update_double(header, "CD1_2", 0.0);
1606  cpl_propertylist_update_double(header, "CD2_1", 0.0);
1607  cpl_propertylist_update_double(header, "CD2_2", 1.0);
1608  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
1609  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
1610 
1611  if (time_normalise) {
1612  dummy = cpl_image_divide_scalar_create(mapped_sky, alltime);
1613 
1614  if (!j) {
1615  if(dfs_save_image_null(frameset, parlist,
1616  mapped_science_sky_tag,
1617  recipe, version)) {
1618  fors_pmos_science_exit(NULL, nscience);
1619  }
1620  }
1621 
1622  if (dfs_save_image_ext(dummy, mapped_science_sky_tag, header)) {
1623  fors_pmos_science_exit(NULL, nscience);
1624  }
1625 
1626  cpl_image_delete(dummy); dummy = NULL;
1627  }
1628  else {
1629 
1630  if (!j) {
1631  if(dfs_save_image_null(frameset, parlist,
1632  mapped_science_sky_tag,
1633  recipe, version)) {
1634  fors_pmos_science_exit(NULL, nscience);
1635  }
1636  }
1637 
1638  if (dfs_save_image_ext(mapped_sky,
1639  mapped_science_sky_tag, header)) {
1640  fors_pmos_science_exit(NULL, nscience);
1641  }
1642 
1643  }
1644 
1645  if (skymedian == 0 && skylocal == 0) {
1646  cpl_image_delete(mapped_sky); mapped_sky = NULL;
1647  }
1648 
1649  if (skylocal) {
1650 
1651  cpl_msg_indent_less();
1652 
1653  cpl_msg_info(recipe, "Local sky determination...");
1654  cpl_msg_indent_more();
1655  skymap = mos_subtract_sky(spectra, slits, polytraces, reference,
1656  startwavelength, endwavelength, dispersion);
1657 
1658  if (skymap) {
1659  if (time_normalise)
1660  cpl_image_divide_scalar(skymap, alltime);
1661 
1662  if (!j) {
1663  if(dfs_save_image_null(frameset, parlist,
1664  unmapped_sky_tag,
1665  recipe, version)) {
1666  fors_pmos_science_exit(NULL, nscience);
1667  }
1668  }
1669 
1670  if (dfs_save_image_ext(skymap, unmapped_sky_tag,
1671  save_header)) {
1672  fors_pmos_science_exit(NULL, nscience);
1673  }
1674 
1675  cpl_image_delete(skymap); skymap = NULL;
1676 
1677  if (!j) {
1678  if(dfs_save_image_null(frameset, parlist,
1679  unmapped_science_tag,
1680  recipe, version)) {
1681  fors_pmos_science_exit(NULL, nscience);
1682  }
1683  }
1684 
1685  if (dfs_save_image_ext(spectra, unmapped_science_tag,
1686  save_header)) {
1687  fors_pmos_science_exit(NULL, nscience);
1688  }
1689 
1690  if (cosmics) {
1691  cpl_msg_info(recipe, "Removing cosmic rays...");
1692  mos_clean_cosmics(spectra, gain, -1., -1.);
1693  }
1694 
1695  /*
1696  * The spatially rectified image, that contained the sky,
1697  * is replaced by a sky-subtracted spatially rectified image:
1698  */
1699 
1700  cpl_image_delete(smapped); smapped = NULL;
1701 
1702  smapped = mos_spatial_calibration(spectra, slits, polytraces,
1703  reference, startwavelength,
1704  endwavelength, dispersion,
1705  flux, NULL);
1706  }
1707  else {
1708  cpl_msg_warning(recipe, "Sky subtraction failure");
1709  if (cosmics)
1710  cpl_msg_warning(recipe,
1711  "Cosmic rays removal not performed!");
1712  cosmics = skylocal = 0;
1713  }
1714  }
1715 
1716  cpl_image_delete(spectra); spectra = NULL;
1717  cpl_table_delete(polytraces); polytraces = NULL;
1718 
1719  if (skyalign >= 0) {
1720  save_header = dfs_load_header(frameset, science_tag, 0);
1721 
1722  if (!j) {
1723  if(dfs_save_image_null(frameset, parlist,
1724  wavelength_map_sky_tag,
1725  recipe, version)) {
1726  fors_pmos_science_exit(NULL, nscience);
1727  }
1728  }
1729 
1730  if (dfs_save_image_ext(wavemap, wavelength_map_sky_tag,
1731  save_header)) {
1732  fors_pmos_science_exit(NULL, nscience);
1733  }
1734  }
1735 
1736  cpl_image_delete(wavemap); wavemap = NULL;
1737 
1738  mapped = mos_wavelength_calibration(smapped, reference,
1739  startwavelength, endwavelength,
1740  dispersion, idscoeff, flux);
1741 
1742  cpl_image_delete(smapped); smapped = NULL;
1743 
1744  if (skyalign >= 0) {
1745  if (!j) {
1746  if (dfs_save_table(frameset, idscoeff, disp_coeff_sky_tag,
1747  NULL, parlist, recipe, version)) {
1748  fors_pmos_science_exit(NULL, nscience);
1749  }
1750  }
1751  }
1752 
1753  if (skymedian) {
1754  cpl_msg_indent_less();
1755  cpl_msg_info(recipe, "Local sky determination...");
1756  cpl_msg_indent_more();
1757 
1758  skylocalmap = mos_sky_local_old(mapped, slits);
1759  cpl_image_subtract(mapped, skylocalmap);
1760  cpl_image_delete(skylocalmap); skylocalmap = NULL;
1761  }
1762 
1763  if (skymedian || skylocal) {
1764 
1765  skylocalmap = cpl_image_subtract_create(mapped_sky, mapped);
1766 
1767  cpl_image_delete(mapped_sky); mapped_sky = NULL;
1768 
1769  if (time_normalise) {
1770  dummy = cpl_image_divide_scalar_create(skylocalmap, alltime);
1771 
1772  if (!j) {
1773  if(dfs_save_image_null(frameset, parlist,
1774  mapped_sky_tag,
1775  recipe, version)) {
1776  fors_pmos_science_exit(NULL, nscience);
1777  }
1778  }
1779 
1780  if (dfs_save_image_ext(dummy, mapped_sky_tag,
1781  header)) {
1782  fors_pmos_science_exit(NULL, nscience);
1783  }
1784 
1785  cpl_image_delete(dummy); dummy = NULL;
1786  }
1787  else {
1788  if (!j) {
1789  if(dfs_save_image_null(frameset, parlist,
1790  mapped_sky_tag,
1791  recipe, version)) {
1792  fors_pmos_science_exit(NULL, nscience);
1793  }
1794  }
1795 
1796  if (dfs_save_image_ext(skylocalmap, mapped_sky_tag,
1797  header)) {
1798  fors_pmos_science_exit(NULL, nscience);
1799  }
1800  }
1801 
1802  skylocalmaps[j] = skylocalmap;
1803 
1804  cpl_msg_indent_less();
1805  cpl_msg_info(recipe, "Object detection...");
1806  cpl_msg_indent_more();
1807 
1808  if (!j) {
1809  origslits = cpl_table_duplicate(slits);
1810  nslits = cpl_table_get_nrow(slits);
1811  }
1812 
1813  if (cosmics || nscience > 1) {
1814  dummy = mos_detect_objects(mapped, slits, slit_margin,
1815  ext_radius, cont_radius);
1816  }
1817  else {
1818  mapped_cleaned = cpl_image_duplicate(mapped);
1819  mos_clean_cosmics(mapped_cleaned, gain, -1., -1.);
1820  dummy = mos_detect_objects(mapped_cleaned, slits, slit_margin,
1821  ext_radius, cont_radius);
1822 
1823  cpl_image_delete(mapped_cleaned); mapped_cleaned = NULL;
1824  }
1825 
1826  cpl_image_delete(dummy); dummy = NULL;
1827 
1828  }
1829 
1830  slitss[j] = slits;
1831  mappeds[j] = mapped;
1832 
1833  cpl_msg_indent_less();
1834 
1835  cpl_propertylist_delete(header); header = NULL;
1836  cpl_propertylist_delete(save_header); save_header = NULL;
1837  }
1838 
1839  cpl_table_delete(offsets); offsets = NULL;
1840  cpl_table_delete(idscoeff); idscoeff = NULL;
1841 
1842  cpl_image_delete(norm_flat); norm_flat = NULL;
1843  cpl_vector_delete(lines); lines = NULL;
1844 
1845 
1846  cpl_msg_indent_less();
1847  cpl_msg_info(recipe,
1848  "Check object detection in both beams for all angles...");
1849  cpl_msg_indent_more();
1850 
1851  /*
1852  * House keeping - selection of objects for which information required
1853  * for Stokes parameters computation is present
1854  */
1855 
1856  error = mos_object_intersect(slitss, origslits, nscience, tolerance);
1857  if (error == CPL_ERROR_DATA_NOT_FOUND) {
1858  cpl_msg_warning(recipe, "No objects found: no Stokes "
1859  "parameters to compute!");
1860  for (j = 0; j < nscience; j++)
1861  cpl_table_delete(slitss[j]);
1862  cpl_free(slitss);
1863  cpl_table_delete(origslits);
1864  return 0;
1865  } else if (error) {
1866  fors_pmos_science_exit("Problem in polarimetric object selection", nscience);
1867  }
1868 
1869  if (dfs_save_table(frameset, origslits, object_table_pol_tag,
1870  NULL, parlist, recipe, version)) {
1871  fors_pmos_science_exit(NULL, nscience);
1872  }
1873 
1874  /*
1875  * Save also object tables per angle after intersection
1876  */
1877 
1878  for (j = 0; j < nscience; j++) {
1879  if (!j) {
1880  if(dfs_save_image_null(frameset, parlist, object_table_tag,
1881  recipe, version)) {
1882  fors_pmos_science_exit(NULL, nscience);
1883  }
1884  }
1885 
1886  if (dfs_save_table_ext(slitss[j], object_table_tag, NULL)) {
1887  fors_pmos_science_exit(NULL, nscience);
1888  }
1889  }
1890 
1891  nobjs_per_slit = fors_get_nobjs_perslit(origslits);
1892 
1893  cpl_msg_indent_less();
1894  cpl_msg_info(recipe, "Object extraction...");
1895  cpl_msg_indent_more();
1896 
1897  for (j = 0; j < nscience; j++) {
1898  int k;
1899 
1900  header = dfs_load_header(frameset, science_tag, 0);
1901 
1902  for (k = 0; k < j; k ++) {
1903  cpl_propertylist_delete(header);
1904  header = dfs_load_header(frameset, NULL, 0);
1905  }
1906 
1907  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
1908  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
1909  cpl_propertylist_update_double(header, "CRVAL1",
1910  startwavelength + (dispersion * group)/2);
1911  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
1912  cpl_propertylist_update_double(header, "CD1_1", dispersion * group);
1913  cpl_propertylist_update_double(header, "CD1_2", 0.0);
1914  cpl_propertylist_update_double(header, "CD2_1", 0.0);
1915  cpl_propertylist_update_double(header, "CD2_2", 1.0);
1916  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
1917  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
1918 
1919  if (skymedian || skylocal) {
1920 
1921  cpl_msg_info(recipe, "Extracting at angle %.2f (%d out of %d) ...",
1922  angles[j], j + 1, nscience);
1923 
1924  images = mos_extract_objects(mappeds[j], NULL, skylocalmaps[j],
1925  origslits,
1926  ext_mode, ron, gain, 1);
1927 
1928  cpl_image_delete(skylocalmaps[j]); skylocalmaps[j] = NULL;
1929 
1930  if (images) {
1931  if (time_normalise)
1932  cpl_image_divide_scalar(images[0], alltime);
1933 
1934  mos_rebin_signal(images, group);
1935 
1936  if (!j) {
1937  if(dfs_save_image_null(frameset, parlist,
1938  reduced_science_tag,
1939  recipe, version)) {
1940  fors_pmos_science_exit(NULL, nscience);
1941  }
1942  }
1943 
1944  if (dfs_save_image_ext(images[0], reduced_science_tag,
1945  header)) {
1946  fors_pmos_science_exit(NULL, nscience);
1947  }
1948 
1949  reduceds[j] = images[0];
1950 
1951  if (time_normalise)
1952  cpl_image_divide_scalar(images[1], alltime);
1953 
1954  mos_rebin_signal(images + 1, group);
1955 
1956  if (!j) {
1957  if(dfs_save_image_null(frameset, parlist,
1958  reduced_sky_tag,
1959  recipe, version)) {
1960  fors_pmos_science_exit(NULL, nscience);
1961  }
1962  }
1963 
1964  if (dfs_save_image_ext(images[1], reduced_sky_tag,
1965  header)) {
1966  fors_pmos_science_exit(NULL, nscience);
1967  }
1968  cpl_image_delete(images[1]);
1969 
1970  if (time_normalise)
1971  cpl_image_divide_scalar(images[2], alltime);
1972 
1973  mos_rebin_error(images + 2, group);
1974 
1975  if (!j) {
1976  if(dfs_save_image_null(frameset, parlist,
1977  reduced_error_tag,
1978  recipe, version)) {
1979  fors_pmos_science_exit(NULL, nscience);
1980  }
1981  }
1982 
1983  if (dfs_save_image_ext(images[2], reduced_error_tag,
1984  header)) {
1985  fors_pmos_science_exit(NULL, nscience);
1986  }
1987 
1988  rerrors[j] = images[2];
1989 
1990  cpl_free(images);
1991  }
1992  else {
1993  cpl_msg_warning(recipe, "No objects found: the products "
1994  "%s, %s, and %s are not created",
1995  reduced_science_tag, reduced_sky_tag,
1996  reduced_error_tag);
1997  }
1998 
1999  }
2000 
2001  if (skymedian || skylocal) {
2002  if (time_normalise)
2003  cpl_image_divide_scalar(mappeds[j], alltime);
2004 
2005  if (!j) {
2006  if(dfs_save_image_null(frameset, parlist,
2007  mapped_science_tag,
2008  recipe, version)) {
2009  fors_pmos_science_exit(NULL, nscience);
2010  }
2011  }
2012 
2013  if (dfs_save_image_ext(mappeds[j], mapped_science_tag,
2014  header)) {
2015  fors_pmos_science_exit(NULL, nscience);
2016  }
2017  }
2018 
2019  cpl_image_delete(mappeds[j]); mappeds[j] = NULL;
2020  cpl_propertylist_delete(header); header = NULL;
2021 
2022  }
2023 
2024  cpl_table_delete(origslits);
2025 
2026  /* Stokes computation */
2027 
2028  nobjects = cpl_image_get_size_y(reduceds[0]) / 2;
2029  nx = cpl_image_get_size_x(reduceds[0]);
2030 
2031  header = cpl_propertylist_new();
2032  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
2033  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
2034  cpl_propertylist_update_double(header, "CRVAL1",
2035  startwavelength + (dispersion * group)/2);
2036  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
2037  cpl_propertylist_update_double(header, "CD1_1", dispersion * group);
2038  cpl_propertylist_update_double(header, "CD1_2", 0.0);
2039  cpl_propertylist_update_double(header, "CD2_1", 0.0);
2040  cpl_propertylist_update_double(header, "CD2_2", 1.0);
2041  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
2042  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
2043 
2044  if (circ) {
2045 
2046  cpl_image *pv_im = NULL;
2047  cpl_image *pi_im = NULL;
2048  cpl_image *pvnull_im = NULL;
2049  cpl_image *pierr_im = NULL;
2050  cpl_image *perr_im = NULL;
2051 
2052  double *p_v = NULL;
2053  double *p_i = NULL;
2054  double *p_vnull = NULL;
2055  double *perr = NULL;
2056  double *pierr = NULL;
2057 
2058  double mean_vnull;
2059 
2060  int p = -1;
2061  int total = 0;
2062 
2063  pv_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2064  perr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2065  pi_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2066  pierr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2067 
2068  p_v = cpl_image_get_data_double(pv_im);
2069  perr = cpl_image_get_data_double(perr_im);
2070  p_i = cpl_image_get_data_double(pi_im);
2071  pierr = cpl_image_get_data_double(pierr_im);
2072 
2073  if (nscience / 2 > 1) {
2074  pvnull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2075  p_vnull = cpl_image_get_data_double(pvnull_im);
2076  }
2077 
2078  for (j = 0; j < nobjects; j++) {
2079 
2080  FILE *file; // Bagoo
2081  char *filename; // Bagoo
2082 
2083  int k, m;
2084 
2085  double * ip_v, * ip_i, * ipierr,
2086  * ip_vnull, * iperr;
2087 
2088  float * data;
2089  float * iff, * ierr;
2090 
2091  ip_v = p_v + (nobjects - 1 - j) * nx;
2092 
2093  if (nscience / 2 > 1)
2094  ip_vnull = p_vnull + (nobjects - 1 - j) * nx;
2095 
2096  iperr = perr + (nobjects - 1 - j) * nx;
2097 
2098  ip_i = p_i + (nobjects - 1 - j) * nx;
2099  ipierr = pierr + (nobjects - 1 - j) * nx;
2100 
2101  total = 0;
2102  for (i = 0; i < nslits; i += 2) {
2103  total += nobjs_per_slit[i];
2104  if (total > j) {
2105  p = i;
2106  break;
2107  }
2108  }
2109 
2110  for (k = 0; k < nscience / 2; k++) {
2111  float *if_o, *if_e, *ifdelta_o, *ifdelta_e;
2112  float *if_o_err, *if_e_err, *ifdelta_o_err, *ifdelta_e_err;
2113 
2114  int pos = fors_find_angle_pos(angles, nscience, 180 * k - 45);
2115  int pos_d = fors_find_angle_pos(angles, nscience, 180 * k + 45);
2116 
2117 
2118  data = cpl_image_get_data_float(reduceds[pos]);
2119 
2120  if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2121  + (total - j - 1)) * nx;
2122 
2123  if_e = data + (2 * (nobjects - total)
2124  + (total - j - 1)) * nx;
2125 
2126  data = cpl_image_get_data_float(reduceds[pos_d]);
2127 
2128  ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2129  + (total - j - 1)) * nx;
2130 
2131  ifdelta_e = data + (2 * (nobjects - total)
2132  + (total - j - 1)) * nx;
2133 
2134  data = cpl_image_get_data_float(rerrors[pos]);
2135 
2136  if_o_err = data
2137  + (2 * (nobjects - total) + nobjs_per_slit[p]
2138  + (total - j - 1)) * nx;
2139 
2140  if_e_err = data + (2 * (nobjects - total)
2141  + (total - j - 1)) * nx;
2142 
2143  data = cpl_image_get_data_float(rerrors[pos_d]);
2144 
2145  ifdelta_o_err = data
2146  + (2 * (nobjects - total) + nobjs_per_slit[p]
2147  + (total - j - 1)) * nx;
2148 
2149  ifdelta_e_err = data + (2 * (nobjects - total)
2150  + (total - j - 1)) * nx;
2151 
2152  if (bagoo) {
2153 
2154  char *signal_to_noise = getenv("SIGNAL_TO_NOISE" );
2155  float s2n = 100.;
2156  char *min_s2n = getenv("MIN_S2N" );
2157  int ms2n = 50;
2158 
2159  if (signal_to_noise)
2160  s2n = atof(signal_to_noise);
2161 
2162  if (min_s2n)
2163  ms2n = atoi(min_s2n);
2164 
2165  /*
2166  * Check whether S/N is > s2n in more than ms2n pixels
2167  * (on first frame, on ordinary beam)
2168  */
2169 
2170  if (k == 0) {
2171  bright = 0;
2172  for (m = 0; m < nx; m++) {
2173  if (if_o_err[m] > 0.0) {
2174  if (if_o[m]/if_o_err[m] > s2n) {
2175  bright++;
2176  if (bright > ms2n) {
2177  break;
2178  }
2179  }
2180  }
2181  }
2182  }
2183 
2184  if (bright > ms2n) {
2185  conta++;
2186  filename = cpl_sprintf("angle_%d_%d.dat",
2187  180*k-45, conta);
2188  file = fopen(filename, "w");
2189 
2190  fprintf(file, "%d\n", p + 2);
2191 
2192  for (m = 0; m < nx; m++) {
2193  double lambda = startwavelength
2194  + dispersion * group * (0.5 + m);
2195  fprintf(file, "%.3f %.9e %.9e %.9e %.9e\n",
2196  lambda, if_o[m], if_o_err[m],
2197  if_e[m], if_e_err[m]);
2198  }
2199 
2200  fclose(file);
2201  cpl_free(filename);
2202 
2203  filename = cpl_sprintf("angle_%d_%d.dat",
2204  180*k+45, conta);
2205  file = fopen(filename, "w");
2206 
2207  fprintf(file, "%d\n", p + 2);
2208 
2209  for (m = 0; m < nx; m++) {
2210  double lambda = startwavelength
2211  + dispersion * group * (0.5 + m);
2212  fprintf(file, "%.3f %.9e %.9e %.9e %.9e\n",
2213  lambda, ifdelta_o[m], ifdelta_o_err[m],
2214  ifdelta_e[m], ifdelta_e_err[m]);
2215  }
2216 
2217  fclose(file);
2218  cpl_free(filename);
2219  }
2220  else {
2221  cpl_msg_info(recipe,
2222  "Extracted signal not written to "
2223  "ASCII (S/N > %.0f only in %d < %d "
2224  "bins)", s2n, bright, ms2n);
2225  }
2226  } // End of bagoo
2227 
2228  for (m = 0; m < nx; m++) {
2229 
2230  double quantity = if_o[m] + if_e[m] == 0.0 ? 0.0 :
2231  (if_o[m] - if_e[m] ) /
2232  (if_o[m] + if_e[m] ) -
2233  (ifdelta_o[m] - ifdelta_e[m]) /
2234  (ifdelta_o[m] + ifdelta_e[m]);
2235 
2236  quantity = isfinite(quantity) ? quantity : 0.0;
2237 
2238  /* PQ map computation */
2239  ip_v[m] += quantity * 0.5 / (nscience / 2);
2240 
2241  /* PQnull map computation */
2242  if (nscience / 2 > 1) {
2243  if (k % 2)
2244  ip_vnull[m] += quantity * 0.5 / (nscience / 2);
2245  else
2246  ip_vnull[m] -= quantity * 0.5 / (nscience / 2);
2247  }
2248 
2249  /* I map computation */
2250  ip_i[m] += (if_o[m] + if_e[m] +
2251  ifdelta_o[m] + ifdelta_e[m]) / nscience;
2252 
2253  /* Variance map computation */
2254  ipierr[m] += (if_o_err[m] * if_o_err[m]
2255  + if_e_err[m] * if_e_err[m]
2256  + ifdelta_o_err[m] * ifdelta_o_err[m]
2257  + ifdelta_e_err[m] * ifdelta_e_err[m])
2258  / nscience / nscience;
2259 
2260  }
2261  }
2262 
2263  /* Error map */
2264  data = cpl_image_get_data_float(reduceds[0]);
2265  iff = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
2266 
2267  data = cpl_image_get_data_float(rerrors[0]);
2268  ierr = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
2269 
2270  for (m = 0; m < nx; m++)
2271  iperr[m] = iff[m] <= 0.0 ?
2272  0.0 : ierr[m] / iff[m] * 0.5 / sqrt (nscience / 2);
2273 
2274  if (nscience / 2 > 1) {
2275  float * weights;
2276  float max, sum, sum2, imean;
2277 
2278  int k;
2279 
2280  /* QC on U NULL */
2281  weights = cpl_malloc(sizeof(float) * nx);
2282 
2283  max = 0.0;
2284  for (k = 0; k < nx; k++) {
2285  if (max < iff[k]) max = iff[k];
2286  }
2287 
2288  for (k = 0; k < nx; k++) {
2289  weights[k] = iff[k] < 0.0 ?
2290  0.0 : iff[k] * iff[k] / (max * max);
2291  }
2292 
2293  sum = 0.0;
2294  sum2 = 0.0;
2295  for (k = 0; k < nx; k++) {
2296  sum += weights[k] * ip_vnull[k];
2297  sum2 += weights[k];
2298  }
2299 
2300  cpl_free(weights);
2301 
2302  imean = sum / sum2;
2303 
2304  mean_vnull += (imean - mean_vnull) / (j + 1.0);
2305  }
2306  }
2307 
2308  if (dfs_save_image(frameset, pv_im, reduced_v_tag, header,
2309  parlist, recipe, version))
2310  fors_pmos_science_exit(NULL, nscience);
2311 
2312  if (dfs_save_image(frameset, pi_im, reduced_i_tag, header,
2313  parlist, recipe, version))
2314  fors_pmos_science_exit(NULL, nscience);
2315 
2316  if (nscience / 2 > 1) {
2317  char *pipefile;
2318  char *keyname;
2319  cpl_propertylist *qheader;
2320 
2321  qheader = dfs_load_header(frameset, science_tag, 0);
2322  cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
2323  cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
2324  cpl_propertylist_update_double(qheader, "CRVAL1",
2325  startwavelength + (dispersion * group)/2);
2326  cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
2327  cpl_propertylist_update_double(qheader, "CD1_1",
2328  dispersion * group);
2329  cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
2330  cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
2331  cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
2332  cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
2333  cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
2334 
2335  if (qc) {
2336  fors_qc_start_group(qheader, "2.0", instrume);
2337 
2338  /*
2339  * QC1 group header
2340  */
2341 
2342  if (fors_qc_write_string("PRO.CATG", reduced_nul_v_tag,
2343  "Product category", instrume))
2344  fors_pmos_science_exit("Cannot write product category to "
2345  "QC log file", nscience);
2346 
2347  if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
2348  "DPR type", instrume))
2349  fors_pmos_science_exit("Missing keyword DPR TYPE in "
2350  "scientific frame header", nscience);
2351 
2352  if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
2353  "Template", instrume))
2354  fors_pmos_science_exit("Missing keyword TPL ID in "
2355  "scientific frame header", nscience);
2356 
2357  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
2358  "Grism name", instrume))
2359  fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
2360  "scientific frame header", nscience);
2361 
2362  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
2363  "Grism identifier", instrume))
2364  fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
2365  "scientific frame header", nscience);
2366 
2367  if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
2368  fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
2369  "Filter name", instrume);
2370 
2371  if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
2372  "Collimator name", instrume))
2373  fors_pmos_science_exit("Missing keyword INS COLL NAME in "
2374  "scientific frame header", nscience);
2375 
2376  if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
2377  "Chip identifier", instrume))
2378  fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
2379  "scientific frame header", nscience);
2380 
2381  if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
2382  "Archive name of input data",
2383  instrume))
2384  fors_pmos_science_exit("Missing keyword ARCFILE in "
2385  "scientific frame header", nscience);
2386 
2387  pipefile = dfs_generate_filename(reduced_nul_v_tag);
2388  if (fors_qc_write_string("PIPEFILE", pipefile,
2389  "Pipeline product name", instrume))
2390  fors_pmos_science_exit("Cannot write PIPEFILE to "
2391  "QC log file", nscience);
2392  cpl_free(pipefile); pipefile = NULL;
2393 
2394 
2395  /*
2396  * QC1 parameters
2397  */
2398 
2399  keyname = "QC.NULL.V.MEAN";
2400 
2401  if (fors_qc_write_qc_double(qheader, mean_vnull,
2402  keyname, NULL,
2403  "Mean V null parameter",
2404  instrume)) {
2405  fors_pmos_science_exit("Cannot write mean Q null "
2406  "parameter to QC log file.", nscience);
2407  }
2408 
2409  keyname = "QC.NANGLES";
2410 
2411  if (fors_qc_write_qc_int(qheader, nscience,
2412  keyname, NULL,
2413  "Number of processed plate angles",
2414  instrume)) {
2415  fors_pmos_science_exit("Cannot write number of processed "
2416  "plate angles.", nscience);
2417  }
2418 
2420  }
2421 
2422  if (dfs_save_image(frameset, pvnull_im, reduced_nul_v_tag, qheader,
2423  parlist, recipe, version))
2424  fors_pmos_science_exit(NULL, nscience);
2425 
2426  cpl_propertylist_delete(qheader);
2427  }
2428 
2429  if (dfs_save_image(frameset, perr_im, reduced_error_v_tag, header,
2430  parlist, recipe, version))
2431  fors_pmos_science_exit(NULL, nscience);
2432 
2433  cpl_image_power(pierr_im, 0.5);
2434 
2435  if (dfs_save_image(frameset, pierr_im, reduced_error_i_tag, header,
2436  parlist, recipe, version))
2437  fors_pmos_science_exit(NULL, nscience);
2438 
2439  cpl_image_delete(pv_im);
2440  cpl_image_delete(pvnull_im);
2441  cpl_image_delete(perr_im);
2442  cpl_image_delete(pi_im);
2443  cpl_image_delete(pierr_im);
2444  }
2445  else { /* Linear polarisation */
2446  cpl_image *pq_im = NULL;
2447  cpl_image *pu_im = NULL;
2448  cpl_image *pl_im = NULL;
2449  cpl_image *pi_im = NULL;
2450 
2451  cpl_image *pqnull_im = NULL;
2452  cpl_image *punull_im = NULL;
2453 
2454  cpl_image *pqerr_im = NULL;
2455  cpl_image *puerr_im = NULL;
2456  cpl_image *plerr_im = NULL;
2457  cpl_image *pierr_im = NULL;
2458 
2459  cpl_image *pang_im = NULL;
2460  cpl_image *pangerr_im = NULL;
2461 
2462  double *p_q = NULL;
2463  double *p_u = NULL;
2464  double *p_l = NULL;
2465  double *p_i = NULL;
2466 
2467  double *p_qnull = NULL;
2468  double *p_unull = NULL;
2469 
2470  double *pqerr = NULL;
2471  double *puerr = NULL;
2472  double *plerr = NULL;
2473  double *pierr = NULL;
2474 
2475  double *pang = NULL;
2476  double *pangerr = NULL;
2477 
2478  int k, m;
2479 
2480  cpl_image *correct_im = cpl_image_new(nx, 1, CPL_TYPE_DOUBLE);
2481  double *correct = cpl_image_get_data_double(correct_im);
2482 
2483  double mean_unull, mean_qnull;
2484 
2485  int p = -1;
2486  int total = 0;
2487 
2488  pq_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2489  pu_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2490  pl_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2491  pi_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2492 
2493  pqerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2494  puerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2495  plerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2496  pierr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2497 
2498  pang_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2499  pangerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2500 
2501  p_q = cpl_image_get_data_double(pq_im);
2502  p_u = cpl_image_get_data_double(pu_im);
2503  p_l = cpl_image_get_data_double(pl_im);
2504  p_i = cpl_image_get_data_double(pi_im);
2505 
2506  if (nscience / 4 > 1) {
2507  pqnull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2508  punull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2509 
2510  p_qnull = cpl_image_get_data_double(pqnull_im);
2511  p_unull = cpl_image_get_data_double(punull_im);
2512  } else {
2513  cpl_msg_warning(cpl_func,
2514  "Not enough pairs to compute null parameters");
2515  }
2516 
2517  pqerr = cpl_image_get_data_double(pqerr_im);
2518  puerr = cpl_image_get_data_double(puerr_im);
2519  plerr = cpl_image_get_data_double(plerr_im);
2520  pierr = cpl_image_get_data_double(pierr_im);
2521 
2522  pang = cpl_image_get_data_double(pang_im);
2523  pangerr = cpl_image_get_data_double(pangerr_im);
2524 
2525  if (chromatism) {
2526  cpl_table * chrotbl =
2527  dfs_load_table(frameset, chrom_table_tag, 1);
2528 
2529  int nrow = cpl_table_get_nrow(chrotbl);
2530  float * lambda = cpl_table_get_data_float(chrotbl, "lambda");
2531  float * theta = cpl_table_get_data_float(chrotbl, "eps_theta");
2532 
2533  for (j = 0; j < nx; j++) {
2534  double c_wave = startwavelength
2535  + (dispersion * group) / 2
2536  + j * dispersion * group;
2537 
2538  int found = 0;
2539 
2540  for (k = 0; k < nrow - 1; k++) {
2541  if (lambda[k] <= c_wave && c_wave < lambda[k + 1]) {
2542  found = 1;
2543  break;
2544  }
2545  }
2546 
2547  if (found) {
2548  correct[j] = (theta [k + 1] - theta [k]) /
2549  (lambda[k + 1] - lambda[k]) *
2550  (c_wave - lambda[k]) + theta[k];
2551  correct[j] *= M_PI / 180; /* Radians */
2552  }
2553  else if (j)
2554  correct[j] = correct[j-1];
2555  else
2556  correct[j] = 0.0;
2557  }
2558 
2559  cpl_table_delete(chrotbl);
2560  }
2561 
2562  for (j = 0; j < nobjects; j++) {
2563  double *ip_q;
2564  double *ip_u;
2565  double *ip_l;
2566  double *ip_i;
2567  double *ipierr;
2568  double *ip_qnull;
2569  double *ip_unull;
2570  double *ipqerr;
2571  double *ipuerr;
2572  double *iplerr;
2573  double *ipang;
2574  double *ipangerr;
2575 
2576  float *data;
2577  float *iffq;
2578  float *ierrq;
2579  float *iffu;
2580  float *ierru;
2581 
2582  int pos, pos_d;
2583 
2584  ip_q = p_q + (nobjects - 1 - j) * nx;
2585  ip_u = p_u + (nobjects - 1 - j) * nx;
2586  ip_l = p_l + (nobjects - 1 - j) * nx;
2587  ip_i = p_i + (nobjects - 1 - j) * nx;
2588 
2589  if (nscience / 4 > 1) {
2590  ip_qnull = p_qnull + (nobjects - 1 - j) * nx;
2591  ip_unull = p_unull + (nobjects - 1 - j) * nx;
2592  }
2593 
2594  ipqerr = pqerr + (nobjects - 1 - j) * nx;
2595  ipuerr = puerr + (nobjects - 1 - j) * nx;
2596  iplerr = plerr + (nobjects - 1 - j) * nx;
2597  ipierr = pierr + (nobjects - 1 - j) * nx;
2598 
2599  ipang = pang + (nobjects - 1 - j) * nx;
2600  ipangerr = pangerr + (nobjects - 1 - j) * nx;
2601 
2602  total = 0;
2603  for (i = 0; i < nslits; i += 2) {
2604  total += nobjs_per_slit[i];
2605  if (total > j) {
2606  p = i;
2607  break;
2608  }
2609  }
2610 
2611  for (k = 0; k < nscience / 4; k++) {
2612  float * if_o, * if_e, * ifdelta_o, * ifdelta_e;
2613  float * if_o_err, * if_e_err, * ifdelta_o_err, * ifdelta_e_err;
2614 
2615  /* First P_Q */
2616 
2617  pos = fors_find_angle_pos(angles, nscience, 90 * k);
2618  pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 45);
2619 
2620  data = cpl_image_get_data_float(reduceds[pos]);
2621 
2622  if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2623  + (total - j - 1)) * nx;
2624 
2625  if_e = data + (2 * (nobjects - total)
2626  + (total - j - 1)) * nx;
2627 
2628  data = cpl_image_get_data_float(reduceds[pos_d]);
2629 
2630  ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2631  + (total - j - 1)) * nx;
2632 
2633  ifdelta_e = data + (2 * (nobjects - total)
2634  + (total - j - 1)) * nx;
2635 
2636  data = cpl_image_get_data_float(rerrors[pos]);
2637 
2638  if_o_err = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2639  + (total - j - 1)) * nx;
2640 
2641  if_e_err = data + (2 * (nobjects - total)
2642  + (total - j - 1)) * nx;
2643 
2644  data = cpl_image_get_data_float(rerrors[pos_d]);
2645 
2646  ifdelta_o_err = data + (2 * (nobjects - total)
2647  + nobjs_per_slit[p] + (total - j - 1)) * nx;
2648 
2649  ifdelta_e_err = data + (2 * (nobjects - total)
2650  + (total - j - 1)) * nx;
2651 
2652  for (m = 0; m < nx; m++) {
2653 
2654  double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
2655  (if_o[m] - if_e[m] ) /
2656  (if_o[m] + if_e[m] ) -
2657  (ifdelta_o[m] - ifdelta_e[m]) /
2658  (ifdelta_o[m] + ifdelta_e[m]);
2659 
2660  quantity = isfinite(quantity) ? quantity : 0.0;
2661 
2662  /* PQ map computation */
2663  ip_q[m] += quantity * 0.5 / (nscience / 4);
2664 
2665  /* PQnull map computation */
2666  if (nscience / 4 > 1) {
2667  if (k % 2)
2668  ip_qnull[m] += quantity * 0.5 / (nscience / 4);
2669  else
2670  ip_qnull[m] -= quantity * 0.5 / (nscience / 4);
2671  }
2672 
2673  /* I map computation */
2674  ip_i[m] += (if_o[m] + if_e[m] +
2675  ifdelta_o[m] + ifdelta_e[m]) / nscience;
2676 
2677  /* Variance map computation */
2678  ipierr[m] += (if_o_err[m] * if_o_err[m]
2679  + if_e_err[m] * if_e_err[m]
2680  + ifdelta_o_err[m] * ifdelta_o_err[m]
2681  + ifdelta_e_err[m] * ifdelta_e_err[m])
2682  / nscience / nscience;
2683  }
2684 
2685  /* Now P_U */
2686 
2687  pos = fors_find_angle_pos(angles, nscience, 90 * k + 22.5);
2688  pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 67.5);
2689 
2690  data = cpl_image_get_data_float(reduceds[pos]);
2691 
2692  if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2693  + (total - j - 1)) * nx;
2694 
2695  if_e = data + (2 * (nobjects - total)
2696  + (total - j - 1)) * nx;
2697 
2698  data = cpl_image_get_data_float(reduceds[pos_d]);
2699 
2700  ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2701  + (total - j - 1)) * nx;
2702 
2703  ifdelta_e = data + (2 * (nobjects - total)
2704  + (total - j - 1)) * nx;
2705 
2706  data = cpl_image_get_data_float(rerrors[pos]);
2707 
2708  if_o_err = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2709  + (total - j - 1)) * nx;
2710 
2711  if_e_err = data + (2 * (nobjects - total)
2712  + (total - j - 1)) * nx;
2713 
2714  data = cpl_image_get_data_float(rerrors[pos_d]);
2715 
2716  ifdelta_o_err = data + (2 * (nobjects - total)
2717  + nobjs_per_slit[p] + (total - j - 1)) * nx;
2718 
2719  ifdelta_e_err = data + (2 * (nobjects - total)
2720  + (total - j - 1)) * nx;
2721 
2722  for (m = 0; m < nx; m++) {
2723 
2724  double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
2725  (if_o[m] - if_e[m] ) /
2726  (if_o[m] + if_e[m] ) -
2727  (ifdelta_o[m] - ifdelta_e[m]) /
2728  (ifdelta_o[m] + ifdelta_e[m]);
2729 
2730  quantity = isfinite(quantity) ? quantity : 0.0;
2731 
2732  /* PU map computation */
2733  ip_u[m] += quantity * 0.5 / (nscience / 4);
2734 
2735  /* PUnull map computation */
2736  if (nscience / 4 > 1) {
2737  if (k % 2)
2738  ip_unull[m] += quantity * 0.5 / (nscience / 4);
2739  else
2740  ip_unull[m] -= quantity * 0.5 / (nscience / 4);
2741  }
2742 
2743  /* I map computation */
2744  ip_i[m] += (if_o[m] + if_e[m] +
2745  ifdelta_o[m] + ifdelta_e[m]) / nscience;
2746 
2747  /* Variance map computation */
2748  ipierr[m] += (if_o_err[m] * if_o_err[m]
2749  + if_e_err[m] * if_e_err[m]
2750  + ifdelta_o_err[m] * ifdelta_o_err[m]
2751  + ifdelta_e_err[m] * ifdelta_e_err[m])
2752  / nscience / nscience;
2753  }
2754  }
2755 
2756  /* Error map */
2757 
2758  pos = fors_find_angle_pos(angles, nscience, 0.0);
2759 
2760  data = cpl_image_get_data_float(reduceds[pos]);
2761  iffq = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
2762 
2763  data = cpl_image_get_data_float(rerrors[pos]);
2764  ierrq = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
2765 
2766  pos = fors_find_angle_pos(angles, nscience, 22.5);
2767 
2768  data = cpl_image_get_data_float(reduceds[pos]);
2769  iffu = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
2770 
2771  data = cpl_image_get_data_float(rerrors[pos]);
2772  ierru = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
2773 
2774  for (m = 0; m < nx; m++) {
2775 
2776  double radicand;
2777 
2778  ipqerr[m] = iffq[m] <= 0.0 ?
2779  0.0 : ierrq[m] / iffq[m] * 0.5 / sqrt (nscience / 4);
2780 
2781  ipuerr[m] = iffu[m] <= 0.0 ?
2782  0.0 : ierru[m] / iffu[m] * 0.5 / sqrt (nscience / 4);
2783 
2784  iplerr[m] = 0.5 * (ipqerr[m] + ipuerr[m]);
2785 
2786  /* PL computation */
2787  ip_l[m] = sqrt(ip_u[m] * ip_u[m] + ip_q[m] * ip_q[m]);
2788 
2789  /* P angle computation */
2790  if (fabs(ip_q[m]) < 0.00001) {
2791  if (ip_u[m] > 0.0) {
2792  ipang[m] = 45.0;
2793  }
2794  else {
2795  ipang[m] = 135.0;
2796  }
2797  }
2798  else {
2799  ipang[m] = 0.5 * atan(ip_u[m] / ip_q[m]) * 180 / M_PI;
2800  if (ip_q[m] > 0.0) {
2801  if (ip_u[m] < 0.0) {
2802  ipang[m] += 180.;
2803  }
2804  }
2805  else {
2806  ipang[m] += 90.;
2807  }
2808  }
2809 
2810  /* Error on the angle computation */
2811  radicand = ip_q[m] * ip_q[m] * ipuerr[m] * ipuerr[m] +
2812  ip_u[m] * ip_u[m] * ipqerr[m] * ipqerr[m];
2813 
2814  ipangerr[m] = (ip_l[m] == 0.0 ? 0.0 :
2815  sqrt(radicand) * 0.5 / (ip_l[m] * ip_l[m]) * 180 / M_PI);
2816 
2817  /*
2818  * This is a quick and dirty patch for FORS2 had the
2819  * Wolly mounted +180 with respect to FORS1. I must
2820  * hardcode it, because there is no such info in the
2821  * header.
2822  */
2823 
2824  if (instrume[4] == '2') {
2825 
2826  double w_rotation = - wollaston * M_PI / 2;
2827 
2828  ipang[m] -= w_rotation * 180 / M_PI;
2829 
2830  ip_q[m] = ip_q[m] * cos(2 * w_rotation)
2831  + ip_u[m] * sin(2 * w_rotation);
2832 
2833  ip_u[m] = ip_u[m] * cos(2 * w_rotation)
2834  - ip_q[m] * sin(2 * w_rotation);
2835  }
2836 
2837  if (chromatism) {
2838  ipang[m] -= correct[m] * 180 / M_PI;
2839 
2840  ip_q[m] = ip_q[m] * cos(2 * correct[m])
2841  + ip_u[m] * sin(2 * correct[m]);
2842 
2843  ip_u[m] = ip_u[m] * cos(2 * correct[m])
2844  - ip_q[m] * sin(2 * correct[m]);
2845  }
2846 
2847  if (ipang[m] < 0.0)
2848  ipang[m] += 180.;
2849  else if (ipang[m] >= 180.0)
2850  ipang[m] -= 180.;
2851  }
2852 
2853  if (nscience / 4 > 1) {
2854  float * weights;
2855  float max, sum, sum2, imean;
2856 
2857  int k;
2858 
2859  /* QC on Q NULL */
2860  weights = cpl_malloc(sizeof(float) * nx);
2861 
2862  max = 0.0;
2863  for (k = 0; k < nx; k++) {
2864  if (max < iffq[k]) max = iffq[k];
2865  }
2866 
2867  for (k = 0; k < nx; k++) {
2868  weights[k] = iffq[k] < 0.0 ?
2869  0.0 : iffq[k] * iffq[k] / (max * max);
2870  }
2871 
2872  sum = 0.0;
2873  sum2 = 0.0;
2874  for (k = 0; k < nx; k++) {
2875  sum += weights[k] * ip_qnull[k];
2876  sum2 += weights[k];
2877  }
2878 
2879  cpl_free(weights);
2880 
2881  imean = sum / sum2;
2882 
2883  mean_qnull += (imean - mean_qnull) / (j + 1.0);
2884 
2885  /* QC on U NULL */
2886  weights = cpl_malloc(sizeof(float) * nx);
2887 
2888  max = 0.0;
2889  for (k = 0; k < nx; k++) {
2890  if (max < iffu[k]) max = iffu[k];
2891  }
2892 
2893  for (k = 0; k < nx; k++) {
2894  weights[k] = iffu[k] < 0.0 ?
2895  0.0 : iffu[k] * iffu[k] / (max * max);
2896  }
2897 
2898  sum = 0.0;
2899  sum2 = 0.0;
2900  for (k = 0; k < nx; k++) {
2901  sum += weights[k] * ip_unull[k];
2902  sum2 += weights[k];
2903  }
2904 
2905  cpl_free(weights);
2906 
2907  imean = sum / sum2;
2908 
2909  mean_unull += (imean - mean_unull) / (j + 1.0);
2910  }
2911  }
2912 
2913  cpl_image_delete(correct_im);
2914 
2915  if (dfs_save_image(frameset, pq_im, reduced_q_tag, header,
2916  parlist, recipe, version))
2917  fors_pmos_science_exit(NULL, nscience);
2918 
2919  if (dfs_save_image(frameset, pu_im, reduced_u_tag, header,
2920  parlist, recipe, version))
2921  fors_pmos_science_exit(NULL, nscience);
2922 
2923  if (qc && standard) {
2924  cpl_table *polsta = dfs_load_table(frameset, std_pmos_table_tag, 1);
2925  cpl_propertylist *qheader = dfs_load_header(frameset,
2926  science_tag, 0);
2927  cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
2928  cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
2929  cpl_propertylist_update_double(qheader, "CRVAL1",
2930  startwavelength + (dispersion * group)/2);
2931  cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
2932  cpl_propertylist_update_double(qheader, "CD1_1",
2933  dispersion * group);
2934  cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
2935  cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
2936  cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
2937  cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
2938  cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
2939 
2940  if (mos_check_polarisation(pq_im, pqerr_im, pu_im, puerr_im,
2941  startwavelength, dispersion, 1000.,
2942  polsta, ra, dec, &filter,
2943  &polarised,
2944  &qc_pl, &qc_pl_err,
2945  &qc_angle, &qc_angle_err)) {
2946  cpl_msg_warning(cpl_func, "No QC can be computed");
2947  }
2948  else {
2949  char *pipefile;
2950  char *keyname;
2951  char *text;
2952  char band[] = {' ', '\0'};
2953 
2954  fors_qc_start_group(qheader, "2.0", instrume);
2955 
2956  /*
2957  * QC1 group header
2958  */
2959 
2960  if (fors_qc_write_string("PRO.CATG", reduced_l_tag,
2961  "Product category", instrume))
2962  fors_pmos_science_exit("Cannot write product category to "
2963  "QC log file", nscience);
2964 
2965  if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
2966  "DPR type", instrume))
2967  fors_pmos_science_exit("Missing keyword DPR TYPE in "
2968  "scientific frame header", nscience);
2969 
2970  if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
2971  "Template", instrume))
2972  fors_pmos_science_exit("Missing keyword TPL ID in "
2973  "scientific frame header", nscience);
2974 
2975  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
2976  "Grism name", instrume))
2977  fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
2978  "scientific frame header", nscience);
2979 
2980  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
2981  "Grism identifier", instrume))
2982  fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
2983  "scientific frame header", nscience);
2984 
2985  if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
2986  fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
2987  "Filter name", instrume);
2988 
2989  if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
2990  "Collimator name", instrume))
2991  fors_pmos_science_exit("Missing keyword INS COLL NAME in "
2992  "scientific frame header", nscience);
2993 
2994  if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
2995  "Chip identifier", instrume))
2996  fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
2997  "scientific frame header", nscience);
2998 
2999  if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
3000  "Archive name of input data",
3001  instrume))
3002  fors_pmos_science_exit("Missing keyword ARCFILE in "
3003  "scientific frame header", nscience);
3004 
3005  pipefile = dfs_generate_filename(reduced_nul_q_tag);
3006  if (fors_qc_write_string("PIPEFILE", pipefile,
3007  "Pipeline product name", instrume))
3008  fors_pmos_science_exit("Cannot write PIPEFILE to "
3009  "QC log file", nscience);
3010  cpl_free(pipefile); pipefile = NULL;
3011 
3012 
3013  /*
3014  * QC1 parameters
3015  */
3016 
3017  keyname = "QC.PMOS.BAND";
3018 
3019  band[0] = filter;
3020  if (fors_qc_write_qc_string(qheader, keyname, band,
3021  "Band where polarisation was "
3022  "measured", instrume)) {
3023  fors_pmos_science_exit("Cannot write QC.PMOS.BAND "
3024  "parameter to QC log file", nscience);
3025  }
3026 
3027  keyname = "QC.PMOS.POLARISED";
3028 
3029  if (fors_qc_write_qc_int(qheader, polarised, keyname, NULL,
3030  "Polarisation is expected (1 = yes, "
3031  "0 = no)", instrume)) {
3032  fors_pmos_science_exit("Cannot write QC.PMOS.POLARISED "
3033  "parameter to QC log file", nscience);
3034  }
3035 
3036  keyname = "QC.PMOS.L.OFFSET";
3037 
3038  if (polarised)
3039  text = "Linear polarisation relative offset";
3040  else
3041  text = "Linear polarisation offset";
3042 
3043  if (fors_qc_write_qc_double(qheader, qc_pl, keyname, NULL,
3044  text, instrume)) {
3045  fors_pmos_science_exit("Cannot write linear polarisation "
3046  "offset to QC log file", nscience);
3047  }
3048 
3049  keyname = "QC.PMOS.L.OFFSETERR";
3050 
3051  if (fors_qc_write_qc_double(qheader, qc_pl_err, keyname, NULL,
3052  "Error on linear polarisation offset",
3053  instrume)) {
3054  fors_pmos_science_exit("Cannot write linear polarisation "
3055  "offset error to QC log file", nscience);
3056  }
3057 
3058  if (polarised) {
3059  keyname = "QC.PMOS.ANGLE.OFFSET";
3060 
3061  if (fors_qc_write_qc_double(qheader, qc_angle, keyname, NULL,
3062  "Polarisation angle offset",
3063  instrume)) {
3064  fors_pmos_science_exit("Cannot write polarisation "
3065  "angle offset to QC log file", nscience);
3066  }
3067 
3068  keyname = "QC.PMOS.ANGLE.OFFSETERR";
3069 
3070  if (fors_qc_write_qc_double(qheader, qc_angle_err, keyname,
3071  NULL, "Error on polarisation "
3072  "angle offset", instrume)) {
3073  fors_pmos_science_exit("Cannot write polarisation "
3074  "angle offset error to QC "
3075  "log file", nscience);
3076  }
3077  }
3078 
3080  }
3081 
3082  if (dfs_save_image(frameset, pl_im, reduced_l_tag, qheader,
3083  parlist, recipe, version))
3084  fors_pmos_science_exit(NULL, nscience);
3085 
3086  cpl_propertylist_delete(qheader);
3087  }
3088  else {
3089  if (dfs_save_image(frameset, pl_im, reduced_l_tag, header,
3090  parlist, recipe, version))
3091  fors_pmos_science_exit(NULL, nscience);
3092  }
3093 
3094  if (nscience / 4 > 1) {
3095  char *pipefile;
3096  char *keyname;
3097  cpl_propertylist *qheader = dfs_load_header(frameset,
3098  science_tag, 0);
3099 
3100  cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
3101  cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
3102  cpl_propertylist_update_double(qheader, "CRVAL1",
3103  startwavelength + (dispersion * group)/2);
3104  cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
3105  cpl_propertylist_update_double(qheader, "CD1_1",
3106  dispersion * group);
3107  cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
3108  cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
3109  cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
3110  cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
3111  cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
3112 
3113  if (qc) {
3114  fors_qc_start_group(qheader, "2.0", instrume);
3115 
3116  /*
3117  * QC1 group header
3118  */
3119 
3120  if (fors_qc_write_string("PRO.CATG", reduced_nul_q_tag,
3121  "Product category", instrume))
3122  fors_pmos_science_exit("Cannot write product category to "
3123  "QC log file", nscience);
3124 
3125  if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
3126  "DPR type", instrume))
3127  fors_pmos_science_exit("Missing keyword DPR TYPE in "
3128  "scientific frame header", nscience);
3129 
3130  if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
3131  "Template", instrume))
3132  fors_pmos_science_exit("Missing keyword TPL ID in "
3133  "scientific frame header", nscience);
3134 
3135  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
3136  "Grism name", instrume))
3137  fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
3138  "scientific frame header", nscience);
3139 
3140  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
3141  "Grism identifier", instrume))
3142  fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
3143  "scientific frame header", nscience);
3144 
3145  if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
3146  fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
3147  "Filter name", instrume);
3148 
3149  if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
3150  "Collimator name", instrume))
3151  fors_pmos_science_exit("Missing keyword INS COLL NAME in "
3152  "scientific frame header", nscience);
3153 
3154  if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
3155  "Chip identifier", instrume))
3156  fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
3157  "scientific frame header", nscience);
3158 
3159  if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
3160  "Archive name of input data",
3161  instrume))
3162  fors_pmos_science_exit("Missing keyword ARCFILE in "
3163  "scientific frame header", nscience);
3164 
3165  pipefile = dfs_generate_filename(reduced_nul_q_tag);
3166  if (fors_qc_write_string("PIPEFILE", pipefile,
3167  "Pipeline product name", instrume))
3168  fors_pmos_science_exit("Cannot write PIPEFILE to "
3169  "QC log file", nscience);
3170  cpl_free(pipefile); pipefile = NULL;
3171 
3172 
3173  /*
3174  * QC1 parameters
3175  */
3176 
3177  keyname = "QC.NULL.Q.MEAN";
3178 
3179  if (fors_qc_write_qc_double(qheader, mean_qnull,
3180  keyname, NULL,
3181  "Mean Q null parameter",
3182  instrume)) {
3183  fors_pmos_science_exit("Cannot write mean Q null "
3184  "parameter to QC log file", nscience);
3185  }
3186 
3187  keyname = "QC.NANGLES";
3188 
3189  if (fors_qc_write_qc_int(qheader, nscience / 2,
3190  keyname, NULL,
3191  "Number of processed plate angles",
3192  instrume)) {
3193  fors_pmos_science_exit("Cannot write number of processed "
3194  "plate angles.", nscience);
3195  }
3196 
3198  }
3199 
3200  if (dfs_save_image(frameset, pqnull_im, reduced_nul_q_tag, qheader,
3201  parlist, recipe, version))
3202  fors_pmos_science_exit(NULL, nscience);
3203 
3204  cpl_propertylist_delete(qheader);
3205 
3206  qheader = dfs_load_header(frameset, science_tag, 0);
3207 
3208  cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
3209  cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
3210  cpl_propertylist_update_double(qheader, "CRVAL1",
3211  startwavelength + (dispersion * group)/2);
3212  cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
3213  cpl_propertylist_update_double(qheader, "CD1_1",
3214  dispersion * group);
3215  cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
3216  cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
3217  cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
3218  cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
3219  cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
3220 
3221  if (qc) {
3222  fors_qc_start_group(qheader, "2.0", instrume);
3223 
3224  /*
3225  * QC1 group header
3226  */
3227 
3228  if (fors_qc_write_string("PRO.CATG", reduced_nul_u_tag,
3229  "Product category", instrume))
3230  fors_pmos_science_exit("Cannot write product category to "
3231  "QC log file", nscience);
3232 
3233  if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
3234  "DPR type", instrume))
3235  fors_pmos_science_exit("Missing keyword DPR TYPE in "
3236  "scientific frame header", nscience);
3237 
3238  if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
3239  "Template", instrume))
3240  fors_pmos_science_exit("Missing keyword TPL ID in "
3241  "scientific frame header", nscience);
3242 
3243  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
3244  "Grism name", instrume))
3245  fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
3246  "scientific frame header", nscience);
3247 
3248  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
3249  "Grism identifier", instrume))
3250  fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
3251  "scientific frame header", nscience);
3252 
3253  if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
3254  fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
3255  "Filter name", instrume);
3256 
3257  if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
3258  "Collimator name", instrume))
3259  fors_pmos_science_exit("Missing keyword INS COLL NAME in "
3260  "scientific frame header", nscience);
3261 
3262  if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
3263  "Chip identifier", instrume))
3264  fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
3265  "scientific frame header", nscience);
3266 
3267  if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
3268  "Archive name of input data",
3269  instrume))
3270  fors_pmos_science_exit("Missing keyword ARCFILE in "
3271  "scientific frame header", nscience);
3272 
3273  pipefile = dfs_generate_filename(reduced_nul_u_tag);
3274  if (fors_qc_write_string("PIPEFILE", pipefile,
3275  "Pipeline product name", instrume))
3276  fors_pmos_science_exit("Cannot write PIPEFILE to "
3277  "QC log file", nscience);
3278  cpl_free(pipefile); pipefile = NULL;
3279 
3280 
3281  /*
3282  * QC1 parameters
3283  */
3284 
3285  keyname = "QC.NULL.U.MEAN";
3286 
3287  if (fors_qc_write_qc_double(qheader, mean_unull,
3288  keyname, NULL,
3289  "Mean U null parameter",
3290  instrume)) {
3291  fors_pmos_science_exit("Cannot write mean U null "
3292  "parameter to QC log file", nscience);
3293  }
3294 
3295  keyname = "QC.NANGLES";
3296 
3297  if (fors_qc_write_qc_int(qheader, nscience / 2,
3298  keyname, NULL,
3299  "Number of processed plate angles",
3300  instrume)) {
3301  fors_pmos_science_exit("Cannot write number of processed "
3302  "plate angles.", nscience);
3303  }
3304 
3306  }
3307 
3308  if (dfs_save_image(frameset, punull_im, reduced_nul_u_tag, qheader,
3309  parlist, recipe, version))
3310  fors_pmos_science_exit(NULL, nscience);
3311 
3312  cpl_propertylist_delete(qheader);
3313  }
3314 
3315  if (dfs_save_image(frameset, pqerr_im, reduced_error_q_tag, header,
3316  parlist, recipe, version))
3317  fors_pmos_science_exit(NULL, nscience);
3318 
3319  if (dfs_save_image(frameset, puerr_im, reduced_error_u_tag, header,
3320  parlist, recipe, version))
3321  fors_pmos_science_exit(NULL, nscience);
3322 
3323  if (dfs_save_image(frameset, plerr_im, reduced_error_l_tag, header,
3324  parlist, recipe, version))
3325  fors_pmos_science_exit(NULL, nscience);
3326 
3327  if (dfs_save_image(frameset, pang_im, reduced_angle_tag, header,
3328  parlist, recipe, version))
3329  fors_pmos_science_exit(NULL, nscience);
3330 
3331  if (dfs_save_image(frameset, pangerr_im, reduced_error_angle_tag,
3332  header, parlist, recipe, version))
3333  fors_pmos_science_exit(NULL, nscience);
3334 
3335  if (dfs_save_image(frameset, pi_im, reduced_i_tag,
3336  header, parlist, recipe, version))
3337  fors_pmos_science_exit(NULL, nscience);
3338 
3339  cpl_image_power(pierr_im, 0.5);
3340 
3341  if (dfs_save_image(frameset, pierr_im, reduced_error_i_tag,
3342  header, parlist, recipe, version))
3343  fors_pmos_science_exit(NULL, nscience);
3344 
3345 /* %%% */
3346 
3347  cpl_image_delete(pq_im);
3348  cpl_image_delete(pu_im);
3349  cpl_image_delete(pl_im);
3350  cpl_image_delete(pi_im);
3351 
3352  cpl_image_delete(pqnull_im);
3353  cpl_image_delete(punull_im);
3354 
3355  cpl_image_delete(pqerr_im);
3356  cpl_image_delete(puerr_im);
3357  cpl_image_delete(plerr_im);
3358  cpl_image_delete(pierr_im);
3359  cpl_image_delete(pang_im);
3360  cpl_image_delete(pangerr_im);
3361  }
3362 
3363  cpl_propertylist_delete(header);
3364 
3365  /* End of Stokes computation */
3366 
3367  for (j = 0; j < nscience; j++) {
3368  cpl_image_delete(reduceds[j]);
3369  cpl_image_delete(rerrors[j]);
3370  cpl_table_delete(slitss[j]);
3371  cpl_image_delete(mappeds[j]);
3372  }
3373 
3374  cpl_free(reduceds);
3375  cpl_free(rerrors);
3376  cpl_free(slitss);
3377  cpl_free(mappeds);
3378 
3379  cpl_free(instrume); instrume = NULL;
3380 
3381  cpl_free(skylocalmaps);
3382 
3383  cpl_free(nobjs_per_slit);
3384 
3385  if (cpl_error_get_code()) {
3386  cpl_msg_error(cpl_error_get_where(), "%s", cpl_error_get_message());
3387  fors_pmos_science_exit(NULL, nscience);
3388  }
3389  else
3390  return 0;
3391 }
3392 
3393 /*----------------------------------------------------------------------------*/
3404 /*----------------------------------------------------------------------------*/
3405 static float * fors_check_angles(cpl_frameset * frameset,
3406  int pmos, const char *tag, int * circ)
3407 {
3408  float *angles = NULL;
3409  cpl_frame *c_frame = NULL;
3410  char *ret_id = NULL;
3411 
3412  int i = 0;
3413 
3414  angles = cpl_malloc(sizeof(float) * pmos);
3415 
3416  for (c_frame = cpl_frameset_find(frameset, tag);
3417  c_frame != NULL; c_frame = cpl_frameset_find(frameset, NULL)) {
3418 
3419  cpl_propertylist * header =
3420  cpl_propertylist_load(cpl_frame_get_filename(c_frame), 0);
3421 
3422  if (!ret_id) {
3423  ret_id = cpl_strdup(cpl_propertylist_get_string(header,
3424  "ESO INS OPTI4 ID"));
3425 
3426  if (ret_id[1] != '5' && ret_id[1] != '4') {
3427  cpl_msg_error(cpl_func,
3428  "Unknown retarder plate id: %s", ret_id);
3429  return NULL;
3430  }
3431  } else {
3432  char * c_ret_id = (char *)
3433  cpl_propertylist_get_string(header, "ESO INS OPTI4 ID");
3434  if (ret_id[1] != c_ret_id[1]) {
3435  cpl_msg_error(cpl_func, "Input frames are not from the same "
3436  "retarder plate");
3437  return NULL;
3438  }
3439  }
3440 
3441  if (ret_id[1] == '5') { /* Linear polarimetry */
3442  if (cpl_propertylist_has(header, "ESO INS RETA2 ROT")) {
3443  angles[i] = (float)floor(2*cpl_propertylist_get_double(header,
3444  "ESO INS RETA2 ROT") + 0.5)/2;
3445  }
3446  else if (cpl_propertylist_has(header, "ESO INS RETA2 POSANG")) {
3447  if (cpl_propertylist_has(header, "ESO ADA POSANG")) {
3448  double reta2pos = cpl_propertylist_get_double(header,
3449  "ESO INS RETA2 POSANG");
3450  double adapos = cpl_propertylist_get_double(header,
3451  "ESO ADA POSANG");
3452  angles[i] = (float)floor(2*(reta2pos - adapos) + 0.5)/2;
3453  }
3454  else {
3455  cpl_msg_error(cpl_func,
3456  "ESO ADA POSANG not found in header");
3457  return NULL;
3458  }
3459  }
3460  else {
3461  cpl_msg_error(cpl_func, "Neither ESO INS RETA2 ROT nor "
3462  "ESO INS RETA2 POSANG found in header");
3463  return NULL;
3464  }
3465  *circ = 0;
3466  } else { /* Circular polarimetry */
3467  if (cpl_propertylist_has(header, "ESO INS RETA4 ROT")) {
3468  angles[i] = (float)floor(2*cpl_propertylist_get_double(header,
3469  "ESO INS RETA4 ROT") + 0.5)/2;
3470  //Check if it makes sense. Change in all other places
3471  if (angles[i] < 0)
3472  angles[i] = angles[i] + 360;
3473  }
3474  else if (cpl_propertylist_has(header, "ESO INS RETA4 POSANG")) {
3475  if (cpl_propertylist_has(header, "ESO ADA POSANG")) {
3476  double reta4pos = cpl_propertylist_get_double(header,
3477  "ESO INS RETA4 POSANG");
3478  double adapos = cpl_propertylist_get_double(header,
3479  "ESO ADA POSANG");
3480  angles[i] = (float)floor(2*(reta4pos - adapos) + 0.5/2);
3481  }
3482  else {
3483  cpl_msg_error(cpl_func,
3484  "ESO ADA POSANG not found in header");
3485  return NULL;
3486  }
3487  }
3488  else {
3489  cpl_msg_error(cpl_func, "Neither ESO INS RETA4 ROT nor "
3490  "ESO INS RETA4 POSANG found in header");
3491  return NULL;
3492  }
3493  *circ = 1;
3494  }
3495 
3496  cpl_propertylist_delete(header);
3497  i++;
3498  }
3499 
3500  cpl_free(ret_id);
3501 
3502  if (*circ) {
3503  if (pmos != 2 && pmos != 4) {
3504  cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
3505  "found, but either 2 or 4 are required for "
3506  "circular polarization measurements!", pmos);
3507  return NULL;
3508  }
3509  } else {
3510  if (pmos != 4 && pmos != 8 && pmos != 16) {
3511  cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
3512  "found, but either 4, 8, or 16 are required for "
3513  "linear polarization measurements!", pmos);
3514  return NULL;
3515  }
3516  }
3517 
3518  /* Check completeness */
3519 
3520  if (*circ) {
3521  for (i = 0; i < pmos; i++) {
3522  if (fors_find_angle_pos(angles, pmos, 90.0 * i - 45.0) < 0) {
3523  const char *cangles;
3524  switch (pmos) {
3525  case 2: cangles = "-45.0, 45.0"; break;
3526  case 4: cangles = "-45.0, 45.0, 135.0, 225.0"; break;
3527  default: assert(0);
3528  }
3529 
3530  cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
3531  "angle %.2f. All angles %s must be provided.",
3532  angles[i], cangles);
3533  return NULL;
3534  }
3535  }
3536  }
3537  else {
3538  for (i = 0; i < pmos; i++) {
3539  if (fors_find_angle_pos(angles, pmos, 22.5 * i) < 0) {
3540  const char *cangles;
3541  switch (pmos) {
3542  case 4: cangles = "0.0, 22.5, 45.0, 67.5"; break;
3543  case 8: cangles = "0.0, 22.5, 45.0, 67.5, "
3544  "90.0, 112.5, 135.0, 157.5"; break;
3545  case 16: cangles = "0.0, 22.5, 45.0, 67.5, "
3546  "90.0, 112.5, 135.0, 157.5, "
3547  "180.0, 202.5, 225.0, 247.5, "
3548  "270.0, 292.5, 315.0, 337.5"; break;
3549  default: assert(0);
3550  }
3551 
3552  cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
3553  "angle %.2f. All angles %s must be provided.",
3554  angles[i], cangles);
3555  return NULL;
3556  }
3557  }
3558  }
3559 
3560  return angles;
3561 }
3562 
3563 /*----------------------------------------------------------------------------*/
3571 /*----------------------------------------------------------------------------*/
3572 static int
3573 fors_find_angle_pos(float * angles, int nangles, float angle)
3574 {
3575  int i, match = 0;
3576 
3577  for (i = 0; i < nangles; i++) {
3578  if (fabs(angles[i] - angle) < 1.0 ||
3579  fabs(angles[i] - 360.0 - angle) < 1.0) {
3580  match = 1;
3581  break;
3582  }
3583  }
3584 
3585  return match ? i : -1;
3586 }
3587 
cpl_image * mos_spatial_calibration(cpl_image *spectra, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion, int flux, cpl_image *calibration)
Spatial remapping of CCD spectra eliminating the spectral curvature.
Definition: moses.c:8539
cpl_error_code fors_qc_write_qc_string(cpl_propertylist *header, const char *name, const char *value, const char *comment, const char *instrument)
Write a string value to the active QC1 PAF object and to a header.
Definition: fors_qc.c:535
int cpl_plugin_get_info(cpl_pluginlist *list)
Build the list of available plugins, for this module.
Definition: fors_bias.c:62
cpl_image * dfs_load_image(cpl_frameset *frameset, const char *category, cpl_type type, int ext, int calib)
Loading image data of given category.
Definition: fors_dfs.c:860
const char * dfs_get_parameter_string(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe string parameter value.
Definition: fors_dfs.c:602
double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, double wavestart, double dispersion, int radius, int highres)
Estimate the spectral distortion modeling goodness.
Definition: moses.c:11047
cpl_propertylist * dfs_load_header(cpl_frameset *frameset, const char *category, int ext)
Loading header associated to data of given category.
Definition: fors_dfs.c:964
cpl_error_code dfs_save_image_null(cpl_frameset *frameset, cpl_parameterlist *parlist, const char *tag, const char *recipename, const char *version)
Save a product with an empty primary extension.
Definition: fors_dfs.c:1893
cpl_error_code dfs_save_image_ext(cpl_image *image, const char *tag, cpl_propertylist *extheader)
Save an image in a extension.
Definition: fors_dfs.c:1850
cpl_error_code fors_qc_write_qc_double(cpl_propertylist *header, double value, const char *name, const char *unit, const char *comment, const char *instrument)
Write an integer value to the active QC1 PAF object and to a header.
Definition: fors_qc.c:604
cpl_image * mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion)
Remapping of spatially rectified wavelengths to original CCD pixels.
Definition: moses.c:11380
cpl_error_code fors_qc_keyword_to_paf(cpl_propertylist *header, const char *name, const char *unit, const char *comment, const char *instrument)
Copy a keyword value to the currently active QC1 PAF object.
Definition: fors_qc.c:425
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_table * mos_wavelength_align(cpl_image *image, cpl_table *slits, double refwave, double firstLambda, double lastLambda, cpl_table *idscoeff, cpl_vector *skylines, int highres, int order, cpl_image *calibration, int sradius)
Modify the input wavelength solution to match reference sky lines.
Definition: moses.c:9986
cpl_error_code fors_qc_start_group(cpl_propertylist *header, const char *qcdic_version, const char *instrument)
Initiate a new QC1 group.
Definition: fors_qc.c:77
cpl_image * mos_remove_bias(cpl_image *image, cpl_image *bias, cpl_table *overscans)
Subtract the bias from a CCD exposure.
Definition: moses.c:3694
cpl_error_code fors_qc_write_string(const char *name, const char *value, const char *comment, const char *instrument)
Add string parameter to current QC1 group.
Definition: fors_qc.c:235
cpl_image * mos_detect_objects(cpl_image *image, cpl_table *slits, int margin, int maxradius, int conradius)
Detect objects in rectified scientific frame.
Definition: moses.c:13944
cpl_image * mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
Local determination of sky.
Definition: moses.c:12717
void fors_dfs_set_groups(cpl_frameset *set)
Set the group as RAW or CALIB in a frameset.
Definition: fors_dfs.c:257
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 mos_check_polarisation(cpl_image *q_image, cpl_image *q_error, cpl_image *u_image, cpl_image *u_error, double startwave, double dispersion, double band, cpl_table *pol_sta, double ra, double dec, char *filter, int *polarisation, double *p_offset, double *p_error, double *a_offset, double *a_error)
Estimate linear polarisation parameters on spectral interval.
Definition: moses.c:18337
cpl_error_code dfs_save_table_ext(cpl_table *table, const char *tag, cpl_propertylist *extheader)
Save a table in a extension (different from the first one)
Definition: fors_dfs.c:1811
cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, int nscience, float tolerance)
Intersect a number of slit tables.
Definition: moses.c:16408
cpl_image * mos_subtract_sky(cpl_image *science, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion)
Subtract the sky from the scientific CCD exposure.
Definition: moses.c:2012
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
cpl_error_code fors_qc_end_group(void)
Close current QC1 PAF file.
Definition: fors_qc.c:200
Definition: list.c:74
cpl_image ** mos_extract_objects(cpl_image *science, cpl_image *science_var, cpl_image *sky, cpl_table *objects, int extraction, double ron, double gain, int ncombined)
Extract detected objects from rectified scientific frame.
Definition: moses.c:14295
cpl_error_code mos_clean_cosmics(cpl_image *image, float gain, float threshold, float ratio)
Remove cosmic rays from sky-subtracted CCD spectral exposure.
Definition: moses.c:13174
cpl_error_code mos_randomise_image(cpl_image *image, double ron, double gain, double bias)
Randomise image.
Definition: moses.c:16174
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