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