fors_pmos_science.c

00001 /* $Id: fors_pmos_science.c,v 1.59 2010/09/14 07:38:16 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: 2010/09/14 07:38:16 $
00024  * $Revision: 1.59 $
00025  * $Name: fors-4_8_6 $
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                 fors_qc_end_group();
02386             }
02387 
02388             if (dfs_save_image(frameset, pvnull_im, reduced_nul_v_tag, qheader, 
02389                                parlist, recipe, version))
02390                 fors_pmos_science_exit(NULL);
02391 
02392             cpl_propertylist_delete(qheader);
02393         }
02394 
02395         if (dfs_save_image(frameset, perr_im, reduced_error_v_tag, header, 
02396                            parlist, recipe, version))
02397             fors_pmos_science_exit(NULL);
02398 
02399         cpl_image_power(pierr_im, 0.5);
02400 
02401         if (dfs_save_image(frameset, pierr_im, reduced_error_i_tag, header, 
02402                            parlist, recipe, version))
02403             fors_pmos_science_exit(NULL);
02404 
02405         cpl_image_delete(pv_im);
02406         cpl_image_delete(pvnull_im);
02407         cpl_image_delete(perr_im);
02408         cpl_image_delete(pi_im);
02409         cpl_image_delete(pierr_im);
02410     } 
02411     else {                            /* Linear polarisation */
02412         cpl_image *pq_im      = NULL;
02413         cpl_image *pu_im      = NULL;
02414         cpl_image *pl_im      = NULL;
02415         cpl_image *pi_im      = NULL;
02416 
02417         cpl_image *pqnull_im  = NULL;
02418         cpl_image *punull_im  = NULL;
02419 
02420         cpl_image *pqerr_im   = NULL;
02421         cpl_image *puerr_im   = NULL;
02422         cpl_image *plerr_im   = NULL;
02423         cpl_image *pierr_im   = NULL;
02424 
02425         cpl_image *pang_im    = NULL;
02426         cpl_image *pangerr_im = NULL;
02427 
02428         double    *p_q        = NULL;
02429         double    *p_u        = NULL;
02430         double    *p_l        = NULL;
02431         double    *p_i        = NULL;
02432 
02433         double    *p_qnull    = NULL;
02434         double    *p_unull    = NULL;
02435 
02436         double    *pqerr      = NULL;
02437         double    *puerr      = NULL;
02438         double    *plerr      = NULL;
02439         double    *pierr      = NULL;
02440 
02441         double    *pang       = NULL;
02442         double    *pangerr    = NULL;
02443 
02444         int        k, m;
02445 
02446         cpl_image *correct_im = cpl_image_new(nx, 1, CPL_TYPE_DOUBLE);
02447         double    *correct    = cpl_image_get_data_double(correct_im);
02448 
02449         double     mean_unull, mean_qnull;
02450 
02451         int        p          = -1;
02452         int        total      = 0;
02453             
02454         pq_im      = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02455         pu_im      = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02456         pl_im      = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02457         pi_im      = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02458 
02459         pqerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02460         puerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02461         plerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02462         pierr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02463 
02464         pang_im    = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02465         pangerr_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02466 
02467         p_q        = cpl_image_get_data_double(pq_im);
02468         p_u        = cpl_image_get_data_double(pu_im);
02469         p_l        = cpl_image_get_data_double(pl_im);
02470         p_i        = cpl_image_get_data_double(pi_im);
02471 
02472         if (nscience / 4 > 1) {
02473             pqnull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02474             punull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02475 
02476             p_qnull = cpl_image_get_data_double(pqnull_im);
02477             p_unull = cpl_image_get_data_double(punull_im);
02478         } else {
02479             cpl_msg_warning(cpl_func, 
02480                             "Not enough pairs to compute null parameters");
02481         }
02482 
02483         pqerr = cpl_image_get_data_double(pqerr_im);
02484         puerr = cpl_image_get_data_double(puerr_im);
02485         plerr = cpl_image_get_data_double(plerr_im);
02486         pierr = cpl_image_get_data_double(pierr_im);
02487 
02488         pang = cpl_image_get_data_double(pang_im);
02489         pangerr = cpl_image_get_data_double(pangerr_im);
02490 
02491         if (chromatism) {
02492             cpl_table * chrotbl = 
02493                 dfs_load_table(frameset, chrom_table_tag, 1);
02494 
02495             int      nrow   = cpl_table_get_nrow(chrotbl);
02496             float  * lambda = cpl_table_get_data_float(chrotbl, "lambda");
02497             float  * theta  = cpl_table_get_data_float(chrotbl, "eps_theta");
02498 
02499             for (j = 0; j < nx; j++) {
02500                 double c_wave = startwavelength 
02501                               + (dispersion * group) / 2 
02502                               + j * dispersion * group;
02503             
02504                 int found = 0;
02505 
02506                 for (k = 0; k < nrow - 1; k++) {
02507                     if (lambda[k] <= c_wave && c_wave < lambda[k + 1]) {
02508                         found = 1;
02509                         break;
02510                     }
02511                 }
02512 
02513                 if (found) {
02514                     correct[j] = (theta [k + 1] - theta [k]) /
02515                                  (lambda[k + 1] - lambda[k]) *
02516                                  (c_wave        - lambda[k])   + theta[k];
02517                     correct[j] *= M_PI / 180;   /* Radians */
02518                 }
02519                 else if (j)
02520                     correct[j] = correct[j-1];
02521                 else
02522                     correct[j] = 0.0;
02523             }
02524 
02525             cpl_table_delete(chrotbl);
02526         }
02527 
02528         for (j = 0; j < nobjects; j++) {
02529             double * ip_q, * ip_u, * ip_l, * ip_i, * ipierr,
02530                    * ip_qnull, * ip_unull, * ipqerr, * ipuerr, * iplerr,
02531                    * ipang, * ipangerr;
02532 
02533             float * data;
02534             float * iffq,  * ierrq, * iffu, * ierru;
02535 
02536             int pos, pos_d;
02537 
02538             ip_q = p_q + (nobjects - 1 - j) * nx;
02539             ip_u = p_u + (nobjects - 1 - j) * nx;
02540             ip_l = p_l + (nobjects - 1 - j) * nx;
02541             ip_i = p_i + (nobjects - 1 - j) * nx;
02542 
02543             if (nscience / 4 > 1) {
02544                 ip_qnull = p_qnull + (nobjects - 1 - j) * nx;
02545                 ip_unull = p_unull + (nobjects - 1 - j) * nx;
02546             }
02547 
02548             ipqerr = pqerr + (nobjects - 1 - j) * nx;
02549             ipuerr = puerr + (nobjects - 1 - j) * nx;
02550             iplerr = plerr + (nobjects - 1 - j) * nx;
02551             ipierr = pierr + (nobjects - 1 - j) * nx;
02552 
02553             ipang = pang + (nobjects - 1 - j) * nx;
02554             ipangerr = pangerr + (nobjects - 1 - j) * nx;
02555 
02556             total = 0;
02557             for (i = 0; i < nslits; i += 2) {
02558                 total += nobjs_per_slit[i];
02559                 if (total > j) {
02560                     p = i;
02561                     break;
02562                 }
02563             }
02564 
02565             for (k = 0; k < nscience / 4; k++) {
02566                 float * if_o, * if_e,  * ifdelta_o, * ifdelta_e;
02567                 float * if_o_err, * if_e_err,  * ifdelta_o_err, * ifdelta_e_err;
02568 
02569                 /* First P_Q */
02570 
02571                 pos   = fors_find_angle_pos(angles, nscience, 90 * k);
02572                 pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 45);
02573 
02574                 data = cpl_image_get_data_float(reduceds[pos]);
02575 
02576                 if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02577                                + (total - j - 1)) * nx;
02578 
02579                 if_e = data + (2 * (nobjects - total) 
02580                                + (total - j - 1)) * nx;
02581 
02582                 data = cpl_image_get_data_float(reduceds[pos_d]);
02583 
02584                 ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02585                                + (total - j - 1)) * nx;
02586 
02587                 ifdelta_e = data + (2 * (nobjects - total) 
02588                                + (total - j - 1)) * nx;
02589 
02590                 data = cpl_image_get_data_float(rerrors[pos]);
02591 
02592                 if_o_err = data + (2 * (nobjects - total) + nobjs_per_slit[p]
02593                                + (total - j - 1)) * nx;
02594 
02595                 if_e_err = data + (2 * (nobjects - total)
02596                                + (total - j - 1)) * nx;
02597 
02598                 data = cpl_image_get_data_float(rerrors[pos_d]);
02599 
02600                 ifdelta_o_err = data + (2 * (nobjects - total) 
02601                               + nobjs_per_slit[p] + (total - j - 1)) * nx;
02602 
02603                 ifdelta_e_err = data + (2 * (nobjects - total)
02604                               + (total - j - 1)) * nx;
02605 
02606                 for (m = 0; m < nx; m++) {
02607 
02608                     double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
02609                         (if_o[m]      - if_e[m]     ) /
02610                         (if_o[m]      + if_e[m]     ) -
02611                         (ifdelta_o[m] - ifdelta_e[m]) /
02612                         (ifdelta_o[m] + ifdelta_e[m]);
02613 
02614                     quantity = isfinite(quantity) ? quantity : 0.0;
02615 
02616                     /* PQ map computation */
02617                     ip_q[m] += quantity * 0.5 / (nscience / 4);
02618 
02619                     /* PQnull map computation */
02620                     if (nscience / 4 > 1) {
02621                         if (k % 2)
02622                             ip_qnull[m] += quantity * 0.5 / (nscience / 4);
02623                         else
02624                             ip_qnull[m] -= quantity * 0.5 / (nscience / 4);
02625                     }
02626 
02627                     /* I map computation */
02628                     ip_i[m] += (if_o[m] + if_e[m] +
02629                                 ifdelta_o[m] + ifdelta_e[m]) / nscience;
02630 
02631                     /* Variance map computation */
02632                     ipierr[m] += (if_o_err[m]      * if_o_err[m]
02633                                 + if_e_err[m]      * if_e_err[m]
02634                                 + ifdelta_o_err[m] * ifdelta_o_err[m]
02635                                 + ifdelta_e_err[m] * ifdelta_e_err[m]) 
02636                                / nscience / nscience;
02637                 }
02638 
02639                 /* Now P_U */
02640 
02641                 pos   = fors_find_angle_pos(angles, nscience, 90 * k + 22.5);
02642                 pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 67.5);
02643 
02644                 data = cpl_image_get_data_float(reduceds[pos]);
02645 
02646                 if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02647                                + (total - j - 1)) * nx;
02648 
02649                 if_e = data + (2 * (nobjects - total) 
02650                                + (total - j - 1)) * nx;
02651 
02652                 data = cpl_image_get_data_float(reduceds[pos_d]);
02653 
02654                 ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02655                                + (total - j - 1)) * nx;
02656 
02657                 ifdelta_e = data + (2 * (nobjects - total) 
02658                                + (total - j - 1)) * nx;
02659 
02660                 data = cpl_image_get_data_float(rerrors[pos]);
02661 
02662                 if_o_err = data + (2 * (nobjects - total) + nobjs_per_slit[p]
02663                                + (total - j - 1)) * nx;
02664 
02665                 if_e_err = data + (2 * (nobjects - total)
02666                                + (total - j - 1)) * nx;
02667 
02668                 data = cpl_image_get_data_float(rerrors[pos_d]);
02669 
02670                 ifdelta_o_err = data + (2 * (nobjects - total)
02671                               + nobjs_per_slit[p] + (total - j - 1)) * nx;
02672 
02673                 ifdelta_e_err = data + (2 * (nobjects - total)
02674                               + (total - j - 1)) * nx;
02675 
02676                 for (m = 0; m < nx; m++) {
02677 
02678                     double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
02679                         (if_o[m]      - if_e[m]     ) /
02680                         (if_o[m]      + if_e[m]     ) -
02681                         (ifdelta_o[m] - ifdelta_e[m]) /
02682                         (ifdelta_o[m] + ifdelta_e[m]);
02683 
02684                     quantity = isfinite(quantity) ? quantity : 0.0;
02685 
02686                     /* PU map computation */
02687                     ip_u[m] += quantity * 0.5 / (nscience / 4);
02688 
02689                     /* PUnull map computation */
02690                     if (nscience / 4 > 1) {
02691                         if (k % 2)
02692                             ip_unull[m] += quantity * 0.5 / (nscience / 4);
02693                         else
02694                             ip_unull[m] -= quantity * 0.5 / (nscience / 4);
02695                     }
02696 
02697                     /* I map computation */
02698                     ip_i[m] += (if_o[m] + if_e[m] +
02699                                 ifdelta_o[m] + ifdelta_e[m]) / nscience;
02700 
02701                     /* Variance map computation */
02702                     ipierr[m] += (if_o_err[m]      * if_o_err[m]
02703                                 + if_e_err[m]      * if_e_err[m]
02704                                 + ifdelta_o_err[m] * ifdelta_o_err[m]
02705                                 + ifdelta_e_err[m] * ifdelta_e_err[m]) 
02706                                / nscience / nscience;
02707                 }
02708             }
02709 
02710             /* Error map */
02711 
02712             pos   = fors_find_angle_pos(angles, nscience, 0.0);
02713 
02714             data = cpl_image_get_data_float(reduceds[pos]);
02715             iffq = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02716 
02717             data = cpl_image_get_data_float(rerrors[pos]);
02718             ierrq = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02719             
02720             pos   = fors_find_angle_pos(angles, nscience, 22.5);
02721 
02722             data = cpl_image_get_data_float(reduceds[pos]);
02723             iffu = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02724 
02725             data = cpl_image_get_data_float(rerrors[pos]);
02726             ierru = data + (2 * (nobjects - total) + (total - j - 1)) * nx;
02727 
02728             for (m = 0; m < nx; m++) {
02729 
02730                 double radicand; 
02731 
02732                 ipqerr[m] = iffq[m] <= 0.0 ? 
02733                     0.0 : ierrq[m] / iffq[m] * 0.5 / sqrt (nscience / 4);
02734 
02735                 ipuerr[m] = iffu[m] <= 0.0 ? 
02736                     0.0 : ierru[m] / iffu[m] * 0.5 / sqrt (nscience / 4);
02737 
02738                 iplerr[m] = CPL_MATH_SQRT1_2 * 0.5 * (ipqerr[m] + ipuerr[m]);
02739 
02740                 /* PL computation */
02741                 ip_l[m] = sqrt(ip_u[m] * ip_u[m] + ip_q[m] * ip_q[m]);
02742 
02743                 /* P angle computation */
02744                 if (fabs(ip_q[m]) < 0.00001) {
02745                     if (ip_u[m] > 0.0) {
02746                         ipang[m] = 45.0;
02747                     }
02748                     else {
02749                         ipang[m] = 135.0;
02750                     }
02751                 }
02752                 else {
02753                     ipang[m] = 0.5 * atan(ip_u[m] / ip_q[m]) * 180 / M_PI;
02754                     if (ip_q[m] > 0.0) {
02755                         if (ip_u[m] < 0.0) {
02756                             ipang[m] += 180.;
02757                         }
02758                     }
02759                     else {
02760                         ipang[m] += 90.;
02761                     }
02762                 }
02763 
02764                 /* Error on the angle computation */
02765                 radicand = ip_q[m] * ip_q[m] * ipuerr[m] * ipuerr[m] + 
02766                            ip_u[m] * ip_u[m] * ipqerr[m] * ipqerr[m];
02767   
02768                 ipangerr[m] = (ip_l[m] == 0.0 ? 0.0 :
02769                      sqrt(radicand) * 0.5 / (ip_l[m] * ip_l[m]) * 180 / M_PI);
02770 
02771                 /*
02772                  * This is a quick and dirty patch for FORS2 had the
02773                  * Wolly mounted +180 with respect to FORS1. I must
02774                  * hardcode it, because there is no such info in the 
02775                  * header.
02776                  */
02777 
02778                 if (instrume[4] == '2') {
02779 
02780                     double w_rotation = - wollaston * M_PI / 2;
02781 
02782                     ipang[m] -= w_rotation * 180 / M_PI;
02783 
02784                     ip_q[m] = ip_q[m] * cos(2 * w_rotation)
02785                             + ip_u[m] * sin(2 * w_rotation);
02786 
02787                     ip_u[m] = ip_u[m] * cos(2 * w_rotation)
02788                             - ip_q[m] * sin(2 * w_rotation);
02789                 }
02790 
02791                 if (chromatism) {
02792                     ipang[m] -= correct[m] * 180 / M_PI;
02793 
02794                     ip_q[m] = ip_q[m] * cos(2 * correct[m])
02795                             + ip_u[m] * sin(2 * correct[m]);
02796     
02797                     ip_u[m] = ip_u[m] * cos(2 * correct[m])
02798                             - ip_q[m] * sin(2 * correct[m]);
02799                 }
02800 
02801                 if (ipang[m] < 0.0)
02802                     ipang[m] += 180.;
02803                 else if (ipang[m] >= 180.0)
02804                     ipang[m] -= 180.;
02805             }
02806 
02807             if (nscience / 4 > 1) {
02808                 float * weights;
02809                 float   max, sum, sum2, imean;
02810 
02811                 int k;
02812 
02813                 /* QC on Q NULL */
02814                 weights = cpl_malloc(sizeof(float) * nx);
02815 
02816                 max = 0.0;
02817                 for (k = 0; k < nx; k++) {
02818                     if (max < iffq[k]) max = iffq[k];
02819                 }
02820             
02821                 for (k = 0; k < nx; k++) {
02822                     weights[k] = iffq[k] < 0.0 ? 
02823                         0.0 : iffq[k] * iffq[k] / (max * max);
02824                 }
02825             
02826                 sum  = 0.0;
02827                 sum2 = 0.0;
02828                 for (k = 0; k < nx; k++) {
02829                     sum  += weights[k] * ip_qnull[k];
02830                     sum2 += weights[k];
02831                 }
02832 
02833                 cpl_free(weights);
02834 
02835                 imean = sum / sum2;
02836 
02837                 mean_qnull += (imean - mean_qnull) / (j + 1.0);
02838                   
02839                 /* QC on U NULL */
02840                 weights = cpl_malloc(sizeof(float) * nx);
02841             
02842                 max = 0.0;
02843                 for (k = 0; k < nx; k++) {
02844                     if (max < iffu[k]) max = iffu[k];
02845                 }
02846             
02847                 for (k = 0; k < nx; k++) {
02848                     weights[k] = iffu[k] < 0.0 ? 
02849                         0.0 : iffu[k] * iffu[k] / (max * max);
02850                 }
02851             
02852                 sum  = 0.0;
02853                 sum2 = 0.0;
02854                 for (k = 0; k < nx; k++) {
02855                     sum  += weights[k] * ip_unull[k];
02856                     sum2 += weights[k];
02857                 }
02858 
02859                 cpl_free(weights);
02860 
02861                 imean = sum / sum2;
02862 
02863                 mean_unull += (imean - mean_unull) / (j + 1.0);
02864             }
02865         }
02866 
02867         cpl_image_delete(correct_im);
02868 
02869         if (dfs_save_image(frameset, pq_im, reduced_q_tag, header, 
02870                            parlist, recipe, version))
02871             fors_pmos_science_exit(NULL);
02872 
02873         if (dfs_save_image(frameset, pu_im, reduced_u_tag, header, 
02874                            parlist, recipe, version))
02875             fors_pmos_science_exit(NULL);
02876 
02877         if (qc && standard) {
02878             cpl_table *polsta = dfs_load_table(frameset, std_pmos_table_tag, 1);
02879             cpl_propertylist *qheader = dfs_load_header(frameset,
02880                                                         science_tag, 0);
02881             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
02882             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
02883             cpl_propertylist_update_double(qheader, "CRVAL1",
02884                                    startwavelength + (dispersion * group)/2);
02885             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
02886             cpl_propertylist_update_double(qheader, "CD1_1",
02887                                            dispersion * group);
02888             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
02889             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
02890             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
02891             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
02892             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
02893 
02894             if (mos_check_polarisation(pq_im, pqerr_im, pu_im, puerr_im,
02895                                        startwavelength, dispersion, 1000.,
02896                                        polsta, ra, dec, &filter,
02897                                        &polarised,
02898                                        &qc_pl, &qc_pl_err, 
02899                                        &qc_angle, &qc_angle_err)) {
02900                 cpl_msg_warning(cpl_func, "No QC can be computed");
02901             }
02902             else {
02903                 char *pipefile;
02904                 char *keyname;
02905                 char *text;
02906                 char  band[] = {' ', '\0'};
02907 
02908                 fors_qc_start_group(qheader, "2.0", instrume);
02909 
02910                 /*
02911                  * QC1 group header
02912                  */
02913 
02914                 if (fors_qc_write_string("PRO.CATG", reduced_l_tag,
02915                                          "Product category", instrume))
02916                     fors_pmos_science_exit("Cannot write product category to "
02917                                            "QC log file");
02918 
02919                 if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
02920                                            "DPR type", instrume))
02921                     fors_pmos_science_exit("Missing keyword DPR TYPE in "
02922                                            "scientific frame header");
02923 
02924                 if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
02925                                            "Template", instrume))
02926                     fors_pmos_science_exit("Missing keyword TPL ID in "
02927                                            "scientific frame header");
02928 
02929                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
02930                                            "Grism name", instrume))
02931                     fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
02932                                            "scientific frame header");
02933 
02934                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
02935                                            "Grism identifier", instrume))
02936                     fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
02937                                            "scientific frame header");
02938 
02939                 if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
02940                     fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
02941                                            "Filter name", instrume);
02942 
02943                 if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
02944                                            "Collimator name", instrume))
02945                     fors_pmos_science_exit("Missing keyword INS COLL NAME in "
02946                                            "scientific frame header");
02947 
02948                 if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
02949                                            "Chip identifier", instrume))
02950                     fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
02951                                            "scientific frame header");
02952 
02953                 if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
02954                                            "Archive name of input data",
02955                                            instrume))
02956                     fors_pmos_science_exit("Missing keyword ARCFILE in "
02957                                            "scientific frame header");
02958 
02959                 pipefile = dfs_generate_filename_tfits(reduced_nul_q_tag);
02960                 if (fors_qc_write_string("PIPEFILE", pipefile,
02961                                          "Pipeline product name", instrume))
02962                     fors_pmos_science_exit("Cannot write PIPEFILE to "
02963                                            "QC log file");
02964                 cpl_free(pipefile); pipefile = NULL;
02965 
02966 
02967                 /*
02968                  * QC1 parameters
02969                  */
02970 
02971                 keyname = "QC.PMOS.BAND";
02972 
02973                 band[0] = filter;
02974                 if (fors_qc_write_qc_string(qheader, keyname, band,
02975                                             "Band where polarisation was "
02976                                             "measured", instrume)) {
02977                     fors_pmos_science_exit("Cannot write QC.PMOS.BAND "
02978                                            "parameter to QC log file");
02979                 }
02980 
02981                 keyname = "QC.PMOS.POLARISED";
02982 
02983                 if (fors_qc_write_qc_int(qheader, polarised, keyname, NULL,
02984                                          "Polarisation is expected (1 = yes, "
02985                                          "0 = no)", instrume)) {
02986                     fors_pmos_science_exit("Cannot write QC.PMOS.POLARISED "
02987                                            "parameter to QC log file");
02988                 }
02989 
02990                 keyname = "QC.PMOS.L.OFFSET";
02991 
02992                 if (polarised)
02993                     text = "Linear polarisation relative offset";
02994                 else
02995                     text = "Linear polarisation offset";
02996 
02997                 if (fors_qc_write_qc_double(qheader, qc_pl, keyname, NULL,
02998                                             text, instrume)) {
02999                     fors_pmos_science_exit("Cannot write linear polarisation "
03000                                            "offset to QC log file");
03001                 }
03002 
03003                 keyname = "QC.PMOS.L.OFFSETERR";
03004 
03005                 if (fors_qc_write_qc_double(qheader, qc_pl_err, keyname, NULL,
03006                                        "Error on linear polarisation offset",
03007                                        instrume)) {
03008                     fors_pmos_science_exit("Cannot write linear polarisation "
03009                                        "offset error to QC log file");
03010                 }
03011 
03012                 if (polarised) {
03013                     keyname = "QC.PMOS.ANGLE.OFFSET";
03014 
03015                     if (fors_qc_write_qc_double(qheader, qc_angle, keyname, NULL,
03016                                                 "Polarisation angle offset",
03017                                                 instrume)) {
03018                         fors_pmos_science_exit("Cannot write polarisation "
03019                                                "angle offset to QC log file");
03020                     }
03021 
03022                     keyname = "QC.PMOS.ANGLE.OFFSETERR";
03023 
03024                     if (fors_qc_write_qc_double(qheader, qc_angle_err, keyname, 
03025                                                 NULL, "Error on polarisation "
03026                                                 "angle offset", instrume)) {
03027                         fors_pmos_science_exit("Cannot write polarisation "
03028                                                "angle offset error to QC "
03029                                                "log file");
03030                     }
03031                 }
03032 
03033                 fors_qc_end_group();
03034             }
03035 
03036             if (dfs_save_image(frameset, pl_im, reduced_l_tag, qheader,
03037                                parlist, recipe, version))
03038                 fors_pmos_science_exit(NULL);
03039 
03040             cpl_propertylist_delete(qheader);
03041         }
03042         else {
03043             if (dfs_save_image(frameset, pl_im, reduced_l_tag, header, 
03044                                parlist, recipe, version))
03045                 fors_pmos_science_exit(NULL);
03046         }
03047 
03048         if (nscience / 4 > 1) {
03049             char *pipefile; 
03050             char *keyname;
03051             cpl_propertylist *qheader = dfs_load_header(frameset, 
03052                                                         science_tag, 0);
03053 
03054             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
03055             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
03056             cpl_propertylist_update_double(qheader, "CRVAL1", 
03057                                    startwavelength + (dispersion * group)/2);
03058             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
03059             cpl_propertylist_update_double(qheader, "CD1_1", 
03060                                            dispersion * group);
03061             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
03062             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
03063             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
03064             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
03065             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
03066 
03067             if (qc) {
03068                 fors_qc_start_group(qheader, "2.0", instrume);
03069 
03070                 /*
03071                  * QC1 group header
03072                  */
03073 
03074                 if (fors_qc_write_string("PRO.CATG", reduced_nul_q_tag,
03075                                          "Product category", instrume))
03076                     fors_pmos_science_exit("Cannot write product category to "
03077                                            "QC log file");
03078 
03079                 if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
03080                                            "DPR type", instrume))
03081                     fors_pmos_science_exit("Missing keyword DPR TYPE in "
03082                                            "scientific frame header");
03083 
03084                 if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
03085                                            "Template", instrume))
03086                     fors_pmos_science_exit("Missing keyword TPL ID in "
03087                                            "scientific frame header");
03088 
03089                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
03090                                            "Grism name", instrume))
03091                     fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
03092                                            "scientific frame header");
03093 
03094                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
03095                                            "Grism identifier", instrume))
03096                     fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
03097                                            "scientific frame header");
03098 
03099                 if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
03100                     fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
03101                                            "Filter name", instrume);
03102 
03103                 if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
03104                                            "Collimator name", instrume))
03105                     fors_pmos_science_exit("Missing keyword INS COLL NAME in "
03106                                            "scientific frame header");
03107 
03108                 if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
03109                                            "Chip identifier", instrume))
03110                     fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
03111                                            "scientific frame header");
03112 
03113                 if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
03114                                            "Archive name of input data", 
03115                                            instrume))
03116                     fors_pmos_science_exit("Missing keyword ARCFILE in "
03117                                            "scientific frame header");
03118 
03119                 pipefile = dfs_generate_filename_tfits(reduced_nul_q_tag);
03120                 if (fors_qc_write_string("PIPEFILE", pipefile,
03121                                          "Pipeline product name", instrume))
03122                     fors_pmos_science_exit("Cannot write PIPEFILE to "
03123                                            "QC log file");
03124                 cpl_free(pipefile); pipefile = NULL;
03125 
03126 
03127                 /*
03128                  * QC1 parameters
03129                  */
03130 
03131                 keyname = "QC.NULL.Q.MEAN";
03132                     
03133                 if (fors_qc_write_qc_double(qheader, mean_qnull,
03134                                             keyname, NULL,
03135                                             "Mean Q null parameter",
03136                                             instrume)) {
03137                     fors_pmos_science_exit("Cannot write mean Q null "
03138                                            "parameter to QC log file");
03139                 }
03140 
03141                 fors_qc_end_group();
03142             }
03143 
03144             if (dfs_save_image(frameset, pqnull_im, reduced_nul_q_tag, qheader, 
03145                                parlist, recipe, version))
03146                 fors_pmos_science_exit(NULL);
03147 
03148             cpl_propertylist_delete(qheader);
03149 
03150             qheader = dfs_load_header(frameset, science_tag, 0);
03151 
03152             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
03153             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
03154             cpl_propertylist_update_double(qheader, "CRVAL1", 
03155                                    startwavelength + (dispersion * group)/2);
03156             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
03157             cpl_propertylist_update_double(qheader, "CD1_1", 
03158                                            dispersion * group);
03159             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
03160             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
03161             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
03162             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
03163             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
03164 
03165             if (qc) {
03166                 fors_qc_start_group(qheader, "2.0", instrume);
03167 
03168                 /*
03169                  * QC1 group header
03170                  */
03171 
03172                 if (fors_qc_write_string("PRO.CATG", reduced_nul_u_tag,
03173                                          "Product category", instrume))
03174                     fors_pmos_science_exit("Cannot write product category to "
03175                                          "QC log file");
03176 
03177                 if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
03178                                            "DPR type", instrume))
03179                     fors_pmos_science_exit("Missing keyword DPR TYPE in "
03180                                            "scientific frame header");
03181 
03182                 if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
03183                                            "Template", instrume))
03184                     fors_pmos_science_exit("Missing keyword TPL ID in "
03185                                            "scientific frame header");
03186 
03187                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
03188                                            "Grism name", instrume))
03189                     fors_pmos_science_exit("Missing keyword INS GRIS1 NAME in "
03190                                            "scientific frame header");
03191 
03192                 if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
03193                                            "Grism identifier", instrume))
03194                     fors_pmos_science_exit("Missing keyword INS GRIS1 ID in "
03195                                            "scientific frame header");
03196 
03197                 if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
03198                     fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
03199                                            "Filter name", instrume);
03200 
03201                 if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
03202                                            "Collimator name", instrume))
03203                     fors_pmos_science_exit("Missing keyword INS COLL NAME in "
03204                                            "scientific frame header");
03205 
03206                 if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
03207                                            "Chip identifier", instrume))
03208                     fors_pmos_science_exit("Missing keyword DET CHIP1 ID in "
03209                                            "scientific frame header");
03210 
03211                 if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
03212                                            "Archive name of input data", 
03213                                            instrume))
03214                     fors_pmos_science_exit("Missing keyword ARCFILE in "
03215                                            "scientific frame header");
03216 
03217                 pipefile = dfs_generate_filename_tfits(reduced_nul_u_tag);
03218                 if (fors_qc_write_string("PIPEFILE", pipefile,
03219                                          "Pipeline product name", instrume))
03220                     fors_pmos_science_exit("Cannot write PIPEFILE to "
03221                                            "QC log file");
03222                 cpl_free(pipefile); pipefile = NULL;
03223 
03224 
03225                 /*
03226                  * QC1 parameters
03227                  */
03228 
03229                 keyname = "QC.NULL.U.MEAN";
03230                     
03231                 if (fors_qc_write_qc_double(qheader, mean_unull,
03232                                             keyname, NULL,
03233                                             "Mean U null parameter",
03234                                             instrume)) {
03235                     fors_pmos_science_exit("Cannot write mean U null "
03236                                            "parameter to QC log file");
03237                 }
03238 
03239                 fors_qc_end_group();
03240             }
03241 
03242             if (dfs_save_image(frameset, punull_im, reduced_nul_u_tag, qheader, 
03243                                parlist, recipe, version))
03244                 fors_pmos_science_exit(NULL);
03245 
03246             cpl_propertylist_delete(qheader);
03247         }
03248 
03249         if (dfs_save_image(frameset, pqerr_im, reduced_error_q_tag, header, 
03250                            parlist, recipe, version))
03251             fors_pmos_science_exit(NULL);
03252 
03253         if (dfs_save_image(frameset, puerr_im, reduced_error_u_tag, header, 
03254                            parlist, recipe, version))
03255             fors_pmos_science_exit(NULL);
03256 
03257         if (dfs_save_image(frameset, plerr_im, reduced_error_l_tag, header, 
03258                            parlist, recipe, version))
03259             fors_pmos_science_exit(NULL);
03260 
03261         if (dfs_save_image(frameset, pang_im, reduced_angle_tag, header, 
03262                            parlist, recipe, version))
03263             fors_pmos_science_exit(NULL);
03264 
03265         if (dfs_save_image(frameset, pangerr_im, reduced_error_angle_tag, 
03266                            header, parlist, recipe, version))
03267             fors_pmos_science_exit(NULL);
03268 
03269         if (dfs_save_image(frameset, pi_im, reduced_i_tag, 
03270                            header, parlist, recipe, version))
03271             fors_pmos_science_exit(NULL);
03272 
03273         cpl_image_power(pierr_im, 0.5);
03274 
03275         if (dfs_save_image(frameset, pierr_im, reduced_error_i_tag, 
03276                            header, parlist, recipe, version))
03277             fors_pmos_science_exit(NULL);
03278 
03279 /* %%% */
03280 
03281         cpl_image_delete(pq_im);
03282         cpl_image_delete(pu_im);
03283         cpl_image_delete(pl_im);
03284         cpl_image_delete(pi_im);
03285 
03286         cpl_image_delete(pqnull_im);
03287         cpl_image_delete(punull_im);
03288 
03289         cpl_image_delete(pqerr_im);
03290         cpl_image_delete(puerr_im);
03291         cpl_image_delete(plerr_im);
03292         cpl_image_delete(pierr_im);
03293         cpl_image_delete(pang_im);
03294         cpl_image_delete(pangerr_im);
03295     }
03296 
03297     cpl_propertylist_delete(header);
03298 
03299     /* End of Stokes computation */
03300 
03301     for (j = 0; j < nscience; j++) {
03302         cpl_image_delete(reduceds[j]);
03303         cpl_image_delete(rerrors[j]);
03304         cpl_table_delete(slitss[j]);
03305         cpl_image_delete(mappeds[j]);
03306     }
03307 
03308     cpl_free(reduceds);
03309     cpl_free(rerrors);
03310     cpl_free(slitss);
03311     cpl_free(mappeds);
03312 
03313     cpl_free(instrume); instrume = NULL;
03314 
03315     cpl_free(skylocalmaps);
03316 
03317     cpl_free(nobjs_per_slit);
03318 
03319     if (cpl_error_get_code()) {
03320         cpl_msg_error(cpl_error_get_where(), cpl_error_get_message());
03321         fors_pmos_science_exit(NULL);
03322     }
03323     else 
03324         return 0;
03325 }
03326 
03327 /*----------------------------------------------------------------------------*/
03338 /*----------------------------------------------------------------------------*/
03339 static float * fors_check_angles(cpl_frameset * frameset,
03340                                  int pmos, const char *tag, int * circ)
03341 {
03342     float     *angles  = NULL;
03343     cpl_frame *c_frame = NULL;
03344     char      *ret_id  = NULL;
03345 
03346     int i = 0;
03347 
03348     angles = cpl_malloc(sizeof(float) * pmos);
03349 
03350     for (c_frame  = cpl_frameset_find(frameset, tag);
03351          c_frame != NULL; c_frame = cpl_frameset_find(frameset, NULL)) {
03352 
03353         cpl_propertylist * header =
03354             cpl_propertylist_load(cpl_frame_get_filename(c_frame), 0);
03355         
03356         if (!ret_id) {
03357             ret_id = cpl_strdup(cpl_propertylist_get_string(header, 
03358                                                         "ESO INS OPTI4 ID"));
03359 
03360             if (ret_id[1] != '5' && ret_id[1] != '4') {
03361                 cpl_msg_error(cpl_func, 
03362                               "Unknown retarder plate id: %s", ret_id);
03363                 return NULL;
03364             }
03365         } else {
03366             char * c_ret_id = (char *)
03367                 cpl_propertylist_get_string(header, "ESO INS OPTI4 ID");
03368             if (ret_id[1] != c_ret_id[1]) {
03369                 cpl_msg_error(cpl_func, "Input frames are not from the same "
03370                               "retarder plate");
03371                 return NULL;
03372             }
03373         }
03374         
03375         if (ret_id[1] == '5') {  /* Linear polarimetry */
03376             if (cpl_propertylist_has(header, "ESO INS RETA2 ROT")) {
03377                 angles[i] = (float)floor(2*cpl_propertylist_get_double(header, 
03378                                                 "ESO INS RETA2 ROT") + 0.5)/2;
03379             }
03380             else if (cpl_propertylist_has(header, "ESO INS RETA2 POSANG")) {
03381                 if (cpl_propertylist_has(header, "ESO ADA POSANG")) {
03382                     double reta2pos = cpl_propertylist_get_double(header, 
03383                                                      "ESO INS RETA2 POSANG");
03384                     double adapos = cpl_propertylist_get_double(header, 
03385                                                      "ESO ADA POSANG");
03386                     angles[i] = (float)floor(2*(reta2pos - adapos) + 0.5)/2;
03387                 }
03388                 else {
03389                     cpl_msg_error(cpl_func, 
03390                                   "ESO ADA POSANG not found in header");
03391                     return NULL;
03392                 }
03393             }
03394             else {
03395                 cpl_msg_error(cpl_func, "Neither ESO INS RETA2 ROT nor "
03396                               "ESO INS RETA2 POSANG found in header");
03397                 return NULL;
03398             }
03399             *circ = 0;
03400         } else {                 /* Circular polarimetry */
03401             if (cpl_propertylist_has(header, "ESO INS RETA4 ROT")) {
03402                 angles[i] = (float)floor(2*cpl_propertylist_get_double(header, 
03403                                                 "ESO INS RETA4 ROT") + 0.5)/2;
03404             }
03405             else if (cpl_propertylist_has(header, "ESO INS RETA4 POSANG")) {
03406                 if (cpl_propertylist_has(header, "ESO ADA POSANG")) {
03407                     double reta4pos = cpl_propertylist_get_double(header, 
03408                                                      "ESO INS RETA4 POSANG");
03409                     double adapos = cpl_propertylist_get_double(header, 
03410                                                      "ESO ADA POSANG");
03411                     angles[i] = (float)floor(2*(reta4pos - adapos) + 0.5/2);
03412                 }
03413                 else {
03414                     cpl_msg_error(cpl_func, 
03415                                   "ESO ADA POSANG not found in header");
03416                     return NULL;
03417                 }
03418             }
03419             else {
03420                 cpl_msg_error(cpl_func, "Neither ESO INS RETA4 ROT nor "
03421                               "ESO INS RETA4 POSANG found in header");
03422                 return NULL;
03423             }
03424             *circ = 1;
03425         }
03426 
03427         cpl_propertylist_delete(header);
03428         i++;
03429     }
03430 
03431     cpl_free(ret_id);
03432 
03433     if (*circ) {
03434         if (pmos != 2 && pmos != 4) {
03435             cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
03436                           "found, but either 2 or 4 are required for "
03437                           "circular polarization measurements!", pmos);
03438             return NULL;
03439         }
03440     } else {
03441         if (pmos != 4 && pmos != 8 && pmos != 16) {
03442             cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
03443                           "found, but either 4, 8, or 16 are required for "
03444                           "linear polarization measurements!", pmos);
03445             return NULL;
03446         }
03447     }
03448     
03449     /* Check completeness */
03450 
03451     if (*circ) {
03452         for (i = 0; i < pmos; i++) {
03453             if (fors_find_angle_pos(angles, pmos, 90.0 * i - 45.0) < 0) {
03454                 const char *cangles;
03455                 switch (pmos) {
03456                 case 2: cangles  = "-45.0, 45.0"; break;
03457                 case 4: cangles  = "-45.0, 45.0, 135.0, 225.0"; break;
03458                 default: assert(0);
03459                 }  
03460 
03461                 cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
03462                               "angle %.2f. All angles %s must be provided.",
03463                               angles[i], cangles);
03464                 return NULL;
03465             }
03466         }
03467     }
03468     else {
03469         for (i = 0; i < pmos; i++) {
03470             if (fors_find_angle_pos(angles, pmos, 22.5 * i) < 0) {
03471                 const char *cangles;
03472                 switch (pmos) {
03473                 case 4: cangles  = "0.0, 22.5, 45.0, 67.5"; break;
03474                 case 8: cangles  = "0.0, 22.5, 45.0, 67.5, "
03475                                    "90.0, 112.5, 135.0, 157.5"; break;
03476                 case 16: cangles = "0.0, 22.5, 45.0, 67.5, "
03477                                    "90.0, 112.5, 135.0, 157.5, "
03478                                    "180.0, 202.5, 225.0, 247.5, "
03479                                    "270.0, 292.5, 315.0, 337.5"; break;
03480                 default: assert(0);
03481                 }  
03482 
03483                 cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
03484                               "angle %.2f. All angles %s must be provided.",
03485                               angles[i], cangles);
03486                 return NULL;
03487             }
03488         }
03489     }
03490 
03491     return angles;
03492 }
03493 
03494 /*----------------------------------------------------------------------------*/
03502 /*----------------------------------------------------------------------------*/
03503 static int
03504 fors_find_angle_pos(float * angles, int nangles, float angle)
03505 {
03506     int i, match = 0;
03507 
03508     for (i = 0; i < nangles; i++) {
03509         if (fabs(angles[i]         - angle) < 1.0 || 
03510             fabs(angles[i] - 360.0 - angle) < 1.0) {
03511             match = 1;
03512             break;
03513         }
03514     }
03515 
03516     return match ? i : -1;
03517 }
03518 

Generated on Fri Mar 4 09:46:00 2011 for FORS Pipeline Reference Manual by  doxygen 1.4.7