FORS Pipeline Reference Manual  5.0.9
fors_pmos_extract.c
1 /* $Id: fors_pmos_extract.c,v 1.10 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.10 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <math.h>
33 #include <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_extract_create(cpl_plugin *);
44 static int fors_pmos_extract_exec(cpl_plugin *);
45 static int fors_pmos_extract_destroy(cpl_plugin *);
46 static int fors_pmos_extract(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_extract_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 "\n"
84 " MASTER_NORM_FLAT_PMOS Calib Normalised flat field .\n"
85 " DISP_COEFF_PMOS Calib Inverse dispersion Y\n"
86 " CURV_COEFF_PMOS Calib Spectral curvature Y\n"
87 " SLIT_LOCATION_PMOS Calib Slits positions table Y\n"
88 " RETARDER_WAVEPLATE_CHROMATISM Calib Chromatism correction .\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_SCI_X_PMOS FITS image X Stokes parameter (and L)\n"
96 " REDUCED_ERROR_X_PMOS FITS image Error on X Stokes parameter\n"
97 " REDUCED_NUL_X_PMOS FITS image Null parameter for X\n"
98 " REDUCED_POL_ANGLE_PMOS FITS image Direction of linear polarization\n"
99 " REDUCED_POL_ANGLE_ERROR_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 " GLOBAL_SKY_SPECTRUM_PMOS FITS table Global sky spectrum\n"
106 " OBJECT_TABLE_SCI_PMOS FITS table Positions of detected objects\n"
107 " OBJECT_TABLE_POL_SCI_PMOS FITS table Positions of real objects\n"
108 "\n"
109 " Only if the sky-alignment of the wavelength solution is requested:\n"
110 " DISP_COEFF_SCI_PMOS FITS table Upgraded dispersion coefficients\n"
111 " WAVELENGTH_MAP_SCI_PMOS FITS image Upgraded wavelength map\n\n";
112 
113 #define fors_pmos_extract_exit(message) \
114 { \
115 if ((const char *)message != NULL) cpl_msg_error(recipe, message); \
116 cpl_free(instrume); \
117 cpl_image_delete(dummy); \
118 cpl_image_delete(mapped_sky); \
119 cpl_image_delete(mapped_cleaned); \
120 cpl_image_delete(skymap); \
121 cpl_image_delete(smapped); \
122 cpl_table_delete(offsets); \
123 cpl_table_delete(sky); \
124 cpl_image_delete(bias); \
125 cpl_image_delete(spectra); \
126 cpl_image_delete(coordinate); \
127 cpl_image_delete(norm_flat); \
128 cpl_image_delete(rainbow); \
129 cpl_image_delete(rectified); \
130 cpl_image_delete(wavemap); \
131 cpl_propertylist_delete(header); \
132 cpl_propertylist_delete(save_header); \
133 cpl_table_delete(grism_table); \
134 cpl_table_delete(idscoeff); \
135 cpl_table_delete(maskslits); \
136 cpl_table_delete(overscans); \
137 cpl_table_delete(polytraces); \
138 cpl_table_delete(wavelengths); \
139 cpl_vector_delete(lines); \
140 cpl_msg_indent_less(); \
141 return -1; \
142 }
143 
144 
145 #define fors_pmos_extract_exit_memcheck(message) \
146 { \
147 if ((const char *)message != NULL) cpl_msg_info(recipe, message); \
148 cpl_free(instrume); \
149 cpl_image_delete(dummy); \
150 cpl_image_delete(mapped_cleaned); \
151 cpl_image_delete(mapped_sky); \
152 cpl_image_delete(skymap); \
153 cpl_image_delete(smapped); \
154 cpl_table_delete(offsets); \
155 cpl_table_delete(sky); \
156 cpl_image_delete(bias); \
157 cpl_image_delete(spectra); \
158 cpl_image_delete(coordinate); \
159 cpl_image_delete(norm_flat); \
160 cpl_image_delete(rainbow); \
161 cpl_image_delete(rectified); \
162 cpl_image_delete(wavemap); \
163 cpl_propertylist_delete(header); \
164 cpl_propertylist_delete(save_header); \
165 cpl_table_delete(grism_table); \
166 cpl_table_delete(idscoeff); \
167 cpl_table_delete(maskslits); \
168 cpl_table_delete(overscans); \
169 cpl_table_delete(polytraces); \
170 cpl_table_delete(wavelengths); \
171 cpl_vector_delete(lines); \
172 cpl_msg_indent_less(); \
173 return 0; \
174 }
175 
176 
188 int cpl_plugin_get_info(cpl_pluginlist *list)
189 {
190  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
191  cpl_plugin *plugin = &recipe->interface;
192 
193  cpl_plugin_init(plugin,
194  CPL_PLUGIN_API,
195  FORS_BINARY_VERSION,
196  CPL_PLUGIN_TYPE_RECIPE,
197  "fors_pmos_extract",
198  "Extraction of scientific spectra",
199  fors_pmos_extract_description,
200  "Carlo Izzo",
201  PACKAGE_BUGREPORT,
202  "This file is currently part of the FORS Instrument Pipeline\n"
203  "Copyright (C) 2002-2010 European Southern Observatory\n\n"
204  "This program is free software; you can redistribute it and/or modify\n"
205  "it under the terms of the GNU General Public License as published by\n"
206  "the Free Software Foundation; either version 2 of the License, or\n"
207  "(at your option) any later version.\n\n"
208  "This program is distributed in the hope that it will be useful,\n"
209  "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
210  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
211  "GNU General Public License for more details.\n\n"
212  "You should have received a copy of the GNU General Public License\n"
213  "along with this program; if not, write to the Free Software Foundation,\n"
214  "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n",
215  fors_pmos_extract_create,
216  fors_pmos_extract_exec,
217  fors_pmos_extract_destroy);
218 
219  cpl_pluginlist_append(list, plugin);
220 
221  return 0;
222 }
223 
224 
235 static int fors_pmos_extract_create(cpl_plugin *plugin)
236 {
237  cpl_recipe *recipe;
238  cpl_parameter *p;
239 
240 
241  /*
242  * Check that the plugin is part of a valid recipe
243  */
244 
245  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
246  recipe = (cpl_recipe *)plugin;
247  else
248  return -1;
249 
250  /*
251  * Create the parameters list in the cpl_recipe object
252  */
253 
254  recipe->parameters = cpl_parameterlist_new();
255 
256 
257  /*
258  * Dispersion
259  */
260 
261  p = cpl_parameter_new_value("fors.fors_pmos_extract.dispersion",
262  CPL_TYPE_DOUBLE,
263  "Resampling step (Angstrom/pixel)",
264  "fors.fors_pmos_extract",
265  0.0);
266  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
267  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
268  cpl_parameterlist_append(recipe->parameters, p);
269 
270  /*
271  * Sky lines alignment
272  */
273 
274  p = cpl_parameter_new_value("fors.fors_pmos_extract.skyalign",
275  CPL_TYPE_INT,
276  "Polynomial order for sky lines alignment, "
277  "or -1 to avoid alignment",
278  "fors.fors_pmos_extract",
279  0);
280  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skyalign");
281  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
282  cpl_parameterlist_append(recipe->parameters, p);
283 
284  /*
285  * Line catalog table column containing the sky reference wavelengths
286  */
287 
288  p = cpl_parameter_new_value("fors.fors_pmos_extract.wcolumn",
289  CPL_TYPE_STRING,
290  "Name of sky line catalog table column "
291  "with wavelengths",
292  "fors.fors_pmos_extract",
293  "WLEN");
294  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
295  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
296  cpl_parameterlist_append(recipe->parameters, p);
297 
298  /*
299  * Start wavelength for spectral extraction
300  */
301 
302  p = cpl_parameter_new_value("fors.fors_pmos_extract.startwavelength",
303  CPL_TYPE_DOUBLE,
304  "Start wavelength in spectral extraction",
305  "fors.fors_pmos_extract",
306  0.0);
307  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
308  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
309  cpl_parameterlist_append(recipe->parameters, p);
310 
311  /*
312  * End wavelength for spectral extraction
313  */
314 
315  p = cpl_parameter_new_value("fors.fors_pmos_extract.endwavelength",
316  CPL_TYPE_DOUBLE,
317  "End wavelength in spectral extraction",
318  "fors.fors_pmos_extract",
319  0.0);
320  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
321  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
322  cpl_parameterlist_append(recipe->parameters, p);
323 
324  /*
325  * Flux conservation
326  */
327 
328  p = cpl_parameter_new_value("fors.fors_pmos_extract.flux",
329  CPL_TYPE_BOOL,
330  "Apply flux conservation",
331  "fors.fors_pmos_extract",
332  TRUE);
333  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
334  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
335  cpl_parameterlist_append(recipe->parameters, p);
336 
337  /*
338  * Apply flat field
339  */
340 
341  p = cpl_parameter_new_value("fors.fors_pmos_extract.flatfield",
342  CPL_TYPE_BOOL,
343  "Apply flat field",
344  "fors.fors_pmos_extract",
345  FALSE);
346  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flatfield");
347  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
348  cpl_parameterlist_append(recipe->parameters, p);
349 
350  /*
351  * Global sky subtraction
352  */
353 
354  p = cpl_parameter_new_value("fors.fors_pmos_extract.skyglobal",
355  CPL_TYPE_BOOL,
356  "Subtract global sky spectrum from CCD",
357  "fors.fors_pmos_extract",
358  FALSE);
359  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skyglobal");
360  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
361  cpl_parameterlist_append(recipe->parameters, p);
362 
363  p = cpl_parameter_new_value("fors.fors_pmos_extract.skymedian",
364  CPL_TYPE_BOOL,
365  "Sky subtraction from extracted slit spectra",
366  "fors.fors_pmos_extract",
367  FALSE);
368  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymedian");
369  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
370  cpl_parameterlist_append(recipe->parameters, p);
371 
372  /*
373  * Local sky subtraction on CCD spectra
374  */
375 
376  p = cpl_parameter_new_value("fors.fors_pmos_extract.skylocal",
377  CPL_TYPE_BOOL,
378  "Sky subtraction from CCD slit spectra",
379  "fors.fors_pmos_extract",
380  TRUE);
381  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skylocal");
382  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
383  cpl_parameterlist_append(recipe->parameters, p);
384 
385  /*
386  * Cosmic rays removal
387  */
388 
389  p = cpl_parameter_new_value("fors.fors_pmos_extract.cosmics",
390  CPL_TYPE_BOOL,
391  "Eliminate cosmic rays hits (only if global "
392  "sky subtraction is also requested)",
393  "fors.fors_pmos_extract",
394  FALSE);
395  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cosmics");
396  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
397  cpl_parameterlist_append(recipe->parameters, p);
398 
399  /*
400  * Slit margin
401  */
402 
403  p = cpl_parameter_new_value("fors.fors_pmos_extract.slit_margin",
404  CPL_TYPE_INT,
405  "Number of pixels to exclude at each slit "
406  "in object detection and extraction",
407  "fors.fors_pmos_extract",
408  3);
409  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "slit_margin");
410  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
411  cpl_parameterlist_append(recipe->parameters, p);
412 
413  /*
414  * Extraction radius
415  */
416 
417  p = cpl_parameter_new_value("fors.fors_pmos_extract.ext_radius",
418  CPL_TYPE_INT,
419  "Maximum extraction radius for detected "
420  "objects (pixel)",
421  "fors.fors_pmos_extract",
422  6);
423  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_radius");
424  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
425  cpl_parameterlist_append(recipe->parameters, p);
426 
427  /*
428  * Contamination radius
429  */
430 
431  p = cpl_parameter_new_value("fors.fors_pmos_extract.cont_radius",
432  CPL_TYPE_INT,
433  "Minimum distance at which two objects "
434  "of equal luminosity do not contaminate "
435  "each other (pixel)",
436  "fors.fors_pmos_extract",
437  0);
438  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cont_radius");
439  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
440  cpl_parameterlist_append(recipe->parameters, p);
441 
442  /*
443  * Object extraction method
444  */
445 
446  p = cpl_parameter_new_value("fors.fors_pmos_extract.ext_mode",
447  CPL_TYPE_INT,
448  "Object extraction method: 0 = aperture, "
449  "1 = Horne optimal extraction",
450  "fors.fors_pmos_extract",
451  1);
452  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_mode");
453  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
454  cpl_parameterlist_append(recipe->parameters, p);
455 
456  /*
457  * Normalise output by exposure time
458  */
459 
460  p = cpl_parameter_new_value("fors.fors_pmos_extract.time_normalise",
461  CPL_TYPE_BOOL,
462  "Normalise output spectra by the exposure time",
463  "fors.fors_pmos_extract",
464  TRUE);
465  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "time_normalise");
466  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
467  cpl_parameterlist_append(recipe->parameters, p);
468 
469  /*
470  * Apply chromatism correction to polarization angle
471  */
472 
473  p = cpl_parameter_new_value("fors.fors_pmos_extract.chromatism",
474  CPL_TYPE_BOOL,
475  "Chromatism correction to polarization angles",
476  "fors.fors_pmos_extract",
477  TRUE);
478  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "chromatism");
479  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
480  cpl_parameterlist_append(recipe->parameters, p);
481 
482  /*
483  * Create check products
484  */
485 
486  p = cpl_parameter_new_value("fors.fors_pmos_extract.check",
487  CPL_TYPE_BOOL,
488  "Create intermediate products",
489  "fors.fors_pmos_extract",
490  FALSE);
491  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "check");
492  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
493  cpl_parameterlist_append(recipe->parameters, p);
494 
495  return 0;
496 }
497 
498 
507 static int fors_pmos_extract_exec(cpl_plugin *plugin)
508 {
509  cpl_recipe *recipe;
510 
511  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
512  recipe = (cpl_recipe *)plugin;
513  else
514  return -1;
515 
516  return fors_pmos_extract(recipe->parameters, recipe->frames);
517 }
518 
519 
528 static int fors_pmos_extract_destroy(cpl_plugin *plugin)
529 {
530  cpl_recipe *recipe;
531 
532  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
533  recipe = (cpl_recipe *)plugin;
534  else
535  return -1;
536 
537  cpl_parameterlist_delete(recipe->parameters);
538 
539  return 0;
540 }
541 
542 
552 static int fors_pmos_extract(cpl_parameterlist *parlist, cpl_frameset *frameset)
553 {
554 
555  const char *recipe = "fors_pmos_extract";
556 
557 
558  /*
559  * Input parameters
560  */
561 
562  double dispersion;
563  int skyalign;
564  const char *wcolumn;
565  double startwavelength;
566  double endwavelength;
567  int flux;
568  int flatfield;
569  int skyglobal;
570  int skylocal;
571  int skymedian;
572  int chromatism;
573  int cosmics;
574  int slit_margin;
575  int ext_radius;
576  int cont_radius;
577  int ext_mode;
578  int time_normalise;
579  int check;
580 
581  /*
582  * CPL objects
583  */
584 
585  cpl_image **images;
586 
587  cpl_image **reduceds = NULL;
588  cpl_image **rerrors = NULL;
589  cpl_table **slitss = NULL;
590  cpl_image **mappeds = NULL;
591  cpl_image **skylocalmaps = NULL;
592 
593  int nobjects = 0;
594 
595  cpl_image *bias = NULL;
596  cpl_image *norm_flat = NULL;
597  cpl_image *spectra = NULL;
598  cpl_image *rectified = NULL;
599  cpl_image *coordinate = NULL;
600  cpl_image *rainbow = NULL;
601  cpl_image *mapped = NULL;
602  cpl_image *mapped_sky = NULL;
603  cpl_image *mapped_cleaned = NULL;
604  cpl_image *smapped = NULL;
605  cpl_image *wavemap = NULL;
606  cpl_image *skymap = NULL;
607  cpl_image *skylocalmap = NULL;
608  cpl_image *dummy = NULL;
609 
610  cpl_table *grism_table = NULL;
611  cpl_table *overscans = NULL;
612  cpl_table *wavelengths = NULL;
613  cpl_table *idscoeff = NULL;
614  cpl_table *slits = NULL;
615  cpl_table *origslits = NULL;
616  cpl_table *maskslits = NULL;
617  cpl_table *polytraces = NULL;
618  cpl_table *offsets = NULL;
619  cpl_table *sky = NULL;
620 
621  cpl_vector *lines = NULL;
622 
623  cpl_propertylist *header = NULL;
624  cpl_propertylist *save_header = NULL;
625 
626  cpl_table *global = NULL;
627  /*
628  * Auxiliary variables
629  */
630 
631  char version[80];
632  char *instrume = NULL;
633  const char *science_tag;
634  const char *master_norm_flat_tag;
635  const char *disp_coeff_sky_tag;
636  const char *wavelength_map_sky_tag;
637  const char *reduced_science_tag;
638  const char *reduced_sky_tag;
639  const char *reduced_error_tag;
640  const char *mapped_science_tag;
641  const char *unmapped_science_tag;
642  const char *mapped_science_sky_tag;
643  const char *mapped_sky_tag;
644  const char *unmapped_sky_tag;
645  const char *global_sky_spectrum_tag;
646  const char *object_table_tag;
647  const char *object_table_pol_tag;
648  const char *skylines_offsets_tag;
649  const char *reduced_q_tag;
650  const char *reduced_u_tag;
651  const char *reduced_v_tag;
652  const char *reduced_l_tag;
653  const char *reduced_error_q_tag;
654  const char *reduced_error_u_tag;
655  const char *reduced_error_v_tag;
656  const char *reduced_error_l_tag;
657  const char *reduced_nul_q_tag;
658  const char *reduced_nul_u_tag;
659  const char *reduced_nul_v_tag;
660  const char *reduced_angle_tag;
661  const char *reduced_error_angle_tag;
662  const char *master_distortion_tag = "MASTER_DISTORTION_TABLE";
663  const char *chrom_table_tag = "RETARDER_WAVEPLATE_CHROMATISM";
664  float *angles = NULL;
665  int pmos, circ;
666  int nscience;
667  double alltime;
668  double mean_rms;
669  int nlines;
670  int rebin;
671  double *line;
672  int nx = 0, ny;
673  int ccd_xsize;
674  double reference;
675  double gain;
676  double ron;
677  int standard;
678  int highres;
679  int i, j;
680  cpl_error_code error;
681 
682  int * nobjs_per_slit;
683  int nslits_out_det = 0;
684 
685 
686  snprintf(version, 80, "%s-%s", PACKAGE, PACKAGE_VERSION);
687 
688  cpl_msg_set_indentation(2);
689 
690  /*
691  * Get configuration parameters
692  */
693 
694  cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
695  cpl_msg_indent_more();
696 
697  if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
698  fors_pmos_extract_exit("Too many in input: GRISM_TABLE");
699 
700  grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
701 
702  dispersion = dfs_get_parameter_double(parlist,
703  "fors.fors_pmos_extract.dispersion", grism_table);
704 
705  if (dispersion <= 0.0)
706  fors_pmos_extract_exit("Invalid resampling step");
707 
708  skyalign = dfs_get_parameter_int(parlist,
709  "fors.fors_pmos_extract.skyalign", NULL);
710 
711  if (skyalign > 2)
712  fors_pmos_extract_exit("Max polynomial degree for sky alignment is 2");
713 
714  wcolumn = dfs_get_parameter_string(parlist,
715  "fors.fors_pmos_extract.wcolumn", NULL);
716 
717  startwavelength = dfs_get_parameter_double(parlist,
718  "fors.fors_pmos_extract.startwavelength", grism_table);
719  if (startwavelength < 3000.0 || startwavelength > 13000.0)
720  fors_pmos_extract_exit("Invalid wavelength");
721 
722  endwavelength = dfs_get_parameter_double(parlist,
723  "fors.fors_pmos_extract.endwavelength", grism_table);
724  if (endwavelength < 3000.0 || endwavelength > 13000.0)
725  fors_pmos_extract_exit("Invalid wavelength");
726 
727  if (endwavelength - startwavelength <= 0.0)
728  fors_pmos_extract_exit("Invalid wavelength interval");
729 
730  flux = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.flux", NULL);
731 
732  flatfield = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.flatfield",
733  NULL);
734 
735  skyglobal = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.skyglobal",
736  NULL);
737  skylocal = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.skylocal",
738  NULL);
739  skymedian = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.skymedian",
740  NULL);
741 /* NSS
742  skymedian = dfs_get_parameter_int(parlist, "fors.fors_pmos_extract.skymedian",
743  NULL);
744 */
745 
746  chromatism =
747  dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.chromatism",
748  NULL);
749 
750  if (skylocal && skyglobal)
751  fors_pmos_extract_exit("Cannot apply both local and global sky subtraction");
752 
753  if (skylocal && skymedian)
754  fors_pmos_extract_exit("Cannot apply sky subtraction both on extracted "
755  "and non-extracted spectra");
756 
757  cosmics = dfs_get_parameter_bool(parlist,
758  "fors.fors_pmos_extract.cosmics", NULL);
759 
760  if (cosmics)
761  if (!(skyglobal || skylocal))
762  fors_pmos_extract_exit("Cosmic rays correction requires "
763  "either skylocal=true or skyglobal=true");
764 
765  slit_margin = dfs_get_parameter_int(parlist,
766  "fors.fors_pmos_extract.slit_margin",
767  NULL);
768  if (slit_margin < 0)
769  fors_pmos_extract_exit("Value must be zero or positive");
770 
771  ext_radius = dfs_get_parameter_int(parlist, "fors.fors_pmos_extract.ext_radius",
772  NULL);
773  if (ext_radius < 0)
774  fors_pmos_extract_exit("Value must be zero or positive");
775 
776  cont_radius = dfs_get_parameter_int(parlist,
777  "fors.fors_pmos_extract.cont_radius",
778  NULL);
779  if (cont_radius < 0)
780  fors_pmos_extract_exit("Value must be zero or positive");
781 
782  ext_mode = dfs_get_parameter_int(parlist, "fors.fors_pmos_extract.ext_mode",
783  NULL);
784  if (ext_mode < 0 || ext_mode > 1)
785  fors_pmos_extract_exit("Invalid object extraction mode");
786 
787  time_normalise = dfs_get_parameter_bool(parlist,
788  "fors.fors_pmos_extract.time_normalise", NULL);
789 
790  check = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.check", NULL);
791  cpl_table_delete(grism_table); grism_table = NULL;
792 
793  if (cpl_error_get_code())
794  fors_pmos_extract_exit("Failure getting the configuration parameters");
795 
796 
797  /*
798  * Check input set-of-frames
799  */
800 
801  cpl_msg_indent_less();
802  cpl_msg_info(recipe, "Check input set-of-frames:");
803  cpl_msg_indent_more();
804 
805  if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
806  cpl_msg_warning(cpl_func,"Input frames are not from the same grism");
807 
808  if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
809  cpl_msg_warning(cpl_func,"Input frames are not from the same filter");
810 
811  if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
812  cpl_msg_warning(cpl_func,"Input frames are not from the same chip");
813 
814  standard = 0;
815  pmos = cpl_frameset_count_tags(frameset, "SCIENCE_PMOS");
816 
817  if (pmos == 0) {
818  pmos = cpl_frameset_count_tags(frameset, "STANDARD_PMOS");
819  standard = 1;
820  }
821 
822  if (pmos == 0)
823  fors_pmos_extract_exit("Missing input scientific frame");
824 
825  angles = fors_check_angles(frameset, pmos,
826  standard ? "STANDARD_PMOS" : "SCIENCE_PMOS",
827  &circ);
828  if (angles == NULL)
829  fors_pmos_extract_exit("Polarization angles could not be read");
830 
831  if (circ)
832  chromatism = 0; /* Chromatism correction unrequired for
833  circular polarimetry */
834 
835 
836  nscience = pmos;
837 
838  reduceds = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
839  rerrors = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
840  slitss = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
841  mappeds = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
842  skylocalmaps = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
843 
844  if (pmos) {
845  cpl_msg_info(recipe, "PMOS data found");
846  if (standard) {
847  science_tag = "STANDARD_PMOS";
848  reduced_science_tag = "REDUCED_STD_PMOS";
849  unmapped_science_tag = "UNMAPPED_STD_PMOS";
850  mapped_science_tag = "MAPPED_STD_PMOS";
851  mapped_science_sky_tag = "MAPPED_ALL_STD_PMOS";
852  skylines_offsets_tag = "SKY_SHIFTS_SLIT_STD_PMOS";
853  wavelength_map_sky_tag = "WAVELENGTH_MAP_STD_PMOS";
854  disp_coeff_sky_tag = "DISP_COEFF_STD_PMOS";
855  mapped_sky_tag = "MAPPED_SKY_STD_PMOS";
856  unmapped_sky_tag = "UNMAPPED_SKY_STD_PMOS";
857  object_table_tag = "OBJECT_TABLE_STD_PMOS";
858  object_table_pol_tag = "OBJECT_TABLE_POL_STD_PMOS";
859  reduced_sky_tag = "REDUCED_SKY_STD_PMOS";
860  reduced_error_tag = "REDUCED_ERROR_STD_PMOS";
861  reduced_q_tag = "REDUCED_Q_STD_PMOS";
862  reduced_u_tag = "REDUCED_U_STD_PMOS";
863  reduced_v_tag = "REDUCED_V_STD_PMOS";
864  reduced_l_tag = "REDUCED_L_STD_PMOS";
865  reduced_error_q_tag = "REDUCED_ERROR_Q_STD_PMOS";
866  reduced_error_u_tag = "REDUCED_ERROR_U_STD_PMOS";
867  reduced_error_v_tag = "REDUCED_ERROR_V_STD_PMOS";
868  reduced_error_l_tag = "REDUCED_ERROR_L_STD_PMOS";
869  reduced_nul_q_tag = "REDUCED_NUL_Q_STD_PMOS";
870  reduced_nul_u_tag = "REDUCED_NUL_U_STD_PMOS";
871  reduced_nul_v_tag = "REDUCED_NUL_V_STD_PMOS";
872  reduced_angle_tag = "REDUCED_ANGLE_STD_PMOS";
873  reduced_error_angle_tag = "REDUCED_ERROR_ANGLE_STD_PMOS";
874  }
875  else {
876  science_tag = "SCIENCE_PMOS";
877  reduced_science_tag = "REDUCED_SCI_PMOS";
878  unmapped_science_tag = "UNMAPPED_SCI_PMOS";
879  mapped_science_tag = "MAPPED_SCI_PMOS";
880  mapped_science_sky_tag = "MAPPED_ALL_SCI_PMOS";
881  skylines_offsets_tag = "SKY_SHIFTS_SLIT_SCI_PMOS";
882  wavelength_map_sky_tag = "WAVELENGTH_MAP_SCI_PMOS";
883  disp_coeff_sky_tag = "DISP_COEFF_SCI_PMOS";
884  mapped_sky_tag = "MAPPED_SKY_SCI_PMOS";
885  unmapped_sky_tag = "UNMAPPED_SKY_SCI_PMOS";
886  object_table_tag = "OBJECT_TABLE_SCI_PMOS";
887  object_table_pol_tag = "OBJECT_TABLE_POL_SCI_PMOS";
888  reduced_sky_tag = "REDUCED_SKY_SCI_PMOS";
889  reduced_error_tag = "REDUCED_ERROR_SCI_PMOS";
890  reduced_q_tag = "REDUCED_Q_SCI_PMOS";
891  reduced_u_tag = "REDUCED_U_SCI_PMOS";
892  reduced_v_tag = "REDUCED_V_SCI_PMOS";
893  reduced_l_tag = "REDUCED_L_SCI_PMOS";
894  reduced_error_q_tag = "REDUCED_ERROR_Q_SCI_PMOS";
895  reduced_error_u_tag = "REDUCED_ERROR_U_SCI_PMOS";
896  reduced_error_v_tag = "REDUCED_ERROR_V_SCI_PMOS";
897  reduced_error_l_tag = "REDUCED_ERROR_L_SCI_PMOS";
898  reduced_nul_q_tag = "REDUCED_NUL_Q_SCI_PMOS";
899  reduced_nul_u_tag = "REDUCED_NUL_U_SCI_PMOS";
900  reduced_nul_v_tag = "REDUCED_NUL_V_SCI_PMOS";
901  reduced_angle_tag = "REDUCED_ANGLE_SCI_PMOS";
902  reduced_error_angle_tag = "REDUCED_ERROR_ANGLE_SCI_PMOS";
903  }
904 
905  master_norm_flat_tag = "MASTER_NORM_FLAT_PMOS";
906  global_sky_spectrum_tag = "GLOBAL_SKY_SPECTRUM_PMOS";
907 
908  if (!cpl_frameset_count_tags(frameset, master_norm_flat_tag)) {
909  master_norm_flat_tag = "MASTER_NORM_FLAT_LONG_PMOS";
910  }
911  }
912 
913  if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") == 0)
914  fors_pmos_extract_exit("Missing required input: MASTER_BIAS");
915 
916  if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") > 1)
917  fors_pmos_extract_exit("Too many in input: MASTER_BIAS");
918 
919  if (skyalign >= 0)
920  if (cpl_frameset_count_tags(frameset, "MASTER_SKYLINECAT") > 1)
921  fors_pmos_extract_exit("Too many in input: MASTER_SKYLINECAT");
922 
923  if (chromatism) {
924  if (cpl_frameset_count_tags(frameset, chrom_table_tag) == 0) {
925  cpl_msg_error(recipe, "Missing required input: %s",
926  chrom_table_tag);
927  fors_pmos_extract_exit(NULL);
928  }
929 
930  if (cpl_frameset_count_tags(frameset, chrom_table_tag) > 1) {
931  cpl_msg_error(recipe, "Too many in input: %s", chrom_table_tag);
932  fors_pmos_extract_exit(NULL);
933  }
934  }
935 
936  if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) > 1) {
937  if (flatfield) {
938  cpl_msg_error(recipe, "Too many in input: %s",
939  master_norm_flat_tag);
940  fors_pmos_extract_exit(NULL);
941  }
942  else {
943  cpl_msg_warning(recipe, "%s in input are ignored, "
944  "since flat field correction was not requested",
945  master_norm_flat_tag);
946  }
947  }
948 
949  if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 1) {
950  if (!flatfield) {
951  cpl_msg_warning(recipe, "%s in input is ignored, "
952  "since flat field correction was not requested",
953  master_norm_flat_tag);
954  }
955  }
956 
957  if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 0) {
958  if (flatfield) {
959  cpl_msg_error(recipe, "Flat field correction was requested, "
960  "but no %s are found in input",
961  master_norm_flat_tag);
962  fors_pmos_extract_exit(NULL);
963  }
964  }
965 
966  if (cpl_frameset_count_tags(frameset, master_distortion_tag) == 0)
967  fors_pmos_extract_exit("Missing required input: MASTER_DISTORTION_TABLE");
968 
969  if (cpl_frameset_count_tags(frameset, master_distortion_tag) > 1)
970  fors_pmos_extract_exit("Too many in input: MASTER_DISTORTION_TABLE");
971 
972  global = dfs_load_table(frameset, master_distortion_tag, 1);
973  if (global == NULL)
974  fors_pmos_extract_exit("Cannot load master distortion table");
975 
976  cpl_msg_indent_less();
977 
978  /*
979  * Get the reference wavelength and the rebin factor along the
980  * dispersion direction from a scientific exposure
981  */
982 
983  header = dfs_load_header(frameset, science_tag, 0);
984 
985  if (header == NULL)
986  fors_pmos_extract_exit("Cannot load scientific frame header");
987 
988  instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
989  if (instrume == NULL)
990  fors_pmos_extract_exit("Missing keyword INSTRUME in scientific header");
991  instrume = cpl_strdup(instrume);
992 
993  if (instrume[4] == '1')
994  snprintf(version, 80, "%s/%s", "fors1", VERSION);
995  if (instrume[4] == '2')
996  snprintf(version, 80, "%s/%s", "fors2", VERSION);
997 
998  reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
999 
1000  if (cpl_error_get_code() != CPL_ERROR_NONE)
1001  fors_pmos_extract_exit("Missing keyword ESO INS GRIS1 WLEN in scientific "
1002  "frame header");
1003 
1004  if (reference < 3000.0) /* Perhaps in nanometers... */
1005  reference *= 10;
1006 
1007  if (reference < 3000.0 || reference > 13000.0) {
1008  cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
1009  "keyword ESO INS GRIS1 WLEN in scientific frame header",
1010  reference);
1011  fors_pmos_extract_exit(NULL);
1012  }
1013 
1014  cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
1015 
1016  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
1017 
1018  if (cpl_error_get_code() != CPL_ERROR_NONE)
1019  fors_pmos_extract_exit("Missing keyword ESO DET WIN1 BINX in scientific "
1020  "frame header");
1021 
1022  if (rebin != 1) {
1023  dispersion *= rebin;
1024  cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
1025  "resampling step used is %f A/pixel", rebin,
1026  dispersion);
1027  }
1028 
1029  gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
1030 
1031  if (cpl_error_get_code() != CPL_ERROR_NONE)
1032  fors_pmos_extract_exit("Missing keyword ESO DET OUT1 CONAD in scientific "
1033  "frame header");
1034 
1035  cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
1036 
1037  ron = cpl_propertylist_get_double(header, "ESO DET OUT1 RON");
1038 
1039  if (cpl_error_get_code() != CPL_ERROR_NONE)
1040  fors_pmos_extract_exit("Missing keyword ESO DET OUT1 RON in scientific "
1041  "frame header");
1042 
1043  ron /= gain; /* Convert from electrons to ADU */
1044 
1045  cpl_msg_info(recipe, "The read-out-noise is: %.2f ADU", ron);
1046 
1047  cpl_msg_info(recipe, "Load normalised flat field (if present)...");
1048  cpl_msg_indent_more();
1049 
1050  if (flatfield) {
1051  norm_flat = dfs_load_image(frameset, master_norm_flat_tag,
1052  CPL_TYPE_FLOAT, 0, 1);
1053  }
1054 
1055  cpl_msg_info(recipe, "Produce mask slit position table...");
1056 
1057  maskslits = mos_load_slits_fors_pmos(header, &nslits_out_det);
1058 
1059  if (skyalign >= 0) {
1060 
1061  cpl_msg_indent_less();
1062  cpl_msg_info(recipe, "Load input sky line catalog...");
1063  cpl_msg_indent_more();
1064 
1065  wavelengths = dfs_load_table(frameset, "MASTER_SKYLINECAT", 1);
1066 
1067  if (wavelengths) {
1068  /*
1069  * Cast the wavelengths into a (double precision) CPL vector
1070  */
1071 
1072  nlines = cpl_table_get_nrow(wavelengths);
1073 
1074  if (nlines == 0)
1075  fors_pmos_extract_exit("Empty input sky line catalog");
1076 
1077  if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
1078  cpl_msg_error(recipe, "Missing column %s in input line "
1079  "catalog table", wcolumn);
1080  fors_pmos_extract_exit(NULL);
1081  }
1082 
1083  line = cpl_malloc(nlines * sizeof(double));
1084 
1085  for (i = 0; i < nlines; i++)
1086  line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
1087 
1088  cpl_table_delete(wavelengths); wavelengths = NULL;
1089 
1090  lines = cpl_vector_wrap(nlines, line);
1091  }
1092  else {
1093  cpl_msg_info(recipe, "No sky line catalog found in input - fine!");
1094  }
1095  }
1096 
1097 
1098  cpl_propertylist_delete(header); header = NULL;
1099 
1100  /*
1101  * Load the wavelength calibration table
1102  */
1103 
1104  for (j = 0; j < nscience; j++) {
1105  int k;
1106 
1107  cpl_msg_indent_less();
1108  cpl_msg_info(recipe, "Processing scientific exposure of angle %.2f "
1109  "(%d out of %d) ...",
1110  angles[j], j + 1, nscience);
1111  cpl_msg_indent_more();
1112 
1113  cpl_msg_info(recipe, "Load scientific exposure...");
1114  cpl_msg_indent_more();
1115 
1116  /*
1117  * FIXME: Horrible workaround to avoid the problem because of the
1118  * multiple encapsulation of cpl_frameset_find() in different
1119  * loading functions
1120  */
1121 
1122  header = dfs_load_header(frameset, science_tag, 0);
1123 
1124  for (k = 0; k < j; k ++) {
1125  cpl_propertylist_delete(header);
1126  header = dfs_load_header(frameset, NULL, 0);
1127  }
1128 
1129  spectra = dfs_load_image(frameset, science_tag, CPL_TYPE_FLOAT, 0, 0);
1130 
1131  for (k = 0; k < j; k ++) {
1132  cpl_image_delete(spectra);
1133  spectra = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
1134  }
1135 
1136  if (spectra == NULL)
1137  fors_pmos_extract_exit("Cannot load scientific frame");
1138 
1139  if (header == NULL)
1140  fors_pmos_extract_exit("Cannot load scientific frame header");
1141 
1142  alltime = cpl_propertylist_get_double(header, "EXPTIME");
1143 
1144  if (cpl_error_get_code() != CPL_ERROR_NONE)
1145  fors_pmos_extract_exit("Missing keyword EXPTIME in scientific "
1146  "frame header");
1147 
1148  /* Leave the header on for the next step... */
1149  //cpl_propertylist_delete(header); header = NULL;
1150 
1151  cpl_msg_info(recipe, "Scientific frame exposure time: %.2f s",
1152  alltime);
1153 
1154  cpl_msg_indent_less();
1155 
1156  /*
1157  * Remove the master bias
1158  */
1159 
1160  cpl_msg_info(recipe, "Remove the master bias...");
1161 
1162  bias = dfs_load_image(frameset, "MASTER_BIAS", CPL_TYPE_FLOAT, 0, 1);
1163 
1164  if (bias == NULL)
1165  fors_pmos_extract_exit("Cannot load master bias");
1166 
1167  overscans = mos_load_overscans_vimos(header, 1);
1168 
1169  dummy = mos_remove_bias(spectra, bias, overscans);
1170  cpl_image_delete(spectra); spectra = dummy; dummy = NULL;
1171  cpl_image_delete(bias); bias = NULL;
1172  cpl_table_delete(overscans); overscans = NULL;
1173 
1174  if (spectra == NULL)
1175  fors_pmos_extract_exit("Cannot remove bias from scientific frame");
1176 
1177  ccd_xsize = nx = cpl_image_get_size_x(spectra);
1178  ny = cpl_image_get_size_y(spectra);
1179 
1180  if (flatfield) {
1181 
1182  if (norm_flat) {
1183  cpl_msg_info(recipe, "Apply flat field correction...");
1184  if (cpl_image_divide(spectra, norm_flat) != CPL_ERROR_NONE) {
1185  cpl_msg_error(recipe, "Failure of flat field correction: %s",
1186  cpl_error_get_message());
1187  fors_pmos_extract_exit(NULL);
1188  }
1189  }
1190  else {
1191  cpl_msg_error(recipe, "Cannot load input %s for flat field "
1192  "correction", master_norm_flat_tag);
1193  fors_pmos_extract_exit(NULL);
1194  }
1195 
1196  }
1197 
1198  /*
1199  * Load the slit location table
1200  */
1201 
1202  slits = mos_build_slit_location(global, maskslits, ny);
1203 
1204  if (slits == NULL) {
1205  fors_pmos_extract_exit("Cannot load slits location table");
1206  } else {
1207  cpl_table_new_column(slits, "pair_id", CPL_TYPE_INT);
1208 
1209  int m, null, size = cpl_table_get_nrow(slits);
1210 
1211  for (m = 0; m < size; m++) {
1212  int slit_id = cpl_table_get(slits, "slit_id", m, &null);
1213 
1214  int pair_id = slit_id % 2 ? slit_id + 1 : slit_id;
1215 
1216  cpl_table_set(slits, "pair_id", m, pair_id);
1217  }
1218  }
1219 
1220  cpl_msg_info(recipe, "Processing scientific spectra...");
1221 
1222  /*
1223  * Load the spectral curvature table
1224  */
1225 
1226  polytraces = mos_build_curv_coeff(global, maskslits, slits);
1227  if (polytraces == NULL)
1228  fors_pmos_extract_exit("Cannot create spectral curvature table");
1229 
1230  /*
1231  * This one will also generate the spatial map from the spectral
1232  * curvature table (in the case of multislit data)
1233  */
1234 
1235  coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
1236 
1237  smapped = mos_spatial_calibration(spectra, slits, polytraces, reference,
1238  startwavelength, endwavelength,
1239  dispersion, flux, coordinate);
1240 
1241  /*
1242  * Generate a rectified wavelength map from the wavelength calibration
1243  * table
1244  */
1245 
1246  /*
1247  * Load the wavelength calibration table
1248  */
1249 
1250  idscoeff = mos_build_disp_coeff(global, slits);
1251  if (idscoeff == NULL)
1252  fors_pmos_extract_exit("Cannot create wavelength calibration table");
1253 
1254  rainbow = mos_map_idscoeff(idscoeff, nx, reference, startwavelength,
1255  endwavelength);
1256 
1257  if (dispersion > 1.0)
1258  highres = 0;
1259  else
1260  highres = 1;
1261 
1262  if (skyalign >= 0) {
1263  if (skyalign) {
1264  cpl_msg_info(recipe, "Align wavelength solution to reference "
1265  "skylines applying %d order residual fit...", skyalign);
1266  }
1267  else {
1268  cpl_msg_info(recipe, "Align wavelength solution to reference "
1269  "skylines applying median offset...");
1270  }
1271 
1272  if (!j) {
1273  offsets = mos_wavelength_align(smapped, slits, reference,
1274  startwavelength, endwavelength,
1275  idscoeff, lines, highres,
1276  skyalign, rainbow, 4);
1277 
1278  if (offsets) {
1279  if (standard)
1280  cpl_msg_warning(recipe, "Alignment of the wavelength solution "
1281  "to reference sky lines may be unreliable in "
1282  "this case!");
1283 
1284  if (dfs_save_table(frameset, offsets, skylines_offsets_tag,
1285  NULL, parlist, recipe, version)) {
1286  fors_pmos_extract_exit(NULL);
1287  }
1288 
1289  } else {
1290  cpl_msg_warning(recipe, "Alignment of the wavelength solution "
1291  "to reference sky lines could not be done!");
1292  skyalign = -1;
1293  }
1294  }
1295 
1296 
1297  }
1298 
1299  wavemap = mos_map_wavelengths(coordinate, rainbow, slits,
1300  polytraces, reference,
1301  startwavelength, endwavelength,
1302  dispersion);
1303 
1304 
1305  cpl_image_delete(rainbow); rainbow = NULL;
1306  cpl_image_delete(coordinate); coordinate = NULL;
1307 
1308  /*
1309  * Here the wavelength calibrated slit spectra are created. This frame
1310  * contains sky_science.
1311  */
1312 
1313  mapped_sky = mos_wavelength_calibration(smapped, reference,
1314  startwavelength, endwavelength,
1315  dispersion, idscoeff, flux);
1316 
1317  if (!j) {
1318  cpl_msg_indent_less();
1319  cpl_msg_info(recipe, "Check applied wavelength against skylines...");
1320  cpl_msg_indent_more();
1321 
1322  mean_rms = mos_distortions_rms(mapped_sky, lines, startwavelength,
1323  dispersion, 6, highres);
1324 
1325 
1326  cpl_msg_info(recipe, "Mean residual: %f", mean_rms);
1327 
1328  mean_rms = cpl_table_get_column_mean(idscoeff, "error");
1329 
1330  cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
1331  mean_rms, mean_rms * dispersion);
1332  }
1333 
1334  save_header = cpl_propertylist_duplicate(header);
1335 
1336  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
1337  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
1338  cpl_propertylist_update_double(header, "CRVAL1",
1339  startwavelength + dispersion/2);
1340  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
1341  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
1342  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
1343  cpl_propertylist_update_double(header, "CD1_1", dispersion);
1344  cpl_propertylist_update_double(header, "CD1_2", 0.0);
1345  cpl_propertylist_update_double(header, "CD2_1", 0.0);
1346  cpl_propertylist_update_double(header, "CD2_2", 1.0);
1347  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
1348  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
1349 
1350  if (time_normalise) {
1351  dummy = cpl_image_divide_scalar_create(mapped_sky, alltime);
1352  if (!j) {
1353  if(dfs_save_image_null(frameset, parlist,
1354  mapped_science_sky_tag,
1355  recipe, version)) {
1356  fors_pmos_extract_exit(NULL);
1357  }
1358  }
1359  if (dfs_save_image_ext(dummy, mapped_science_sky_tag, header)) {
1360  fors_pmos_extract_exit(NULL);
1361  }
1362  cpl_image_delete(dummy); dummy = NULL;
1363  }
1364  else {
1365 
1366  if (!j) {
1367  if(dfs_save_image_null(frameset, parlist,
1368  mapped_science_sky_tag,
1369  recipe, version)) {
1370  fors_pmos_extract_exit(NULL);
1371  }
1372  }
1373 
1374  if (dfs_save_image_ext(mapped_sky,
1375  mapped_science_sky_tag, header)) {
1376  fors_pmos_extract_exit(NULL);
1377  }
1378 
1379  }
1380 
1381 /* if (skyglobal == 0 && skymedian < 0) { NSS */
1382  if (skyglobal == 0 && skymedian == 0 && skylocal == 0) {
1383  cpl_image_delete(mapped_sky); mapped_sky = NULL;
1384  }
1385 
1386  if (skyglobal || skylocal) {
1387 
1388  cpl_msg_indent_less();
1389 
1390  if (skyglobal) {
1391  cpl_msg_info(recipe, "Global sky determination...");
1392  cpl_msg_indent_more();
1393  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
1394 
1395  sky = mos_sky_map_super(spectra, wavemap, dispersion,
1396  2.0, 50, skymap);
1397 
1398  if (sky) {
1399  cpl_image_subtract(spectra, skymap);
1400  }
1401  else {
1402  cpl_image_delete(skymap); skymap = NULL;
1403  }
1404  }
1405  else {
1406  cpl_msg_info(recipe, "Local sky determination...");
1407  cpl_msg_indent_more();
1408  skymap = mos_subtract_sky(spectra, slits, polytraces, reference,
1409  startwavelength, endwavelength, dispersion);
1410  }
1411 
1412  if (skymap) {
1413  if (skyglobal) {
1414  if (time_normalise)
1415  cpl_table_divide_scalar(sky, "sky", alltime);
1416 
1417 /* Old saving:
1418 
1419  if (!j) {
1420  if (dfs_save_table(frameset, sky,
1421  global_sky_spectrum_tag,
1422  NULL, parlist, recipe, version)) {
1423  fors_pmos_extract_exit(NULL);
1424  }
1425  } else {
1426  if (dfs_save_table_ext(sky, global_sky_spectrum_tag,
1427  NULL)) {
1428  fors_pmos_extract_exit(NULL);
1429  }
1430  }
1431 
1432 End of old saving */
1433 
1434  if (!j) {
1435  if(dfs_save_image_null(frameset, parlist,
1436  global_sky_spectrum_tag,
1437  recipe, version)) {
1438  fors_pmos_extract_exit(NULL);
1439  }
1440  }
1441 
1442  if (dfs_save_table_ext(sky, global_sky_spectrum_tag,
1443  NULL)) {
1444  fors_pmos_extract_exit(NULL);
1445  }
1446 
1447 
1448  cpl_table_delete(sky); sky = NULL;
1449  }
1450 
1451 // save_header = dfs_load_header(frameset, science_tag, 0);
1452 
1453  if (time_normalise)
1454  cpl_image_divide_scalar(skymap, alltime);
1455 
1456  if (!j) {
1457  if(dfs_save_image_null(frameset, parlist,
1458  unmapped_sky_tag,
1459  recipe, version)) {
1460  fors_pmos_extract_exit(NULL);
1461  }
1462  }
1463 
1464  if (dfs_save_image_ext(skymap, unmapped_sky_tag,
1465  save_header)) {
1466  fors_pmos_extract_exit(NULL);
1467  }
1468 
1469  cpl_image_delete(skymap); skymap = NULL;
1470 
1471  if (!j) {
1472  if(dfs_save_image_null(frameset, parlist,
1473  unmapped_science_tag,
1474  recipe, version)) {
1475  fors_pmos_extract_exit(NULL);
1476  }
1477  }
1478 
1479  if (dfs_save_image_ext(spectra, unmapped_science_tag,
1480  save_header)) {
1481  fors_pmos_extract_exit(NULL);
1482  }
1483 
1484 // cpl_propertylist_delete(save_header); save_header = NULL;
1485 
1486  if (cosmics) {
1487  cpl_msg_info(recipe, "Removing cosmic rays...");
1488  mos_clean_cosmics(spectra, gain, -1., -1.);
1489  }
1490 
1491  /*
1492  * The spatially rectified image, that contained the sky,
1493  * is replaced by a sky-subtracted spatially rectified image:
1494  */
1495 
1496  cpl_image_delete(smapped); smapped = NULL;
1497 
1498  smapped = mos_spatial_calibration(spectra, slits, polytraces,
1499  reference, startwavelength,
1500  endwavelength, dispersion,
1501  flux, NULL);
1502  }
1503  else {
1504  cpl_msg_warning(recipe, "Sky subtraction failure");
1505  if (cosmics)
1506  cpl_msg_warning(recipe, "Cosmic rays removal not performed!");
1507  cosmics = skylocal = skyglobal = 0;
1508  }
1509  }
1510 
1511  cpl_image_delete(spectra); spectra = NULL;
1512  cpl_table_delete(polytraces); polytraces = NULL;
1513 
1514  if (skyalign >= 0) {
1515  save_header = dfs_load_header(frameset, science_tag, 0);
1516 
1517  if (!j) {
1518  if(dfs_save_image_null(frameset, parlist,
1519  wavelength_map_sky_tag,
1520  recipe, version)) {
1521  fors_pmos_extract_exit(NULL);
1522  }
1523  }
1524 
1525  if (dfs_save_image_ext(wavemap, wavelength_map_sky_tag,
1526  save_header)) {
1527  fors_pmos_extract_exit(NULL);
1528  }
1529 
1530 // cpl_propertylist_delete(save_header); save_header = NULL;
1531  }
1532 
1533  cpl_image_delete(wavemap); wavemap = NULL;
1534 
1535  mapped = mos_wavelength_calibration(smapped, reference,
1536  startwavelength, endwavelength,
1537  dispersion, idscoeff, flux);
1538 
1539  cpl_image_delete(smapped); smapped = NULL;
1540 
1541  if (skyalign >= 0) {
1542  if (!j) {
1543  if (dfs_save_table(frameset, idscoeff, disp_coeff_sky_tag,
1544  NULL, parlist, recipe, version)) {
1545  fors_pmos_extract_exit(NULL);
1546  }
1547  }
1548  }
1549 
1550 /* if (skymedian >= 0) { NSS */
1551  if (skymedian) {
1552  cpl_msg_indent_less();
1553  cpl_msg_info(recipe, "Local sky determination...");
1554  cpl_msg_indent_more();
1555 
1556 /* NSS skylocalmap = mos_sky_local(mapped, slits, skymedian); */
1557 /* skylocalmap = mos_sky_local(mapped, slits, 0); */
1558  skylocalmap = mos_sky_local_old(mapped, slits);
1559  cpl_image_subtract(mapped, skylocalmap);
1560 /*
1561  if (dfs_save_image(frameset, skylocalmap, mapped_sky_tag, header,
1562  parlist, recipe, version))
1563  fors_pmos_extract_exit(NULL);
1564 */
1565  cpl_image_delete(skylocalmap); skylocalmap = NULL;
1566  }
1567 
1568 /* if (skyglobal || skymedian >= 0 || skylocal) { NSS */
1569  if (skyglobal || skymedian || skylocal) {
1570 
1571  skylocalmap = cpl_image_subtract_create(mapped_sky, mapped);
1572 
1573  cpl_image_delete(mapped_sky); mapped_sky = NULL;
1574 
1575  if (time_normalise) {
1576  dummy = cpl_image_divide_scalar_create(skylocalmap, alltime);
1577 
1578  if (!j) {
1579  if(dfs_save_image_null(frameset, parlist,
1580  mapped_sky_tag,
1581  recipe, version)) {
1582  fors_pmos_extract_exit(NULL);
1583  }
1584  }
1585 
1586  if (dfs_save_image_ext(dummy, mapped_sky_tag,
1587  header)) {
1588  fors_pmos_extract_exit(NULL);
1589  }
1590 
1591  cpl_image_delete(dummy); dummy = NULL;
1592  }
1593  else {
1594  if (!j) {
1595  if(dfs_save_image_null(frameset, parlist,
1596  mapped_sky_tag,
1597  recipe, version)) {
1598  fors_pmos_extract_exit(NULL);
1599  }
1600  }
1601 
1602  if (dfs_save_image_ext(skylocalmap, mapped_sky_tag,
1603  header)) {
1604  fors_pmos_extract_exit(NULL);
1605  }
1606  }
1607 
1608  skylocalmaps[j] = skylocalmap;
1609 
1610  cpl_msg_indent_less();
1611  cpl_msg_info(recipe, "Object detection...");
1612  cpl_msg_indent_more();
1613 
1614  if (!j)
1615  origslits = cpl_table_duplicate(slits);
1616 
1617  if (cosmics || nscience > 1) {
1618  dummy = mos_detect_objects(mapped, slits, slit_margin, ext_radius,
1619  cont_radius);
1620  }
1621  else {
1622  mapped_cleaned = cpl_image_duplicate(mapped);
1623  mos_clean_cosmics(mapped_cleaned, gain, -1., -1.);
1624  dummy = mos_detect_objects(mapped_cleaned, slits, slit_margin,
1625  ext_radius, cont_radius);
1626 
1627  cpl_image_delete(mapped_cleaned); mapped_cleaned = NULL;
1628  }
1629 
1630  cpl_image_delete(dummy); dummy = NULL;
1631 
1632  if (check) {
1633 
1634 /* Old saving:
1635 
1636  if (!j) {
1637  if (dfs_save_table(frameset, slits, object_table_tag,
1638  NULL, parlist, recipe, version)) {
1639  fors_pmos_extract_exit(NULL);
1640  }
1641  } else {
1642  if (dfs_save_table_ext(slits, object_table_tag, NULL)) {
1643  fors_pmos_extract_exit(NULL);
1644  }
1645  }
1646 
1647 End old saving */
1648 
1649  if (!j) {
1650  if(dfs_save_image_null(frameset, parlist,
1651  object_table_tag,
1652  recipe, version)) {
1653  fors_pmos_extract_exit(NULL);
1654  }
1655  }
1656 
1657  if (dfs_save_table_ext(slits, object_table_tag, NULL)) {
1658  fors_pmos_extract_exit(NULL);
1659  }
1660  }
1661  }
1662 
1663  slitss[j] = slits;
1664  mappeds[j] = mapped;
1665 
1666  cpl_msg_indent_less();
1667 
1668  cpl_propertylist_delete(header); header = NULL;
1669  cpl_propertylist_delete(save_header); save_header = NULL;
1670 
1671  cpl_table_delete(idscoeff); idscoeff = NULL;
1672  }
1673 
1674  cpl_table_delete(offsets); offsets = NULL;
1675 
1676  cpl_image_delete(norm_flat); norm_flat = NULL;
1677  cpl_vector_delete(lines); lines = NULL;
1678 
1679  cpl_table_delete(maskslits); maskslits = NULL;
1680 
1681 
1682  cpl_msg_indent_less();
1683  cpl_msg_info(recipe,
1684  "Check object detection in both beams for all angles...");
1685  cpl_msg_indent_more();
1686 
1687  /* House keeping - selection of objects for which information required
1688  for Stokes parameters computation is present */
1689  error = mos_object_intersect(slitss, origslits, nscience, 5.0);
1690  if (error == CPL_ERROR_DATA_NOT_FOUND) {
1691  cpl_msg_warning(recipe, "No objects found: no Stokes "
1692  "parameters to compute!");
1693  for (j = 0; j < nscience; j++)
1694  cpl_table_delete(slitss[j]);
1695  cpl_free(slitss);
1696  cpl_table_delete(origslits);
1697  return 0;
1698  } else if (error) {
1699  fors_pmos_extract_exit("Problem in polarimetric object selection");
1700  }
1701 
1702  if (dfs_save_table(frameset, origslits, object_table_pol_tag,
1703  NULL, parlist, recipe, version)) {
1704  fors_pmos_extract_exit(NULL);
1705  }
1706 
1707  nobjs_per_slit = fors_get_nobjs_perslit(origslits);
1708 
1709  cpl_msg_indent_less();
1710  cpl_msg_info(recipe, "Object extraction...");
1711  cpl_msg_indent_more();
1712 
1713  for (j = 0; j < nscience; j++) {
1714  int k;
1715 
1716  header = dfs_load_header(frameset, science_tag, 0);
1717 
1718  for (k = 0; k < j; k ++) {
1719  cpl_propertylist_delete(header);
1720  header = dfs_load_header(frameset, NULL, 0);
1721  }
1722 
1723  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
1724  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
1725  cpl_propertylist_update_double(header, "CRVAL1",
1726  startwavelength + dispersion/2);
1727  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
1728  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
1729  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
1730  cpl_propertylist_update_double(header, "CD1_1", dispersion);
1731  cpl_propertylist_update_double(header, "CD1_2", 0.0);
1732  cpl_propertylist_update_double(header, "CD2_1", 0.0);
1733  cpl_propertylist_update_double(header, "CD2_2", 1.0);
1734  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
1735  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
1736 
1737  if (skyglobal || skymedian || skylocal) {
1738 
1739  cpl_msg_info(recipe, "Extracting at angle %.2f (%d out of %d) ...",
1740  angles[j], j + 1, nscience);
1741 
1742  images = mos_extract_objects(mappeds[j], NULL, skylocalmaps[j],
1743  origslits,
1744  ext_mode, ron, gain, 1);
1745 
1746  cpl_image_delete(skylocalmaps[j]); skylocalmaps[j] = NULL;
1747 
1748  if (images) {
1749  if (time_normalise)
1750  cpl_image_divide_scalar(images[0], alltime);
1751 
1752  if (!j) {
1753  if(dfs_save_image_null(frameset, parlist,
1754  reduced_science_tag,
1755  recipe, version)) {
1756  fors_pmos_extract_exit(NULL);
1757  }
1758  }
1759 
1760  if (dfs_save_image_ext(images[0], reduced_science_tag,
1761  header)) {
1762  fors_pmos_extract_exit(NULL);
1763  }
1764 
1765  reduceds[j] = images[0];
1766 // cpl_image_delete(images[0]);
1767 
1768  if (time_normalise)
1769  cpl_image_divide_scalar(images[1], alltime);
1770 
1771  if (!j) {
1772  if(dfs_save_image_null(frameset, parlist,
1773  reduced_sky_tag,
1774  recipe, version)) {
1775  fors_pmos_extract_exit(NULL);
1776  }
1777  }
1778 
1779  if (dfs_save_image_ext(images[1], reduced_sky_tag,
1780  header)) {
1781  fors_pmos_extract_exit(NULL);
1782  }
1783  cpl_image_delete(images[1]);
1784 
1785  if (time_normalise)
1786  cpl_image_divide_scalar(images[2], alltime);
1787 
1788  if (!j) {
1789  if(dfs_save_image_null(frameset, parlist,
1790  reduced_error_tag,
1791  recipe, version)) {
1792  fors_pmos_extract_exit(NULL);
1793  }
1794  }
1795 
1796  if (dfs_save_image_ext(images[2], reduced_error_tag,
1797  header)) {
1798  fors_pmos_extract_exit(NULL);
1799  }
1800 
1801  rerrors[j] = images[2];
1802 // cpl_image_delete(images[2]);
1803 
1804  cpl_free(images);
1805  }
1806  else {
1807  cpl_msg_warning(recipe, "No objects found: the products "
1808  "%s, %s, and %s are not created",
1809  reduced_science_tag, reduced_sky_tag,
1810  reduced_error_tag);
1811  }
1812 
1813  }
1814 
1815 // slitss[j] = slits;
1816 // cpl_table_delete(slits); slits = NULL;
1817 
1818 
1819 /* if (skyglobal || skymedian >= 0) { NSS */
1820  if (skyglobal || skymedian || skylocal) {
1821  if (time_normalise)
1822  cpl_image_divide_scalar(mappeds[j], alltime);
1823 
1824  if (!j) {
1825  if(dfs_save_image_null(frameset, parlist,
1826  mapped_science_tag,
1827  recipe, version)) {
1828  fors_pmos_extract_exit(NULL);
1829  }
1830  }
1831 
1832  if (dfs_save_image_ext(mappeds[j], mapped_science_tag,
1833  header)) {
1834  fors_pmos_extract_exit(NULL);
1835  }
1836  }
1837 
1838  cpl_image_delete(mappeds[j]); mappeds[j] = NULL;
1839  cpl_propertylist_delete(header); header = NULL;
1840 
1841  }
1842 
1843  cpl_table_delete(origslits);
1844 
1845  /* Stokes computation */
1846 
1847  nobjects = cpl_image_get_size_y(reduceds[0]) / 2;
1848  nx = cpl_image_get_size_x(reduceds[0]);
1849 
1850  header = cpl_propertylist_new();
1851  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
1852  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
1853  cpl_propertylist_update_double(header, "CRVAL1",
1854  startwavelength + dispersion/2);
1855  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
1856  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
1857  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
1858  cpl_propertylist_update_double(header, "CD1_1", dispersion);
1859  cpl_propertylist_update_double(header, "CD1_2", 0.0);
1860  cpl_propertylist_update_double(header, "CD2_1", 0.0);
1861  cpl_propertylist_update_double(header, "CD2_2", 1.0);
1862  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
1863  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
1864 
1865  if (circ) {
1866 
1867  cpl_image *pv_im = NULL;
1868  cpl_image *pvnull_im = NULL;
1869  cpl_image *perr_im = NULL;
1870 
1871  double *p_v = NULL;
1872  double *p_vnull = NULL;
1873  double *perr = NULL;
1874 
1875  double mean_vnull;
1876 
1877  int p = -1;
1878  int total = 0;
1879 
1880  pv_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
1881  perr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
1882 
1883  p_v = cpl_image_get_data_double(pv_im);
1884  perr = cpl_image_get_data_double(perr_im);
1885 
1886  if (nscience / 2 > 1) {
1887  pvnull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
1888  p_vnull = cpl_image_get_data_double(pvnull_im);
1889  }
1890 
1891  for (j = 0; j < nobjects; j++) {
1892  int k, m;
1893 
1894  double * ip_v,
1895  * ip_vnull, * iperr;
1896 
1897  float * data;
1898  float * iff, * ierr;
1899 
1900  ip_v = p_v + (nobjects - 1 - j) * nx;
1901 
1902  if (nscience / 2 > 1)
1903  ip_vnull = p_vnull + (nobjects - 1 - j) * nx;
1904 
1905  iperr = perr + (nobjects - 1 - j) * nx;
1906 
1907  while (total < j + 1) {
1908  p++;
1909  total += nobjs_per_slit[p];
1910  }
1911 
1912  for (k = 0; k < nscience / 2; k++) {
1913  float * if_o, * if_e, * ifdelta_o, * ifdelta_e;
1914 
1915  int pos = fors_find_angle_pos(angles, nscience, 180 * k - 45);
1916  int pos_d = fors_find_angle_pos(angles, nscience, 180 * k + 45);
1917 
1918  data = cpl_image_get_data_float(reduceds[pos]);
1919 
1920  if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
1921  + (total - j - 1)) * nx;
1922 
1923  if_e = data + (2 * (nobjects - total)
1924  + (total - j - 1)) * nx;
1925 
1926 // if_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
1927 // if_e = data + 2 * (nobjects - 1 - j) * nx;
1928 
1929  data = cpl_image_get_data_float(reduceds[pos_d]);
1930 
1931  ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
1932  + (total - j - 1)) * nx;
1933 
1934  ifdelta_e = data + (2 * (nobjects - total)
1935  + (total - j - 1)) * nx;
1936 
1937 // ifdelta_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
1938 // ifdelta_e = data + 2 * (nobjects - 1 - j) * nx;
1939 
1940  for (m = 0; m < nx; m++) {
1941 
1942  double quantity = if_o[m] + if_e[m] == 0.0 ? 0.0 :
1943  (if_o[m] - if_e[m] ) /
1944  (if_o[m] + if_e[m] ) -
1945  (ifdelta_o[m] - ifdelta_e[m]) /
1946  (ifdelta_o[m] + ifdelta_e[m]);
1947 
1948  quantity = isfinite(quantity) ? quantity : 0.0;
1949 
1950  /* PQ map computation */
1951  ip_v[m] += quantity * 0.5 / (nscience / 2);
1952 
1953  /* PQnull map computation */
1954  if (nscience / 2 > 1) {
1955  if (k % 2)
1956  ip_vnull[m] += quantity * 0.5 / (nscience / 2);
1957  else
1958  ip_vnull[m] -= quantity * 0.5 / (nscience / 2);
1959  }
1960  }
1961  }
1962 
1963  /* Error map */
1964  data = cpl_image_get_data_float(reduceds[0]);
1965  iff = data + 2 * (nobjects - 1 - j) * nx;
1966 
1967  data = cpl_image_get_data_float(rerrors[0]);
1968  ierr = data + 2 * (nobjects - 1 - j) * nx;
1969 
1970  for (m = 0; m < nx; m++)
1971  iperr[m] = iff[m] <= 0.0 ?
1972  0.0 : ierr[m] / iff[m] * 0.5 / sqrt (nscience / 2);
1973 
1974  if (nscience / 2 > 1) {
1975  float * weights;
1976  float max, sum, sum2, imean;
1977 
1978  int k;
1979 
1980  /* QC on U NULL */
1981  weights = cpl_malloc(sizeof(float) * nx);
1982 
1983  max = 0.0;
1984  for (k = 0; k < nx; k++) {
1985  if (max < iff[k]) max = iff[k];
1986  }
1987 
1988  for (k = 0; k < nx; k++) {
1989  weights[k] = iff[k] < 0.0 ?
1990  0.0 : iff[k] * iff[k] / (max * max);
1991  }
1992 
1993  sum = 0.0;
1994  sum2 = 0.0;
1995  for (k = 0; k < nx; k++) {
1996  sum += weights[k] * ip_vnull[k];
1997  sum2 += weights[k];
1998  }
1999 
2000  cpl_free(weights);
2001 
2002  imean = sum / sum2;
2003 
2004  mean_vnull += (imean - mean_vnull) / (j + 1.0);
2005  }
2006  }
2007 
2008  if (dfs_save_image(frameset, pv_im, reduced_v_tag, header,
2009  parlist, recipe, version))
2010  fors_pmos_extract_exit(NULL);
2011 
2012  if (nscience / 2 > 1) {
2013  char * pipefile, * keyname;
2014  cpl_propertylist * qheader = dfs_load_header(frameset, science_tag, 0);
2015 
2016  cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
2017  cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
2018  cpl_propertylist_update_double(qheader, "CRVAL1",
2019  startwavelength + dispersion/2);
2020  cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
2021  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
2022  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
2023  cpl_propertylist_update_double(qheader, "CD1_1", dispersion);
2024  cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
2025  cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
2026  cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
2027  cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
2028  cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
2029 
2030  fors_qc_start_group(qheader, "2.0", instrume);
2031 
2032  /*
2033  * QC1 group header
2034  */
2035 
2036  if (fors_qc_write_string("PRO.CATG", reduced_nul_v_tag,
2037  "Product category", instrume))
2038  fors_pmos_extract_exit("Cannot write product category to "
2039  "QC log file");
2040 
2041  if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
2042  "DPR type", instrume))
2043  fors_pmos_extract_exit("Missing keyword DPR TYPE in arc "
2044  "lamp header");
2045 
2046  if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
2047  "Template", instrume))
2048  fors_pmos_extract_exit("Missing keyword TPL ID in arc "
2049  "lamp header");
2050 
2051  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
2052  "Grism name", instrume))
2053  fors_pmos_extract_exit("Missing keyword INS GRIS1 NAME in arc "
2054  "lamp header");
2055 
2056  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
2057  "Grism identifier", instrume))
2058  fors_pmos_extract_exit("Missing keyword INS GRIS1 ID in arc "
2059  "lamp header");
2060 
2061  if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
2062  fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
2063  "Filter name", instrume);
2064 
2065  if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
2066  "Collimator name", instrume))
2067  fors_pmos_extract_exit("Missing keyword INS COLL NAME in arc "
2068  "lamp header");
2069 
2070  if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
2071  "Chip identifier", instrume))
2072  fors_pmos_extract_exit("Missing keyword DET CHIP1 ID in arc "
2073  "lamp header");
2074 
2075  if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
2076  "Archive name of input data",
2077  instrume))
2078  fors_pmos_extract_exit("Missing keyword ARCFILE in arc "
2079  "lamp header");
2080 
2081  pipefile = dfs_generate_filename(reduced_nul_v_tag);
2082  if (fors_qc_write_string("PIPEFILE", pipefile,
2083  "Pipeline product name", instrume))
2084  fors_pmos_extract_exit("Cannot write PIPEFILE to QC log file");
2085  cpl_free(pipefile); pipefile = NULL;
2086 
2087 
2088  /*
2089  * QC1 parameters
2090  */
2091 
2092  keyname = "QC.NULL.V.MEAN";
2093 
2094  if (fors_qc_write_qc_double(qheader, mean_vnull,
2095  keyname, NULL,
2096  "Mean V null parameter",
2097  instrume)) {
2098  fors_pmos_extract_exit("Cannot write mean Q null parameter "
2099  "to QC log file");
2100  }
2101 
2103 
2104  if (dfs_save_image(frameset, pvnull_im, reduced_nul_v_tag, qheader,
2105  parlist, recipe, version))
2106  fors_pmos_extract_exit(NULL);
2107 
2108  cpl_propertylist_delete(qheader);
2109  }
2110 
2111  if (dfs_save_image(frameset, perr_im, reduced_error_v_tag, header,
2112  parlist, recipe, version))
2113  fors_pmos_extract_exit(NULL);
2114 
2115  cpl_image_delete(pv_im);
2116  cpl_image_delete(pvnull_im);
2117  cpl_image_delete(perr_im);
2118  } else {
2119  cpl_image *pq_im = NULL;
2120  cpl_image *pu_im = NULL;
2121  cpl_image *pl_im = NULL;
2122 
2123  cpl_image *pqnull_im = NULL;
2124  cpl_image *punull_im = NULL;
2125 
2126  cpl_image *pqerr_im = NULL;
2127  cpl_image *puerr_im = NULL;
2128  cpl_image *plerr_im = NULL;
2129 
2130  cpl_image *pang_im = NULL;
2131  cpl_image *pangerr_im = NULL;
2132 
2133  double *p_q = NULL;
2134  double *p_u = NULL;
2135  double *p_l = NULL;
2136 
2137  double *p_qnull = NULL;
2138  double *p_unull = NULL;
2139 
2140  double *pqerr = NULL;
2141  double *puerr = NULL;
2142  double *plerr = NULL;
2143 
2144  double *pang = NULL;
2145  double *pangerr = NULL;
2146 
2147  int k, m;
2148 
2149  cpl_image * correct_im = cpl_image_new(nx, 1, CPL_TYPE_DOUBLE);
2150  double * correct = cpl_image_get_data_double(correct_im);
2151 
2152  double mean_unull, mean_qnull;
2153 
2154  int p = -1;
2155  int total = 0;
2156 
2157  pq_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2158  pu_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2159  pl_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2160 
2161  pqerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2162  puerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2163  plerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2164 
2165  pang_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2166  pangerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2167 
2168  p_q = cpl_image_get_data_double(pq_im);
2169  p_u = cpl_image_get_data_double(pu_im);
2170  p_l = cpl_image_get_data_double(pl_im);
2171 
2172  if (nscience / 4 > 1) {
2173  pqnull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2174  punull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
2175 
2176  p_qnull = cpl_image_get_data_double(pqnull_im);
2177  p_unull = cpl_image_get_data_double(punull_im);
2178  } else {
2179  cpl_msg_warning(cpl_func,
2180  "Not enough pairs to compute null parameters");
2181  }
2182 
2183  pqerr = cpl_image_get_data_double(pqerr_im);
2184  puerr = cpl_image_get_data_double(puerr_im);
2185  plerr = cpl_image_get_data_double(plerr_im);
2186 
2187  pang = cpl_image_get_data_double(pang_im);
2188  pangerr = cpl_image_get_data_double(pangerr_im);
2189 
2190  if (chromatism) {
2191  cpl_table * chrotbl =
2192  dfs_load_table(frameset, chrom_table_tag, 1);
2193 
2194  int nrow = cpl_table_get_nrow(chrotbl);
2195  float * lambda = cpl_table_get_data_float(chrotbl, "lambda");
2196  float * theta = cpl_table_get_data_float(chrotbl, "eps_theta");
2197 
2198  for (j = 0; j < nx; j++) {
2199  double c_wave =
2200  startwavelength + dispersion / 2 + j * dispersion;
2201 
2202  int found = 0;
2203 
2204  for (k = 0; k < nrow - 1; k++) {
2205  if (lambda[k] <= c_wave && c_wave < lambda[k + 1]) {
2206  found = 1;
2207  break;
2208  }
2209  }
2210 
2211  if (found) {
2212  correct[j] = (theta [k + 1] - theta [k]) /
2213  (lambda[k + 1] - lambda[k]) *
2214  (c_wave - lambda[k]) + theta[k];
2215  correct[j] *= M_PI / 180; /* Radians */
2216  }
2217  else if (j)
2218  correct[j] = correct[j-1];
2219  else
2220  correct[j] = 0.0;
2221 
2222  }
2223 
2224  cpl_table_delete(chrotbl);
2225  }
2226 
2227  for (j = 0; j < nobjects; j++) {
2228  double * ip_q, * ip_u, * ip_l,
2229  * ip_qnull, * ip_unull, * ipqerr, * ipuerr, * iplerr,
2230  * ipang, * ipangerr;
2231 
2232  float * data;
2233  float * iffq, * ierrq, * iffu, * ierru;
2234 
2235  int pos, pos_d;
2236 
2237  ip_q = p_q + (nobjects - 1 - j) * nx;
2238  ip_u = p_u + (nobjects - 1 - j) * nx;
2239  ip_l = p_l + (nobjects - 1 - j) * nx;
2240 
2241  if (nscience / 4 > 1) {
2242  ip_qnull = p_qnull + (nobjects - 1 - j) * nx;
2243  ip_unull = p_unull + (nobjects - 1 - j) * nx;
2244  }
2245 
2246  ipqerr = pqerr + (nobjects - 1 - j) * nx;
2247  ipuerr = puerr + (nobjects - 1 - j) * nx;
2248  iplerr = plerr + (nobjects - 1 - j) * nx;
2249 
2250  ipang = pang + (nobjects - 1 - j) * nx;
2251  ipangerr = pangerr + (nobjects - 1 - j) * nx;
2252 
2253  while (total < j + 1) {
2254  p++;
2255  total += nobjs_per_slit[p];
2256  }
2257 
2258  for (k = 0; k < nscience / 4; k++) {
2259  float * if_o, * if_e, * ifdelta_o, * ifdelta_e;
2260 
2261  /* First P_Q */
2262 
2263  pos = fors_find_angle_pos(angles, nscience, 90 * k);
2264  pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 45);
2265 
2266  data = cpl_image_get_data_float(reduceds[pos]);
2267 
2268  if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2269  + (total - j - 1)) * nx;
2270 
2271  if_e = data + (2 * (nobjects - total)
2272  + (total - j - 1)) * nx;
2273 
2274 // if_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
2275 // if_e = data + 2 * (nobjects - 1 - j) * nx;
2276 
2277  data = cpl_image_get_data_float(reduceds[pos_d]);
2278 
2279  ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2280  + (total - j - 1)) * nx;
2281 
2282  ifdelta_e = data + (2 * (nobjects - total)
2283  + (total - j - 1)) * nx;
2284 
2285 // ifdelta_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
2286 // ifdelta_e = data + 2 * (nobjects - 1 - j) * nx;
2287 
2288  for (m = 0; m < nx; m++) {
2289 
2290  double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
2291  (if_o[m] - if_e[m] ) /
2292  (if_o[m] + if_e[m] ) -
2293  (ifdelta_o[m] - ifdelta_e[m]) /
2294  (ifdelta_o[m] + ifdelta_e[m]);
2295 
2296  quantity = isfinite(quantity) ? quantity : 0.0;
2297 
2298  /* PQ map computation */
2299  ip_q[m] += quantity * 0.5 / (nscience / 4);
2300 
2301  /* PQnull map computation */
2302  if (nscience / 4 > 1) {
2303  if (k % 2)
2304  ip_qnull[m] += quantity * 0.5 / (nscience / 4);
2305  else
2306  ip_qnull[m] -= quantity * 0.5 / (nscience / 4);
2307  }
2308  }
2309 
2310  /* Now P_U */
2311 
2312  pos = fors_find_angle_pos(angles, nscience, 90 * k + 22.5);
2313  pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 67.5);
2314 
2315  data = cpl_image_get_data_float(reduceds[pos]);
2316 
2317  if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2318  + (total - j - 1)) * nx;
2319 
2320  if_e = data + (2 * (nobjects - total)
2321  + (total - j - 1)) * nx;
2322 
2323 // if_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
2324 // if_e = data + 2 * (nobjects - 1 - j) * nx;
2325 
2326  data = cpl_image_get_data_float(reduceds[pos_d]);
2327 
2328  ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p]
2329  + (total - j - 1)) * nx;
2330 
2331  ifdelta_e = data + (2 * (nobjects - total)
2332  + (total - j - 1)) * nx;
2333 
2334 // ifdelta_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
2335 // ifdelta_e = data + 2 * (nobjects - 1 - j) * nx;
2336 
2337  for (m = 0; m < nx; m++) {
2338 
2339  double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
2340  (if_o[m] - if_e[m] ) /
2341  (if_o[m] + if_e[m] ) -
2342  (ifdelta_o[m] - ifdelta_e[m]) /
2343  (ifdelta_o[m] + ifdelta_e[m]);
2344 
2345  quantity = isfinite(quantity) ? quantity : 0.0;
2346 
2347  /* PU map computation */
2348  ip_u[m] += quantity * 0.5 / (nscience / 4);
2349 
2350  /* PUnull map computation */
2351  if (nscience / 4 > 1) {
2352  if (k % 2)
2353  ip_unull[m] += quantity * 0.5 / (nscience / 4);
2354  else
2355  ip_unull[m] -= quantity * 0.5 / (nscience / 4);
2356  }
2357  }
2358  }
2359 
2360  /* Error map */
2361 
2362  pos = fors_find_angle_pos(angles, nscience, 0.0);
2363 
2364  data = cpl_image_get_data_float(reduceds[pos]);
2365  iffq = data + 2 * (nobjects - 1 - j) * nx;
2366 
2367  data = cpl_image_get_data_float(rerrors[pos]);
2368  ierrq = data + 2 * (nobjects - 1 - j) * nx;
2369 
2370  pos = fors_find_angle_pos(angles, nscience, 22.5);
2371 
2372  data = cpl_image_get_data_float(reduceds[pos]);
2373  iffu = data + 2 * (nobjects - 1 - j) * nx;
2374 
2375  data = cpl_image_get_data_float(rerrors[pos]);
2376  ierru = data + 2 * (nobjects - 1 - j) * nx;
2377 
2378  for (m = 0; m < nx; m++) {
2379 
2380  double radicand;
2381 
2382  ipqerr[m] = iffq[m] <= 0.0 ?
2383  0.0 : ierrq[m] / iffq[m] * 0.5 / sqrt (nscience / 4);
2384 
2385  ipuerr[m] = iffu[m] <= 0.0 ?
2386  0.0 : ierru[m] / iffu[m] * 0.5 / sqrt (nscience / 4);
2387 
2388  iplerr[m] = CPL_MATH_SQRT1_2 * 0.5 * (ipqerr[m] + ipuerr[m]);
2389 
2390 /* Added: */
2391  if (chromatism) {
2392  ip_q[m] = ip_q[m] * cos(2 * correct[m]) -
2393  ip_u[m] * sin(2 * correct[m]);
2394 
2395  ip_u[m] = ip_q[m] * sin(2 * correct[m]) +
2396  ip_u[m] * cos(2 * correct[m]);
2397  }
2398 /* End added */
2399 
2400  /* PL computation */
2401  ip_l[m] = sqrt(ip_u[m] * ip_u[m] + ip_q[m] * ip_q[m]);
2402 
2403  /* P angle computation */
2404  ipang[m] = (ip_q[m] == 0.0 ?
2405  (ip_u[m] > 0.0 ? 45.0 : 135.0)
2406  : 0.5 * (atan2(ip_u[m], ip_q[m]) * 180 / M_PI +
2407  ((atan2(ip_u[m], ip_q[m]) > 0.0 ? 0.0 : 360.0))));
2408 
2409  /* Error on the angle computation */
2410  radicand = ip_q[m] * ip_q[m] * ipuerr[m] * ipuerr[m] +
2411  ip_u[m] * ip_u[m] * ipqerr[m] * ipqerr[m];
2412 
2413  ipangerr[m] = ip_l[m] == 0.0 ? 0.0 :
2414  sqrt(radicand) * 0.5 / (ip_l[m] * ip_l[m]) * 180 / M_PI;
2415 
2416  /*
2417  * Note: no need to apply chromatism correction to angle,
2418  * it is implicit in Q and U correction applied before.
2419  */
2420 
2421 /* Removed:
2422  if (chromatism) {
2423  ipang[m] -= correct[m];
2424 
2425  ip_q[m] = ip_q[m] * cos(2 * correct[m]) -
2426  ip_u[m] * sin(2 * correct[m]);
2427 
2428  ip_u[m] = ip_q[m] * sin(2 * correct[m]) +
2429  ip_u[m] * cos(2 * correct[m]);
2430 
2431  }
2432  end removed */
2433  }
2434 
2435  if (nscience / 4 > 1) {
2436  float * weights;
2437  float max, sum, sum2, imean;
2438 
2439  int k;
2440 
2441  /* QC on U NULL */
2442  weights = cpl_malloc(sizeof(float) * nx);
2443 
2444  max = 0.0;
2445  for (k = 0; k < nx; k++) {
2446  if (max < iffq[k]) max = iffq[k];
2447  }
2448 
2449  for (k = 0; k < nx; k++) {
2450  weights[k] = iffq[k] < 0.0 ?
2451  0.0 : iffq[k] * iffq[k] / (max * max);
2452  }
2453 
2454  sum = 0.0;
2455  sum2 = 0.0;
2456  for (k = 0; k < nx; k++) {
2457  sum += weights[k] * ip_qnull[k];
2458  sum2 += weights[k];
2459  }
2460 
2461  cpl_free(weights);
2462 
2463  imean = sum / sum2;
2464 
2465  mean_qnull += (imean - mean_qnull) / (j + 1.0);
2466 
2467  /* QC on U NULL */
2468  weights = cpl_malloc(sizeof(float) * nx);
2469 
2470  max = 0.0;
2471  for (k = 0; k < nx; k++) {
2472  if (max < iffu[k]) max = iffu[k];
2473  }
2474 
2475  for (k = 0; k < nx; k++) {
2476  weights[k] = iffu[k] < 0.0 ?
2477  0.0 : iffu[k] * iffu[k] / (max * max);
2478  }
2479 
2480  sum = 0.0;
2481  sum2 = 0.0;
2482  for (k = 0; k < nx; k++) {
2483  sum += weights[k] * ip_unull[k];
2484  sum2 += weights[k];
2485  }
2486 
2487  cpl_free(weights);
2488 
2489  imean = sum / sum2;
2490 
2491  mean_unull += (imean - mean_unull) / (j + 1.0);
2492  }
2493  }
2494 
2495  cpl_image_delete(correct_im);
2496 
2497  if (dfs_save_image(frameset, pq_im, reduced_q_tag, header,
2498  parlist, recipe, version))
2499  fors_pmos_extract_exit(NULL);
2500 
2501  if (dfs_save_image(frameset, pu_im, reduced_u_tag, header,
2502  parlist, recipe, version))
2503  fors_pmos_extract_exit(NULL);
2504 
2505  if (dfs_save_image(frameset, pl_im, reduced_l_tag, header,
2506  parlist, recipe, version))
2507  fors_pmos_extract_exit(NULL);
2508 
2509  if (nscience / 4 > 1) {
2510  char *pipefile;
2511  char *keyname;
2512  cpl_propertylist *qheader = dfs_load_header(frameset,
2513  science_tag, 0);
2514 
2515  cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
2516  cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
2517  cpl_propertylist_update_double(qheader, "CRVAL1",
2518  startwavelength + dispersion/2);
2519  cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
2520  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
2521  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
2522  cpl_propertylist_update_double(qheader, "CD1_1", dispersion);
2523  cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
2524  cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
2525  cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
2526  cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
2527  cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
2528 
2529  fors_qc_start_group(qheader, "2.0", instrume);
2530 
2531  /*
2532  * QC1 group header
2533  */
2534 
2535  if (fors_qc_write_string("PRO.CATG", reduced_nul_q_tag,
2536  "Product category", instrume))
2537  fors_pmos_extract_exit("Cannot write product category to "
2538  "QC log file");
2539 
2540  if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
2541  "DPR type", instrume))
2542  fors_pmos_extract_exit("Missing keyword DPR TYPE in arc "
2543  "lamp header");
2544 
2545  if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
2546  "Template", instrume))
2547  fors_pmos_extract_exit("Missing keyword TPL ID in arc "
2548  "lamp header");
2549 
2550  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
2551  "Grism name", instrume))
2552  fors_pmos_extract_exit("Missing keyword INS GRIS1 NAME in arc "
2553  "lamp header");
2554 
2555  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
2556  "Grism identifier", instrume))
2557  fors_pmos_extract_exit("Missing keyword INS GRIS1 ID in arc "
2558  "lamp header");
2559 
2560  if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
2561  fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
2562  "Filter name", instrume);
2563 
2564  if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
2565  "Collimator name", instrume))
2566  fors_pmos_extract_exit("Missing keyword INS COLL NAME in arc "
2567  "lamp header");
2568 
2569  if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
2570  "Chip identifier", instrume))
2571  fors_pmos_extract_exit("Missing keyword DET CHIP1 ID in arc "
2572  "lamp header");
2573 
2574  if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
2575  "Archive name of input data",
2576  instrume))
2577  fors_pmos_extract_exit("Missing keyword ARCFILE in arc "
2578  "lamp header");
2579 
2580  pipefile = dfs_generate_filename(reduced_nul_q_tag);
2581  if (fors_qc_write_string("PIPEFILE", pipefile,
2582  "Pipeline product name", instrume))
2583  fors_pmos_extract_exit("Cannot write PIPEFILE to QC log file");
2584  cpl_free(pipefile); pipefile = NULL;
2585 
2586 
2587  /*
2588  * QC1 parameters
2589  */
2590 
2591  keyname = "QC.NULL.Q.MEAN";
2592 
2593  if (fors_qc_write_qc_double(qheader, mean_qnull,
2594  keyname, NULL,
2595  "Mean Q null parameter",
2596  instrume)) {
2597  fors_pmos_extract_exit("Cannot write mean Q null parameter "
2598  "to QC log file");
2599  }
2600 
2602 
2603  if (dfs_save_image(frameset, pqnull_im, reduced_nul_q_tag, qheader,
2604  parlist, recipe, version))
2605  fors_pmos_extract_exit(NULL);
2606 
2607  cpl_propertylist_delete(qheader);
2608 
2609  qheader = dfs_load_header(frameset, science_tag, 0);
2610 
2611  cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
2612  cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
2613  cpl_propertylist_update_double(qheader, "CRVAL1",
2614  startwavelength + dispersion/2);
2615  cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
2616  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
2617  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
2618  cpl_propertylist_update_double(qheader, "CD1_1", dispersion);
2619  cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
2620  cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
2621  cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
2622  cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
2623  cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
2624 
2625  fors_qc_start_group(qheader, "2.0", instrume);
2626 
2627  /*
2628  * QC1 group header
2629  */
2630 
2631  if (fors_qc_write_string("PRO.CATG", reduced_nul_u_tag,
2632  "Product category", instrume))
2633  fors_pmos_extract_exit("Cannot write product category to "
2634  "QC log file");
2635 
2636  if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
2637  "DPR type", instrume))
2638  fors_pmos_extract_exit("Missing keyword DPR TYPE in arc "
2639  "lamp header");
2640 
2641  if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
2642  "Template", instrume))
2643  fors_pmos_extract_exit("Missing keyword TPL ID in arc "
2644  "lamp header");
2645 
2646  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
2647  "Grism name", instrume))
2648  fors_pmos_extract_exit("Missing keyword INS GRIS1 NAME in arc "
2649  "lamp header");
2650 
2651  if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
2652  "Grism identifier", instrume))
2653  fors_pmos_extract_exit("Missing keyword INS GRIS1 ID in arc "
2654  "lamp header");
2655 
2656  if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
2657  fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
2658  "Filter name", instrume);
2659 
2660  if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
2661  "Collimator name", instrume))
2662  fors_pmos_extract_exit("Missing keyword INS COLL NAME in arc "
2663  "lamp header");
2664 
2665  if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
2666  "Chip identifier", instrume))
2667  fors_pmos_extract_exit("Missing keyword DET CHIP1 ID in arc "
2668  "lamp header");
2669 
2670  if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
2671  "Archive name of input data",
2672  instrume))
2673  fors_pmos_extract_exit("Missing keyword ARCFILE in arc "
2674  "lamp header");
2675 
2676  pipefile = dfs_generate_filename(reduced_nul_u_tag);
2677  if (fors_qc_write_string("PIPEFILE", pipefile,
2678  "Pipeline product name", instrume))
2679  fors_pmos_extract_exit("Cannot write PIPEFILE to QC log file");
2680  cpl_free(pipefile); pipefile = NULL;
2681 
2682 
2683  /*
2684  * QC1 parameters
2685  */
2686 
2687  keyname = "QC.NULL.U.MEAN";
2688 
2689  if (fors_qc_write_qc_double(qheader, mean_unull,
2690  keyname, NULL,
2691  "Mean U null parameter",
2692  instrume)) {
2693  fors_pmos_extract_exit("Cannot write mean U null parameter "
2694  "to QC log file");
2695  }
2696 
2698 
2699  if (dfs_save_image(frameset, punull_im, reduced_nul_u_tag, qheader,
2700  parlist, recipe, version))
2701  fors_pmos_extract_exit(NULL);
2702 
2703  cpl_propertylist_delete(qheader);
2704  }
2705 
2706  if (dfs_save_image(frameset, pqerr_im, reduced_error_q_tag, header,
2707  parlist, recipe, version))
2708  fors_pmos_extract_exit(NULL);
2709 
2710  if (dfs_save_image(frameset, puerr_im, reduced_error_u_tag, header,
2711  parlist, recipe, version))
2712  fors_pmos_extract_exit(NULL);
2713 
2714  if (dfs_save_image(frameset, plerr_im, reduced_error_l_tag, header,
2715  parlist, recipe, version))
2716  fors_pmos_extract_exit(NULL);
2717 
2718  if (dfs_save_image(frameset, pang_im, reduced_angle_tag, header,
2719  parlist, recipe, version))
2720  fors_pmos_extract_exit(NULL);
2721 
2722  if (dfs_save_image(frameset, pangerr_im, reduced_error_angle_tag,
2723  header, parlist, recipe, version))
2724  fors_pmos_extract_exit(NULL);
2725 
2726  cpl_image_delete(pq_im);
2727  cpl_image_delete(pu_im);
2728  cpl_image_delete(pl_im);
2729 
2730  cpl_image_delete(pqnull_im);
2731  cpl_image_delete(punull_im);
2732 
2733  cpl_image_delete(pqerr_im);
2734  cpl_image_delete(puerr_im);
2735  cpl_image_delete(plerr_im);
2736  cpl_image_delete(pang_im);
2737  cpl_image_delete(pangerr_im);
2738  }
2739 
2740  cpl_propertylist_delete(header);
2741 
2742  /* End of Stokes computation */
2743 
2744  for (j = 0; j < nscience; j++) {
2745  cpl_image_delete(reduceds[j]);
2746  cpl_image_delete(rerrors[j]);
2747  cpl_table_delete(slitss[j]);
2748  cpl_image_delete(mappeds[j]);
2749  }
2750 
2751  cpl_free(reduceds);
2752  cpl_free(rerrors);
2753  cpl_free(slitss);
2754  cpl_free(mappeds);
2755 
2756  cpl_free(instrume); instrume = NULL;
2757 
2758  cpl_free(skylocalmaps);
2759  cpl_free(nobjs_per_slit);
2760 
2761  if (cpl_error_get_code()) {
2762  cpl_msg_error(cpl_error_get_where(), "%s", cpl_error_get_message());
2763  fors_pmos_extract_exit(NULL);
2764  }
2765  else
2766  return 0;
2767 }
2768 
2769 /*----------------------------------------------------------------------------*/
2780 /*----------------------------------------------------------------------------*/
2781 static float * fors_check_angles(cpl_frameset * frameset,
2782  int pmos, const char *tag, int * circ)
2783 {
2784  float *angles = NULL;
2785  cpl_frame *c_frame = NULL;
2786  char *ret_id = NULL;
2787 
2788  int i = 0;
2789 
2790  angles = cpl_malloc(sizeof(float) * pmos);
2791 
2792  for (c_frame = cpl_frameset_find(frameset, tag);
2793  c_frame != NULL; c_frame = cpl_frameset_find(frameset, NULL)) {
2794 
2795  cpl_propertylist * header =
2796  cpl_propertylist_load(cpl_frame_get_filename(c_frame), 0);
2797 
2798  if (!ret_id) {
2799  ret_id = cpl_strdup(cpl_propertylist_get_string(header,
2800  "ESO INS OPTI4 ID"));
2801 
2802  if (ret_id[1] != '5' && ret_id[1] != '4') {
2803  cpl_msg_error(cpl_func,
2804  "Unknown retarder plate id: %s", ret_id);
2805  return NULL;
2806  }
2807  } else {
2808  char * c_ret_id = (char *)
2809  cpl_propertylist_get_string(header, "ESO INS OPTI4 ID");
2810  if (ret_id[1] != c_ret_id[1]) {
2811  cpl_msg_error(cpl_func, "Input frames are not from the same "
2812  "retarder plate");
2813  return NULL;
2814  }
2815  }
2816 
2817  if (ret_id[1] == '5') { /* Linear polarimetry */
2818  angles[i] = (float)
2819  cpl_propertylist_get_double(header, "ESO INS RETA2 ROT");
2820  *circ = 0;
2821  } else { /* Circular polarimetry */
2822  angles[i] = (float)
2823  cpl_propertylist_get_double(header, "ESO INS RETA4 ROT");
2824  *circ = 1;
2825  }
2826 
2827  cpl_propertylist_delete(header);
2828  i++;
2829  }
2830 
2831  cpl_free(ret_id);
2832 
2833  if (*circ) {
2834  if (pmos != 2 && pmos != 4) {
2835  cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
2836  "found, but either 2 or 4 are required for "
2837  "circular polarization measurements!", pmos);
2838  return NULL;
2839  }
2840  } else {
2841  if (pmos != 4 && pmos != 8 && pmos != 16) {
2842  cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
2843  "found, but either 4, 8, or 16 are required for "
2844  "linear polarization measurements!", pmos);
2845  return NULL;
2846  }
2847  }
2848 
2849  /* Check completeness */
2850 
2851  if (*circ) {
2852  for (i = 0; i < pmos; i++) {
2853  if (fors_find_angle_pos(angles, pmos, 90.0 * i - 45.0) < 0) {
2854  const char *cangles;
2855  switch (pmos) {
2856  case 2: cangles = "-45.0, 45.0"; break;
2857  case 4: cangles = "-45.0, 45.0, 135.0, 225.0"; break;
2858  default: assert(0);
2859  }
2860 
2861  cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
2862  "angle %.2f. All angles %s must be provided.",
2863  angles[i], cangles);
2864  return NULL;
2865  }
2866  }
2867  }
2868  else {
2869  for (i = 0; i < pmos; i++) {
2870  if (fors_find_angle_pos(angles, pmos, 22.5 * i) < 0) {
2871  const char *cangles;
2872  switch (pmos) {
2873  case 4: cangles = "0.0, 22.5, 45.0, 67.5"; break;
2874  case 8: cangles = "0.0, 22.5, 45.0, 67.5, "
2875  "90.0, 112.5, 135.0, 157.5"; break;
2876  case 16: cangles = "0.0, 22.5, 45.0, 67.5, "
2877  "90.0, 112.5, 135.0, 157.5, "
2878  "180.0, 202.5, 225.0, 247.5, "
2879  "270.0, 292.5, 315.0, 337.5"; break;
2880  default: assert(0);
2881  }
2882 
2883  cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
2884  "angle %.2f. All angles %s must be provided.",
2885  angles[i], cangles);
2886  return NULL;
2887  }
2888  }
2889  }
2890 
2891  return angles;
2892 }
2893 
2894 /*----------------------------------------------------------------------------*/
2902 /*----------------------------------------------------------------------------*/
2903 static int
2904 fors_find_angle_pos(float * angles, int nangles, float angle)
2905 {
2906  int i, match = 0;
2907 
2908  for (i = 0; i < nangles; i++) {
2909  if (fabs(angles[i] - angle) < 1.0 ||
2910  fabs(angles[i] - 360.0 - angle) < 1.0) {
2911  match = 1;
2912  break;
2913  }
2914  }
2915 
2916  return match ? i : -1;
2917 }
cpl_image * mos_spatial_calibration(cpl_image *spectra, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion, int flux, cpl_image *calibration)
Spatial remapping of CCD spectra eliminating the spectral curvature.
Definition: moses.c:8539
cpl_table * mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
Build the IDS coefficients table from a global distortions table.
Definition: moses.c:1888
int cpl_plugin_get_info(cpl_pluginlist *list)
Build the list of available plugins, for this module.
Definition: fors_bias.c:62
cpl_image * dfs_load_image(cpl_frameset *frameset, const char *category, cpl_type type, int ext, int calib)
Loading image data of given category.
Definition: fors_dfs.c:860
const char * dfs_get_parameter_string(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe string parameter value.
Definition: fors_dfs.c:602
double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, double wavestart, double dispersion, int radius, int highres)
Estimate the spectral distortion modeling goodness.
Definition: moses.c:11047
cpl_propertylist * dfs_load_header(cpl_frameset *frameset, const char *category, int ext)
Loading header associated to data of given category.
Definition: fors_dfs.c:964
cpl_error_code dfs_save_image_null(cpl_frameset *frameset, cpl_parameterlist *parlist, const char *tag, const char *recipename, const char *version)
Save a product with an empty primary extension.
Definition: fors_dfs.c:1893
cpl_error_code dfs_save_image_ext(cpl_image *image, const char *tag, cpl_propertylist *extheader)
Save an image in a extension.
Definition: fors_dfs.c:1850
cpl_error_code fors_qc_write_qc_double(cpl_propertylist *header, double value, const char *name, const char *unit, const char *comment, const char *instrument)
Write an integer value to the active QC1 PAF object and to a header.
Definition: fors_qc.c:604
cpl_image * mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion)
Remapping of spatially rectified wavelengths to original CCD pixels.
Definition: moses.c:11380
cpl_error_code fors_qc_keyword_to_paf(cpl_propertylist *header, const char *name, const char *unit, const char *comment, const char *instrument)
Copy a keyword value to the currently active QC1 PAF object.
Definition: fors_qc.c:425
cpl_image * mos_wavelength_calibration(cpl_image *image, double refwave, double firstLambda, double lastLambda, double dispersion, cpl_table *idscoeff, int flux)
Remap at constant wavelength step an image of rectified scientific spectra.
Definition: moses.c:9697
cpl_table * mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap, double dispersion, double factor, int minpoints, cpl_image *skymap)
Create a CCD median sky map.
Definition: moses.c:12142
cpl_table * mos_wavelength_align(cpl_image *image, cpl_table *slits, double refwave, double firstLambda, double lastLambda, cpl_table *idscoeff, cpl_vector *skylines, int highres, int order, cpl_image *calibration, int sradius)
Modify the input wavelength solution to match reference sky lines.
Definition: moses.c:9986
cpl_error_code fors_qc_start_group(cpl_propertylist *header, const char *qcdic_version, const char *instrument)
Initiate a new QC1 group.
Definition: fors_qc.c:77
cpl_image * mos_remove_bias(cpl_image *image, cpl_image *bias, cpl_table *overscans)
Subtract the bias from a CCD exposure.
Definition: moses.c:3694
cpl_error_code fors_qc_write_string(const char *name, const char *value, const char *comment, const char *instrument)
Add string parameter to current QC1 group.
Definition: fors_qc.c:235
cpl_image * mos_detect_objects(cpl_image *image, cpl_table *slits, int margin, int maxradius, int conradius)
Detect objects in rectified scientific frame.
Definition: moses.c:13944
cpl_image * mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
Local determination of sky.
Definition: moses.c:12717
int dfs_get_parameter_bool(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe boolean parameter value.
Definition: fors_dfs.c:701
int dfs_equal_keyword(cpl_frameset *frameset, const char *keyword)
Saving table data of given category.
Definition: fors_dfs.c:1683
cpl_error_code dfs_save_table_ext(cpl_table *table, const char *tag, cpl_propertylist *extheader)
Save a table in a extension (different from the first one)
Definition: fors_dfs.c:1811
cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, int nscience, float tolerance)
Intersect a number of slit tables.
Definition: moses.c:16408
cpl_image * mos_subtract_sky(cpl_image *science, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion)
Subtract the sky from the scientific CCD exposure.
Definition: moses.c:2012
cpl_image * mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference, double blue, double red)
Create a wavelengths map from an IDS coefficients table.
Definition: moses.c:11269
int dfs_save_image(cpl_frameset *frameset, const cpl_image *image, const char *category, cpl_propertylist *header, const cpl_parameterlist *parlist, const char *recipename, const char *version)
Saving image data of given category.
Definition: fors_dfs.c:1451
cpl_table * dfs_load_table(cpl_frameset *frameset, const char *category, int ext)
Loading table data of given category.
Definition: fors_dfs.c:916
int dfs_get_parameter_int(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe integer parameter value.
Definition: fors_dfs.c:407
int dfs_save_table(cpl_frameset *frameset, const cpl_table *table, const char *category, cpl_propertylist *header, const cpl_parameterlist *parlist, const char *recipename, const char *version)
Saving table data of given category.
Definition: fors_dfs.c:1575
cpl_error_code fors_qc_end_group(void)
Close current QC1 PAF file.
Definition: fors_qc.c:200
Definition: list.c:74
cpl_image ** mos_extract_objects(cpl_image *science, cpl_image *science_var, cpl_image *sky, cpl_table *objects, int extraction, double ron, double gain, int ncombined)
Extract detected objects from rectified scientific frame.
Definition: moses.c:14295
cpl_error_code mos_clean_cosmics(cpl_image *image, float gain, float threshold, float ratio)
Remove cosmic rays from sky-subtracted CCD spectral exposure.
Definition: moses.c:13174
cpl_table * mos_load_slits_fors_pmos(cpl_propertylist *header, int *nslits_out_det)
Create PMOS slit location table from FITS header of FORS1/2 MOS data.
Definition: moses.c:17163
cpl_table * mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits, cpl_table *slits)
Build the curvature coefficients table from a global distortions table.
Definition: moses.c:1733
double dfs_get_parameter_double(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe double parameter value.
Definition: fors_dfs.c:504
cpl_table * mos_load_overscans_vimos(const cpl_propertylist *header, int check_consistency)
Get the overscan positions from FITS header of VIMOS data.
Definition: moses.c:15723
cpl_table * mos_build_slit_location(cpl_table *global, cpl_table *maskslits, int ysize)
Build the slit location table from a global distortions table.
Definition: moses.c:1586