fors_pmos_science.c

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

Generated on 12 Feb 2016 for FORS Pipeline Reference Manual by  doxygen 1.6.1