FORS Pipeline Reference Manual 4.9.9
fors_pmos_science.c
00001 /* $Id: fors_pmos_science.c,v 1.61 2011/04/01 14:29:04 cizzo Exp $
00002  *
00003  * This file is part of the FORS Data Reduction Pipeline
00004  * Copyright (C) 2002-2010 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 /*
00022  * $Author: cizzo $
00023  * $Date: 2011/04/01 14:29:04 $
00024  * $Revision: 1.61 $
00025  * $Name: fors-4_9_9 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <math.h>
00033 #include <string.h>
00034 #include <ctype.h>
00035 #include <assert.h>
00036 
00037 #include <cpl.h>
00038 #include <moses.h>
00039 #include <fors_dfs.h>
00040 #include <fors_utils.h>
00041 #include <fors_qc.h>
00042 
00043 static int fors_pmos_science_create(cpl_plugin *);
00044 static int fors_pmos_science_exec(cpl_plugin *);
00045 static int fors_pmos_science_destroy(cpl_plugin *);
00046 static int fors_pmos_science(cpl_parameterlist *, cpl_frameset *);
00047 
00048 static float * fors_check_angles(cpl_frameset *, int, const char *, int *);
00049 static int
00050 fors_find_angle_pos(float * angles, int nangles, float angle);
00051 
00052 static char fors_pmos_science_description[] =
00053 "This recipe is used to reduce scientific spectra using the extraction\n"
00054 "mask and the products created by the recipe fors_mpol_calib. The spectra\n"
00055 "are bias subtracted, flat fielded (if a normalised flat field is specified)\n"
00056 "and remapped eliminating the optical distortions. The wavelength calibration\n"
00057 "can be optionally upgraded using a number of sky lines: if no sky lines\n"
00058 "catalog of wavelengths is specified, an internal one is used instead.\n"
00059 "If the alignment to the sky lines is performed, the input dispersion\n"
00060 "coefficients table is upgraded and saved to disk, and a new CCD wavelengths\n"
00061 "map is created.\n"
00062 "This recipe accepts both FORS1 and FORS2 frames. A grism table (typically\n"
00063 "depending on the instrument mode, and in particular on the grism used)\n"
00064 "may also be specified: this table contains a default recipe parameter\n" 
00065 "setting to control the way spectra are extracted for a specific instrument\n"
00066 "mode, as it is used for automatic run of the pipeline on Paranal and in\n" 
00067 "Garching. If this table is specified, it will modify the default recipe\n" 
00068 "parameter setting, with the exception of those parameters which have been\n" 
00069 "explicitly modifyed on the command line. If a grism table is not specified,\n"
00070 "the input recipe parameters values will always be read from the command\n" 
00071 "line, or from an esorex configuration file if present, or from their\n" 
00072 "generic default values (that are rarely meaningful).\n" 
00073 "Either a scientific or a standard star exposure can be specified in input.\n"
00074 "The acronym SCI on products should be read STD in case of standard stars\n"
00075 "observations.\n\n"
00076 "Input files:\n\n"
00077 "  DO category:               Type:       Explanation:            Required:\n"
00078 "  SCIENCE_PMOS                  Raw         Scientific exposure      Y\n"
00079 "  or STANDARD_PMOS              Raw         Standard star exposure   Y\n"
00080 "  MASTER_BIAS                   Calib       Master bias              Y\n"
00081 "  GRISM_TABLE                   Calib       Grism table              .\n"
00082 "  MASTER_SKYLINECAT             Calib       Sky lines catalog        .\n"
00083 "  MASTER_NORM_FLAT_PMOS         Calib       Normalised flat field    .\n"
00084 "  DISP_COEFF_PMOS               Calib       Inverse dispersion       Y\n"
00085 "  CURV_COEFF_PMOS               Calib       Spectral curvature       Y\n"
00086 "  SLIT_LOCATION_PMOS            Calib       Slits positions table    Y\n"
00087 "  RETARDER_WAVEPLATE_CHROMATISM Calib       Chromatism correction    .\n"
00088 "  STD_PMOS_TABLE                Calib       Linear pol. of std stars .\n"
00089 "\n"
00090 "Output files:\n\n"
00091 "  DO category:               Data type:  Explanation:\n"
00092 "  REDUCED_SCI_PMOS             FITS image  Extracted scientific spectra\n"
00093 "  REDUCED_SKY_SCI_PMOS         FITS image  Extracted sky spectra\n"
00094 "  REDUCED_ERROR_SCI_PMOS       FITS image  Errors on extracted spectra\n"
00095 "  REDUCED_X_SCI_PMOS           FITS image  X Stokes parameter (and L)\n"
00096 "  REDUCED_ERROR_X_SCI_PMOS     FITS image  Error on X Stokes parameter\n"
00097 "  REDUCED_NUL_X_SCI_PMOS       FITS image  Null parameter for X\n"
00098 "  REDUCED_ANGLE_SCI_PMOS       FITS image  Direction of linear polarization\n"
00099 "  REDUCED_ERROR_ANGLE_SCI_PMOS FITS image  Error on polarization direction\n"
00100 "  UNMAPPED_SCI_PMOS            FITS image  Sky subtracted scientific spectra\n"
00101 "  MAPPED_SCI_PMOS              FITS image  Rectified scientific spectra\n"
00102 "  MAPPED_ALL_SCI_PMOS          FITS image  Rectified science spectra with sky\n"
00103 "  MAPPED_SKY_SCI_PMOS          FITS image  Rectified sky spectra\n"
00104 "  UNMAPPED_SKY_SCI_PMOS        FITS image  Sky on CCD\n"
00105 "  OBJECT_TABLE_SCI_PMOS        FITS table  Positions of detected objects\n"
00106 "  OBJECT_TABLE_POL_SCI_PMOS    FITS table  Positions of real objects\n"
00107 "\n"
00108 "  Only if the sky-alignment of the wavelength solution is requested:\n"
00109 "  DISP_COEFF_SCI_PMOS          FITS table  Upgraded dispersion coefficients\n"
00110 "  WAVELENGTH_MAP_SCI_PMOS      FITS image  Upgraded wavelength map\n\n";
00111 
00112 #define fors_pmos_science_exit(message)            \
00113 {                                             \
00114 if (message) cpl_msg_error(recipe, message);  \
00115 cpl_free(instrume);                           \
00116 cpl_image_delete(dummy);                      \
00117 cpl_image_delete(mapped_sky);                 \
00118 cpl_image_delete(mapped_cleaned);             \
00119 cpl_image_delete(skymap);                     \
00120 cpl_image_delete(smapped);                    \
00121 cpl_table_delete(offsets);                    \
00122 cpl_table_delete(sky);                        \
00123 cpl_image_delete(bias);                       \
00124 cpl_image_delete(spectra);                    \
00125 cpl_image_delete(coordinate);                 \
00126 cpl_image_delete(norm_flat);                  \
00127 cpl_image_delete(rainbow);                    \
00128 cpl_image_delete(rectified);                  \
00129 cpl_image_delete(wavemap);                    \
00130 cpl_propertylist_delete(header);              \
00131 cpl_propertylist_delete(save_header);         \
00132 cpl_table_delete(grism_table);                \
00133 cpl_table_delete(idscoeff);                   \
00134 cpl_table_delete(maskslits);                  \
00135 cpl_table_delete(overscans);                  \
00136 cpl_table_delete(polytraces);                 \
00137 cpl_table_delete(wavelengths);                \
00138 cpl_table_delete(mask_science);               \
00139 cpl_table_delete(mask_arc);                   \
00140 cpl_table_delete(mask_flat);                  \
00141 cpl_vector_delete(lines);                     \
00142 cpl_msg_indent_less();                        \
00143 return -1;                                    \
00144 }
00145 
00146 
00147 #define fors_pmos_science_exit_memcheck(message)   \
00148 {                                             \
00149 if (message) cpl_msg_info(recipe, message);   \
00150 cpl_free(instrume);                           \
00151 cpl_image_delete(dummy);                      \
00152 cpl_image_delete(mapped_cleaned);             \
00153 cpl_image_delete(mapped_sky);                 \
00154 cpl_image_delete(skymap);                     \
00155 cpl_image_delete(smapped);                    \
00156 cpl_table_delete(offsets);                    \
00157 cpl_table_delete(sky);                        \
00158 cpl_image_delete(bias);                       \
00159 cpl_image_delete(spectra);                    \
00160 cpl_image_delete(coordinate);                 \
00161 cpl_image_delete(norm_flat);                  \
00162 cpl_image_delete(rainbow);                    \
00163 cpl_image_delete(rectified);                  \
00164 cpl_image_delete(wavemap);                    \
00165 cpl_propertylist_delete(header);              \
00166 cpl_propertylist_delete(save_header);         \
00167 cpl_table_delete(grism_table);                \
00168 cpl_table_delete(idscoeff);                   \
00169 cpl_table_delete(maskslits);                  \
00170 cpl_table_delete(overscans);                  \
00171 cpl_table_delete(polytraces);                 \
00172 cpl_table_delete(wavelengths);                \
00173 cpl_table_delete(mask_science);               \
00174 cpl_table_delete(mask_arc);                   \
00175 cpl_table_delete(mask_flat);                  \
00176 cpl_vector_delete(lines);                     \
00177 cpl_msg_indent_less();                        \
00178 return 0;                                     \
00179 }
00180 
00181 
00193 int cpl_plugin_get_info(cpl_pluginlist *list)
00194 {
00195     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
00196     cpl_plugin *plugin = &recipe->interface;
00197 
00198     cpl_plugin_init(plugin,
00199                     CPL_PLUGIN_API,
00200                     FORS_BINARY_VERSION,
00201                     CPL_PLUGIN_TYPE_RECIPE,
00202                     "fors_pmos_science",
00203                     "Extraction of scientific spectra",
00204                     fors_pmos_science_description,
00205                     "Carlo Izzo",
00206                     PACKAGE_BUGREPORT,
00207     "This file is currently part of the FORS Instrument Pipeline\n"
00208     "Copyright (C) 2002-2010 European Southern Observatory\n\n"
00209     "This program is free software; you can redistribute it and/or modify\n"
00210     "it under the terms of the GNU General Public License as published by\n"
00211     "the Free Software Foundation; either version 2 of the License, or\n"
00212     "(at your option) any later version.\n\n"
00213     "This program is distributed in the hope that it will be useful,\n"
00214     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00215     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00216     "GNU General Public License for more details.\n\n"
00217     "You should have received a copy of the GNU General Public License\n"
00218     "along with this program; if not, write to the Free Software Foundation,\n"
00219     "Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n",
00220                     fors_pmos_science_create,
00221                     fors_pmos_science_exec,
00222                     fors_pmos_science_destroy);
00223 
00224     cpl_pluginlist_append(list, plugin);
00225     
00226     return 0;
00227 }
00228 
00229 
00240 static int fors_pmos_science_create(cpl_plugin *plugin)
00241 {
00242     cpl_recipe    *recipe;
00243     cpl_parameter *p;
00244 
00245 
00246     /* 
00247      * Check that the plugin is part of a valid recipe 
00248      */
00249 
00250     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00251         recipe = (cpl_recipe *)plugin;
00252     else 
00253         return -1;
00254 
00255     /* 
00256      * Create the parameters list in the cpl_recipe object 
00257      */
00258 
00259     recipe->parameters = cpl_parameterlist_new(); 
00260 
00261 
00262     /*
00263      * Dispersion
00264      */
00265 
00266     p = cpl_parameter_new_value("fors.fors_pmos_science.dispersion",
00267                                 CPL_TYPE_DOUBLE,
00268                                 "Expected spectral dispersion (Angstrom/pixel)",
00269                                 "fors.fors_pmos_science",
00270                                 0.0);
00271     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
00272     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00273     cpl_parameterlist_append(recipe->parameters, p);
00274 
00275     /*
00276      * Rebin
00277      */
00278 
00279     p = cpl_parameter_new_value("fors.fors_pmos_science.rebin",
00280                                 CPL_TYPE_INT,
00281                                 "Rebin (pixel)",
00282                                 "fors.fors_pmos_science",
00283                                 1);
00284     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rebin");
00285     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00286     cpl_parameterlist_append(recipe->parameters, p);
00287 
00288     /*
00289      * Sky lines alignment
00290      */
00291 
00292     p = cpl_parameter_new_value("fors.fors_pmos_science.skyalign",
00293                                 CPL_TYPE_INT,
00294                                 "Polynomial order for sky lines alignment, "
00295                                 "or -1 to avoid alignment",
00296                                 "fors.fors_pmos_science",
00297                                 0);
00298     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skyalign");
00299     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00300     cpl_parameterlist_append(recipe->parameters, p);
00301 
00302     /*
00303      * Line catalog table column containing the sky reference wavelengths
00304      */
00305 
00306     p = cpl_parameter_new_value("fors.fors_pmos_science.wcolumn",
00307                                 CPL_TYPE_STRING,
00308                                 "Name of sky line catalog table column "
00309                                 "with wavelengths",
00310                                 "fors.fors_pmos_science",
00311                                 "WLEN");
00312     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
00313     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00314     cpl_parameterlist_append(recipe->parameters, p);
00315 
00316     /*
00317      * Start wavelength for spectral extraction
00318      */
00319 
00320     p = cpl_parameter_new_value("fors.fors_pmos_science.startwavelength",
00321                                 CPL_TYPE_DOUBLE,
00322                                 "Start wavelength in spectral extraction",
00323                                 "fors.fors_pmos_science",
00324                                 0.0);
00325     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
00326     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00327     cpl_parameterlist_append(recipe->parameters, p);
00328 
00329     /*
00330      * End wavelength for spectral extraction
00331      */
00332 
00333     p = cpl_parameter_new_value("fors.fors_pmos_science.endwavelength",
00334                                 CPL_TYPE_DOUBLE,
00335                                 "End wavelength in spectral extraction",
00336                                 "fors.fors_pmos_science",
00337                                 0.0);
00338     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
00339     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00340     cpl_parameterlist_append(recipe->parameters, p);
00341 
00342     /*
00343      * Flux conservation
00344      */
00345 
00346     p = cpl_parameter_new_value("fors.fors_pmos_science.flux",
00347                                 CPL_TYPE_BOOL,
00348                                 "Apply flux conservation",
00349                                 "fors.fors_pmos_science",
00350                                 TRUE);
00351     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
00352     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00353     cpl_parameterlist_append(recipe->parameters, p);
00354 
00355     /*
00356      * Apply flat field
00357      */
00358 
00359     p = cpl_parameter_new_value("fors.fors_pmos_science.flatfield",
00360                                 CPL_TYPE_BOOL,
00361                                 "Apply flat field",
00362                                 "fors.fors_pmos_science",
00363                                 TRUE);
00364     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flatfield");
00365     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00366     cpl_parameterlist_append(recipe->parameters, p);
00367 
00368     /*
00369      * Median sky subtraction method
00370      */
00371 
00372     p = cpl_parameter_new_value("fors.fors_pmos_science.skymedian",
00373                                 CPL_TYPE_BOOL,
00374                                 "Sky subtraction from extracted slit spectra",
00375                                 "fors.fors_pmos_science",
00376                                 FALSE);
00377     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymedian");
00378     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00379     cpl_parameterlist_append(recipe->parameters, p);
00380 
00381     /*
00382      * Local sky subtraction on CCD spectra
00383      */
00384 
00385     p = cpl_parameter_new_value("fors.fors_pmos_science.skylocal",
00386                                 CPL_TYPE_BOOL,
00387                                 "Sky subtraction from CCD slit spectra",
00388                                 "fors.fors_pmos_science",
00389                                 TRUE);
00390     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skylocal");
00391     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00392     cpl_parameterlist_append(recipe->parameters, p);
00393 
00394     /*
00395      * Cosmic rays removal
00396      */
00397 
00398     p = cpl_parameter_new_value("fors.fors_pmos_science.cosmics",
00399                                 CPL_TYPE_BOOL,
00400                                 "Eliminate cosmic rays hits (only if local "
00401                                 "sky subtraction is also requested)",
00402                                 "fors.fors_pmos_science",
00403                                 FALSE);
00404     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cosmics");
00405     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00406     cpl_parameterlist_append(recipe->parameters, p);
00407 
00408     /*
00409      * Slit margin
00410      */
00411 
00412     p = cpl_parameter_new_value("fors.fors_pmos_science.slit_margin",
00413                                 CPL_TYPE_INT,
00414                                 "Number of pixels to exclude at each slit "
00415                                 "in object detection and extraction",
00416                                 "fors.fors_pmos_science",
00417                                 3);
00418     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "slit_margin");
00419     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00420     cpl_parameterlist_append(recipe->parameters, p);
00421 
00422     /*
00423      * Extraction radius
00424      */
00425 
00426     p = cpl_parameter_new_value("fors.fors_pmos_science.ext_radius",
00427                                 CPL_TYPE_INT,
00428                                 "Maximum extraction radius for detected "
00429                                 "objects (pixel)",
00430                                 "fors.fors_pmos_science",
00431                                 12);
00432     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_radius");
00433     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00434     cpl_parameterlist_append(recipe->parameters, p);
00435 
00436     /*
00437      * Contamination radius
00438      */
00439 
00440     p = cpl_parameter_new_value("fors.fors_pmos_science.cont_radius",
00441                                 CPL_TYPE_INT,
00442                                 "Minimum distance at which two objects "
00443                                 "of equal luminosity do not contaminate "
00444                                 "each other (pixel)",
00445                                 "fors.fors_pmos_science",
00446                                 0);
00447     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cont_radius");
00448     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00449     cpl_parameterlist_append(recipe->parameters, p);
00450 
00451     /*
00452      * Object extraction method
00453      */
00454 
00455     p = cpl_parameter_new_value("fors.fors_pmos_science.ext_mode",
00456                                 CPL_TYPE_INT,
00457                                 "Object extraction method: 0 = aperture, "
00458                                 "1 = Horne optimal extraction",
00459                                 "fors.fors_pmos_science",
00460                                 1);
00461     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_mode");
00462     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00463     cpl_parameterlist_append(recipe->parameters, p);
00464 
00465     /*
00466      * Tolerance in object matching
00467      */
00468 
00469     p = cpl_parameter_new_value("fors.fors_pmos_science.match_tolerance",
00470                                 CPL_TYPE_DOUBLE,
00471                                 "Tolerance for matching spectra from the "
00472                                 "same object at different angles and beams "
00473                                 "(pixel)",
00474                                 "fors.fors_pmos_science",
00475                                 5.0);
00476     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "match_tolerance");
00477     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00478     cpl_parameterlist_append(recipe->parameters, p);
00479 
00480     /*
00481      * Normalise output by exposure time
00482      */
00483 
00484     p = cpl_parameter_new_value("fors.fors_pmos_science.time_normalise",
00485                                 CPL_TYPE_BOOL,
00486                                 "Normalise output spectra by the exposure time",
00487                                 "fors.fors_pmos_science",
00488                                 TRUE);
00489     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "time_normalise");
00490     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00491     cpl_parameterlist_append(recipe->parameters, p);
00492 
00493     /*
00494      * Apply chromatism correction to polarization angle
00495      */
00496 
00497     p = cpl_parameter_new_value("fors.fors_pmos_science.chromatism",
00498                                 CPL_TYPE_BOOL,
00499                                 "Chromatism correction to polarization angles",
00500                                 "fors.fors_pmos_science",
00501                                 TRUE);
00502     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "chromatism");
00503     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00504     cpl_parameterlist_append(recipe->parameters, p);
00505 
00506     /*
00507      * Rotation correction for linear polarisation
00508      */
00509 
00510     p = cpl_parameter_new_value("fors.fors_pmos_science.wollaston",
00511                                 CPL_TYPE_BOOL,
00512                      "Wollaston mounting (FORS2 only): true = 0 degrees "
00513                      "(ord. beam on top, extr. beam on bottom), "
00514                      "false = 180 degrees (beams are reversed), for FORS1 "
00515                      "is frozen to true",
00516                                 "fors.fors_pmos_science",
00517                                 TRUE);
00518     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wollaston");
00519     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00520     cpl_parameterlist_append(recipe->parameters, p);
00521 
00522     /*
00523      * Create check products
00524      */
00525 
00526     p = cpl_parameter_new_value("fors.fors_pmos_science.check",
00527                                 CPL_TYPE_BOOL,
00528                                 "Create intermediate products",
00529                                 "fors.fors_pmos_science",
00530                                 FALSE);
00531     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "check");
00532     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00533     cpl_parameterlist_append(recipe->parameters, p);
00534 
00535     /*
00536      * Computation of QC1 parameters
00537      */
00538 
00539     p = cpl_parameter_new_value("fors.fors_pmos_science.qc",
00540                                 CPL_TYPE_BOOL,
00541                                 "Compute QC1 parameters",
00542                                 "fors.fors_pmos_science",
00543                                 TRUE);
00544     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "qc");
00545     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00546     cpl_parameterlist_append(recipe->parameters, p);
00547 
00548     return 0;
00549 }
00550 
00551 
00560 static int fors_pmos_science_exec(cpl_plugin *plugin)
00561 {
00562     cpl_recipe *recipe;
00563     
00564     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00565         recipe = (cpl_recipe *)plugin;
00566     else 
00567         return -1;
00568 
00569     return fors_pmos_science(recipe->parameters, recipe->frames);
00570 }
00571 
00572 
00581 static int fors_pmos_science_destroy(cpl_plugin *plugin)
00582 {
00583     cpl_recipe *recipe;
00584     
00585     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00586         recipe = (cpl_recipe *)plugin;
00587     else 
00588         return -1;
00589 
00590     cpl_parameterlist_delete(recipe->parameters); 
00591 
00592     return 0;
00593 }
00594 
00595 
00605 static int fors_pmos_science(cpl_parameterlist *parlist, cpl_frameset *frameset)
00606 {
00607 
00608     const char *recipe = "fors_pmos_science";
00609 
00610 
00611     /*
00612      * Input parameters
00613      */
00614 
00615     double      dispersion;
00616     int         group;
00617     int         skyalign;
00618     const char *wcolumn;
00619     double      startwavelength;
00620     double      endwavelength;
00621     int         flux;
00622     int         flatfield;
00623     int         skylocal;
00624     int         skymedian;
00625     int         chromatism;
00626     double      wollaston;
00627     int         cosmics;
00628     int         slit_margin;
00629     int         ext_radius;
00630     int         cont_radius;
00631     int         ext_mode;
00632     double      tolerance;
00633     int         time_normalise;
00634     int         check;
00635     int         qc;
00636 
00637     /*
00638      * CPL objects
00639      */
00640 
00641     cpl_image       **images;
00642 
00643     cpl_image       **reduceds       = NULL;
00644     cpl_image       **rerrors        = NULL;
00645     cpl_table       **slitss         = NULL;
00646     cpl_image       **mappeds        = NULL;
00647     cpl_image       **skylocalmaps   = NULL;
00648     
00649     int nobjects = 0;
00650 
00651     cpl_image        *bias           = NULL;
00652     cpl_image        *norm_flat      = NULL;
00653     cpl_image        *spectra        = NULL;
00654     cpl_image        *rectified      = NULL;
00655     cpl_image        *coordinate     = NULL;
00656     cpl_image        *rainbow        = NULL;
00657     cpl_image        *mapped         = NULL;
00658     cpl_image        *mapped_sky     = NULL;
00659     cpl_image        *mapped_cleaned = NULL;
00660     cpl_image        *smapped        = NULL;
00661     cpl_image        *wavemap        = NULL;
00662     cpl_image        *skymap         = NULL;
00663     cpl_image        *skylocalmap    = NULL;
00664     cpl_image        *dummy          = NULL;
00665 
00666     cpl_table        *grism_table    = NULL;
00667     cpl_table        *overscans      = NULL;
00668     cpl_table        *wavelengths    = NULL;
00669     cpl_table        *idscoeff       = NULL;
00670     cpl_table        *slits          = NULL;
00671     cpl_table        *origslits      = NULL;
00672     cpl_table        *maskslits      = NULL;
00673     cpl_table        *mask_science   = NULL;
00674     cpl_table        *mask_arc       = NULL;
00675     cpl_table        *mask_flat      = NULL;
00676     cpl_table        *polytraces     = NULL;
00677     cpl_table        *offsets        = NULL;
00678     cpl_table        *sky            = NULL;
00679 
00680     cpl_vector       *lines          = NULL;
00681 
00682     cpl_propertylist *header         = NULL;
00683     cpl_propertylist *save_header    = NULL;
00684 
00685     /*
00686      * Auxiliary variables
00687      */
00688 
00689     char    version[80];
00690     char   *instrume = NULL;
00691     const char   *science_tag;
00692     const char   *master_norm_flat_tag;
00693     const char   *disp_coeff_tag;
00694     const char   *disp_coeff_sky_tag;
00695     const char   *wavelength_map_sky_tag;
00696     const char   *curv_coeff_tag;
00697     const char   *slit_location_tag;
00698     const char   *reduced_science_tag;
00699     const char   *reduced_sky_tag;
00700     const char   *reduced_error_tag;
00701     const char   *mapped_science_tag;
00702     const char   *unmapped_science_tag;
00703     const char   *mapped_science_sky_tag;
00704     const char   *mapped_sky_tag;
00705     const char   *unmapped_sky_tag;
00706     const char   *object_table_tag;
00707     const char   *object_table_pol_tag;
00708     const char   *skylines_offsets_tag;
00709     const char   *reduced_q_tag;
00710     const char   *reduced_u_tag;
00711     const char   *reduced_v_tag;
00712     const char   *reduced_l_tag;
00713     const char   *reduced_i_tag;
00714     const char   *reduced_error_q_tag;
00715     const char   *reduced_error_u_tag;
00716     const char   *reduced_error_v_tag;
00717     const char   *reduced_error_l_tag;
00718     const char   *reduced_error_i_tag;
00719     const char   *reduced_nul_q_tag;
00720     const char   *reduced_nul_u_tag;
00721     const char   *reduced_nul_v_tag;
00722     const char   *reduced_angle_tag;
00723     const char   *reduced_error_angle_tag;
00724     const char   *chrom_table_tag = "RETARDER_WAVEPLATE_CHROMATISM";
00725     const char   *std_pmos_table_tag = "STD_PMOS_TABLE";
00726     float  *angles = NULL;
00727     int     pmos, circ;
00728     int     nscience;
00729     double  alltime;
00730     double  mean_rms;
00731     int     nlines;
00732     int     rebin;
00733     double *line;
00734     int     nx = 0, ny;
00735     int     ccd_xsize, ccd_ysize;
00736     double  reference;
00737     double  gain;
00738     double  ron;
00739     double  ra, dec;
00740     char    filter;
00741     double  qc_angle;
00742     double  qc_angle_err;
00743     double  qc_pl;
00744     double  qc_pl_err;
00745     int     standard;
00746     int     polarised;
00747     int     highres;
00748     int     i, j;
00749 
00750     int    *nobjs_per_slit;
00751     int     nslits;
00752 
00753     int     bagoo = 0;
00754     double  blevel = 0.0;
00755     int     doit = 0;           // montecarlo simulation
00756     int     conta = 0;          // Bagoo, conta gli oggetti con S/N > s2n
00757     int     bright = 0;         // Bagoo, marca un oggetto con S/N > s2n
00758 
00759     cpl_error_code error;
00760 
00761     snprintf(version, 80, "%s-%s", PACKAGE, PACKAGE_VERSION);
00762 
00763     if (bagoo) {
00764         char *montecarlo = getenv("MONTECARLO");
00765 
00766         if (montecarlo) {
00767             doit = atoi(montecarlo);
00768         }
00769     }
00770 
00771     cpl_msg_set_indentation(2);
00772 
00773     if (dfs_files_dont_exist(frameset))
00774         fors_pmos_science_exit(NULL);
00775 
00776     fors_dfs_set_groups(frameset);
00777 
00778 
00779     /* 
00780      * Get configuration parameters
00781      */
00782 
00783     cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00784     cpl_msg_indent_more();
00785 
00786     if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00787         fors_pmos_science_exit("Too many in input: GRISM_TABLE");
00788 
00789     grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00790 
00791     dispersion = dfs_get_parameter_double(parlist, 
00792                     "fors.fors_pmos_science.dispersion", grism_table);
00793 
00794     if (dispersion <= 0.0)
00795         fors_pmos_science_exit("Invalid spectral dispersion");
00796 
00797     group = dfs_get_parameter_int(parlist,
00798                     "fors.fors_pmos_science.rebin", NULL);
00799 
00800     if (group < 1)
00801         fors_pmos_science_exit("Invalid rebin factor");
00802 
00803     skyalign = dfs_get_parameter_int(parlist, 
00804                     "fors.fors_pmos_science.skyalign", NULL);
00805 
00806     if (skyalign > 2)
00807         fors_pmos_science_exit("Max polynomial degree for sky alignment is 2");
00808 
00809     wcolumn = dfs_get_parameter_string(parlist, 
00810                     "fors.fors_pmos_science.wcolumn", NULL);
00811 
00812     startwavelength = dfs_get_parameter_double(parlist, 
00813                     "fors.fors_pmos_science.startwavelength", grism_table);
00814     if (startwavelength < 3000.0 || startwavelength > 13000.0)
00815         fors_pmos_science_exit("Invalid wavelength");
00816 
00817     endwavelength = dfs_get_parameter_double(parlist, 
00818                     "fors.fors_pmos_science.endwavelength", grism_table);
00819     if (endwavelength < 3000.0 || endwavelength > 13000.0)
00820         fors_pmos_science_exit("Invalid wavelength");
00821 
00822     if (endwavelength - startwavelength <= 0.0)
00823         fors_pmos_science_exit("Invalid wavelength interval");
00824 
00825     flux = dfs_get_parameter_bool(parlist, "fors.fors_pmos_science.flux", NULL);
00826 
00827     flatfield = dfs_get_parameter_bool(parlist, 
00828                                        "fors.fors_pmos_science.flatfield", 
00829                                        NULL);
00830 
00831     skylocal  = dfs_get_parameter_bool(parlist, 
00832                                        "fors.fors_pmos_science.skylocal", 
00833                                        NULL);
00834     skymedian = dfs_get_parameter_bool(parlist, 
00835                                        "fors.fors_pmos_science.skymedian", 
00836                                        NULL);
00837     
00838     chromatism = dfs_get_parameter_bool(parlist, 
00839                                         "fors.fors_pmos_science.chromatism", 
00840                                         NULL);
00841 
00842     wollaston = dfs_get_parameter_bool(parlist,
00843                                        "fors.fors_pmos_science.wollaston",
00844                                        NULL);
00845 
00846     wollaston = wollaston ? 0 : 1;
00847 
00848     if (skylocal && skymedian)
00849         fors_pmos_science_exit("Cannot apply sky subtraction both on "
00850                                "extracted and non-extracted spectra");
00851 
00852     cosmics = dfs_get_parameter_bool(parlist, 
00853                                      "fors.fors_pmos_science.cosmics", NULL);
00854 
00855     if (cosmics)
00856         if (!skylocal)
00857             fors_pmos_science_exit("Cosmic rays correction requires "
00858                                    "skylocal=true");
00859 
00860     slit_margin = dfs_get_parameter_int(parlist, 
00861                                         "fors.fors_pmos_science.slit_margin",
00862                                         NULL);
00863     if (slit_margin < 0)
00864         fors_pmos_science_exit("Value must be zero or positive");
00865 
00866     ext_radius = dfs_get_parameter_int(parlist, 
00867                                        "fors.fors_pmos_science.ext_radius",
00868                                        NULL);
00869     if (ext_radius < 0)
00870         fors_pmos_science_exit("Value must be zero or positive");
00871 
00872     cont_radius = dfs_get_parameter_int(parlist, 
00873                                         "fors.fors_pmos_science.cont_radius",
00874                                         NULL);
00875     if (cont_radius < 0)
00876         fors_pmos_science_exit("Value must be zero or positive");
00877 
00878     ext_mode = dfs_get_parameter_int(parlist, "fors.fors_pmos_science.ext_mode",
00879                                        NULL);
00880     if (ext_mode < 0 || ext_mode > 1)
00881         fors_pmos_science_exit("Invalid object extraction mode");
00882 
00883     tolerance = dfs_get_parameter_double(parlist, 
00884                     "fors.fors_pmos_science.match_tolerance", NULL);
00885     if (tolerance <= 0.0)
00886         fors_pmos_science_exit("Invalid object match tolerance");
00887 
00888     time_normalise = dfs_get_parameter_bool(parlist, 
00889                              "fors.fors_pmos_science.time_normalise", NULL);
00890 
00891     check = dfs_get_parameter_bool(parlist, 
00892                              "fors.fors_pmos_science.check", NULL);
00893 
00894     qc = dfs_get_parameter_bool(parlist, "fors.fors_pmos_science.qc", NULL);
00895 
00896     cpl_table_delete(grism_table); grism_table = NULL;
00897 
00898     if (cpl_error_get_code())
00899         fors_pmos_science_exit("Failure getting the configuration parameters");
00900 
00901     
00902     /* 
00903      * Check input set-of-frames
00904      */
00905 
00906     cpl_msg_indent_less();
00907     cpl_msg_info(recipe, "Check input set-of-frames:");
00908     cpl_msg_indent_more();
00909 
00910     {
00911         cpl_frameset *subframeset = cpl_frameset_duplicate(frameset);
00912         cpl_frameset_erase(subframeset, "MASTER_BIAS");
00913 
00914         if (!dfs_equal_keyword(subframeset, "ESO INS GRIS1 ID"))
00915             fors_pmos_science_exit("Input frames are not from the same grism");
00916 
00917         if (!dfs_equal_keyword(subframeset, "ESO INS FILT1 ID"))
00918             fors_pmos_science_exit("Input frames are not from the same filter");
00919 
00920         if (!dfs_equal_keyword(subframeset, "ESO DET CHIP1 ID"))
00921             fors_pmos_science_exit("Input frames are not from the same chip");
00922 
00923         cpl_frameset_delete(subframeset);
00924     }
00925 
00926     standard = 0;
00927     pmos = cpl_frameset_count_tags(frameset, "SCIENCE_PMOS");
00928 
00929     if (pmos == 0) {
00930         pmos = cpl_frameset_count_tags(frameset, "STANDARD_PMOS");
00931         standard = 1;
00932     }
00933 
00934     if (pmos == 0)
00935         fors_pmos_science_exit("Missing input scientific frame");
00936 
00937     angles = fors_check_angles(frameset, pmos, 
00938                                 standard ? "STANDARD_PMOS" : "SCIENCE_PMOS", 
00939                                 &circ);
00940     if (angles == NULL)
00941         fors_pmos_science_exit("Polarization angles could not be read");
00942 
00943     if (circ)
00944         chromatism = 0; /* Chromatism correction unrequired for 
00945                            circular polarimetry */
00946 
00947 
00948     nscience = pmos;
00949 
00950     reduceds = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
00951     rerrors  = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
00952     slitss   = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
00953     mappeds  = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
00954     skylocalmaps = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
00955 
00956     if (pmos) {
00957         cpl_msg_info(recipe, "PMOS data found");
00958         if (standard) {
00959             science_tag             = "STANDARD_PMOS";
00960             reduced_science_tag     = "REDUCED_STD_PMOS";
00961             unmapped_science_tag    = "UNMAPPED_STD_PMOS";
00962             mapped_science_tag      = "MAPPED_STD_PMOS";
00963             mapped_science_sky_tag  = "MAPPED_ALL_STD_PMOS";
00964             skylines_offsets_tag    = "SKY_SHIFTS_SLIT_STD_PMOS";
00965             wavelength_map_sky_tag  = "WAVELENGTH_MAP_STD_PMOS";
00966             disp_coeff_sky_tag      = "DISP_COEFF_STD_PMOS";
00967             mapped_sky_tag          = "MAPPED_SKY_STD_PMOS";
00968             unmapped_sky_tag        = "UNMAPPED_SKY_STD_PMOS";
00969             object_table_tag        = "OBJECT_TABLE_STD_PMOS";
00970             object_table_pol_tag    = "OBJECT_TABLE_POL_STD_PMOS";
00971             reduced_sky_tag         = "REDUCED_SKY_STD_PMOS";
00972             reduced_error_tag       = "REDUCED_ERROR_STD_PMOS";
00973             reduced_q_tag           = "REDUCED_Q_STD_PMOS";
00974             reduced_u_tag           = "REDUCED_U_STD_PMOS";
00975             reduced_v_tag           = "REDUCED_V_STD_PMOS";
00976             reduced_l_tag           = "REDUCED_L_STD_PMOS";
00977             reduced_i_tag           = "REDUCED_I_STD_PMOS";
00978             reduced_error_q_tag     = "REDUCED_ERROR_Q_STD_PMOS";
00979             reduced_error_u_tag     = "REDUCED_ERROR_U_STD_PMOS";
00980             reduced_error_v_tag     = "REDUCED_ERROR_V_STD_PMOS";
00981             reduced_error_l_tag     = "REDUCED_ERROR_L_STD_PMOS";
00982             reduced_error_i_tag     = "REDUCED_ERROR_I_STD_PMOS";
00983             reduced_nul_q_tag       = "REDUCED_NUL_Q_STD_PMOS";
00984             reduced_nul_u_tag       = "REDUCED_NUL_U_STD_PMOS";
00985             reduced_nul_v_tag       = "REDUCED_NUL_V_STD_PMOS";
00986             reduced_angle_tag       = "REDUCED_ANGLE_STD_PMOS";
00987             reduced_error_angle_tag = "REDUCED_ERROR_ANGLE_STD_PMOS";
00988         }
00989         else {
00990             science_tag             = "SCIENCE_PMOS";
00991             reduced_science_tag     = "REDUCED_SCI_PMOS";
00992             unmapped_science_tag    = "UNMAPPED_SCI_PMOS";
00993             mapped_science_tag      = "MAPPED_SCI_PMOS";
00994             mapped_science_sky_tag  = "MAPPED_ALL_SCI_PMOS";
00995             skylines_offsets_tag    = "SKY_SHIFTS_SLIT_SCI_PMOS";
00996             wavelength_map_sky_tag  = "WAVELENGTH_MAP_SCI_PMOS";
00997             disp_coeff_sky_tag      = "DISP_COEFF_SCI_PMOS";
00998             mapped_sky_tag          = "MAPPED_SKY_SCI_PMOS";
00999             unmapped_sky_tag        = "UNMAPPED_SKY_SCI_PMOS";
01000             object_table_tag        = "OBJECT_TABLE_SCI_PMOS";
01001             object_table_pol_tag    = "OBJECT_TABLE_POL_SCI_PMOS";
01002             reduced_sky_tag         = "REDUCED_SKY_SCI_PMOS";
01003             reduced_error_tag       = "REDUCED_ERROR_SCI_PMOS";
01004             reduced_q_tag           = "REDUCED_Q_SCI_PMOS";
01005             reduced_u_tag           = "REDUCED_U_SCI_PMOS";
01006             reduced_v_tag           = "REDUCED_V_SCI_PMOS";
01007             reduced_l_tag           = "REDUCED_L_SCI_PMOS";
01008             reduced_i_tag           = "REDUCED_I_SCI_PMOS";
01009             reduced_error_q_tag     = "REDUCED_ERROR_Q_SCI_PMOS";
01010             reduced_error_u_tag     = "REDUCED_ERROR_U_SCI_PMOS";
01011             reduced_error_v_tag     = "REDUCED_ERROR_V_SCI_PMOS";
01012             reduced_error_l_tag     = "REDUCED_ERROR_L_SCI_PMOS";
01013             reduced_error_i_tag     = "REDUCED_ERROR_I_SCI_PMOS";
01014             reduced_nul_q_tag       = "REDUCED_NUL_Q_SCI_PMOS";
01015             reduced_nul_u_tag       = "REDUCED_NUL_U_SCI_PMOS";
01016             reduced_nul_v_tag       = "REDUCED_NUL_V_SCI_PMOS";
01017             reduced_angle_tag       = "REDUCED_ANGLE_SCI_PMOS";
01018             reduced_error_angle_tag = "REDUCED_ERROR_ANGLE_SCI_PMOS";
01019         }
01020 
01021         master_norm_flat_tag    = "MASTER_NORM_FLAT_PMOS";
01022         disp_coeff_tag          = "DISP_COEFF_PMOS";
01023         curv_coeff_tag          = "CURV_COEFF_PMOS";
01024         slit_location_tag       = "SLIT_LOCATION_PMOS";
01025 
01026         if (!cpl_frameset_count_tags(frameset, master_norm_flat_tag)) {
01027             master_norm_flat_tag    = "MASTER_NORM_FLAT_LONG_PMOS";
01028             disp_coeff_tag          = "DISP_COEFF_LONG_PMOS";
01029             slit_location_tag       = "SLIT_LOCATION_LONG_PMOS";
01030         }
01031     }
01032 
01033     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") == 0)
01034         fors_pmos_science_exit("Missing required input: MASTER_BIAS");
01035 
01036     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") > 1)
01037         fors_pmos_science_exit("Too many in input: MASTER_BIAS");
01038 
01039     if (skyalign >= 0)
01040         if (cpl_frameset_count_tags(frameset, "MASTER_SKYLINECAT") > 1)
01041             fors_pmos_science_exit("Too many in input: MASTER_SKYLINECAT");
01042 
01043     if (cpl_frameset_count_tags(frameset, disp_coeff_tag) == 0) {
01044         cpl_msg_error(recipe, "Missing required input: %s", disp_coeff_tag);
01045         fors_pmos_science_exit(NULL);
01046     }
01047 
01048     if (cpl_frameset_count_tags(frameset, disp_coeff_tag) > 1) {
01049         cpl_msg_error(recipe, "Too many in input: %s", disp_coeff_tag);
01050         fors_pmos_science_exit(NULL);
01051     }
01052 
01053     if (cpl_frameset_count_tags(frameset, slit_location_tag) == 0) {
01054         cpl_msg_error(recipe, "Missing required input: %s",
01055                       slit_location_tag);
01056         fors_pmos_science_exit(NULL);
01057     }
01058 
01059     if (cpl_frameset_count_tags(frameset, slit_location_tag) > 1) {
01060         cpl_msg_error(recipe, "Too many in input: %s", slit_location_tag);
01061         fors_pmos_science_exit(NULL);
01062     }
01063 
01064     if (chromatism) {
01065         if (cpl_frameset_count_tags(frameset, chrom_table_tag) == 0) {
01066             cpl_msg_error(recipe, "Missing required input: %s",
01067                           chrom_table_tag);
01068             fors_pmos_science_exit(NULL);
01069         }
01070 
01071         if (cpl_frameset_count_tags(frameset, chrom_table_tag) > 1) {
01072             cpl_msg_error(recipe, "Too many in input: %s", chrom_table_tag);
01073             fors_pmos_science_exit(NULL);
01074         }
01075     }
01076 
01077     if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) > 1) {
01078         if (flatfield) {
01079             cpl_msg_error(recipe, "Too many in input: %s", 
01080                           master_norm_flat_tag);
01081             fors_pmos_science_exit(NULL);
01082         }
01083         else {
01084             cpl_msg_warning(recipe, "%s in input are ignored, "
01085                             "since flat field correction was not requested", 
01086                             master_norm_flat_tag);
01087         }
01088     }
01089 
01090     if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 1) {
01091         if (!flatfield) {
01092             cpl_msg_warning(recipe, "%s in input is ignored, "
01093                             "since flat field correction was not requested", 
01094                             master_norm_flat_tag);
01095         }
01096     }
01097 
01098     if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 0) {
01099         if (flatfield) {
01100             cpl_msg_error(recipe, "Flat field correction was requested, "
01101                           "but no %s are found in input",
01102                           master_norm_flat_tag);
01103             fors_pmos_science_exit(NULL);
01104         }
01105     }
01106 
01107     if (standard) {
01108         if (cpl_frameset_count_tags(frameset, std_pmos_table_tag) > 1) {
01109             cpl_msg_error(recipe, "Too many in input: %s", std_pmos_table_tag);
01110             fors_pmos_science_exit(NULL);
01111         }
01112 
01113         if (qc) {
01114             if (cpl_frameset_count_tags(frameset, std_pmos_table_tag) == 0) {
01115                 cpl_msg_error(recipe, "QC computation was requested, but no "
01116                               "%s is found in input", std_pmos_table_tag);
01117                 fors_pmos_science_exit(NULL);
01118             }
01119         }
01120     }
01121 
01122     cpl_msg_indent_less();
01123 
01124 
01125     /*
01126      * Get the reference wavelength and the rebin factor along the
01127      * dispersion direction from a scientific exposure
01128      */
01129 
01130     header = dfs_load_header(frameset, science_tag, 0);
01131 
01132     if (header == NULL)
01133         fors_pmos_science_exit("Cannot load scientific frame header");
01134 
01135     instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
01136     if (instrume == NULL)
01137         fors_pmos_science_exit("Missing keyword INSTRUME in scientific header");
01138     instrume = cpl_strdup(instrume);
01139 
01140     if (instrume[4] == '1')
01141         snprintf(version, 80, "%s/%s", "fors1", VERSION);
01142     if (instrume[4] == '2')
01143         snprintf(version, 80, "%s/%s", "fors2", VERSION);
01144 
01145     reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
01146 
01147     if (cpl_error_get_code() != CPL_ERROR_NONE)
01148         fors_pmos_science_exit("Missing keyword ESO INS GRIS1 WLEN in scientific "
01149                         "frame header");
01150 
01151     if (reference < 3000.0)   /* Perhaps in nanometers... */
01152         reference *= 10;
01153 
01154     if (reference < 3000.0 || reference > 13000.0) {
01155         cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
01156                       "keyword ESO INS GRIS1 WLEN in scientific frame header",
01157                       reference);
01158         fors_pmos_science_exit(NULL);
01159     }
01160 
01161     cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
01162 
01163     rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
01164 
01165     if (cpl_error_get_code() != CPL_ERROR_NONE)
01166         fors_pmos_science_exit("Missing keyword ESO DET WIN1 BINX in "
01167                                "scientific frame header");
01168 
01169     if (rebin != 1) {
01170         dispersion *= rebin;
01171         cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
01172                         "spectral dispersion used is %f A/pixel", rebin, 
01173                         dispersion);
01174         ext_radius /= rebin;
01175         cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
01176                         "extraction radius used is %d pixel", rebin, 
01177                         ext_radius);
01178     }
01179 
01180     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
01181 
01182     if (cpl_error_get_code() != CPL_ERROR_NONE)
01183         fors_pmos_science_exit("Missing keyword ESO DET OUT1 CONAD in "
01184                           "scientific frame header");
01185 
01186     cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
01187 
01188     ron = cpl_propertylist_get_double(header, "ESO DET OUT1 RON");
01189 
01190     if (cpl_error_get_code() != CPL_ERROR_NONE)
01191         fors_pmos_science_exit("Missing keyword ESO DET OUT1 RON in "
01192                                "scientific frame header");
01193 
01194     ron /= gain;     /* Convert from electrons to ADU */
01195 
01196     cpl_msg_info(recipe, "The read-out-noise is: %.2f ADU", ron);
01197 
01198     if (cpl_frameset_count_tags(frameset, curv_coeff_tag) == 0) {
01199         cpl_msg_error(recipe, "Missing required input: %s", curv_coeff_tag);
01200         fors_pmos_science_exit(NULL);
01201     }
01202 
01203     if (cpl_frameset_count_tags(frameset, curv_coeff_tag) > 1) {
01204         cpl_msg_error(recipe, "Too many in input: %s", curv_coeff_tag);
01205         fors_pmos_science_exit(NULL);
01206     }
01207 
01208     cpl_msg_info(recipe, "Load normalised flat field (if present)...");
01209     cpl_msg_indent_more();
01210 
01211     if (flatfield) {
01212         norm_flat = dfs_load_image(frameset, master_norm_flat_tag, 
01213                                    CPL_TYPE_FLOAT, 0, 1);
01214     }
01215 
01216     if (skyalign >= 0) {
01217 
01218         cpl_msg_indent_less();
01219         cpl_msg_info(recipe, "Load input sky line catalog...");
01220         cpl_msg_indent_more();
01221 
01222         wavelengths = dfs_load_table(frameset, "MASTER_SKYLINECAT", 1);
01223 
01224         if (wavelengths) {
01225             /*
01226              * Cast the wavelengths into a (double precision) CPL vector
01227              */
01228 
01229             nlines = cpl_table_get_nrow(wavelengths);
01230 
01231             if (nlines == 0)
01232                 fors_pmos_science_exit("Empty input sky line catalog");
01233 
01234             if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
01235                 cpl_msg_error(recipe, "Missing column %s in input line "
01236                               "catalog table", wcolumn);
01237                 fors_pmos_science_exit(NULL);
01238             }
01239 
01240             line = cpl_malloc(nlines * sizeof(double));
01241     
01242             for (i = 0; i < nlines; i++)
01243                 line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
01244 
01245             cpl_table_delete(wavelengths); wavelengths = NULL;
01246 
01247             lines = cpl_vector_wrap(nlines, line);
01248         }
01249         else {
01250             cpl_msg_info(recipe, "No sky line catalog found in input - fine!");
01251         }
01252     }
01253 
01254     /*
01255      * Keep a table of slit positions according to science, in order to 
01256      * check its consistency with those from arc and flat.
01257      */
01258 
01259     mask_science = mos_load_slits_fors_mos(header);
01260 
01261     cpl_propertylist_delete(header); header = NULL;
01262 
01263     cpl_table_name_column(mask_science, "xtop", "science");
01264 
01265     /*
01266      * Load the wavelength calibration table
01267      */
01268 
01269     idscoeff = dfs_load_table(frameset, disp_coeff_tag, 1);
01270 
01271     if (idscoeff == NULL)
01272         fors_pmos_science_exit("Cannot load wavelength calibration table");
01273 
01274     /*
01275      * Keep a table of slit positions according to arc, in order to 
01276      * check its consistency with those from science and flat.
01277      */
01278 
01279     header = dfs_load_header(frameset, disp_coeff_tag, 0);
01280 
01281     mask_arc = mos_load_slits_fors_mos(header);
01282 
01283     cpl_propertylist_delete(header); header = NULL;
01284 
01285     if (cpl_table_move_column(mask_science, "xtop", mask_arc)) {
01286         cpl_error_reset();
01287         cpl_msg_warning(recipe, 
01288                         "Slit configuration of science and arc differs!");
01289         cpl_table_delete(mask_arc); mask_arc = NULL;
01290         goto skip;
01291     }
01292     cpl_table_name_column(mask_science, "xtop", "arc");
01293     cpl_table_delete(mask_arc); mask_arc = NULL;
01294 
01295     if (norm_flat) {
01296 
01297         /*
01298          * Keep a table of slit positions according to arc, in order to 
01299          * check its consistency with those from science and flat.
01300          */
01301 
01302         header = dfs_load_header(frameset, master_norm_flat_tag, 0);
01303 
01304         mask_flat = mos_load_slits_fors_mos(header);
01305 
01306         cpl_propertylist_delete(header); header = NULL;
01307 
01308         if (cpl_table_move_column(mask_science, "xtop", mask_flat)) {
01309             cpl_error_reset();
01310             cpl_msg_warning(recipe, 
01311                             "Slit configuration of science and flat differs!");
01312             cpl_table_delete(mask_flat); mask_flat = NULL;
01313             goto skip;
01314         }
01315         cpl_table_name_column(mask_science, "xtop", "flat");
01316         cpl_table_delete(mask_flat); mask_flat = NULL;
01317     }
01318 
01319     cpl_table_duplicate_column(mask_science, "diff", mask_science, "science");
01320     cpl_table_subtract_columns(mask_science, "diff", "arc");
01321     cpl_table_abs_column(mask_science, "diff");
01322 
01323     if (cpl_table_get_column_max(mask_science, "diff") > 0.01)  {
01324         cpl_msg_warning(recipe, 
01325                         "Slit configuration of science and arc differs!");
01326         goto skip;
01327     }
01328 
01329     if (norm_flat) {
01330         cpl_table_erase_column(mask_science, "diff");
01331 
01332         cpl_table_duplicate_column(mask_science, "diff", 
01333                                    mask_science, "science");
01334         cpl_table_subtract_columns(mask_science, "diff", "flat");
01335         cpl_table_abs_column(mask_science, "diff");
01336 
01337         if (cpl_table_get_column_max(mask_science, "diff") > 0.01)  {
01338             cpl_msg_warning(recipe, 
01339                             "Slit configuration of science and flat differs!");
01340             goto skip;
01341         }
01342     }
01343 
01344 skip:
01345 
01346     cpl_table_delete(mask_science); mask_science = NULL;
01347 
01348     for (j = 0; j < nscience; j++) {
01349         int k;
01350 
01351         cpl_msg_indent_less();
01352         cpl_msg_info(recipe, "Processing scientific exposure of angle %.2f "
01353                      "(%d out of %d) ...",
01354                      angles[j], j + 1, nscience);
01355         cpl_msg_indent_more();
01356 
01357         cpl_msg_info(recipe, "Load scientific exposure...");
01358         cpl_msg_indent_more();
01359 
01360 
01361         /*
01362          * FIXME: Horrible workaround to avoid the problem because of the
01363          * multiple encapsulation of cpl_frameset_find() in different 
01364          * loading functions
01365          */
01366 
01367         header = dfs_load_header(frameset, science_tag, 0);
01368 
01369         for (k = 0; k < j; k ++) {
01370             cpl_propertylist_delete(header);
01371             header = dfs_load_header(frameset, NULL, 0);
01372         }
01373 
01374         spectra = dfs_load_image(frameset, science_tag, CPL_TYPE_FLOAT, 0, 0);
01375 
01376         for (k = 0; k < j; k ++) {
01377             cpl_image_delete(spectra);
01378             spectra = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01379         }
01380 
01381         if (spectra == NULL)
01382             fors_pmos_science_exit("Cannot load scientific frame");
01383             
01384         if (header == NULL)
01385             fors_pmos_science_exit("Cannot load scientific frame header");
01386 
01387         alltime = cpl_propertylist_get_double(header, "EXPTIME");
01388 
01389         if (cpl_error_get_code() != CPL_ERROR_NONE)
01390             fors_pmos_science_exit("Missing keyword EXPTIME in scientific "
01391                                    "frame header");
01392 
01393         cpl_msg_info(recipe, "Scientific frame exposure time: %.2f s", 
01394                      alltime);
01395 
01396         ra = cpl_propertylist_get_double(header, "RA");
01397         dec = cpl_propertylist_get_double(header, "DEC");
01398 
01399         if (cpl_error_get_code() != CPL_ERROR_NONE)
01400             fors_pmos_science_exit("Missing keywords RA and DEC in scientific "
01401                                    "frame header");
01402 
01403         /* Leave the header on for the next step... */
01404 
01405         cpl_msg_indent_less();
01406 
01407         /*
01408          * Remove the master bias
01409          */
01410 
01411         cpl_msg_info(recipe, "Remove the master bias...");
01412 
01413         bias = dfs_load_image(frameset, "MASTER_BIAS", CPL_TYPE_FLOAT, 0, 1);
01414 
01415         if (bias == NULL)
01416             fors_pmos_science_exit("Cannot load master bias");
01417 
01418         if (doit) {
01419             if (j == 0)
01420                blevel = cpl_image_get_mean(bias); 
01421             mos_randomise_image(spectra, ron, gain, blevel);
01422         }
01423 
01424         overscans = mos_load_overscans_fors(header);
01425 
01426         dummy = mos_remove_bias(spectra, bias, overscans);
01427         cpl_image_delete(spectra); spectra = dummy; dummy = NULL;
01428         cpl_image_delete(bias); bias = NULL;
01429         cpl_table_delete(overscans); overscans = NULL;
01430 
01431         if (spectra == NULL)
01432             fors_pmos_science_exit("Cannot remove bias from scientific frame");
01433 
01434         ccd_xsize = nx = cpl_image_get_size_x(spectra);
01435         ccd_ysize = ny = cpl_image_get_size_y(spectra);
01436 
01437         if (flatfield) {
01438 
01439             if (norm_flat) {
01440                 cpl_msg_info(recipe, "Apply flat field correction...");
01441                 if (cpl_image_divide(spectra, norm_flat) != CPL_ERROR_NONE) {
01442                     cpl_msg_error(recipe, 
01443                                   "Failure of flat field correction: %s",
01444                                   cpl_error_get_message());
01445                     fors_pmos_science_exit(NULL);
01446                 }
01447             }
01448             else {
01449                 cpl_msg_error(recipe, "Cannot load input %s for flat field "
01450                               "correction", master_norm_flat_tag);
01451                 fors_pmos_science_exit(NULL);
01452             }
01453 
01454         }
01455 
01456         /*
01457          * Load the spectral curvature table
01458          */
01459 
01460         polytraces = dfs_load_table(frameset, curv_coeff_tag, 1);
01461         if (polytraces == NULL)
01462             fors_pmos_science_exit("Cannot load spectral curvature table");
01463 
01464         /*
01465          * Load the slit location table
01466          */
01467 
01468         slits = dfs_load_table(frameset, slit_location_tag, 1);
01469         if (slits == NULL)
01470             fors_pmos_science_exit("Cannot load slits location table");
01471 
01472         cpl_msg_info(recipe, "Processing scientific spectra...");
01473 
01474         /*
01475          * This one will also generate the spatial map from the spectral 
01476          * curvature table (in the case of multislit data)
01477          */
01478 
01479         coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01480 
01481         smapped = mos_spatial_calibration(spectra, slits, polytraces, 
01482                                           reference, startwavelength, 
01483                                           endwavelength, dispersion, 
01484                                           flux, coordinate);
01485 
01486         /*
01487          * Generate a rectified wavelength map from the wavelength calibration 
01488          * table
01489          */
01490 
01491         rainbow = mos_map_idscoeff(idscoeff, nx, reference, startwavelength, 
01492                                    endwavelength);
01493 
01494         if (dispersion > 1.0)
01495             highres = 0;
01496         else
01497             highres = 1;
01498 
01499         if (skyalign >= 0) {
01500             if (skyalign) {
01501                 cpl_msg_info(recipe, 
01502                              "Align wavelength solution to reference skylines "
01503                              "applying %d order residual fit...", skyalign);
01504             }
01505             else {
01506                 cpl_msg_info(recipe, "Align wavelength solution to reference "
01507                              "skylines applying median offset...");
01508             }
01509 
01510             if (!j) {
01511                 offsets = mos_wavelength_align(smapped, slits, reference, 
01512                                                startwavelength, endwavelength, 
01513                                                idscoeff, lines, highres, 
01514                                                skyalign, rainbow, 4);
01515                 if (offsets) {
01516                     if (standard)
01517                         cpl_msg_warning(recipe, "Alignment of the wavelength "
01518                                         "solution to reference sky lines may "
01519                                         "be unreliable in this case!");
01520 
01521                     if (dfs_save_table(frameset, offsets, skylines_offsets_tag,
01522                                        NULL, parlist, recipe, version)) {
01523                         fors_pmos_science_exit(NULL);
01524                     }
01525 
01526                 } else {
01527                     cpl_msg_warning(recipe, "Alignment of the wavelength "
01528                                     "solution to reference sky lines could "
01529                                     "not be done!");
01530                     skyalign = -1;
01531                 }
01532             }
01533 
01534 
01535         }
01536 
01537         wavemap = mos_map_wavelengths(coordinate, rainbow, slits, 
01538                                       polytraces, reference, 
01539                                       startwavelength, endwavelength,
01540                                       dispersion);
01541 
01542 
01543         cpl_image_delete(rainbow); rainbow = NULL;
01544         cpl_image_delete(coordinate); coordinate = NULL;
01545 
01546         /*
01547          * Here the wavelength calibrated slit spectra are created. This frame
01548          * contains sky_science.
01549          */
01550 
01551         mapped_sky = mos_wavelength_calibration(smapped, reference,
01552                                                 startwavelength, endwavelength,
01553                                                 dispersion, idscoeff, flux);
01554 
01555         if (!j) {
01556             cpl_msg_indent_less();
01557             cpl_msg_info(recipe, 
01558                          "Check applied wavelength against skylines...");
01559             cpl_msg_indent_more();
01560 
01561             mean_rms = mos_distortions_rms(mapped_sky, lines, startwavelength,
01562                                            dispersion, 6, highres);
01563 
01564 
01565             cpl_msg_info(recipe, "Mean residual: %f", mean_rms);
01566 
01567             mean_rms = cpl_table_get_column_mean(idscoeff, "error");
01568 
01569             cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
01570                          mean_rms, mean_rms * dispersion);
01571         }
01572 
01573         save_header = cpl_propertylist_duplicate(header);
01574 
01575         cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01576         cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01577         cpl_propertylist_update_double(header, "CRVAL1", 
01578                                        startwavelength + dispersion/2);
01579         cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01580         cpl_propertylist_update_double(header, "CD1_1", dispersion);
01581         cpl_propertylist_update_double(header, "CD1_2", 0.0);
01582         cpl_propertylist_update_double(header, "CD2_1", 0.0);
01583         cpl_propertylist_update_double(header, "CD2_2", 1.0);
01584         cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01585         cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01586 
01587         if (time_normalise) {
01588             dummy = cpl_image_divide_scalar_create(mapped_sky, alltime);
01589 
01590             if (!j) {
01591                 if(dfs_save_image_null(frameset, parlist,
01592                                        mapped_science_sky_tag,
01593                                        recipe, version)) {
01594                     fors_pmos_science_exit(NULL);
01595                 }
01596             }
01597 
01598             if (dfs_save_image_ext(dummy, mapped_science_sky_tag, header)) {
01599                 fors_pmos_science_exit(NULL);
01600             }
01601 
01602             cpl_image_delete(dummy); dummy = NULL;
01603         }
01604         else {
01605 
01606             if (!j) {
01607                 if(dfs_save_image_null(frameset, parlist,
01608                                        mapped_science_sky_tag,
01609                                        recipe, version)) {
01610                     fors_pmos_science_exit(NULL);
01611                 }
01612             }
01613 
01614             if (dfs_save_image_ext(mapped_sky,
01615                                    mapped_science_sky_tag, header)) {
01616                 fors_pmos_science_exit(NULL);
01617             }
01618 
01619         }
01620 
01621         if (skymedian == 0 && skylocal == 0) {
01622             cpl_image_delete(mapped_sky); mapped_sky = NULL;
01623         }
01624 
01625         if (skylocal) {
01626 
01627             cpl_msg_indent_less();
01628 
01629             cpl_msg_info(recipe, "Local sky determination...");
01630             cpl_msg_indent_more();
01631             skymap = mos_subtract_sky(spectra, slits, polytraces, reference,
01632                                   startwavelength, endwavelength, dispersion);
01633 
01634             if (skymap) {
01635                 if (time_normalise)
01636                     cpl_image_divide_scalar(skymap, alltime);
01637 
01638                 if (!j) {
01639                     if(dfs_save_image_null(frameset, parlist,
01640                                            unmapped_sky_tag,
01641                                            recipe, version)) {
01642                         fors_pmos_science_exit(NULL);
01643                     }
01644                 }
01645 
01646                 if (dfs_save_image_ext(skymap, unmapped_sky_tag,
01647                                        save_header)) {
01648                     fors_pmos_science_exit(NULL);
01649                 }
01650 
01651                 cpl_image_delete(skymap); skymap = NULL;
01652 
01653                 if (!j) {
01654                     if(dfs_save_image_null(frameset, parlist,
01655                                            unmapped_science_tag,
01656                                            recipe, version)) {
01657                         fors_pmos_science_exit(NULL);
01658                     }
01659                 }
01660 
01661                 if (dfs_save_image_ext(spectra, unmapped_science_tag,
01662                                        save_header)) {
01663                     fors_pmos_science_exit(NULL);
01664                 }
01665 
01666                 if (cosmics) {
01667                     cpl_msg_info(recipe, "Removing cosmic rays...");
01668                     mos_clean_cosmics(spectra, gain, -1., -1.);
01669                 }
01670 
01671                 /*
01672                  * The spatially rectified image, that contained the sky,
01673                  * is replaced by a sky-subtracted spatially rectified image:
01674                  */
01675 
01676                 cpl_image_delete(smapped); smapped = NULL;
01677 
01678                 smapped = mos_spatial_calibration(spectra, slits, polytraces, 
01679                                                   reference, startwavelength, 
01680                                                   endwavelength, dispersion, 
01681                                                   flux, NULL);
01682             }
01683             else {
01684                 cpl_msg_warning(recipe, "Sky subtraction failure");
01685                 if (cosmics)
01686                     cpl_msg_warning(recipe, 
01687                                     "Cosmic rays removal not performed!");
01688                 cosmics = skylocal = 0;
01689             }
01690         }
01691 
01692         cpl_image_delete(spectra); spectra = NULL;
01693         cpl_table_delete(polytraces); polytraces = NULL;
01694 
01695         if (skyalign >= 0) {
01696             save_header = dfs_load_header(frameset, science_tag, 0);
01697 
01698             if (!j) {
01699                 if(dfs_save_image_null(frameset, parlist,
01700                                        wavelength_map_sky_tag,
01701                                        recipe, version)) {
01702                     fors_pmos_science_exit(NULL);
01703                 }
01704             }
01705 
01706             if (dfs_save_image_ext(wavemap, wavelength_map_sky_tag,
01707                                    save_header)) {
01708                 fors_pmos_science_exit(NULL);
01709             }
01710         }
01711 
01712         cpl_image_delete(wavemap); wavemap = NULL;
01713 
01714         mapped = mos_wavelength_calibration(smapped, reference,
01715                                             startwavelength, endwavelength,
01716                                             dispersion, idscoeff, flux);
01717 
01718         cpl_image_delete(smapped); smapped = NULL;
01719 
01720         if (skyalign >= 0) {
01721             if (!j) {
01722                 if (dfs_save_table(frameset, idscoeff, disp_coeff_sky_tag,
01723                                    NULL, parlist, recipe, version)) {
01724                     fors_pmos_science_exit(NULL);
01725                 }
01726             }
01727         }
01728 
01729         if (skymedian) {
01730             cpl_msg_indent_less();
01731             cpl_msg_info(recipe, "Local sky determination...");
01732             cpl_msg_indent_more();
01733        
01734             skylocalmap = mos_sky_local_old(mapped, slits);       
01735             cpl_image_subtract(mapped, skylocalmap);
01736             cpl_image_delete(skylocalmap); skylocalmap = NULL;
01737         }
01738 
01739         if (skymedian || skylocal) {
01740 
01741             skylocalmap = cpl_image_subtract_create(mapped_sky, mapped);
01742 
01743             cpl_image_delete(mapped_sky); mapped_sky = NULL;
01744 
01745             if (time_normalise) {
01746                 dummy = cpl_image_divide_scalar_create(skylocalmap, alltime);
01747 
01748                 if (!j) {
01749                     if(dfs_save_image_null(frameset, parlist,
01750                                            mapped_sky_tag,
01751                                            recipe, version)) {
01752                         fors_pmos_science_exit(NULL);
01753                     }
01754                 }
01755 
01756                 if (dfs_save_image_ext(dummy, mapped_sky_tag,
01757                                        header)) {
01758                     fors_pmos_science_exit(NULL);
01759                 }
01760 
01761                 cpl_image_delete(dummy); dummy = NULL;
01762             }
01763             else {
01764                 if (!j) {
01765                     if(dfs_save_image_null(frameset, parlist,
01766                                            mapped_sky_tag,
01767                                            recipe, version)) {
01768                         fors_pmos_science_exit(NULL);
01769                     }
01770                 }
01771 
01772                 if (dfs_save_image_ext(skylocalmap, mapped_sky_tag,
01773                                        header)) {
01774                     fors_pmos_science_exit(NULL);
01775                 }
01776             }
01777 
01778             skylocalmaps[j] = skylocalmap;
01779 
01780             cpl_msg_indent_less();
01781             cpl_msg_info(recipe, "Object detection...");
01782             cpl_msg_indent_more();
01783 
01784             if (!j) {
01785                 origslits = cpl_table_duplicate(slits);
01786                 nslits = cpl_table_get_nrow(slits);
01787             }
01788 
01789             if (cosmics || nscience > 1) {
01790                 dummy = mos_detect_objects(mapped, slits, slit_margin, 
01791                                            ext_radius, cont_radius);
01792             }
01793             else {
01794                 mapped_cleaned = cpl_image_duplicate(mapped);
01795                 mos_clean_cosmics(mapped_cleaned, gain, -1., -1.);
01796                 dummy = mos_detect_objects(mapped_cleaned, slits, slit_margin, 
01797                                            ext_radius, cont_radius);
01798 
01799                 cpl_image_delete(mapped_cleaned); mapped_cleaned = NULL;
01800             }
01801 
01802             cpl_image_delete(dummy); dummy = NULL;
01803 
01804         }
01805 
01806         slitss[j]  = slits;
01807         mappeds[j] = mapped;
01808 
01809         cpl_msg_indent_less();
01810 
01811         cpl_propertylist_delete(header); header = NULL;
01812         cpl_propertylist_delete(save_header); save_header = NULL;
01813     }
01814 
01815     cpl_table_delete(offsets); offsets = NULL;
01816     cpl_table_delete(idscoeff); idscoeff = NULL;
01817 
01818     cpl_image_delete(norm_flat); norm_flat = NULL;
01819     cpl_vector_delete(lines); lines = NULL;
01820 
01821         
01822     cpl_msg_indent_less();
01823     cpl_msg_info(recipe, 
01824                  "Check object detection in both beams for all angles...");
01825     cpl_msg_indent_more();
01826 
01827     /* 
01828      * House keeping - selection of objects for which information required 
01829      * for Stokes parameters computation is present 
01830      */
01831 
01832     error = mos_object_intersect(slitss, origslits, nscience, tolerance);
01833     if (error == CPL_ERROR_DATA_NOT_FOUND) {
01834         cpl_msg_warning(recipe, "No objects found: no Stokes "
01835                        "parameters to compute!");
01836         for (j = 0; j < nscience; j++)
01837             cpl_table_delete(slitss[j]);
01838         cpl_free(slitss);
01839         cpl_table_delete(origslits);
01840         return 0;
01841     } else if (error) {
01842         fors_pmos_science_exit("Problem in polarimetric object selection");
01843     }
01844 
01845     if (dfs_save_table(frameset, origslits, object_table_pol_tag,
01846                        NULL, parlist, recipe, version)) {
01847         fors_pmos_science_exit(NULL);
01848     }
01849 
01850     /*
01851      * Save also object tables per angle after intersection
01852      */
01853 
01854     for (j = 0; j < nscience; j++) {
01855         if (!j) {
01856             if(dfs_save_image_null(frameset, parlist, object_table_tag,
01857                                    recipe, version)) {
01858                 fors_pmos_science_exit(NULL);
01859             }
01860         }
01861 
01862         if (dfs_save_table_ext(slitss[j], object_table_tag, NULL)) {
01863             fors_pmos_science_exit(NULL);
01864         }
01865     }
01866 
01867     nobjs_per_slit = fors_get_nobjs_perslit(origslits);
01868 
01869     cpl_msg_indent_less();
01870     cpl_msg_info(recipe, "Object extraction...");
01871     cpl_msg_indent_more();
01872 
01873     for (j = 0; j < nscience; j++) {
01874         int k;
01875 
01876         header = dfs_load_header(frameset, science_tag, 0);
01877 
01878         for (k = 0; k < j; k ++) {
01879             cpl_propertylist_delete(header);
01880             header = dfs_load_header(frameset, NULL, 0);
01881         }
01882 
01883         cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01884         cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01885         cpl_propertylist_update_double(header, "CRVAL1", 
01886                                 startwavelength + (dispersion * group)/2);
01887         cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01888         cpl_propertylist_update_double(header, "CD1_1", dispersion * group);
01889         cpl_propertylist_update_double(header, "CD1_2", 0.0);
01890         cpl_propertylist_update_double(header, "CD2_1", 0.0);
01891         cpl_propertylist_update_double(header, "CD2_2", 1.0);
01892         cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01893         cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01894 
01895         if (skymedian || skylocal) {
01896 
01897             cpl_msg_info(recipe, "Extracting at angle %.2f (%d out of %d) ...",
01898                          angles[j], j + 1, nscience);
01899 
01900             images = mos_extract_objects(mappeds[j], skylocalmaps[j],
01901                                          origslits, 
01902                                          ext_mode, ron, gain, 1);
01903 
01904             cpl_image_delete(skylocalmaps[j]); skylocalmaps[j] = NULL;
01905 
01906             if (images) {
01907                 if (time_normalise)
01908                     cpl_image_divide_scalar(images[0], alltime);
01909 
01910                 mos_rebin_signal(images, group);
01911 
01912                 if (!j) {
01913                     if(dfs_save_image_null(frameset, parlist,
01914                                            reduced_science_tag,
01915                                            recipe, version)) {
01916                         fors_pmos_science_exit(NULL);
01917                     }
01918                 }
01919 
01920                 if (dfs_save_image_ext(images[0], reduced_science_tag,
01921                                        header)) {
01922                     fors_pmos_science_exit(NULL);
01923                 }
01924 
01925                 reduceds[j] = images[0];
01926     
01927                 if (time_normalise)
01928                     cpl_image_divide_scalar(images[1], alltime);
01929 
01930                 mos_rebin_signal(images + 1, group);
01931 
01932                 if (!j) {
01933                     if(dfs_save_image_null(frameset, parlist,
01934                                            reduced_sky_tag,
01935                                            recipe, version)) {
01936                         fors_pmos_science_exit(NULL);
01937                     }
01938                 }
01939 
01940                 if (dfs_save_image_ext(images[1], reduced_sky_tag,
01941                                        header)) {
01942                     fors_pmos_science_exit(NULL);
01943                 }
01944                 cpl_image_delete(images[1]);
01945     
01946                 if (time_normalise)
01947                     cpl_image_divide_scalar(images[2], alltime);
01948 
01949                 mos_rebin_error(images + 2, group);
01950 
01951                 if (!j) {
01952                     if(dfs_save_image_null(frameset, parlist,
01953                                            reduced_error_tag,
01954                                            recipe, version)) {
01955                         fors_pmos_science_exit(NULL);
01956                     }
01957                 }
01958 
01959                 if (dfs_save_image_ext(images[2], reduced_error_tag,
01960                                        header)) {
01961                     fors_pmos_science_exit(NULL);
01962                 }
01963 
01964                 rerrors[j] = images[2];
01965 
01966                 cpl_free(images);
01967             }
01968             else {
01969                 cpl_msg_warning(recipe, "No objects found: the products "
01970                                 "%s, %s, and %s are not created", 
01971                                 reduced_science_tag, reduced_sky_tag, 
01972                                 reduced_error_tag);
01973             }
01974 
01975         }
01976 
01977         if (skymedian || skylocal) {
01978             if (time_normalise)
01979                 cpl_image_divide_scalar(mappeds[j], alltime);
01980 
01981             if (!j) {
01982                 if(dfs_save_image_null(frameset, parlist,
01983                                        mapped_science_tag,
01984                                        recipe, version)) {
01985                     fors_pmos_science_exit(NULL);
01986                 }
01987             }
01988 
01989             if (dfs_save_image_ext(mappeds[j], mapped_science_tag,
01990                                    header)) {
01991                 fors_pmos_science_exit(NULL);
01992             }
01993         }
01994 
01995         cpl_image_delete(mappeds[j]); mappeds[j] = NULL;
01996         cpl_propertylist_delete(header); header = NULL;
01997 
01998     }
01999 
02000     cpl_table_delete(origslits);
02001 
02002     /* Stokes computation */
02003 
02004     nobjects = cpl_image_get_size_y(reduceds[0]) / 2;
02005     nx       = cpl_image_get_size_x(reduceds[0]);
02006 
02007     header = cpl_propertylist_new();
02008     cpl_propertylist_update_double(header, "CRPIX1", 1.0);
02009     cpl_propertylist_update_double(header, "CRPIX2", 1.0);
02010     cpl_propertylist_update_double(header, "CRVAL1", 
02011                                    startwavelength + (dispersion * group)/2);
02012     cpl_propertylist_update_double(header, "CRVAL2", 1.0);
02013     cpl_propertylist_update_double(header, "CD1_1", dispersion * group);
02014     cpl_propertylist_update_double(header, "CD1_2", 0.0);
02015     cpl_propertylist_update_double(header, "CD2_1", 0.0);
02016     cpl_propertylist_update_double(header, "CD2_2", 1.0);
02017     cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
02018     cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
02019     
02020     if (circ) {
02021 
02022         cpl_image        *pv_im          = NULL;
02023         cpl_image        *pi_im          = NULL;
02024         cpl_image        *pvnull_im      = NULL;
02025         cpl_image        *pierr_im       = NULL;
02026         cpl_image        *perr_im        = NULL;
02027 
02028         double           *p_v            = NULL;
02029         double           *p_i            = NULL;
02030         double           *p_vnull        = NULL;
02031         double           *perr           = NULL;
02032         double           *pierr           = NULL;
02033 
02034         double            mean_vnull;
02035 
02036         int p = -1;
02037         int total = 0;
02038 
02039         pv_im     = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02040         perr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02041         pi_im     = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02042         pierr_im  = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02043 
02044         p_v     = cpl_image_get_data_double(pv_im);
02045         perr    = cpl_image_get_data_double(perr_im);
02046         p_i     = cpl_image_get_data_double(pi_im);
02047         pierr   = cpl_image_get_data_double(pierr_im);
02048 
02049         if (nscience / 2 > 1) {
02050             pvnull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02051             p_vnull = cpl_image_get_data_double(pvnull_im);
02052         }
02053 
02054         for (j = 0; j < nobjects; j++) {
02055 
02056             FILE *file;               // Bagoo
02057             char *filename;           // Bagoo
02058 
02059             int k, m;
02060 
02061             double * ip_v, * ip_i, * ipierr,
02062                    * ip_vnull, * iperr;
02063 
02064             float * data;
02065             float * iff,  * ierr;
02066 
02067             ip_v = p_v + (nobjects - 1 - j) * nx;
02068 
02069             if (nscience / 2 > 1)
02070                 ip_vnull = p_vnull + (nobjects - 1 - j) * nx;
02071 
02072             iperr = perr + (nobjects - 1 - j) * nx;
02073 
02074             ip_i = p_i + (nobjects - 1 - j) * nx;
02075             ipierr = pierr + (nobjects - 1 - j) * nx;
02076 
02077             total = 0;
02078             for (i = 0; i < nslits; i += 2) {
02079                 total += nobjs_per_slit[i];
02080                 if (total > j) {
02081                     p = i;
02082                     break;
02083                 }
02084             }
02085 
02086             for (k = 0; k < nscience / 2; k++) {
02087                 float *if_o,  *if_e,  *ifdelta_o, *ifdelta_e;
02088                 float *if_o_err,  *if_e_err,  *ifdelta_o_err, *ifdelta_e_err;
02089 
02090                 int pos   = fors_find_angle_pos(angles, nscience, 180 * k - 45);
02091                 int pos_d = fors_find_angle_pos(angles, nscience, 180 * k + 45);
02092 
02093 
02094                 data = cpl_image_get_data_float(reduceds[pos]);
02095 
02096                 if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02097                      + (total - j - 1)) * nx;
02098 
02099                 if_e = data + (2 * (nobjects - total) 
02100                      + (total - j - 1)) * nx;
02101 
02102                 data = cpl_image_get_data_float(reduceds[pos_d]);
02103 
02104                 ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02105                           + (total - j - 1)) * nx;
02106 
02107                 ifdelta_e = data + (2 * (nobjects - total) 
02108                           + (total - j - 1)) * nx;
02109 
02110                 data = cpl_image_get_data_float(rerrors[pos]);
02111 
02112                 if_o_err = data 
02113                          + (2 * (nobjects - total) + nobjs_per_slit[p]
02114                          + (total - j - 1)) * nx;
02115 
02116                 if_e_err = data + (2 * (nobjects - total)
02117                          + (total - j - 1)) * nx;
02118 
02119                 data = cpl_image_get_data_float(rerrors[pos_d]);
02120 
02121                 ifdelta_o_err = data 
02122                               + (2 * (nobjects - total) + nobjs_per_slit[p]
02123                               + (total - j - 1)) * nx;
02124 
02125                 ifdelta_e_err = data + (2 * (nobjects - total)
02126                               + (total - j - 1)) * nx;
02127 
02128                 if (bagoo) {
02129 
02130                     char *signal_to_noise  = getenv("SIGNAL_TO_NOISE" );
02131                     float s2n = 100.;
02132                     char *min_s2n  = getenv("MIN_S2N" );
02133                     int   ms2n = 50;
02134 
02135                     if (signal_to_noise)
02136                         s2n = atof(signal_to_noise);
02137 
02138                     if (min_s2n)
02139                         ms2n = atoi(min_s2n);
02140 
02141                     /*
02142                      * Check whether S/N is > s2n in more than ms2n pixels
02143                      * (on first frame, on ordinary beam)
02144                      */
02145 
02146                     if (k == 0) {
02147                         bright = 0;
02148                         for (m = 0; m < nx; m++) {
02149                             if (if_o_err[m] > 0.0) {
02150                                 if (if_o[m]/if_o_err[m] > s2n) {
02151                                     bright++;
02152                                     if (bright > ms2n) {
02153                                         break;
02154                                     }
02155                                 }
02156                             }
02157                         }
02158                     }
02159 
02160                     if (bright > ms2n) {
02161                         conta++;
02162                         filename = cpl_sprintf("angle_%d_%d.dat", 
02163                                                180*k-45, conta);
02164                         file = fopen(filename, "w");
02165     
02166                         fprintf(file, "%d\n", p + 2);
02167 
02168                         for (m = 0; m < nx; m++) {
02169                             double lambda = startwavelength 
02170                                           + dispersion * group * (0.5 + m);
02171                             fprintf(file, "%.3f %.9e %.9e %.9e %.9e\n",
02172                                     lambda, if_o[m], if_o_err[m], 
02173                                     if_e[m], if_e_err[m]);
02174                         }
02175 
02176                         fclose(file);
02177                         cpl_free(filename);
02178 
02179                         filename = cpl_sprintf("angle_%d_%d.dat", 
02180                                                180*k+45, conta);
02181                         file = fopen(filename, "w");
02182 
02183                         fprintf(file, "%d\n", p + 2);
02184 
02185                         for (m = 0; m < nx; m++) {
02186                             double lambda = startwavelength 
02187                                           + dispersion * group * (0.5 + m);
02188                             fprintf(file, "%.3f %.9e %.9e %.9e %.9e\n",
02189                                     lambda, ifdelta_o[m], ifdelta_o_err[m], 
02190                                     ifdelta_e[m], ifdelta_e_err[m]);
02191                         }
02192     
02193                         fclose(file);
02194                         cpl_free(filename);
02195                     }
02196                     else {
02197                         cpl_msg_info(recipe, 
02198                                      "Extracted signal not written to "
02199                                      "ASCII (S/N > %.0f only in %d < %d "
02200                                      "bins)", s2n, bright, ms2n);
02201                     }
02202                 }  // End of bagoo
02203 
02204                 for (m = 0; m < nx; m++) {
02205 
02206                     double quantity = if_o[m] + if_e[m] == 0.0 ? 0.0 :
02207                         (if_o[m]      - if_e[m]     ) /
02208                         (if_o[m]      + if_e[m]     ) -
02209                         (ifdelta_o[m] - ifdelta_e[m]) /
02210                         (ifdelta_o[m] + ifdelta_e[m]);
02211 
02212                     quantity = isfinite(quantity) ? quantity : 0.0;
02213 
02214                     /* PQ map computation */
02215                     ip_v[m] += quantity * 0.5 / (nscience / 2);
02216 
02217                     /* PQnull map computation */
02218                     if (nscience / 2 > 1) {
02219                         if (k % 2)
02220                             ip_vnull[m] += quantity * 0.5 / (nscience / 2);
02221                         else
02222                             ip_vnull[m] -= quantity * 0.5 / (nscience / 2);
02223                     }
02224 
02225                     /* I map computation */
02226                     ip_i[m] += (if_o[m] + if_e[m] + 
02227                                 ifdelta_o[m] + ifdelta_e[m]) / nscience;
02228 
02229                     /* Variance map computation */
02230                     ipierr[m] += (if_o_err[m]      * if_o_err[m]
02231                                 + if_e_err[m]      * if_e_err[m]
02232                                 + ifdelta_o_err[m] * ifdelta_o_err[m]
02233                                 + ifdelta_e_err[m] * ifdelta_e_err[m]) 
02234                                / nscience / nscience;
02235 
02236                 }
02237             }
02238 
02239             /* Error map */
02240             data = cpl_image_get_data_float(reduceds[0]);
02241             iff  = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02242 
02243             data = cpl_image_get_data_float(rerrors[0]);
02244             ierr = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02245 
02246             for (m = 0; m < nx; m++)
02247                 iperr[m] = iff[m] <= 0.0 ? 
02248                     0.0 : ierr[m] / iff[m] * 0.5 / sqrt (nscience / 2);
02249 
02250             if (nscience / 2 > 1) {
02251                 float * weights;
02252                 float   max, sum, sum2, imean;
02253 
02254                 int k;
02255 
02256                 /* QC on U NULL */
02257                 weights = cpl_malloc(sizeof(float) * nx);
02258 
02259                 max = 0.0;
02260                 for (k = 0; k < nx; k++) {
02261                     if (max < iff[k]) max = iff[k];
02262                 }
02263             
02264                 for (k = 0; k < nx; k++) {
02265                     weights[k] = iff[k] < 0.0 ? 
02266                         0.0 : iff[k] * iff[k] / (max * max);
02267                 }
02268             
02269                 sum  = 0.0;
02270                 sum2 = 0.0;
02271                 for (k = 0; k < nx; k++) {
02272                     sum  += weights[k] * ip_vnull[k];
02273                     sum2 += weights[k];
02274                 }
02275 
02276                 cpl_free(weights);
02277 
02278                 imean = sum / sum2;
02279 
02280                 mean_vnull += (imean - mean_vnull) / (j + 1.0);
02281             }
02282         }
02283 
02284         if (dfs_save_image(frameset, pv_im, reduced_v_tag, header, 
02285                            parlist, recipe, version))
02286             fors_pmos_science_exit(NULL);
02287 
02288         if (dfs_save_image(frameset, pi_im, reduced_i_tag, header, 
02289                            parlist, recipe, version))
02290             fors_pmos_science_exit(NULL);
02291 
02292         if (nscience / 2 > 1) {
02293             char             *pipefile;
02294             char             *keyname;
02295             cpl_propertylist *qheader;
02296 
02297             qheader = dfs_load_header(frameset, science_tag, 0);
02298             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
02299             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
02300             cpl_propertylist_update_double(qheader, "CRVAL1", 
02301                                    startwavelength + (dispersion * group)/2);
02302             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
02303             cpl_propertylist_update_double(qheader, "CD1_1", 
02304                                            dispersion * group);
02305             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
02306             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
02307             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
02308             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
02309             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
02310 
02311             if (qc) {
02312                 fors_qc_start_group(qheader, "2.0", instrume);
02313 
02314                 /*
02315                  * QC1 group header
02316                  */
02317 
02318                 if (fors_qc_write_string("PRO.CATG", reduced_nul_v_tag,
02319                                          "Product category", instrume))
02320                     fors_pmos_science_exit("Cannot write product category to "
02321                                            "QC log file");
02322 
02323                 if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
02324                                            "DPR type", instrume))
02325                     fors_pmos_science_exit("Missing keyword DPR TYPE in "
02326                                            "scientific frame header");
02327     
02328                 if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
02329                                            "Template", instrume))
02330                     fors_pmos_science_exit("Missing keyword TPL ID in "
02331                                            "scientific frame header");
02332     
02333                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
02334                                            "Grism name", instrume))
02335                     fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
02336                                            "scientific frame header");
02337 
02338                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
02339                                            "Grism identifier", instrume))
02340                     fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
02341                                            "scientific frame header");
02342 
02343                 if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
02344                     fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
02345                                            "Filter name", instrume);
02346 
02347                 if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
02348                                            "Collimator name", instrume))
02349                     fors_pmos_science_exit("Missing keyword INS COLL NAME in "
02350                                            "scientific frame header");
02351 
02352                 if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
02353                                            "Chip identifier", instrume))
02354                     fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
02355                                            "scientific frame header");
02356 
02357                 if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
02358                                            "Archive name of input data", 
02359                                            instrume))
02360                     fors_pmos_science_exit("Missing keyword ARCFILE in "
02361                                            "scientific frame header");
02362 
02363                 pipefile = dfs_generate_filename_tfits(reduced_nul_v_tag);
02364                 if (fors_qc_write_string("PIPEFILE", pipefile,
02365                                          "Pipeline product name", instrume))
02366                     fors_pmos_science_exit("Cannot write PIPEFILE to "
02367                                            "QC log file");
02368                 cpl_free(pipefile); pipefile = NULL;
02369 
02370 
02371                 /*
02372                  * QC1 parameters
02373                  */
02374 
02375                 keyname = "QC.NULL.V.MEAN";
02376                     
02377                 if (fors_qc_write_qc_double(qheader, mean_vnull,
02378                                             keyname, NULL,
02379                                             "Mean V null parameter",
02380                                             instrume)) {
02381                     fors_pmos_science_exit("Cannot write mean Q null "
02382                                            "parameter to QC log file.");
02383                 }
02384 
02385                 keyname = "QC.NANGLES";
02386 
02387                 if (fors_qc_write_qc_int(qheader, nscience,
02388                                          keyname, NULL,
02389                                          "Number of processed plate angles",
02390                                          instrume)) {
02391                     fors_pmos_science_exit("Cannot write number of processed "
02392                                            "plate angles.");
02393                 }
02394 
02395                 fors_qc_end_group();
02396             }
02397 
02398             if (dfs_save_image(frameset, pvnull_im, reduced_nul_v_tag, qheader, 
02399                                parlist, recipe, version))
02400                 fors_pmos_science_exit(NULL);
02401 
02402             cpl_propertylist_delete(qheader);
02403         }
02404 
02405         if (dfs_save_image(frameset, perr_im, reduced_error_v_tag, header, 
02406                            parlist, recipe, version))
02407             fors_pmos_science_exit(NULL);
02408 
02409         cpl_image_power(pierr_im, 0.5);
02410 
02411         if (dfs_save_image(frameset, pierr_im, reduced_error_i_tag, header, 
02412                            parlist, recipe, version))
02413             fors_pmos_science_exit(NULL);
02414 
02415         cpl_image_delete(pv_im);
02416         cpl_image_delete(pvnull_im);
02417         cpl_image_delete(perr_im);
02418         cpl_image_delete(pi_im);
02419         cpl_image_delete(pierr_im);
02420     } 
02421     else {                            /* Linear polarisation */
02422         cpl_image *pq_im      = NULL;
02423         cpl_image *pu_im      = NULL;
02424         cpl_image *pl_im      = NULL;
02425         cpl_image *pi_im      = NULL;
02426 
02427         cpl_image *pqnull_im  = NULL;
02428         cpl_image *punull_im  = NULL;
02429 
02430         cpl_image *pqerr_im   = NULL;
02431         cpl_image *puerr_im   = NULL;
02432         cpl_image *plerr_im   = NULL;
02433         cpl_image *pierr_im   = NULL;
02434 
02435         cpl_image *pang_im    = NULL;
02436         cpl_image *pangerr_im = NULL;
02437 
02438         double    *p_q        = NULL;
02439         double    *p_u        = NULL;
02440         double    *p_l        = NULL;
02441         double    *p_i        = NULL;
02442 
02443         double    *p_qnull    = NULL;
02444         double    *p_unull    = NULL;
02445 
02446         double    *pqerr      = NULL;
02447         double    *puerr      = NULL;
02448         double    *plerr      = NULL;
02449         double    *pierr      = NULL;
02450 
02451         double    *pang       = NULL;
02452         double    *pangerr    = NULL;
02453 
02454         int        k, m;
02455 
02456         cpl_image *correct_im = cpl_image_new(nx, 1, CPL_TYPE_DOUBLE);
02457         double    *correct    = cpl_image_get_data_double(correct_im);
02458 
02459         double     mean_unull, mean_qnull;
02460 
02461         int        p          = -1;
02462         int        total      = 0;
02463             
02464         pq_im      = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02465         pu_im      = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02466         pl_im      = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02467         pi_im      = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02468 
02469         pqerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02470         puerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02471         plerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02472         pierr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02473 
02474         pang_im    = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02475         pangerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02476 
02477         p_q        = cpl_image_get_data_double(pq_im);
02478         p_u        = cpl_image_get_data_double(pu_im);
02479         p_l        = cpl_image_get_data_double(pl_im);
02480         p_i        = cpl_image_get_data_double(pi_im);
02481 
02482         if (nscience / 4 > 1) {
02483             pqnull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02484             punull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02485 
02486             p_qnull = cpl_image_get_data_double(pqnull_im);
02487             p_unull = cpl_image_get_data_double(punull_im);
02488         } else {
02489             cpl_msg_warning(cpl_func, 
02490                             "Not enough pairs to compute null parameters");
02491         }
02492 
02493         pqerr = cpl_image_get_data_double(pqerr_im);
02494         puerr = cpl_image_get_data_double(puerr_im);
02495         plerr = cpl_image_get_data_double(plerr_im);
02496         pierr = cpl_image_get_data_double(pierr_im);
02497 
02498         pang = cpl_image_get_data_double(pang_im);
02499         pangerr = cpl_image_get_data_double(pangerr_im);
02500 
02501         if (chromatism) {
02502             cpl_table * chrotbl = 
02503                 dfs_load_table(frameset, chrom_table_tag, 1);
02504 
02505             int      nrow   = cpl_table_get_nrow(chrotbl);
02506             float  * lambda = cpl_table_get_data_float(chrotbl, "lambda");
02507             float  * theta  = cpl_table_get_data_float(chrotbl, "eps_theta");
02508 
02509             for (j = 0; j < nx; j++) {
02510                 double c_wave = startwavelength 
02511                               + (dispersion * group) / 2 
02512                               + j * dispersion * group;
02513             
02514                 int found = 0;
02515 
02516                 for (k = 0; k < nrow - 1; k++) {
02517                     if (lambda[k] <= c_wave && c_wave < lambda[k + 1]) {
02518                         found = 1;
02519                         break;
02520                     }
02521                 }
02522 
02523                 if (found) {
02524                     correct[j] = (theta [k + 1] - theta [k]) /
02525                                  (lambda[k + 1] - lambda[k]) *
02526                                  (c_wave        - lambda[k])   + theta[k];
02527                     correct[j] *= M_PI / 180;   /* Radians */
02528                 }
02529                 else if (j)
02530                     correct[j] = correct[j-1];
02531                 else
02532                     correct[j] = 0.0;
02533             }
02534 
02535             cpl_table_delete(chrotbl);
02536         }
02537 
02538         for (j = 0; j < nobjects; j++) {
02539             double *ip_q;
02540             double *ip_u;
02541             double *ip_l;
02542             double *ip_i;
02543             double *ipierr;
02544             double *ip_qnull;
02545             double *ip_unull;
02546             double *ipqerr;
02547             double *ipuerr;
02548             double *iplerr;
02549             double *ipang;
02550             double *ipangerr;
02551 
02552             float  *data;
02553             float  *iffq;
02554             float  *ierrq;
02555             float  *iffu;
02556             float  *ierru;
02557 
02558             int pos, pos_d;
02559 
02560             ip_q = p_q + (nobjects - 1 - j) * nx;
02561             ip_u = p_u + (nobjects - 1 - j) * nx;
02562             ip_l = p_l + (nobjects - 1 - j) * nx;
02563             ip_i = p_i + (nobjects - 1 - j) * nx;
02564 
02565             if (nscience / 4 > 1) {
02566                 ip_qnull = p_qnull + (nobjects - 1 - j) * nx;
02567                 ip_unull = p_unull + (nobjects - 1 - j) * nx;
02568             }
02569 
02570             ipqerr = pqerr + (nobjects - 1 - j) * nx;
02571             ipuerr = puerr + (nobjects - 1 - j) * nx;
02572             iplerr = plerr + (nobjects - 1 - j) * nx;
02573             ipierr = pierr + (nobjects - 1 - j) * nx;
02574 
02575             ipang = pang + (nobjects - 1 - j) * nx;
02576             ipangerr = pangerr + (nobjects - 1 - j) * nx;
02577 
02578             total = 0;
02579             for (i = 0; i < nslits; i += 2) {
02580                 total += nobjs_per_slit[i];
02581                 if (total > j) {
02582                     p = i;
02583                     break;
02584                 }
02585             }
02586 
02587             for (k = 0; k < nscience / 4; k++) {
02588                 float * if_o, * if_e,  * ifdelta_o, * ifdelta_e;
02589                 float * if_o_err, * if_e_err,  * ifdelta_o_err, * ifdelta_e_err;
02590 
02591                 /* First P_Q */
02592 
02593                 pos   = fors_find_angle_pos(angles, nscience, 90 * k);
02594                 pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 45);
02595 
02596                 data = cpl_image_get_data_float(reduceds[pos]);
02597 
02598                 if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02599                                + (total - j - 1)) * nx;
02600 
02601                 if_e = data + (2 * (nobjects - total) 
02602                                + (total - j - 1)) * nx;
02603 
02604                 data = cpl_image_get_data_float(reduceds[pos_d]);
02605 
02606                 ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02607                                + (total - j - 1)) * nx;
02608 
02609                 ifdelta_e = data + (2 * (nobjects - total) 
02610                                + (total - j - 1)) * nx;
02611 
02612                 data = cpl_image_get_data_float(rerrors[pos]);
02613 
02614                 if_o_err = data + (2 * (nobjects - total) + nobjs_per_slit[p]
02615                                + (total - j - 1)) * nx;
02616 
02617                 if_e_err = data + (2 * (nobjects - total)
02618                                + (total - j - 1)) * nx;
02619 
02620                 data = cpl_image_get_data_float(rerrors[pos_d]);
02621 
02622                 ifdelta_o_err = data + (2 * (nobjects - total) 
02623                               + nobjs_per_slit[p] + (total - j - 1)) * nx;
02624 
02625                 ifdelta_e_err = data + (2 * (nobjects - total)
02626                               + (total - j - 1)) * nx;
02627 
02628                 for (m = 0; m < nx; m++) {
02629 
02630                     double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
02631                         (if_o[m]      - if_e[m]     ) /
02632                         (if_o[m]      + if_e[m]     ) -
02633                         (ifdelta_o[m] - ifdelta_e[m]) /
02634                         (ifdelta_o[m] + ifdelta_e[m]);
02635 
02636                     quantity = isfinite(quantity) ? quantity : 0.0;
02637 
02638                     /* PQ map computation */
02639                     ip_q[m] += quantity * 0.5 / (nscience / 4);
02640 
02641                     /* PQnull map computation */
02642                     if (nscience / 4 > 1) {
02643                         if (k % 2)
02644                             ip_qnull[m] += quantity * 0.5 / (nscience / 4);
02645                         else
02646                             ip_qnull[m] -= quantity * 0.5 / (nscience / 4);
02647                     }
02648 
02649                     /* I map computation */
02650                     ip_i[m] += (if_o[m] + if_e[m] +
02651                                 ifdelta_o[m] + ifdelta_e[m]) / nscience;
02652 
02653                     /* Variance map computation */
02654                     ipierr[m] += (if_o_err[m]      * if_o_err[m]
02655                                 + if_e_err[m]      * if_e_err[m]
02656                                 + ifdelta_o_err[m] * ifdelta_o_err[m]
02657                                 + ifdelta_e_err[m] * ifdelta_e_err[m]) 
02658                                / nscience / nscience;
02659                 }
02660 
02661                 /* Now P_U */
02662 
02663                 pos   = fors_find_angle_pos(angles, nscience, 90 * k + 22.5);
02664                 pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 67.5);
02665 
02666                 data = cpl_image_get_data_float(reduceds[pos]);
02667 
02668                 if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02669                                + (total - j - 1)) * nx;
02670 
02671                 if_e = data + (2 * (nobjects - total) 
02672                                + (total - j - 1)) * nx;
02673 
02674                 data = cpl_image_get_data_float(reduceds[pos_d]);
02675 
02676                 ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02677                                + (total - j - 1)) * nx;
02678 
02679                 ifdelta_e = data + (2 * (nobjects - total) 
02680                                + (total - j - 1)) * nx;
02681 
02682                 data = cpl_image_get_data_float(rerrors[pos]);
02683 
02684                 if_o_err = data + (2 * (nobjects - total) + nobjs_per_slit[p]
02685                                + (total - j - 1)) * nx;
02686 
02687                 if_e_err = data + (2 * (nobjects - total)
02688                                + (total - j - 1)) * nx;
02689 
02690                 data = cpl_image_get_data_float(rerrors[pos_d]);
02691 
02692                 ifdelta_o_err = data + (2 * (nobjects - total)
02693                               + nobjs_per_slit[p] + (total - j - 1)) * nx;
02694 
02695                 ifdelta_e_err = data + (2 * (nobjects - total)
02696                               + (total - j - 1)) * nx;
02697 
02698                 for (m = 0; m < nx; m++) {
02699 
02700                     double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
02701                         (if_o[m]      - if_e[m]     ) /
02702                         (if_o[m]      + if_e[m]     ) -
02703                         (ifdelta_o[m] - ifdelta_e[m]) /
02704                         (ifdelta_o[m] + ifdelta_e[m]);
02705 
02706                     quantity = isfinite(quantity) ? quantity : 0.0;
02707 
02708                     /* PU map computation */
02709                     ip_u[m] += quantity * 0.5 / (nscience / 4);
02710 
02711                     /* PUnull map computation */
02712                     if (nscience / 4 > 1) {
02713                         if (k % 2)
02714                             ip_unull[m] += quantity * 0.5 / (nscience / 4);
02715                         else
02716                             ip_unull[m] -= quantity * 0.5 / (nscience / 4);
02717                     }
02718 
02719                     /* I map computation */
02720                     ip_i[m] += (if_o[m] + if_e[m] +
02721                                 ifdelta_o[m] + ifdelta_e[m]) / nscience;
02722 
02723                     /* Variance map computation */
02724                     ipierr[m] += (if_o_err[m]      * if_o_err[m]
02725                                 + if_e_err[m]      * if_e_err[m]
02726                                 + ifdelta_o_err[m] * ifdelta_o_err[m]
02727                                 + ifdelta_e_err[m] * ifdelta_e_err[m]) 
02728                                / nscience / nscience;
02729                 }
02730             }
02731 
02732             /* Error map */
02733 
02734             pos   = fors_find_angle_pos(angles, nscience, 0.0);
02735 
02736             data = cpl_image_get_data_float(reduceds[pos]);
02737             iffq = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02738 
02739             data = cpl_image_get_data_float(rerrors[pos]);
02740             ierrq = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02741             
02742             pos   = fors_find_angle_pos(angles, nscience, 22.5);
02743 
02744             data = cpl_image_get_data_float(reduceds[pos]);
02745             iffu = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02746 
02747             data = cpl_image_get_data_float(rerrors[pos]);
02748             ierru = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02749 
02750             for (m = 0; m < nx; m++) {
02751 
02752                 double radicand; 
02753 
02754                 ipqerr[m] = iffq[m] <= 0.0 ? 
02755                     0.0 : ierrq[m] / iffq[m] * 0.5 / sqrt (nscience / 4);
02756 
02757                 ipuerr[m] = iffu[m] <= 0.0 ? 
02758                     0.0 : ierru[m] / iffu[m] * 0.5 / sqrt (nscience / 4);
02759 
02760                 iplerr[m] = 0.5 * (ipqerr[m] + ipuerr[m]);
02761 
02762                 /* PL computation */
02763                 ip_l[m] = sqrt(ip_u[m] * ip_u[m] + ip_q[m] * ip_q[m]);
02764 
02765                 /* P angle computation */
02766                 if (fabs(ip_q[m]) < 0.00001) {
02767                     if (ip_u[m] > 0.0) {
02768                         ipang[m] = 45.0;
02769                     }
02770                     else {
02771                         ipang[m] = 135.0;
02772                     }
02773                 }
02774                 else {
02775                     ipang[m] = 0.5 * atan(ip_u[m] / ip_q[m]) * 180 / M_PI;
02776                     if (ip_q[m] > 0.0) {
02777                         if (ip_u[m] < 0.0) {
02778                             ipang[m] += 180.;
02779                         }
02780                     }
02781                     else {
02782                         ipang[m] += 90.;
02783                     }
02784                 }
02785 
02786                 /* Error on the angle computation */
02787                 radicand = ip_q[m] * ip_q[m] * ipuerr[m] * ipuerr[m] + 
02788                            ip_u[m] * ip_u[m] * ipqerr[m] * ipqerr[m];
02789   
02790                 ipangerr[m] = (ip_l[m] == 0.0 ? 0.0 :
02791                      sqrt(radicand) * 0.5 / (ip_l[m] * ip_l[m]) * 180 / M_PI);
02792 
02793                 /*
02794                  * This is a quick and dirty patch for FORS2 had the
02795                  * Wolly mounted +180 with respect to FORS1. I must
02796                  * hardcode it, because there is no such info in the 
02797                  * header.
02798                  */
02799 
02800                 if (instrume[4] == '2') {
02801 
02802                     double w_rotation = - wollaston * M_PI / 2;
02803 
02804                     ipang[m] -= w_rotation * 180 / M_PI;
02805 
02806                     ip_q[m] = ip_q[m] * cos(2 * w_rotation)
02807                             + ip_u[m] * sin(2 * w_rotation);
02808 
02809                     ip_u[m] = ip_u[m] * cos(2 * w_rotation)
02810                             - ip_q[m] * sin(2 * w_rotation);
02811                 }
02812 
02813                 if (chromatism) {
02814                     ipang[m] -= correct[m] * 180 / M_PI;
02815 
02816                     ip_q[m] = ip_q[m] * cos(2 * correct[m])
02817                             + ip_u[m] * sin(2 * correct[m]);
02818     
02819                     ip_u[m] = ip_u[m] * cos(2 * correct[m])
02820                             - ip_q[m] * sin(2 * correct[m]);
02821                 }
02822 
02823                 if (ipang[m] < 0.0)
02824                     ipang[m] += 180.;
02825                 else if (ipang[m] >= 180.0)
02826                     ipang[m] -= 180.;
02827             }
02828 
02829             if (nscience / 4 > 1) {
02830                 float * weights;
02831                 float   max, sum, sum2, imean;
02832 
02833                 int k;
02834 
02835                 /* QC on Q NULL */
02836                 weights = cpl_malloc(sizeof(float) * nx);
02837 
02838                 max = 0.0;
02839                 for (k = 0; k < nx; k++) {
02840                     if (max < iffq[k]) max = iffq[k];
02841                 }
02842             
02843                 for (k = 0; k < nx; k++) {
02844                     weights[k] = iffq[k] < 0.0 ? 
02845                         0.0 : iffq[k] * iffq[k] / (max * max);
02846                 }
02847             
02848                 sum  = 0.0;
02849                 sum2 = 0.0;
02850                 for (k = 0; k < nx; k++) {
02851                     sum  += weights[k] * ip_qnull[k];
02852                     sum2 += weights[k];
02853                 }
02854 
02855                 cpl_free(weights);
02856 
02857                 imean = sum / sum2;
02858 
02859                 mean_qnull += (imean - mean_qnull) / (j + 1.0);
02860                   
02861                 /* QC on U NULL */
02862                 weights = cpl_malloc(sizeof(float) * nx);
02863             
02864                 max = 0.0;
02865                 for (k = 0; k < nx; k++) {
02866                     if (max < iffu[k]) max = iffu[k];
02867                 }
02868             
02869                 for (k = 0; k < nx; k++) {
02870                     weights[k] = iffu[k] < 0.0 ? 
02871                         0.0 : iffu[k] * iffu[k] / (max * max);
02872                 }
02873             
02874                 sum  = 0.0;
02875                 sum2 = 0.0;
02876                 for (k = 0; k < nx; k++) {
02877                     sum  += weights[k] * ip_unull[k];
02878                     sum2 += weights[k];
02879                 }
02880 
02881                 cpl_free(weights);
02882 
02883                 imean = sum / sum2;
02884 
02885                 mean_unull += (imean - mean_unull) / (j + 1.0);
02886             }
02887         }
02888 
02889         cpl_image_delete(correct_im);
02890 
02891         if (dfs_save_image(frameset, pq_im, reduced_q_tag, header, 
02892                            parlist, recipe, version))
02893             fors_pmos_science_exit(NULL);
02894 
02895         if (dfs_save_image(frameset, pu_im, reduced_u_tag, header, 
02896                            parlist, recipe, version))
02897             fors_pmos_science_exit(NULL);
02898 
02899         if (qc && standard) {
02900             cpl_table *polsta = dfs_load_table(frameset, std_pmos_table_tag, 1);
02901             cpl_propertylist *qheader = dfs_load_header(frameset,
02902                                                         science_tag, 0);
02903             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
02904             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
02905             cpl_propertylist_update_double(qheader, "CRVAL1",
02906                                    startwavelength + (dispersion * group)/2);
02907             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
02908             cpl_propertylist_update_double(qheader, "CD1_1",
02909                                            dispersion * group);
02910             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
02911             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
02912             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
02913             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
02914             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
02915 
02916             if (mos_check_polarisation(pq_im, pqerr_im, pu_im, puerr_im,
02917                                        startwavelength, dispersion, 1000.,
02918                                        polsta, ra, dec, &filter,
02919                                        &polarised,
02920                                        &qc_pl, &qc_pl_err, 
02921                                        &qc_angle, &qc_angle_err)) {
02922                 cpl_msg_warning(cpl_func, "No QC can be computed");
02923             }
02924             else {
02925                 char *pipefile;
02926                 char *keyname;
02927                 char *text;
02928                 char  band[] = {' ', '\0'};
02929 
02930                 fors_qc_start_group(qheader, "2.0", instrume);
02931 
02932                 /*
02933                  * QC1 group header
02934                  */
02935 
02936                 if (fors_qc_write_string("PRO.CATG", reduced_l_tag,
02937                                          "Product category", instrume))
02938                     fors_pmos_science_exit("Cannot write product category to "
02939                                            "QC log file");
02940 
02941                 if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
02942                                            "DPR type", instrume))
02943                     fors_pmos_science_exit("Missing keyword DPR TYPE in "
02944                                            "scientific frame header");
02945 
02946                 if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
02947                                            "Template", instrume))
02948                     fors_pmos_science_exit("Missing keyword TPL ID in "
02949                                            "scientific frame header");
02950 
02951                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
02952                                            "Grism name", instrume))
02953                     fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
02954                                            "scientific frame header");
02955 
02956                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
02957                                            "Grism identifier", instrume))
02958                     fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
02959                                            "scientific frame header");
02960 
02961                 if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
02962                     fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
02963                                            "Filter name", instrume);
02964 
02965                 if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
02966                                            "Collimator name", instrume))
02967                     fors_pmos_science_exit("Missing keyword INS COLL NAME in "
02968                                            "scientific frame header");
02969 
02970                 if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
02971                                            "Chip identifier", instrume))
02972                     fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
02973                                            "scientific frame header");
02974 
02975                 if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
02976                                            "Archive name of input data",
02977                                            instrume))
02978                     fors_pmos_science_exit("Missing keyword ARCFILE in "
02979                                            "scientific frame header");
02980 
02981                 pipefile = dfs_generate_filename_tfits(reduced_nul_q_tag);
02982                 if (fors_qc_write_string("PIPEFILE", pipefile,
02983                                          "Pipeline product name", instrume))
02984                     fors_pmos_science_exit("Cannot write PIPEFILE to "
02985                                            "QC log file");
02986                 cpl_free(pipefile); pipefile = NULL;
02987 
02988 
02989                 /*
02990                  * QC1 parameters
02991                  */
02992 
02993                 keyname = "QC.PMOS.BAND";
02994 
02995                 band[0] = filter;
02996                 if (fors_qc_write_qc_string(qheader, keyname, band,
02997                                             "Band where polarisation was "
02998                                             "measured", instrume)) {
02999                     fors_pmos_science_exit("Cannot write QC.PMOS.BAND "
03000                                            "parameter to QC log file");
03001                 }
03002 
03003                 keyname = "QC.PMOS.POLARISED";
03004 
03005                 if (fors_qc_write_qc_int(qheader, polarised, keyname, NULL,
03006                                          "Polarisation is expected (1 = yes, "
03007                                          "0 = no)", instrume)) {
03008                     fors_pmos_science_exit("Cannot write QC.PMOS.POLARISED "
03009                                            "parameter to QC log file");
03010                 }
03011 
03012                 keyname = "QC.PMOS.L.OFFSET";
03013 
03014                 if (polarised)
03015                     text = "Linear polarisation relative offset";
03016                 else
03017                     text = "Linear polarisation offset";
03018 
03019                 if (fors_qc_write_qc_double(qheader, qc_pl, keyname, NULL,
03020                                             text, instrume)) {
03021                     fors_pmos_science_exit("Cannot write linear polarisation "
03022                                            "offset to QC log file");
03023                 }
03024 
03025                 keyname = "QC.PMOS.L.OFFSETERR";
03026 
03027                 if (fors_qc_write_qc_double(qheader, qc_pl_err, keyname, NULL,
03028                                        "Error on linear polarisation offset",
03029                                        instrume)) {
03030                     fors_pmos_science_exit("Cannot write linear polarisation "
03031                                        "offset error to QC log file");
03032                 }
03033 
03034                 if (polarised) {
03035                     keyname = "QC.PMOS.ANGLE.OFFSET";
03036 
03037                     if (fors_qc_write_qc_double(qheader, qc_angle, keyname, NULL,
03038                                                 "Polarisation angle offset",
03039                                                 instrume)) {
03040                         fors_pmos_science_exit("Cannot write polarisation "
03041                                                "angle offset to QC log file");
03042                     }
03043 
03044                     keyname = "QC.PMOS.ANGLE.OFFSETERR";
03045 
03046                     if (fors_qc_write_qc_double(qheader, qc_angle_err, keyname, 
03047                                                 NULL, "Error on polarisation "
03048                                                 "angle offset", instrume)) {
03049                         fors_pmos_science_exit("Cannot write polarisation "
03050                                                "angle offset error to QC "
03051                                                "log file");
03052                     }
03053                 }
03054 
03055                 fors_qc_end_group();
03056             }
03057 
03058             if (dfs_save_image(frameset, pl_im, reduced_l_tag, qheader,
03059                                parlist, recipe, version))
03060                 fors_pmos_science_exit(NULL);
03061 
03062             cpl_propertylist_delete(qheader);
03063         }
03064         else {
03065             if (dfs_save_image(frameset, pl_im, reduced_l_tag, header, 
03066                                parlist, recipe, version))
03067                 fors_pmos_science_exit(NULL);
03068         }
03069 
03070         if (nscience / 4 > 1) {
03071             char *pipefile; 
03072             char *keyname;
03073             cpl_propertylist *qheader = dfs_load_header(frameset, 
03074                                                         science_tag, 0);
03075 
03076             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
03077             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
03078             cpl_propertylist_update_double(qheader, "CRVAL1", 
03079                                    startwavelength + (dispersion * group)/2);
03080             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
03081             cpl_propertylist_update_double(qheader, "CD1_1", 
03082                                            dispersion * group);
03083             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
03084             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
03085             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
03086             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
03087             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
03088 
03089             if (qc) {
03090                 fors_qc_start_group(qheader, "2.0", instrume);
03091 
03092                 /*
03093                  * QC1 group header
03094                  */
03095 
03096                 if (fors_qc_write_string("PRO.CATG", reduced_nul_q_tag,
03097                                          "Product category", instrume))
03098                     fors_pmos_science_exit("Cannot write product category to "
03099                                            "QC log file");
03100 
03101                 if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
03102                                            "DPR type", instrume))
03103                     fors_pmos_science_exit("Missing keyword DPR TYPE in "
03104                                            "scientific frame header");
03105 
03106                 if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
03107                                            "Template", instrume))
03108                     fors_pmos_science_exit("Missing keyword TPL ID in "
03109                                            "scientific frame header");
03110 
03111                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
03112                                            "Grism name", instrume))
03113                     fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
03114                                            "scientific frame header");
03115 
03116                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
03117                                            "Grism identifier", instrume))
03118                     fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
03119                                            "scientific frame header");
03120 
03121                 if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
03122                     fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
03123                                            "Filter name", instrume);
03124 
03125                 if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
03126                                            "Collimator name", instrume))
03127                     fors_pmos_science_exit("Missing keyword INS COLL NAME in "
03128                                            "scientific frame header");
03129 
03130                 if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
03131                                            "Chip identifier", instrume))
03132                     fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
03133                                            "scientific frame header");
03134 
03135                 if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
03136                                            "Archive name of input data", 
03137                                            instrume))
03138                     fors_pmos_science_exit("Missing keyword ARCFILE in "
03139                                            "scientific frame header");
03140 
03141                 pipefile = dfs_generate_filename_tfits(reduced_nul_q_tag);
03142                 if (fors_qc_write_string("PIPEFILE", pipefile,
03143                                          "Pipeline product name", instrume))
03144                     fors_pmos_science_exit("Cannot write PIPEFILE to "
03145                                            "QC log file");
03146                 cpl_free(pipefile); pipefile = NULL;
03147 
03148 
03149                 /*
03150                  * QC1 parameters
03151                  */
03152 
03153                 keyname = "QC.NULL.Q.MEAN";
03154                     
03155                 if (fors_qc_write_qc_double(qheader, mean_qnull,
03156                                             keyname, NULL,
03157                                             "Mean Q null parameter",
03158                                             instrume)) {
03159                     fors_pmos_science_exit("Cannot write mean Q null "
03160                                            "parameter to QC log file");
03161                 }
03162 
03163                 keyname = "QC.NANGLES";
03164 
03165                 if (fors_qc_write_qc_int(qheader, nscience / 2,
03166                                          keyname, NULL,
03167                                          "Number of processed plate angles",
03168                                          instrume)) {
03169                     fors_pmos_science_exit("Cannot write number of processed "
03170                                            "plate angles.");
03171                 }
03172 
03173                 fors_qc_end_group();
03174             }
03175 
03176             if (dfs_save_image(frameset, pqnull_im, reduced_nul_q_tag, qheader, 
03177                                parlist, recipe, version))
03178                 fors_pmos_science_exit(NULL);
03179 
03180             cpl_propertylist_delete(qheader);
03181 
03182             qheader = dfs_load_header(frameset, science_tag, 0);
03183 
03184             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
03185             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
03186             cpl_propertylist_update_double(qheader, "CRVAL1", 
03187                                    startwavelength + (dispersion * group)/2);
03188             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
03189             cpl_propertylist_update_double(qheader, "CD1_1", 
03190                                            dispersion * group);
03191             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
03192             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
03193             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
03194             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
03195             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
03196 
03197             if (qc) {
03198                 fors_qc_start_group(qheader, "2.0", instrume);
03199 
03200                 /*
03201                  * QC1 group header
03202                  */
03203 
03204                 if (fors_qc_write_string("PRO.CATG", reduced_nul_u_tag,
03205                                          "Product category", instrume))
03206                     fors_pmos_science_exit("Cannot write product category to "
03207                                          "QC log file");
03208 
03209                 if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
03210                                            "DPR type", instrume))
03211                     fors_pmos_science_exit("Missing keyword DPR TYPE in "
03212                                            "scientific frame header");
03213 
03214                 if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
03215                                            "Template", instrume))
03216                     fors_pmos_science_exit("Missing keyword TPL ID in "
03217                                            "scientific frame header");
03218 
03219                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
03220                                            "Grism name", instrume))
03221                     fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
03222                                            "scientific frame header");
03223 
03224                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
03225                                            "Grism identifier", instrume))
03226                     fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
03227                                            "scientific frame header");
03228 
03229                 if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
03230                     fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
03231                                            "Filter name", instrume);
03232 
03233                 if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
03234                                            "Collimator name", instrume))
03235                     fors_pmos_science_exit("Missing keyword INS COLL NAME in "
03236                                            "scientific frame header");
03237 
03238                 if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
03239                                            "Chip identifier", instrume))
03240                     fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
03241                                            "scientific frame header");
03242 
03243                 if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
03244                                            "Archive name of input data", 
03245                                            instrume))
03246                     fors_pmos_science_exit("Missing keyword ARCFILE in "
03247                                            "scientific frame header");
03248 
03249                 pipefile = dfs_generate_filename_tfits(reduced_nul_u_tag);
03250                 if (fors_qc_write_string("PIPEFILE", pipefile,
03251                                          "Pipeline product name", instrume))
03252                     fors_pmos_science_exit("Cannot write PIPEFILE to "
03253                                            "QC log file");
03254                 cpl_free(pipefile); pipefile = NULL;
03255 
03256 
03257                 /*
03258                  * QC1 parameters
03259                  */
03260 
03261                 keyname = "QC.NULL.U.MEAN";
03262                     
03263                 if (fors_qc_write_qc_double(qheader, mean_unull,
03264                                             keyname, NULL,
03265                                             "Mean U null parameter",
03266                                             instrume)) {
03267                     fors_pmos_science_exit("Cannot write mean U null "
03268                                            "parameter to QC log file");
03269                 }
03270 
03271                 keyname = "QC.NANGLES";
03272 
03273                 if (fors_qc_write_qc_int(qheader, nscience / 2,
03274                                          keyname, NULL,
03275                                          "Number of processed plate angles",
03276                                          instrume)) {
03277                     fors_pmos_science_exit("Cannot write number of processed "
03278                                            "plate angles.");
03279                 }
03280 
03281                 fors_qc_end_group();
03282             }
03283 
03284             if (dfs_save_image(frameset, punull_im, reduced_nul_u_tag, qheader, 
03285                                parlist, recipe, version))
03286                 fors_pmos_science_exit(NULL);
03287 
03288             cpl_propertylist_delete(qheader);
03289         }
03290 
03291         if (dfs_save_image(frameset, pqerr_im, reduced_error_q_tag, header, 
03292                            parlist, recipe, version))
03293             fors_pmos_science_exit(NULL);
03294 
03295         if (dfs_save_image(frameset, puerr_im, reduced_error_u_tag, header, 
03296                            parlist, recipe, version))
03297             fors_pmos_science_exit(NULL);
03298 
03299         if (dfs_save_image(frameset, plerr_im, reduced_error_l_tag, header, 
03300                            parlist, recipe, version))
03301             fors_pmos_science_exit(NULL);
03302 
03303         if (dfs_save_image(frameset, pang_im, reduced_angle_tag, header, 
03304                            parlist, recipe, version))
03305             fors_pmos_science_exit(NULL);
03306 
03307         if (dfs_save_image(frameset, pangerr_im, reduced_error_angle_tag, 
03308                            header, parlist, recipe, version))
03309             fors_pmos_science_exit(NULL);
03310 
03311         if (dfs_save_image(frameset, pi_im, reduced_i_tag, 
03312                            header, parlist, recipe, version))
03313             fors_pmos_science_exit(NULL);
03314 
03315         cpl_image_power(pierr_im, 0.5);
03316 
03317         if (dfs_save_image(frameset, pierr_im, reduced_error_i_tag, 
03318                            header, parlist, recipe, version))
03319             fors_pmos_science_exit(NULL);
03320 
03321 /* %%% */
03322 
03323         cpl_image_delete(pq_im);
03324         cpl_image_delete(pu_im);
03325         cpl_image_delete(pl_im);
03326         cpl_image_delete(pi_im);
03327 
03328         cpl_image_delete(pqnull_im);
03329         cpl_image_delete(punull_im);
03330 
03331         cpl_image_delete(pqerr_im);
03332         cpl_image_delete(puerr_im);
03333         cpl_image_delete(plerr_im);
03334         cpl_image_delete(pierr_im);
03335         cpl_image_delete(pang_im);
03336         cpl_image_delete(pangerr_im);
03337     }
03338 
03339     cpl_propertylist_delete(header);
03340 
03341     /* End of Stokes computation */
03342 
03343     for (j = 0; j < nscience; j++) {
03344         cpl_image_delete(reduceds[j]);
03345         cpl_image_delete(rerrors[j]);
03346         cpl_table_delete(slitss[j]);
03347         cpl_image_delete(mappeds[j]);
03348     }
03349 
03350     cpl_free(reduceds);
03351     cpl_free(rerrors);
03352     cpl_free(slitss);
03353     cpl_free(mappeds);
03354 
03355     cpl_free(instrume); instrume = NULL;
03356 
03357     cpl_free(skylocalmaps);
03358 
03359     cpl_free(nobjs_per_slit);
03360 
03361     if (cpl_error_get_code()) {
03362         cpl_msg_error(cpl_error_get_where(), cpl_error_get_message());
03363         fors_pmos_science_exit(NULL);
03364     }
03365     else 
03366         return 0;
03367 }
03368 
03369 /*----------------------------------------------------------------------------*/
03380 /*----------------------------------------------------------------------------*/
03381 static float * fors_check_angles(cpl_frameset * frameset,
03382                                  int pmos, const char *tag, int * circ)
03383 {
03384     float     *angles  = NULL;
03385     cpl_frame *c_frame = NULL;
03386     char      *ret_id  = NULL;
03387 
03388     int i = 0;
03389 
03390     angles = cpl_malloc(sizeof(float) * pmos);
03391 
03392     for (c_frame  = cpl_frameset_find(frameset, tag);
03393          c_frame != NULL; c_frame = cpl_frameset_find(frameset, NULL)) {
03394 
03395         cpl_propertylist * header =
03396             cpl_propertylist_load(cpl_frame_get_filename(c_frame), 0);
03397         
03398         if (!ret_id) {
03399             ret_id = cpl_strdup(cpl_propertylist_get_string(header, 
03400                                                         "ESO INS OPTI4 ID"));
03401 
03402             if (ret_id[1] != '5' && ret_id[1] != '4') {
03403                 cpl_msg_error(cpl_func, 
03404                               "Unknown retarder plate id: %s", ret_id);
03405                 return NULL;
03406             }
03407         } else {
03408             char * c_ret_id = (char *)
03409                 cpl_propertylist_get_string(header, "ESO INS OPTI4 ID");
03410             if (ret_id[1] != c_ret_id[1]) {
03411                 cpl_msg_error(cpl_func, "Input frames are not from the same "
03412                               "retarder plate");
03413                 return NULL;
03414             }
03415         }
03416         
03417         if (ret_id[1] == '5') {  /* Linear polarimetry */
03418             if (cpl_propertylist_has(header, "ESO INS RETA2 ROT")) {
03419                 angles[i] = (float)floor(2*cpl_propertylist_get_double(header, 
03420                                                 "ESO INS RETA2 ROT") + 0.5)/2;
03421             }
03422             else if (cpl_propertylist_has(header, "ESO INS RETA2 POSANG")) {
03423                 if (cpl_propertylist_has(header, "ESO ADA POSANG")) {
03424                     double reta2pos = cpl_propertylist_get_double(header, 
03425                                                      "ESO INS RETA2 POSANG");
03426                     double adapos = cpl_propertylist_get_double(header, 
03427                                                      "ESO ADA POSANG");
03428                     angles[i] = (float)floor(2*(reta2pos - adapos) + 0.5)/2;
03429                 }
03430                 else {
03431                     cpl_msg_error(cpl_func, 
03432                                   "ESO ADA POSANG not found in header");
03433                     return NULL;
03434                 }
03435             }
03436             else {
03437                 cpl_msg_error(cpl_func, "Neither ESO INS RETA2 ROT nor "
03438                               "ESO INS RETA2 POSANG found in header");
03439                 return NULL;
03440             }
03441             *circ = 0;
03442         } else {                 /* Circular polarimetry */
03443             if (cpl_propertylist_has(header, "ESO INS RETA4 ROT")) {
03444                 angles[i] = (float)floor(2*cpl_propertylist_get_double(header, 
03445                                                 "ESO INS RETA4 ROT") + 0.5)/2;
03446             }
03447             else if (cpl_propertylist_has(header, "ESO INS RETA4 POSANG")) {
03448                 if (cpl_propertylist_has(header, "ESO ADA POSANG")) {
03449                     double reta4pos = cpl_propertylist_get_double(header, 
03450                                                      "ESO INS RETA4 POSANG");
03451                     double adapos = cpl_propertylist_get_double(header, 
03452                                                      "ESO ADA POSANG");
03453                     angles[i] = (float)floor(2*(reta4pos - adapos) + 0.5/2);
03454                 }
03455                 else {
03456                     cpl_msg_error(cpl_func, 
03457                                   "ESO ADA POSANG not found in header");
03458                     return NULL;
03459                 }
03460             }
03461             else {
03462                 cpl_msg_error(cpl_func, "Neither ESO INS RETA4 ROT nor "
03463                               "ESO INS RETA4 POSANG found in header");
03464                 return NULL;
03465             }
03466             *circ = 1;
03467         }
03468 
03469         cpl_propertylist_delete(header);
03470         i++;
03471     }
03472 
03473     cpl_free(ret_id);
03474 
03475     if (*circ) {
03476         if (pmos != 2 && pmos != 4) {
03477             cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
03478                           "found, but either 2 or 4 are required for "
03479                           "circular polarization measurements!", pmos);
03480             return NULL;
03481         }
03482     } else {
03483         if (pmos != 4 && pmos != 8 && pmos != 16) {
03484             cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
03485                           "found, but either 4, 8, or 16 are required for "
03486                           "linear polarization measurements!", pmos);
03487             return NULL;
03488         }
03489     }
03490     
03491     /* Check completeness */
03492 
03493     if (*circ) {
03494         for (i = 0; i < pmos; i++) {
03495             if (fors_find_angle_pos(angles, pmos, 90.0 * i - 45.0) < 0) {
03496                 const char *cangles;
03497                 switch (pmos) {
03498                 case 2: cangles  = "-45.0, 45.0"; break;
03499                 case 4: cangles  = "-45.0, 45.0, 135.0, 225.0"; break;
03500                 default: assert(0);
03501                 }  
03502 
03503                 cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
03504                               "angle %.2f. All angles %s must be provided.",
03505                               angles[i], cangles);
03506                 return NULL;
03507             }
03508         }
03509     }
03510     else {
03511         for (i = 0; i < pmos; i++) {
03512             if (fors_find_angle_pos(angles, pmos, 22.5 * i) < 0) {
03513                 const char *cangles;
03514                 switch (pmos) {
03515                 case 4: cangles  = "0.0, 22.5, 45.0, 67.5"; break;
03516                 case 8: cangles  = "0.0, 22.5, 45.0, 67.5, "
03517                                    "90.0, 112.5, 135.0, 157.5"; break;
03518                 case 16: cangles = "0.0, 22.5, 45.0, 67.5, "
03519                                    "90.0, 112.5, 135.0, 157.5, "
03520                                    "180.0, 202.5, 225.0, 247.5, "
03521                                    "270.0, 292.5, 315.0, 337.5"; break;
03522                 default: assert(0);
03523                 }  
03524 
03525                 cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
03526                               "angle %.2f. All angles %s must be provided.",
03527                               angles[i], cangles);
03528                 return NULL;
03529             }
03530         }
03531     }
03532 
03533     return angles;
03534 }
03535 
03536 /*----------------------------------------------------------------------------*/
03544 /*----------------------------------------------------------------------------*/
03545 static int
03546 fors_find_angle_pos(float * angles, int nangles, float angle)
03547 {
03548     int i, match = 0;
03549 
03550     for (i = 0; i < nangles; i++) {
03551         if (fabs(angles[i]         - angle) < 1.0 || 
03552             fabs(angles[i] - 360.0 - angle) < 1.0) {
03553             match = 1;
03554             break;
03555         }
03556     }
03557 
03558     return match ? i : -1;
03559 }
03560