FORS Pipeline Reference Manual 4.9.20
fors_pmos_extract.c
00001 /* $Id: fors_pmos_extract.c,v 1.7 2013/02/28 15:15:25 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/02/28 15:15:25 $
00024  * $Revision: 1.7 $
00025  * $Name: fors-4_9_20 $
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_extract_create(cpl_plugin *);
00044 static int fors_pmos_extract_exec(cpl_plugin *);
00045 static int fors_pmos_extract_destroy(cpl_plugin *);
00046 static int fors_pmos_extract(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_extract_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 "\n"
00084 "  MASTER_NORM_FLAT_PMOS         Calib       Normalised flat field   .\n"
00085 "  DISP_COEFF_PMOS               Calib       Inverse dispersion      Y\n"
00086 "  CURV_COEFF_PMOS               Calib       Spectral curvature      Y\n"
00087 "  SLIT_LOCATION_PMOS            Calib       Slits positions table   Y\n"
00088 "  RETARDER_WAVEPLATE_CHROMATISM Calib       Chromatism correction   .\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_SCI_X_PMOS           FITS image  X Stokes parameter (and L)\n"
00096 "  REDUCED_ERROR_X_PMOS         FITS image  Error on X Stokes parameter\n"
00097 "  REDUCED_NUL_X_PMOS           FITS image  Null parameter for X\n"
00098 "  REDUCED_POL_ANGLE_PMOS       FITS image  Direction of linear polarization\n"
00099 "  REDUCED_POL_ANGLE_ERROR_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 "  GLOBAL_SKY_SPECTRUM_PMOS     FITS table  Global sky spectrum\n"
00106 "  OBJECT_TABLE_SCI_PMOS        FITS table  Positions of detected objects\n"
00107 "  OBJECT_TABLE_POL_SCI_PMOS    FITS table  Positions of real objects\n"
00108 "\n"
00109 "  Only if the sky-alignment of the wavelength solution is requested:\n"
00110 "  DISP_COEFF_SCI_PMOS          FITS table  Upgraded dispersion coefficients\n"
00111 "  WAVELENGTH_MAP_SCI_PMOS      FITS image  Upgraded wavelength map\n\n";
00112 
00113 #define fors_pmos_extract_exit(message)            \
00114 {                                             \
00115 if (message) cpl_msg_error(recipe, message);  \
00116 cpl_free(instrume);                           \
00117 cpl_image_delete(dummy);                      \
00118 cpl_image_delete(mapped_sky);                 \
00119 cpl_image_delete(mapped_cleaned);             \
00120 cpl_image_delete(skymap);                     \
00121 cpl_image_delete(smapped);                    \
00122 cpl_table_delete(offsets);                    \
00123 cpl_table_delete(sky);                        \
00124 cpl_image_delete(bias);                       \
00125 cpl_image_delete(spectra);                    \
00126 cpl_image_delete(coordinate);                 \
00127 cpl_image_delete(norm_flat);                  \
00128 cpl_image_delete(rainbow);                    \
00129 cpl_image_delete(rectified);                  \
00130 cpl_image_delete(wavemap);                    \
00131 cpl_propertylist_delete(header);              \
00132 cpl_propertylist_delete(save_header);         \
00133 cpl_table_delete(grism_table);                \
00134 cpl_table_delete(idscoeff);                   \
00135 cpl_table_delete(maskslits);                  \
00136 cpl_table_delete(overscans);                  \
00137 cpl_table_delete(polytraces);                 \
00138 cpl_table_delete(wavelengths);                \
00139 cpl_vector_delete(lines);                     \
00140 cpl_msg_indent_less();                        \
00141 return -1;                                    \
00142 }
00143 
00144 
00145 #define fors_pmos_extract_exit_memcheck(message)   \
00146 {                                             \
00147 if (message) cpl_msg_info(recipe, message);   \
00148 cpl_free(instrume);                           \
00149 cpl_image_delete(dummy);                      \
00150 cpl_image_delete(mapped_cleaned);             \
00151 cpl_image_delete(mapped_sky);                 \
00152 cpl_image_delete(skymap);                     \
00153 cpl_image_delete(smapped);                    \
00154 cpl_table_delete(offsets);                    \
00155 cpl_table_delete(sky);                        \
00156 cpl_image_delete(bias);                       \
00157 cpl_image_delete(spectra);                    \
00158 cpl_image_delete(coordinate);                 \
00159 cpl_image_delete(norm_flat);                  \
00160 cpl_image_delete(rainbow);                    \
00161 cpl_image_delete(rectified);                  \
00162 cpl_image_delete(wavemap);                    \
00163 cpl_propertylist_delete(header);              \
00164 cpl_propertylist_delete(save_header);         \
00165 cpl_table_delete(grism_table);                \
00166 cpl_table_delete(idscoeff);                   \
00167 cpl_table_delete(maskslits);                  \
00168 cpl_table_delete(overscans);                  \
00169 cpl_table_delete(polytraces);                 \
00170 cpl_table_delete(wavelengths);                \
00171 cpl_vector_delete(lines);                     \
00172 cpl_msg_indent_less();                        \
00173 return 0;                                     \
00174 }
00175 
00176 
00188 int cpl_plugin_get_info(cpl_pluginlist *list)
00189 {
00190     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
00191     cpl_plugin *plugin = &recipe->interface;
00192 
00193     cpl_plugin_init(plugin,
00194                     CPL_PLUGIN_API,
00195                     FORS_BINARY_VERSION,
00196                     CPL_PLUGIN_TYPE_RECIPE,
00197                     "fors_pmos_extract",
00198                     "Extraction of scientific spectra",
00199                     fors_pmos_extract_description,
00200                     "Carlo Izzo",
00201                     PACKAGE_BUGREPORT,
00202     "This file is currently part of the FORS Instrument Pipeline\n"
00203     "Copyright (C) 2002-2010 European Southern Observatory\n\n"
00204     "This program is free software; you can redistribute it and/or modify\n"
00205     "it under the terms of the GNU General Public License as published by\n"
00206     "the Free Software Foundation; either version 2 of the License, or\n"
00207     "(at your option) any later version.\n\n"
00208     "This program is distributed in the hope that it will be useful,\n"
00209     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00210     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00211     "GNU General Public License for more details.\n\n"
00212     "You should have received a copy of the GNU General Public License\n"
00213     "along with this program; if not, write to the Free Software Foundation,\n"
00214     "Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n",
00215                     fors_pmos_extract_create,
00216                     fors_pmos_extract_exec,
00217                     fors_pmos_extract_destroy);
00218 
00219     cpl_pluginlist_append(list, plugin);
00220     
00221     return 0;
00222 }
00223 
00224 
00235 static int fors_pmos_extract_create(cpl_plugin *plugin)
00236 {
00237     cpl_recipe    *recipe;
00238     cpl_parameter *p;
00239 
00240 
00241     /* 
00242      * Check that the plugin is part of a valid recipe 
00243      */
00244 
00245     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00246         recipe = (cpl_recipe *)plugin;
00247     else 
00248         return -1;
00249 
00250     /* 
00251      * Create the parameters list in the cpl_recipe object 
00252      */
00253 
00254     recipe->parameters = cpl_parameterlist_new(); 
00255 
00256 
00257     /*
00258      * Dispersion
00259      */
00260 
00261     p = cpl_parameter_new_value("fors.fors_pmos_extract.dispersion",
00262                                 CPL_TYPE_DOUBLE,
00263                                 "Resampling step (Angstrom/pixel)",
00264                                 "fors.fors_pmos_extract",
00265                                 0.0);
00266     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
00267     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00268     cpl_parameterlist_append(recipe->parameters, p);
00269 
00270     /*
00271      * Sky lines alignment
00272      */
00273 
00274     p = cpl_parameter_new_value("fors.fors_pmos_extract.skyalign",
00275                                 CPL_TYPE_INT,
00276                                 "Polynomial order for sky lines alignment, "
00277                                 "or -1 to avoid alignment",
00278                                 "fors.fors_pmos_extract",
00279                                 0);
00280     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skyalign");
00281     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00282     cpl_parameterlist_append(recipe->parameters, p);
00283 
00284     /*
00285      * Line catalog table column containing the sky reference wavelengths
00286      */
00287 
00288     p = cpl_parameter_new_value("fors.fors_pmos_extract.wcolumn",
00289                                 CPL_TYPE_STRING,
00290                                 "Name of sky line catalog table column "
00291                                 "with wavelengths",
00292                                 "fors.fors_pmos_extract",
00293                                 "WLEN");
00294     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
00295     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00296     cpl_parameterlist_append(recipe->parameters, p);
00297 
00298     /*
00299      * Start wavelength for spectral extraction
00300      */
00301 
00302     p = cpl_parameter_new_value("fors.fors_pmos_extract.startwavelength",
00303                                 CPL_TYPE_DOUBLE,
00304                                 "Start wavelength in spectral extraction",
00305                                 "fors.fors_pmos_extract",
00306                                 0.0);
00307     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
00308     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00309     cpl_parameterlist_append(recipe->parameters, p);
00310 
00311     /*
00312      * End wavelength for spectral extraction
00313      */
00314 
00315     p = cpl_parameter_new_value("fors.fors_pmos_extract.endwavelength",
00316                                 CPL_TYPE_DOUBLE,
00317                                 "End wavelength in spectral extraction",
00318                                 "fors.fors_pmos_extract",
00319                                 0.0);
00320     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
00321     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00322     cpl_parameterlist_append(recipe->parameters, p);
00323 
00324     /*
00325      * Flux conservation
00326      */
00327 
00328     p = cpl_parameter_new_value("fors.fors_pmos_extract.flux",
00329                                 CPL_TYPE_BOOL,
00330                                 "Apply flux conservation",
00331                                 "fors.fors_pmos_extract",
00332                                 TRUE);
00333     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
00334     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00335     cpl_parameterlist_append(recipe->parameters, p);
00336 
00337     /*
00338      * Apply flat field
00339      */
00340 
00341     p = cpl_parameter_new_value("fors.fors_pmos_extract.flatfield",
00342                                 CPL_TYPE_BOOL,
00343                                 "Apply flat field",
00344                                 "fors.fors_pmos_extract",
00345                                 FALSE);
00346     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flatfield");
00347     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00348     cpl_parameterlist_append(recipe->parameters, p);
00349 
00350     /*
00351      * Global sky subtraction
00352      */
00353 
00354     p = cpl_parameter_new_value("fors.fors_pmos_extract.skyglobal",
00355                                 CPL_TYPE_BOOL,
00356                                 "Subtract global sky spectrum from CCD",
00357                                 "fors.fors_pmos_extract",
00358                                 FALSE);
00359     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skyglobal");
00360     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00361     cpl_parameterlist_append(recipe->parameters, p);
00362 
00363     p = cpl_parameter_new_value("fors.fors_pmos_extract.skymedian",
00364                                 CPL_TYPE_BOOL,
00365                                 "Sky subtraction from extracted slit spectra",
00366                                 "fors.fors_pmos_extract",
00367                                 FALSE);
00368     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymedian");
00369     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00370     cpl_parameterlist_append(recipe->parameters, p);
00371 
00372     /*
00373      * Local sky subtraction on CCD spectra
00374      */
00375 
00376     p = cpl_parameter_new_value("fors.fors_pmos_extract.skylocal",
00377                                 CPL_TYPE_BOOL,
00378                                 "Sky subtraction from CCD slit spectra",
00379                                 "fors.fors_pmos_extract",
00380                                 TRUE);
00381     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skylocal");
00382     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00383     cpl_parameterlist_append(recipe->parameters, p);
00384 
00385     /*
00386      * Cosmic rays removal
00387      */
00388 
00389     p = cpl_parameter_new_value("fors.fors_pmos_extract.cosmics",
00390                                 CPL_TYPE_BOOL,
00391                                 "Eliminate cosmic rays hits (only if global "
00392                                 "sky subtraction is also requested)",
00393                                 "fors.fors_pmos_extract",
00394                                 FALSE);
00395     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cosmics");
00396     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00397     cpl_parameterlist_append(recipe->parameters, p);
00398 
00399     /*
00400      * Slit margin
00401      */
00402 
00403     p = cpl_parameter_new_value("fors.fors_pmos_extract.slit_margin",
00404                                 CPL_TYPE_INT,
00405                                 "Number of pixels to exclude at each slit "
00406                                 "in object detection and extraction",
00407                                 "fors.fors_pmos_extract",
00408                                 3);
00409     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "slit_margin");
00410     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00411     cpl_parameterlist_append(recipe->parameters, p);
00412 
00413     /*
00414      * Extraction radius
00415      */
00416 
00417     p = cpl_parameter_new_value("fors.fors_pmos_extract.ext_radius",
00418                                 CPL_TYPE_INT,
00419                                 "Maximum extraction radius for detected "
00420                                 "objects (pixel)",
00421                                 "fors.fors_pmos_extract",
00422                                 6);
00423     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_radius");
00424     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00425     cpl_parameterlist_append(recipe->parameters, p);
00426 
00427     /*
00428      * Contamination radius
00429      */
00430 
00431     p = cpl_parameter_new_value("fors.fors_pmos_extract.cont_radius",
00432                                 CPL_TYPE_INT,
00433                                 "Minimum distance at which two objects "
00434                                 "of equal luminosity do not contaminate "
00435                                 "each other (pixel)",
00436                                 "fors.fors_pmos_extract",
00437                                 0);
00438     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cont_radius");
00439     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00440     cpl_parameterlist_append(recipe->parameters, p);
00441 
00442     /*
00443      * Object extraction method
00444      */
00445 
00446     p = cpl_parameter_new_value("fors.fors_pmos_extract.ext_mode",
00447                                 CPL_TYPE_INT,
00448                                 "Object extraction method: 0 = aperture, "
00449                                 "1 = Horne optimal extraction",
00450                                 "fors.fors_pmos_extract",
00451                                 1);
00452     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_mode");
00453     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00454     cpl_parameterlist_append(recipe->parameters, p);
00455 
00456     /*
00457      * Normalise output by exposure time
00458      */
00459 
00460     p = cpl_parameter_new_value("fors.fors_pmos_extract.time_normalise",
00461                                 CPL_TYPE_BOOL,
00462                                 "Normalise output spectra by the exposure time",
00463                                 "fors.fors_pmos_extract",
00464                                 TRUE);
00465     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "time_normalise");
00466     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00467     cpl_parameterlist_append(recipe->parameters, p);
00468 
00469     /*
00470      * Apply chromatism correction to polarization angle
00471      */
00472 
00473     p = cpl_parameter_new_value("fors.fors_pmos_extract.chromatism",
00474                                 CPL_TYPE_BOOL,
00475                                 "Chromatism correction to polarization angles",
00476                                 "fors.fors_pmos_extract",
00477                                 TRUE);
00478     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "chromatism");
00479     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00480     cpl_parameterlist_append(recipe->parameters, p);
00481 
00482     /*
00483      * Create check products
00484      */
00485 
00486     p = cpl_parameter_new_value("fors.fors_pmos_extract.check",
00487                                 CPL_TYPE_BOOL,
00488                                 "Create intermediate products",
00489                                 "fors.fors_pmos_extract",
00490                                 FALSE);
00491     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "check");
00492     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00493     cpl_parameterlist_append(recipe->parameters, p);
00494 
00495     return 0;
00496 }
00497 
00498 
00507 static int fors_pmos_extract_exec(cpl_plugin *plugin)
00508 {
00509     cpl_recipe *recipe;
00510     
00511     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00512         recipe = (cpl_recipe *)plugin;
00513     else 
00514         return -1;
00515 
00516     return fors_pmos_extract(recipe->parameters, recipe->frames);
00517 }
00518 
00519 
00528 static int fors_pmos_extract_destroy(cpl_plugin *plugin)
00529 {
00530     cpl_recipe *recipe;
00531     
00532     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00533         recipe = (cpl_recipe *)plugin;
00534     else 
00535         return -1;
00536 
00537     cpl_parameterlist_delete(recipe->parameters); 
00538 
00539     return 0;
00540 }
00541 
00542 
00552 static int fors_pmos_extract(cpl_parameterlist *parlist, cpl_frameset *frameset)
00553 {
00554 
00555     const char *recipe = "fors_pmos_extract";
00556 
00557 
00558     /*
00559      * Input parameters
00560      */
00561 
00562     double      dispersion;
00563     int         skyalign;
00564     const char *wcolumn;
00565     double      startwavelength;
00566     double      endwavelength;
00567     int         flux;
00568     int         flatfield;
00569     int         skyglobal;
00570     int         skylocal;
00571     int         skymedian;
00572     int         chromatism;
00573     int         cosmics;
00574     int         slit_margin;
00575     int         ext_radius;
00576     int         cont_radius;
00577     int         ext_mode;
00578     int         time_normalise;
00579     int         check;
00580 
00581     /*
00582      * CPL objects
00583      */
00584 
00585     cpl_image       **images;
00586 
00587     cpl_image       **reduceds       = NULL;
00588     cpl_image       **rerrors        = NULL;
00589     cpl_table       **slitss         = NULL;
00590     cpl_image       **mappeds        = NULL;
00591     cpl_image       **skylocalmaps   = NULL;
00592     
00593     int nobjects = 0;
00594 
00595     cpl_image        *bias           = NULL;
00596     cpl_image        *norm_flat      = NULL;
00597     cpl_image        *spectra        = NULL;
00598     cpl_image        *rectified      = NULL;
00599     cpl_image        *coordinate     = NULL;
00600     cpl_image        *rainbow        = NULL;
00601     cpl_image        *mapped         = NULL;
00602     cpl_image        *mapped_sky     = NULL;
00603     cpl_image        *mapped_cleaned = NULL;
00604     cpl_image        *smapped        = NULL;
00605     cpl_image        *wavemap        = NULL;
00606     cpl_image        *skymap         = NULL;
00607     cpl_image        *skylocalmap    = NULL;
00608     cpl_image        *dummy          = NULL;
00609 
00610     cpl_table        *grism_table    = NULL;
00611     cpl_table        *overscans      = NULL;
00612     cpl_table        *wavelengths    = NULL;
00613     cpl_table        *idscoeff       = NULL;
00614     cpl_table        *slits          = NULL;
00615     cpl_table        *origslits     = NULL;
00616     cpl_table        *maskslits      = NULL;
00617     cpl_table        *polytraces     = NULL;
00618     cpl_table        *offsets        = NULL;
00619     cpl_table        *sky            = NULL;
00620 
00621     cpl_vector       *lines          = NULL;
00622 
00623     cpl_propertylist *header         = NULL;
00624     cpl_propertylist *save_header    = NULL;
00625 
00626     cpl_table        *global         = NULL;
00627     /*
00628      * Auxiliary variables
00629      */
00630 
00631     char    version[80];
00632     char   *instrume = NULL;
00633     const char   *science_tag;
00634     const char   *master_norm_flat_tag;
00635     const char   *disp_coeff_sky_tag;
00636     const char   *wavelength_map_sky_tag;
00637     const char   *reduced_science_tag;
00638     const char   *reduced_sky_tag;
00639     const char   *reduced_error_tag;
00640     const char   *mapped_science_tag;
00641     const char   *unmapped_science_tag;
00642     const char   *mapped_science_sky_tag;
00643     const char   *mapped_sky_tag;
00644     const char   *unmapped_sky_tag;
00645     const char   *global_sky_spectrum_tag;
00646     const char   *object_table_tag;
00647     const char   *object_table_pol_tag;
00648     const char   *skylines_offsets_tag;
00649     const char   *reduced_q_tag;
00650     const char   *reduced_u_tag;
00651     const char   *reduced_v_tag;
00652     const char   *reduced_l_tag;
00653     const char   *reduced_error_q_tag;
00654     const char   *reduced_error_u_tag;
00655     const char   *reduced_error_v_tag;
00656     const char   *reduced_error_l_tag;
00657     const char   *reduced_nul_q_tag;
00658     const char   *reduced_nul_u_tag;
00659     const char   *reduced_nul_v_tag;
00660     const char   *reduced_angle_tag;
00661     const char   *reduced_error_angle_tag;
00662     const char   *master_distortion_tag = "MASTER_DISTORTION_TABLE";
00663     const char   *chrom_table_tag = "RETARDER_WAVEPLATE_CHROMATISM";
00664     float *angles = NULL;
00665     int     pmos, circ;
00666     int     nscience;
00667     double  alltime;
00668     double  mean_rms;
00669     int     nlines;
00670     int     rebin;
00671     double *line;
00672     int     nx = 0, ny;
00673     int     ccd_xsize, ccd_ysize;
00674     double  reference;
00675     double  gain;
00676     double  ron;
00677     int     standard;
00678     int     highres;
00679     int     i, j;
00680     cpl_error_code error;
00681 
00682     int * nobjs_per_slit;
00683     int   nslits_out_det = 0;
00684 
00685 
00686     snprintf(version, 80, "%s-%s", PACKAGE, PACKAGE_VERSION);
00687 
00688     cpl_msg_set_indentation(2);
00689 
00690     if (dfs_files_dont_exist(frameset))
00691         fors_pmos_extract_exit(NULL);
00692 
00693 
00694     /* 
00695      * Get configuration parameters
00696      */
00697 
00698     cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00699     cpl_msg_indent_more();
00700 
00701     if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00702         fors_pmos_extract_exit("Too many in input: GRISM_TABLE");
00703 
00704     grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00705 
00706     dispersion = dfs_get_parameter_double(parlist, 
00707                     "fors.fors_pmos_extract.dispersion", grism_table);
00708 
00709     if (dispersion <= 0.0)
00710         fors_pmos_extract_exit("Invalid resampling step");
00711 
00712     skyalign = dfs_get_parameter_int(parlist, 
00713                     "fors.fors_pmos_extract.skyalign", NULL);
00714 
00715     if (skyalign > 2)
00716         fors_pmos_extract_exit("Max polynomial degree for sky alignment is 2");
00717 
00718     wcolumn = dfs_get_parameter_string(parlist, 
00719                     "fors.fors_pmos_extract.wcolumn", NULL);
00720 
00721     startwavelength = dfs_get_parameter_double(parlist, 
00722                     "fors.fors_pmos_extract.startwavelength", grism_table);
00723     if (startwavelength < 3000.0 || startwavelength > 13000.0)
00724         fors_pmos_extract_exit("Invalid wavelength");
00725 
00726     endwavelength = dfs_get_parameter_double(parlist, 
00727                     "fors.fors_pmos_extract.endwavelength", grism_table);
00728     if (endwavelength < 3000.0 || endwavelength > 13000.0)
00729         fors_pmos_extract_exit("Invalid wavelength");
00730 
00731     if (endwavelength - startwavelength <= 0.0)
00732         fors_pmos_extract_exit("Invalid wavelength interval");
00733 
00734     flux = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.flux", NULL);
00735 
00736     flatfield = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.flatfield", 
00737                                        NULL);
00738 
00739     skyglobal = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.skyglobal", 
00740                                        NULL);
00741     skylocal  = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.skylocal", 
00742                                        NULL);
00743     skymedian = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.skymedian", 
00744                                        NULL);
00745 /* NSS
00746     skymedian = dfs_get_parameter_int(parlist, "fors.fors_pmos_extract.skymedian", 
00747                                        NULL);
00748 */
00749     
00750     chromatism = 
00751     dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.chromatism", 
00752                    NULL);
00753 
00754     if (skylocal && skyglobal)
00755         fors_pmos_extract_exit("Cannot apply both local and global sky subtraction");
00756 
00757     if (skylocal && skymedian)
00758         fors_pmos_extract_exit("Cannot apply sky subtraction both on extracted "
00759                           "and non-extracted spectra");
00760 
00761     cosmics = dfs_get_parameter_bool(parlist, 
00762                                      "fors.fors_pmos_extract.cosmics", NULL);
00763 
00764     if (cosmics)
00765         if (!(skyglobal || skylocal))
00766             fors_pmos_extract_exit("Cosmic rays correction requires "
00767                               "either skylocal=true or skyglobal=true");
00768 
00769     slit_margin = dfs_get_parameter_int(parlist, 
00770                                         "fors.fors_pmos_extract.slit_margin",
00771                                         NULL);
00772     if (slit_margin < 0)
00773         fors_pmos_extract_exit("Value must be zero or positive");
00774 
00775     ext_radius = dfs_get_parameter_int(parlist, "fors.fors_pmos_extract.ext_radius",
00776                                        NULL);
00777     if (ext_radius < 0)
00778         fors_pmos_extract_exit("Value must be zero or positive");
00779 
00780     cont_radius = dfs_get_parameter_int(parlist, 
00781                                         "fors.fors_pmos_extract.cont_radius",
00782                                         NULL);
00783     if (cont_radius < 0)
00784         fors_pmos_extract_exit("Value must be zero or positive");
00785 
00786     ext_mode = dfs_get_parameter_int(parlist, "fors.fors_pmos_extract.ext_mode",
00787                                        NULL);
00788     if (ext_mode < 0 || ext_mode > 1)
00789         fors_pmos_extract_exit("Invalid object extraction mode");
00790 
00791     time_normalise = dfs_get_parameter_bool(parlist, 
00792                              "fors.fors_pmos_extract.time_normalise", NULL);
00793 
00794     check = dfs_get_parameter_bool(parlist, "fors.fors_pmos_extract.check", NULL);
00795     cpl_table_delete(grism_table); grism_table = NULL;
00796 
00797     if (cpl_error_get_code())
00798         fors_pmos_extract_exit("Failure getting the configuration parameters");
00799 
00800     
00801     /* 
00802      * Check input set-of-frames
00803      */
00804 
00805     cpl_msg_indent_less();
00806     cpl_msg_info(recipe, "Check input set-of-frames:");
00807     cpl_msg_indent_more();
00808 
00809     if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
00810         fors_pmos_extract_exit("Input frames are not from the same grism");
00811 
00812     if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
00813         fors_pmos_extract_exit("Input frames are not from the same filter");
00814 
00815     if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
00816         fors_pmos_extract_exit("Input frames are not from the same chip");
00817 
00818     standard = 0;
00819     pmos = cpl_frameset_count_tags(frameset, "SCIENCE_PMOS");
00820 
00821     if (pmos == 0) {
00822         pmos = cpl_frameset_count_tags(frameset, "STANDARD_PMOS");
00823         standard = 1;
00824     }
00825 
00826     if (pmos == 0)
00827         fors_pmos_extract_exit("Missing input scientific frame");
00828 
00829     angles = fors_check_angles(frameset, pmos, 
00830                                 standard ? "STANDARD_PMOS" : "SCIENCE_PMOS", 
00831                                 &circ);
00832     if (angles == NULL)
00833         fors_pmos_extract_exit("Polarization angles could not be read");
00834 
00835     if (circ)
00836         chromatism = 0; /* Chromatism correction unrequired for 
00837                            circular polarimetry */
00838 
00839 
00840     nscience = pmos;
00841 
00842     reduceds = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
00843     rerrors  = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
00844     slitss   = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
00845     mappeds  = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
00846     skylocalmaps = (cpl_image **)cpl_malloc(sizeof(cpl_image *) * nscience);
00847 
00848     if (pmos) {
00849         cpl_msg_info(recipe, "PMOS data found");
00850         if (standard) {
00851             science_tag             = "STANDARD_PMOS";
00852             reduced_science_tag     = "REDUCED_STD_PMOS";
00853             unmapped_science_tag    = "UNMAPPED_STD_PMOS";
00854             mapped_science_tag      = "MAPPED_STD_PMOS";
00855             mapped_science_sky_tag  = "MAPPED_ALL_STD_PMOS";
00856             skylines_offsets_tag    = "SKY_SHIFTS_SLIT_STD_PMOS";
00857             wavelength_map_sky_tag  = "WAVELENGTH_MAP_STD_PMOS";
00858             disp_coeff_sky_tag      = "DISP_COEFF_STD_PMOS";
00859             mapped_sky_tag          = "MAPPED_SKY_STD_PMOS";
00860             unmapped_sky_tag        = "UNMAPPED_SKY_STD_PMOS";
00861             object_table_tag        = "OBJECT_TABLE_STD_PMOS";
00862             object_table_pol_tag    = "OBJECT_TABLE_POL_STD_PMOS";
00863             reduced_sky_tag         = "REDUCED_SKY_STD_PMOS";
00864             reduced_error_tag       = "REDUCED_ERROR_STD_PMOS";
00865             reduced_q_tag           = "REDUCED_Q_STD_PMOS";
00866             reduced_u_tag           = "REDUCED_U_STD_PMOS";
00867             reduced_v_tag           = "REDUCED_V_STD_PMOS";
00868             reduced_l_tag           = "REDUCED_L_STD_PMOS";
00869             reduced_error_q_tag     = "REDUCED_ERROR_Q_STD_PMOS";
00870             reduced_error_u_tag     = "REDUCED_ERROR_U_STD_PMOS";
00871             reduced_error_v_tag     = "REDUCED_ERROR_V_STD_PMOS";
00872             reduced_error_l_tag     = "REDUCED_ERROR_L_STD_PMOS";
00873             reduced_nul_q_tag       = "REDUCED_NUL_Q_STD_PMOS";
00874             reduced_nul_u_tag       = "REDUCED_NUL_U_STD_PMOS";
00875             reduced_nul_v_tag       = "REDUCED_NUL_V_STD_PMOS";
00876             reduced_angle_tag       = "REDUCED_ANGLE_STD_PMOS";
00877             reduced_error_angle_tag = "REDUCED_ERROR_ANGLE_STD_PMOS";
00878         }
00879         else {
00880             science_tag             = "SCIENCE_PMOS";
00881             reduced_science_tag     = "REDUCED_SCI_PMOS";
00882             unmapped_science_tag    = "UNMAPPED_SCI_PMOS";
00883             mapped_science_tag      = "MAPPED_SCI_PMOS";
00884             mapped_science_sky_tag  = "MAPPED_ALL_SCI_PMOS";
00885             skylines_offsets_tag    = "SKY_SHIFTS_SLIT_SCI_PMOS";
00886             wavelength_map_sky_tag  = "WAVELENGTH_MAP_SCI_PMOS";
00887             disp_coeff_sky_tag      = "DISP_COEFF_SCI_PMOS";
00888             mapped_sky_tag          = "MAPPED_SKY_SCI_PMOS";
00889             unmapped_sky_tag        = "UNMAPPED_SKY_SCI_PMOS";
00890             object_table_tag        = "OBJECT_TABLE_SCI_PMOS";
00891             object_table_pol_tag    = "OBJECT_TABLE_POL_SCI_PMOS";
00892             reduced_sky_tag         = "REDUCED_SKY_SCI_PMOS";
00893             reduced_error_tag       = "REDUCED_ERROR_SCI_PMOS";
00894             reduced_q_tag           = "REDUCED_Q_SCI_PMOS";
00895             reduced_u_tag           = "REDUCED_U_SCI_PMOS";
00896             reduced_v_tag           = "REDUCED_V_SCI_PMOS";
00897             reduced_l_tag           = "REDUCED_L_SCI_PMOS";
00898             reduced_error_q_tag     = "REDUCED_ERROR_Q_SCI_PMOS";
00899             reduced_error_u_tag     = "REDUCED_ERROR_U_SCI_PMOS";
00900             reduced_error_v_tag     = "REDUCED_ERROR_V_SCI_PMOS";
00901             reduced_error_l_tag     = "REDUCED_ERROR_L_SCI_PMOS";
00902             reduced_nul_q_tag       = "REDUCED_NUL_Q_SCI_PMOS";
00903             reduced_nul_u_tag       = "REDUCED_NUL_U_SCI_PMOS";
00904             reduced_nul_v_tag       = "REDUCED_NUL_V_SCI_PMOS";
00905             reduced_angle_tag       = "REDUCED_ANGLE_SCI_PMOS";
00906             reduced_error_angle_tag = "REDUCED_ERROR_ANGLE_SCI_PMOS";
00907         }
00908 
00909         master_norm_flat_tag    = "MASTER_NORM_FLAT_PMOS";
00910         global_sky_spectrum_tag = "GLOBAL_SKY_SPECTRUM_PMOS";
00911 
00912         if (!cpl_frameset_count_tags(frameset, master_norm_flat_tag)) {
00913             master_norm_flat_tag    = "MASTER_NORM_FLAT_LONG_PMOS";
00914         }
00915     }
00916 
00917     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") == 0)
00918         fors_pmos_extract_exit("Missing required input: MASTER_BIAS");
00919 
00920     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") > 1)
00921         fors_pmos_extract_exit("Too many in input: MASTER_BIAS");
00922 
00923     if (skyalign >= 0)
00924         if (cpl_frameset_count_tags(frameset, "MASTER_SKYLINECAT") > 1)
00925             fors_pmos_extract_exit("Too many in input: MASTER_SKYLINECAT");
00926 
00927     if (chromatism) {
00928     if (cpl_frameset_count_tags(frameset, chrom_table_tag) == 0) {
00929         cpl_msg_error(recipe, "Missing required input: %s",
00930               chrom_table_tag);
00931         fors_pmos_extract_exit(NULL);
00932     }
00933 
00934     if (cpl_frameset_count_tags(frameset, chrom_table_tag) > 1) {
00935         cpl_msg_error(recipe, "Too many in input: %s", chrom_table_tag);
00936         fors_pmos_extract_exit(NULL);
00937     }
00938     }
00939 
00940     if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) > 1) {
00941         if (flatfield) {
00942             cpl_msg_error(recipe, "Too many in input: %s", 
00943                           master_norm_flat_tag);
00944             fors_pmos_extract_exit(NULL);
00945         }
00946         else {
00947             cpl_msg_warning(recipe, "%s in input are ignored, "
00948                             "since flat field correction was not requested", 
00949                             master_norm_flat_tag);
00950         }
00951     }
00952 
00953     if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 1) {
00954         if (!flatfield) {
00955             cpl_msg_warning(recipe, "%s in input is ignored, "
00956                             "since flat field correction was not requested", 
00957                             master_norm_flat_tag);
00958         }
00959     }
00960 
00961     if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 0) {
00962         if (flatfield) {
00963             cpl_msg_error(recipe, "Flat field correction was requested, "
00964                           "but no %s are found in input",
00965                           master_norm_flat_tag);
00966             fors_pmos_extract_exit(NULL);
00967         }
00968     }
00969 
00970     if (cpl_frameset_count_tags(frameset, master_distortion_tag) == 0)
00971         fors_pmos_extract_exit("Missing required input: MASTER_DISTORTION_TABLE");
00972     
00973     if (cpl_frameset_count_tags(frameset, master_distortion_tag) > 1)
00974     fors_pmos_extract_exit("Too many in input: MASTER_DISTORTION_TABLE");
00975     
00976     global = dfs_load_table(frameset, master_distortion_tag, 1);
00977     if (global == NULL)
00978     fors_pmos_extract_exit("Cannot load master distortion table");
00979     
00980     cpl_msg_indent_less();
00981 
00982     /*
00983      * Get the reference wavelength and the rebin factor along the
00984      * dispersion direction from a scientific exposure
00985      */
00986 
00987     header = dfs_load_header(frameset, science_tag, 0);
00988 
00989     if (header == NULL)
00990         fors_pmos_extract_exit("Cannot load scientific frame header");
00991 
00992     instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
00993     if (instrume == NULL)
00994         fors_pmos_extract_exit("Missing keyword INSTRUME in scientific header");
00995     instrume = cpl_strdup(instrume);
00996 
00997     if (instrume[4] == '1')
00998         snprintf(version, 80, "%s/%s", "fors1", VERSION);
00999     if (instrume[4] == '2')
01000         snprintf(version, 80, "%s/%s", "fors2", VERSION);
01001 
01002     reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
01003 
01004     if (cpl_error_get_code() != CPL_ERROR_NONE)
01005         fors_pmos_extract_exit("Missing keyword ESO INS GRIS1 WLEN in scientific "
01006                         "frame header");
01007 
01008     if (reference < 3000.0)   /* Perhaps in nanometers... */
01009         reference *= 10;
01010 
01011     if (reference < 3000.0 || reference > 13000.0) {
01012         cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
01013                       "keyword ESO INS GRIS1 WLEN in scientific frame header",
01014                       reference);
01015         fors_pmos_extract_exit(NULL);
01016     }
01017 
01018     cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
01019 
01020     rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
01021 
01022     if (cpl_error_get_code() != CPL_ERROR_NONE)
01023         fors_pmos_extract_exit("Missing keyword ESO DET WIN1 BINX in scientific "
01024                         "frame header");
01025 
01026     if (rebin != 1) {
01027         dispersion *= rebin;
01028         cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
01029                         "resampling step used is %f A/pixel", rebin, 
01030                         dispersion);
01031     }
01032 
01033     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
01034 
01035     if (cpl_error_get_code() != CPL_ERROR_NONE)
01036         fors_pmos_extract_exit("Missing keyword ESO DET OUT1 CONAD in scientific "
01037                           "frame header");
01038 
01039     cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
01040 
01041     ron = cpl_propertylist_get_double(header, "ESO DET OUT1 RON");
01042 
01043     if (cpl_error_get_code() != CPL_ERROR_NONE)
01044         fors_pmos_extract_exit("Missing keyword ESO DET OUT1 RON in scientific "
01045                           "frame header");
01046 
01047     ron /= gain;     /* Convert from electrons to ADU */
01048 
01049     cpl_msg_info(recipe, "The read-out-noise is: %.2f ADU", ron);
01050 
01051     cpl_msg_info(recipe, "Load normalised flat field (if present)...");
01052     cpl_msg_indent_more();
01053 
01054     if (flatfield) {
01055     norm_flat = dfs_load_image(frameset, master_norm_flat_tag, 
01056                    CPL_TYPE_FLOAT, 0, 1);
01057     }
01058 
01059     cpl_msg_info(recipe, "Produce mask slit position table...");
01060 
01061     maskslits = mos_load_slits_fors_pmos(header, &nslits_out_det);
01062 
01063     if (skyalign >= 0) {
01064 
01065     cpl_msg_indent_less();
01066     cpl_msg_info(recipe, "Load input sky line catalog...");
01067     cpl_msg_indent_more();
01068 
01069     wavelengths = dfs_load_table(frameset, "MASTER_SKYLINECAT", 1);
01070 
01071     if (wavelengths) {
01072         /*
01073          * Cast the wavelengths into a (double precision) CPL vector
01074          */
01075 
01076         nlines = cpl_table_get_nrow(wavelengths);
01077 
01078         if (nlines == 0)
01079         fors_pmos_extract_exit("Empty input sky line catalog");
01080 
01081         if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
01082         cpl_msg_error(recipe, "Missing column %s in input line "
01083                   "catalog table", wcolumn);
01084         fors_pmos_extract_exit(NULL);
01085         }
01086 
01087         line = cpl_malloc(nlines * sizeof(double));
01088     
01089         for (i = 0; i < nlines; i++)
01090         line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
01091 
01092         cpl_table_delete(wavelengths); wavelengths = NULL;
01093 
01094         lines = cpl_vector_wrap(nlines, line);
01095     }
01096     else {
01097         cpl_msg_info(recipe, "No sky line catalog found in input - fine!");
01098     }
01099     }
01100 
01101 
01102     cpl_propertylist_delete(header); header = NULL;
01103 
01104     /*
01105      * Load the wavelength calibration table
01106      */
01107 
01108     for (j = 0; j < nscience; j++) {
01109     int k;
01110 
01111     cpl_msg_indent_less();
01112     cpl_msg_info(recipe, "Processing scientific exposure of angle %.2f "
01113              "(%d out of %d) ...",
01114              angles[j], j + 1, nscience);
01115     cpl_msg_indent_more();
01116 
01117     cpl_msg_info(recipe, "Load scientific exposure...");
01118     cpl_msg_indent_more();
01119 
01120     /*
01121      * FIXME: Horrible workaround to avoid the problem because of the
01122      * multiple encapsulation of cpl_frameset_find() in different 
01123      * loading functions
01124      */
01125 
01126     header = dfs_load_header(frameset, science_tag, 0);
01127 
01128     for (k = 0; k < j; k ++) {
01129         cpl_propertylist_delete(header);
01130         header = dfs_load_header(frameset, NULL, 0);
01131     }
01132 
01133     spectra = dfs_load_image(frameset, science_tag, CPL_TYPE_FLOAT, 0, 0);
01134 
01135     for (k = 0; k < j; k ++) {
01136         cpl_image_delete(spectra);
01137         spectra = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01138     }
01139 
01140     if (spectra == NULL)
01141         fors_pmos_extract_exit("Cannot load scientific frame");
01142         
01143     if (header == NULL)
01144         fors_pmos_extract_exit("Cannot load scientific frame header");
01145 
01146     alltime = cpl_propertylist_get_double(header, "EXPTIME");
01147 
01148     if (cpl_error_get_code() != CPL_ERROR_NONE)
01149         fors_pmos_extract_exit("Missing keyword EXPTIME in scientific "
01150                    "frame header");
01151 
01152     /* Leave the header on for the next step... */
01153     //cpl_propertylist_delete(header); header = NULL;
01154 
01155     cpl_msg_info(recipe, "Scientific frame exposure time: %.2f s", 
01156              alltime);
01157 
01158     cpl_msg_indent_less();
01159 
01160     /*
01161      * Remove the master bias
01162      */
01163 
01164     cpl_msg_info(recipe, "Remove the master bias...");
01165 
01166     bias = dfs_load_image(frameset, "MASTER_BIAS", CPL_TYPE_FLOAT, 0, 1);
01167 
01168     if (bias == NULL)
01169         fors_pmos_extract_exit("Cannot load master bias");
01170 
01171     overscans = mos_load_overscans_vimos(header, 1);
01172 
01173     dummy = mos_remove_bias(spectra, bias, overscans);
01174     cpl_image_delete(spectra); spectra = dummy; dummy = NULL;
01175     cpl_image_delete(bias); bias = NULL;
01176     cpl_table_delete(overscans); overscans = NULL;
01177 
01178     if (spectra == NULL)
01179         fors_pmos_extract_exit("Cannot remove bias from scientific frame");
01180 
01181     ccd_xsize = nx = cpl_image_get_size_x(spectra);
01182     ccd_ysize = ny = cpl_image_get_size_y(spectra);
01183 
01184     if (flatfield) {
01185 
01186         if (norm_flat) {
01187         cpl_msg_info(recipe, "Apply flat field correction...");
01188         if (cpl_image_divide(spectra, norm_flat) != CPL_ERROR_NONE) {
01189             cpl_msg_error(recipe, "Failure of flat field correction: %s",
01190                   cpl_error_get_message());
01191             fors_pmos_extract_exit(NULL);
01192         }
01193         }
01194         else {
01195         cpl_msg_error(recipe, "Cannot load input %s for flat field "
01196                   "correction", master_norm_flat_tag);
01197         fors_pmos_extract_exit(NULL);
01198         }
01199 
01200     }
01201 
01202     /*
01203      * Load the slit location table
01204      */
01205 
01206     slits = mos_build_slit_location(global, maskslits, ny);
01207 
01208     if (slits == NULL) {
01209         fors_pmos_extract_exit("Cannot load slits location table");
01210     } else {
01211         cpl_table_new_column(slits, "pair_id", CPL_TYPE_INT);
01212 
01213         int m, null, size = cpl_table_get_nrow(slits);
01214 
01215         for (m = 0; m < size; m++) {
01216         int slit_id = cpl_table_get(slits, "slit_id", m, &null);
01217 
01218         int pair_id = slit_id % 2 ? slit_id + 1 : slit_id;
01219 
01220         cpl_table_set(slits, "pair_id", m, pair_id);
01221         }
01222     }
01223     
01224     cpl_msg_info(recipe, "Processing scientific spectra...");
01225 
01226     /*
01227      * Load the spectral curvature table
01228      */
01229 
01230     polytraces = mos_build_curv_coeff(global, maskslits, slits);
01231     if (polytraces == NULL)
01232         fors_pmos_extract_exit("Cannot create spectral curvature table");
01233  
01234     /*
01235      * This one will also generate the spatial map from the spectral 
01236      * curvature table (in the case of multislit data)
01237      */
01238 
01239     coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01240 
01241     smapped = mos_spatial_calibration(spectra, slits, polytraces, reference,
01242                       startwavelength, endwavelength,
01243                       dispersion, flux, coordinate);
01244 
01245     /*
01246      * Generate a rectified wavelength map from the wavelength calibration 
01247      * table
01248      */
01249 
01250     /*
01251      * Load the wavelength calibration table
01252      */
01253  
01254     idscoeff = mos_build_disp_coeff(global, slits);
01255     if (idscoeff == NULL)
01256         fors_pmos_extract_exit("Cannot create wavelength calibration table");
01257 
01258     rainbow = mos_map_idscoeff(idscoeff, nx, reference, startwavelength, 
01259                    endwavelength);
01260 
01261     if (dispersion > 1.0)
01262         highres = 0;
01263     else
01264         highres = 1;
01265 
01266     if (skyalign >= 0) {
01267         if (skyalign) {
01268         cpl_msg_info(recipe, "Align wavelength solution to reference "
01269                  "skylines applying %d order residual fit...", skyalign);
01270         }
01271         else {
01272         cpl_msg_info(recipe, "Align wavelength solution to reference "
01273                  "skylines applying median offset...");
01274         }
01275 
01276         if (!j) {
01277         offsets = mos_wavelength_align(smapped, slits, reference, 
01278                            startwavelength, endwavelength, 
01279                            idscoeff, lines, highres, 
01280                            skyalign, rainbow, 4);
01281 
01282         if (offsets) {
01283             if (standard)
01284             cpl_msg_warning(recipe, "Alignment of the wavelength solution "
01285                     "to reference sky lines may be unreliable in "
01286                     "this case!");
01287 
01288             if (dfs_save_table(frameset, offsets, skylines_offsets_tag,
01289                        NULL, parlist, recipe, version)) {
01290             fors_pmos_extract_exit(NULL);
01291             }
01292 
01293         } else {
01294             cpl_msg_warning(recipe, "Alignment of the wavelength solution "
01295                     "to reference sky lines could not be done!");
01296             skyalign = -1;
01297         }
01298         }
01299 
01300 
01301     }
01302 
01303     wavemap = mos_map_wavelengths(coordinate, rainbow, slits, 
01304                       polytraces, reference, 
01305                       startwavelength, endwavelength,
01306                       dispersion);
01307 
01308 
01309     cpl_image_delete(rainbow); rainbow = NULL;
01310     cpl_image_delete(coordinate); coordinate = NULL;
01311 
01312     /*
01313      * Here the wavelength calibrated slit spectra are created. This frame
01314      * contains sky_science.
01315      */
01316 
01317     mapped_sky = mos_wavelength_calibration(smapped, reference,
01318                         startwavelength, endwavelength,
01319                         dispersion, idscoeff, flux);
01320 
01321     if (!j) {
01322         cpl_msg_indent_less();
01323         cpl_msg_info(recipe, "Check applied wavelength against skylines...");
01324         cpl_msg_indent_more();
01325 
01326         mean_rms = mos_distortions_rms(mapped_sky, lines, startwavelength,
01327                        dispersion, 6, highres);
01328 
01329 
01330         cpl_msg_info(recipe, "Mean residual: %f", mean_rms);
01331 
01332         mean_rms = cpl_table_get_column_mean(idscoeff, "error");
01333 
01334         cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
01335              mean_rms, mean_rms * dispersion);
01336     }
01337 
01338     save_header = cpl_propertylist_duplicate(header);
01339 
01340     cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01341     cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01342     cpl_propertylist_update_double(header, "CRVAL1", 
01343                        startwavelength + dispersion/2);
01344     cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01345     /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
01346        cpl_propertylist_update_double(header, "CDELT2", 1.0); */
01347     cpl_propertylist_update_double(header, "CD1_1", dispersion);
01348     cpl_propertylist_update_double(header, "CD1_2", 0.0);
01349     cpl_propertylist_update_double(header, "CD2_1", 0.0);
01350     cpl_propertylist_update_double(header, "CD2_2", 1.0);
01351     cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01352     cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01353 
01354     if (time_normalise) {
01355         dummy = cpl_image_divide_scalar_create(mapped_sky, alltime);
01356         if (!j) {
01357         if(dfs_save_image_null(frameset, parlist,
01358                        mapped_science_sky_tag,
01359                        recipe, version)) {
01360             fors_pmos_extract_exit(NULL);
01361         }
01362         }
01363         if (dfs_save_image_ext(dummy, mapped_science_sky_tag, header)) {
01364         fors_pmos_extract_exit(NULL);
01365         }
01366         cpl_image_delete(dummy); dummy = NULL;
01367     }
01368     else {
01369 
01370         if (!j) {
01371         if(dfs_save_image_null(frameset, parlist,
01372                        mapped_science_sky_tag,
01373                        recipe, version)) {
01374             fors_pmos_extract_exit(NULL);
01375         }
01376         }
01377 
01378         if (dfs_save_image_ext(mapped_sky,
01379                    mapped_science_sky_tag, header)) {
01380         fors_pmos_extract_exit(NULL);
01381         }
01382 
01383     }
01384 
01385 /*    if (skyglobal == 0 && skymedian < 0) {    NSS */
01386     if (skyglobal == 0 && skymedian == 0 && skylocal == 0) {
01387         cpl_image_delete(mapped_sky); mapped_sky = NULL;
01388     }
01389 
01390     if (skyglobal || skylocal) {
01391 
01392         cpl_msg_indent_less();
01393 
01394         if (skyglobal) {
01395         cpl_msg_info(recipe, "Global sky determination...");
01396         cpl_msg_indent_more();
01397         skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01398 
01399         sky = mos_sky_map_super(spectra, wavemap, dispersion, 
01400                     2.0, 50, skymap);
01401 
01402         if (sky) {
01403             cpl_image_subtract(spectra, skymap);
01404         }
01405         else {
01406             cpl_image_delete(skymap); skymap = NULL;
01407         }
01408         }
01409         else {
01410         cpl_msg_info(recipe, "Local sky determination...");
01411         cpl_msg_indent_more();
01412         skymap = mos_subtract_sky(spectra, slits, polytraces, reference,
01413                       startwavelength, endwavelength, dispersion);
01414         }
01415 
01416         if (skymap) {
01417         if (skyglobal) {
01418             if (time_normalise)
01419             cpl_table_divide_scalar(sky, "sky", alltime);
01420 
01421 /* Old saving:
01422 
01423             if (!j) {
01424             if (dfs_save_table(frameset, sky,
01425                        global_sky_spectrum_tag, 
01426                        NULL, parlist, recipe, version)) {
01427                 fors_pmos_extract_exit(NULL);
01428             }
01429             } else {
01430             if (dfs_save_table_ext(sky, global_sky_spectrum_tag, 
01431                            NULL)) {
01432                 fors_pmos_extract_exit(NULL);
01433             }
01434             }
01435 
01436 End of old saving */
01437 
01438                     if (!j) {
01439                         if(dfs_save_image_null(frameset, parlist, 
01440                                                global_sky_spectrum_tag,
01441                                                recipe, version)) {
01442                 fors_pmos_extract_exit(NULL);
01443                         }
01444                     }
01445     
01446                     if (dfs_save_table_ext(sky, global_sky_spectrum_tag, 
01447                                            NULL)) {
01448                         fors_pmos_extract_exit(NULL);
01449                     }
01450     
01451     
01452             cpl_table_delete(sky); sky = NULL;
01453         }
01454 
01455 //      save_header = dfs_load_header(frameset, science_tag, 0);
01456 
01457         if (time_normalise)
01458             cpl_image_divide_scalar(skymap, alltime);
01459 
01460         if (!j) {
01461             if(dfs_save_image_null(frameset, parlist,
01462                        unmapped_sky_tag,
01463                        recipe, version)) {
01464             fors_pmos_extract_exit(NULL);
01465             }
01466         }
01467 
01468         if (dfs_save_image_ext(skymap, unmapped_sky_tag,
01469                        save_header)) {
01470             fors_pmos_extract_exit(NULL);
01471         }
01472 
01473         cpl_image_delete(skymap); skymap = NULL;
01474 
01475         if (!j) {
01476             if(dfs_save_image_null(frameset, parlist,
01477                        unmapped_science_tag,
01478                        recipe, version)) {
01479             fors_pmos_extract_exit(NULL);
01480             }
01481         }
01482 
01483         if (dfs_save_image_ext(spectra, unmapped_science_tag,
01484                        save_header)) {
01485             fors_pmos_extract_exit(NULL);
01486         }
01487 
01488 //      cpl_propertylist_delete(save_header); save_header = NULL;
01489 
01490         if (cosmics) {
01491             cpl_msg_info(recipe, "Removing cosmic rays...");
01492             mos_clean_cosmics(spectra, gain, -1., -1.);
01493         }
01494 
01495         /*
01496          * The spatially rectified image, that contained the sky,
01497          * is replaced by a sky-subtracted spatially rectified image:
01498          */
01499 
01500         cpl_image_delete(smapped); smapped = NULL;
01501 
01502         smapped = mos_spatial_calibration(spectra, slits, polytraces, 
01503                           reference, startwavelength, 
01504                           endwavelength, dispersion, 
01505                           flux, NULL);
01506         }
01507         else {
01508         cpl_msg_warning(recipe, "Sky subtraction failure");
01509         if (cosmics)
01510             cpl_msg_warning(recipe, "Cosmic rays removal not performed!");
01511         cosmics = skylocal = skyglobal = 0;
01512         }
01513     }
01514 
01515     cpl_image_delete(spectra); spectra = NULL;
01516     cpl_table_delete(polytraces); polytraces = NULL;
01517 
01518     if (skyalign >= 0) {
01519         save_header = dfs_load_header(frameset, science_tag, 0);
01520 
01521         if (!j) {
01522         if(dfs_save_image_null(frameset, parlist,
01523                        wavelength_map_sky_tag,
01524                        recipe, version)) {
01525             fors_pmos_extract_exit(NULL);
01526         }
01527         }
01528 
01529         if (dfs_save_image_ext(wavemap, wavelength_map_sky_tag,
01530                    save_header)) {
01531         fors_pmos_extract_exit(NULL);
01532         }
01533 
01534 //      cpl_propertylist_delete(save_header); save_header = NULL;
01535     }
01536 
01537     cpl_image_delete(wavemap); wavemap = NULL;
01538 
01539     mapped = mos_wavelength_calibration(smapped, reference,
01540                         startwavelength, endwavelength,
01541                         dispersion, idscoeff, flux);
01542 
01543     cpl_image_delete(smapped); smapped = NULL;
01544 
01545     if (skyalign >= 0) {
01546         if (!j) {
01547         if (dfs_save_table(frameset, idscoeff, disp_coeff_sky_tag,
01548                    NULL, parlist, recipe, version)) {
01549             fors_pmos_extract_exit(NULL);
01550         }
01551         }
01552     }
01553 
01554 /*    if (skymedian >= 0) {    NSS */
01555     if (skymedian) {
01556             cpl_msg_indent_less();
01557             cpl_msg_info(recipe, "Local sky determination...");
01558             cpl_msg_indent_more();
01559        
01560 /*   NSS      skylocalmap = mos_sky_local(mapped, slits, skymedian); */
01561 /*            skylocalmap = mos_sky_local(mapped, slits, 0);        */
01562             skylocalmap = mos_sky_local_old(mapped, slits);       
01563             cpl_image_subtract(mapped, skylocalmap);
01564 /*
01565   if (dfs_save_image(frameset, skylocalmap, mapped_sky_tag, header, 
01566   parlist, recipe, version))
01567   fors_pmos_extract_exit(NULL);
01568 */
01569             cpl_image_delete(skylocalmap); skylocalmap = NULL;
01570     }
01571 
01572 /*    if (skyglobal || skymedian >= 0 || skylocal) {   NSS */
01573     if (skyglobal || skymedian || skylocal) {
01574 
01575         skylocalmap = cpl_image_subtract_create(mapped_sky, mapped);
01576 
01577         cpl_image_delete(mapped_sky); mapped_sky = NULL;
01578 
01579         if (time_normalise) {
01580         dummy = cpl_image_divide_scalar_create(skylocalmap, alltime);
01581 
01582         if (!j) {
01583             if(dfs_save_image_null(frameset, parlist,
01584                        mapped_sky_tag,
01585                        recipe, version)) {
01586             fors_pmos_extract_exit(NULL);
01587             }
01588         }
01589 
01590         if (dfs_save_image_ext(dummy, mapped_sky_tag,
01591                        header)) {
01592             fors_pmos_extract_exit(NULL);
01593         }
01594 
01595         cpl_image_delete(dummy); dummy = NULL;
01596         }
01597         else {
01598         if (!j) {
01599             if(dfs_save_image_null(frameset, parlist,
01600                        mapped_sky_tag,
01601                        recipe, version)) {
01602             fors_pmos_extract_exit(NULL);
01603             }
01604         }
01605 
01606         if (dfs_save_image_ext(skylocalmap, mapped_sky_tag,
01607                        header)) {
01608             fors_pmos_extract_exit(NULL);
01609         }
01610         }
01611 
01612         skylocalmaps[j] = skylocalmap;
01613 
01614         cpl_msg_indent_less();
01615         cpl_msg_info(recipe, "Object detection...");
01616         cpl_msg_indent_more();
01617 
01618         if (!j)
01619         origslits = cpl_table_duplicate(slits);
01620 
01621         if (cosmics || nscience > 1) {
01622         dummy = mos_detect_objects(mapped, slits, slit_margin, ext_radius, 
01623                        cont_radius);
01624         }
01625         else {
01626         mapped_cleaned = cpl_image_duplicate(mapped);
01627         mos_clean_cosmics(mapped_cleaned, gain, -1., -1.);
01628         dummy = mos_detect_objects(mapped_cleaned, slits, slit_margin, 
01629                        ext_radius, cont_radius);
01630 
01631         cpl_image_delete(mapped_cleaned); mapped_cleaned = NULL;
01632         }
01633 
01634         cpl_image_delete(dummy); dummy = NULL;
01635 
01636         if (check) {
01637 
01638 /* Old saving:
01639 
01640         if (!j) {
01641             if (dfs_save_table(frameset, slits, object_table_tag,
01642                        NULL, parlist, recipe, version)) {
01643             fors_pmos_extract_exit(NULL);
01644             }
01645         } else {
01646             if (dfs_save_table_ext(slits, object_table_tag, NULL)) {
01647             fors_pmos_extract_exit(NULL);
01648             }
01649         }
01650 
01651 End old saving */
01652 
01653                 if (!j) {
01654                     if(dfs_save_image_null(frameset, parlist,
01655                                            object_table_tag,
01656                                            recipe, version)) {
01657                         fors_pmos_extract_exit(NULL);
01658                     }
01659                 }
01660 
01661                 if (dfs_save_table_ext(slits, object_table_tag, NULL)) {
01662                     fors_pmos_extract_exit(NULL);
01663                 }
01664         }
01665     }
01666 
01667     slitss[j]  = slits;
01668     mappeds[j] = mapped;
01669 
01670     cpl_msg_indent_less();
01671 
01672     cpl_propertylist_delete(header); header = NULL;
01673     cpl_propertylist_delete(save_header); save_header = NULL;
01674 
01675     cpl_table_delete(idscoeff); idscoeff = NULL;
01676     }
01677 
01678     cpl_table_delete(offsets); offsets = NULL;
01679 
01680     cpl_image_delete(norm_flat); norm_flat = NULL;
01681     cpl_vector_delete(lines); lines = NULL;
01682 
01683     cpl_table_delete(maskslits); maskslits = NULL;
01684 
01685     
01686     cpl_msg_indent_less();
01687     cpl_msg_info(recipe, 
01688          "Check object detection in both beams for all angles...");
01689     cpl_msg_indent_more();
01690 
01691     /* House keeping - selection of objects for which information required 
01692        for Stokes parameters computation is present */
01693     error = mos_object_intersect(slitss, origslits, nscience, 5.0);
01694     if (error == CPL_ERROR_DATA_NOT_FOUND) {
01695         cpl_msg_warning(recipe, "No objects found: no Stokes "
01696                        "parameters to compute!");
01697         for (j = 0; j < nscience; j++)
01698             cpl_table_delete(slitss[j]);
01699         cpl_free(slitss);
01700         cpl_table_delete(origslits);
01701         return 0;
01702     } else if (error) {
01703         fors_pmos_extract_exit("Problem in polarimetric object selection");
01704     }
01705 
01706     if (dfs_save_table(frameset, origslits, object_table_pol_tag,
01707                NULL, parlist, recipe, version)) {
01708     fors_pmos_extract_exit(NULL);
01709     }
01710 
01711     nobjs_per_slit = fors_get_nobjs_perslit(origslits);
01712 
01713     cpl_msg_indent_less();
01714     cpl_msg_info(recipe, "Object extraction...");
01715     cpl_msg_indent_more();
01716 
01717     for (j = 0; j < nscience; j++) {
01718     int k;
01719 
01720     header = dfs_load_header(frameset, science_tag, 0);
01721 
01722     for (k = 0; k < j; k ++) {
01723         cpl_propertylist_delete(header);
01724         header = dfs_load_header(frameset, NULL, 0);
01725     }
01726 
01727     cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01728     cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01729     cpl_propertylist_update_double(header, "CRVAL1", 
01730                        startwavelength + dispersion/2);
01731     cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01732     /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
01733        cpl_propertylist_update_double(header, "CDELT2", 1.0); */
01734     cpl_propertylist_update_double(header, "CD1_1", dispersion);
01735     cpl_propertylist_update_double(header, "CD1_2", 0.0);
01736     cpl_propertylist_update_double(header, "CD2_1", 0.0);
01737     cpl_propertylist_update_double(header, "CD2_2", 1.0);
01738     cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01739     cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01740 
01741     if (skyglobal || skymedian || skylocal) {
01742 
01743         cpl_msg_info(recipe, "Extracting at angle %.2f (%d out of %d) ...",
01744              angles[j], j + 1, nscience);
01745 
01746         images = mos_extract_objects(mappeds[j], skylocalmaps[j],
01747                      origslits, 
01748                      ext_mode, ron, gain, 1);
01749 
01750         cpl_image_delete(skylocalmaps[j]); skylocalmaps[j] = NULL;
01751 
01752         if (images) {
01753         if (time_normalise)
01754             cpl_image_divide_scalar(images[0], alltime);
01755 
01756         if (!j) {
01757             if(dfs_save_image_null(frameset, parlist,
01758                        reduced_science_tag,
01759                        recipe, version)) {
01760             fors_pmos_extract_exit(NULL);
01761             }
01762         }
01763 
01764         if (dfs_save_image_ext(images[0], reduced_science_tag,
01765                        header)) {
01766             fors_pmos_extract_exit(NULL);
01767         }
01768 
01769         reduceds[j] = images[0];
01770 //      cpl_image_delete(images[0]);
01771     
01772         if (time_normalise)
01773             cpl_image_divide_scalar(images[1], alltime);
01774 
01775         if (!j) {
01776             if(dfs_save_image_null(frameset, parlist,
01777                        reduced_sky_tag,
01778                        recipe, version)) {
01779             fors_pmos_extract_exit(NULL);
01780             }
01781         }
01782 
01783         if (dfs_save_image_ext(images[1], reduced_sky_tag,
01784                        header)) {
01785             fors_pmos_extract_exit(NULL);
01786         }
01787         cpl_image_delete(images[1]);
01788     
01789         if (time_normalise)
01790             cpl_image_divide_scalar(images[2], alltime);
01791 
01792         if (!j) {
01793             if(dfs_save_image_null(frameset, parlist,
01794                        reduced_error_tag,
01795                        recipe, version)) {
01796             fors_pmos_extract_exit(NULL);
01797             }
01798         }
01799 
01800         if (dfs_save_image_ext(images[2], reduced_error_tag,
01801                        header)) {
01802             fors_pmos_extract_exit(NULL);
01803         }
01804 
01805         rerrors[j] = images[2];
01806 //      cpl_image_delete(images[2]);
01807 
01808         cpl_free(images);
01809         }
01810         else {
01811         cpl_msg_warning(recipe, "No objects found: the products "
01812                 "%s, %s, and %s are not created", 
01813                 reduced_science_tag, reduced_sky_tag, 
01814                 reduced_error_tag);
01815         }
01816 
01817     }
01818 
01819 //  slitss[j] = slits;
01820 //  cpl_table_delete(slits); slits = NULL;
01821 
01822 
01823 /*    if (skyglobal || skymedian >= 0) {   NSS */
01824     if (skyglobal || skymedian || skylocal) {
01825         if (time_normalise)
01826         cpl_image_divide_scalar(mappeds[j], alltime);
01827 
01828         if (!j) {
01829         if(dfs_save_image_null(frameset, parlist,
01830                        mapped_science_tag,
01831                        recipe, version)) {
01832             fors_pmos_extract_exit(NULL);
01833         }
01834         }
01835 
01836         if (dfs_save_image_ext(mappeds[j], mapped_science_tag,
01837                    header)) {
01838         fors_pmos_extract_exit(NULL);
01839         }
01840     }
01841 
01842     cpl_image_delete(mappeds[j]); mappeds[j] = NULL;
01843     cpl_propertylist_delete(header); header = NULL;
01844 
01845     }
01846 
01847     cpl_table_delete(origslits);
01848 
01849     /* Stokes computation */
01850 
01851     nobjects = cpl_image_get_size_y(reduceds[0]) / 2;
01852     nx       = cpl_image_get_size_x(reduceds[0]);
01853 
01854     header = cpl_propertylist_new();
01855     cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01856     cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01857     cpl_propertylist_update_double(header, "CRVAL1", 
01858                    startwavelength + dispersion/2);
01859     cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01860     /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
01861        cpl_propertylist_update_double(header, "CDELT2", 1.0); */
01862     cpl_propertylist_update_double(header, "CD1_1", dispersion);
01863     cpl_propertylist_update_double(header, "CD1_2", 0.0);
01864     cpl_propertylist_update_double(header, "CD2_1", 0.0);
01865     cpl_propertylist_update_double(header, "CD2_2", 1.0);
01866     cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01867     cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01868     
01869     if (circ) {
01870 
01871     cpl_image        *pv_im          = NULL;
01872     cpl_image        *pvnull_im      = NULL;
01873     cpl_image        *perr_im        = NULL;
01874 
01875     double           *p_v            = NULL;
01876     double           *p_vnull        = NULL;
01877     double           *perr           = NULL;
01878 
01879     double            mean_vnull;
01880 
01881     int p = -1;
01882     int total = 0;
01883 
01884     pv_im     = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
01885     perr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
01886 
01887     p_v     = cpl_image_get_data_double(pv_im);
01888     perr    = cpl_image_get_data_double(perr_im);
01889 
01890     if (nscience / 2 > 1) {
01891         pvnull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
01892         p_vnull = cpl_image_get_data_double(pvnull_im);
01893     }
01894 
01895     for (j = 0; j < nobjects; j++) {
01896         int k, m;
01897 
01898         double * ip_v,
01899            * ip_vnull, * iperr;
01900 
01901         float * data;
01902         float * iff,  * ierr;
01903 
01904         ip_v = p_v + (nobjects - 1 - j) * nx;
01905 
01906         if (nscience / 2 > 1)
01907         ip_vnull = p_vnull + (nobjects - 1 - j) * nx;
01908 
01909         iperr = perr + (nobjects - 1 - j) * nx;
01910 
01911         while (total < j + 1) {
01912         p++;
01913         total += nobjs_per_slit[p];
01914         }
01915 
01916         for (k = 0; k < nscience / 2; k++) {
01917         float * if_o,  * if_e,  * ifdelta_o, * ifdelta_e;
01918 
01919         int pos   = fors_find_angle_pos(angles, nscience, 180 * k - 45);
01920         int pos_d = fors_find_angle_pos(angles, nscience, 180 * k + 45);
01921 
01922         data = cpl_image_get_data_float(reduceds[pos]);
01923 
01924         if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
01925                    + (total - j - 1)) * nx;
01926 
01927         if_e = data + (2 * (nobjects - total) 
01928                    + (total - j - 1)) * nx;
01929 
01930 //      if_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
01931 //      if_e = data +  2 * (nobjects - 1 - j)      * nx;
01932 
01933         data = cpl_image_get_data_float(reduceds[pos_d]);
01934 
01935         ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
01936                    + (total - j - 1)) * nx;
01937 
01938         ifdelta_e = data + (2 * (nobjects - total) 
01939                    + (total - j - 1)) * nx;
01940 
01941 //      ifdelta_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
01942 //      ifdelta_e = data +  2 * (nobjects - 1 - j)      * nx;
01943 
01944         for (m = 0; m < nx; m++) {
01945 
01946             double quantity = if_o[m] + if_e[m] == 0.0 ? 0.0 :
01947             (if_o[m]      - if_e[m]     ) /
01948             (if_o[m]      + if_e[m]     ) -
01949             (ifdelta_o[m] - ifdelta_e[m]) /
01950             (ifdelta_o[m] + ifdelta_e[m]);
01951 
01952             quantity = isfinite(quantity) ? quantity : 0.0;
01953 
01954             /* PQ map computation */
01955             ip_v[m] += quantity * 0.5 / (nscience / 2);
01956 
01957             /* PQnull map computation */
01958             if (nscience / 2 > 1) {
01959             if (k % 2)
01960                 ip_vnull[m] += quantity * 0.5 / (nscience / 2);
01961             else
01962                 ip_vnull[m] -= quantity * 0.5 / (nscience / 2);
01963             }
01964         }
01965         }
01966 
01967         /* Error map */
01968         data = cpl_image_get_data_float(reduceds[0]);
01969         iff  = data +  2 * (nobjects - 1 - j)      * nx;
01970 
01971         data = cpl_image_get_data_float(rerrors[0]);
01972         ierr = data +  2 * (nobjects - 1 - j)      * nx;
01973 
01974         for (m = 0; m < nx; m++)
01975         iperr[m] = iff[m] <= 0.0 ? 
01976             0.0 : ierr[m] / iff[m] * 0.5 / sqrt (nscience / 2);
01977 
01978         if (nscience / 2 > 1) {
01979         float * weights;
01980         float   max, sum, sum2, imean;
01981 
01982         int k;
01983 
01984         /* QC on U NULL */
01985         weights = cpl_malloc(sizeof(float) * nx);
01986 
01987         max = 0.0;
01988         for (k = 0; k < nx; k++) {
01989             if (max < iff[k]) max = iff[k];
01990         }
01991         
01992         for (k = 0; k < nx; k++) {
01993             weights[k] = iff[k] < 0.0 ? 
01994             0.0 : iff[k] * iff[k] / (max * max);
01995         }
01996         
01997         sum  = 0.0;
01998         sum2 = 0.0;
01999         for (k = 0; k < nx; k++) {
02000             sum  += weights[k] * ip_vnull[k];
02001             sum2 += weights[k];
02002         }
02003 
02004         cpl_free(weights);
02005 
02006         imean = sum / sum2;
02007 
02008         mean_vnull += (imean - mean_vnull) / (j + 1.0);
02009         }
02010     }
02011 
02012     if (dfs_save_image(frameset, pv_im, reduced_v_tag, header, 
02013                parlist, recipe, version))
02014         fors_pmos_extract_exit(NULL);
02015 
02016     if (nscience / 2 > 1) {
02017         char * pipefile, * keyname;
02018         cpl_propertylist * qheader = dfs_load_header(frameset, science_tag, 0);
02019 
02020             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
02021             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
02022             cpl_propertylist_update_double(qheader, "CRVAL1", 
02023                                startwavelength + dispersion/2);
02024             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
02025             /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
02026                cpl_propertylist_update_double(header, "CDELT2", 1.0); */
02027             cpl_propertylist_update_double(qheader, "CD1_1", dispersion);
02028             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
02029             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
02030             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
02031             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
02032             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
02033 
02034         fors_qc_start_group(qheader, "2.0", instrume);
02035 
02036         /*
02037          * QC1 group header
02038          */
02039 
02040         if (fors_qc_write_string("PRO.CATG", reduced_nul_v_tag,
02041                      "Product category", instrume))
02042         fors_pmos_extract_exit("Cannot write product category to "
02043                      "QC log file");
02044 
02045         if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
02046                        "DPR type", instrume))
02047         fors_pmos_extract_exit("Missing keyword DPR TYPE in arc "
02048                      "lamp header");
02049 
02050         if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
02051                        "Template", instrume))
02052         fors_pmos_extract_exit("Missing keyword TPL ID in arc "
02053                      "lamp header");
02054 
02055         if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
02056                        "Grism name", instrume))
02057         fors_pmos_extract_exit("Missing keyword INS GRIS1 NAME in arc "
02058                      "lamp header");
02059 
02060         if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
02061                        "Grism identifier", instrume))
02062         fors_pmos_extract_exit("Missing keyword INS GRIS1 ID in arc "
02063                      "lamp header");
02064 
02065         if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
02066         fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
02067                        "Filter name", instrume);
02068 
02069         if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
02070                        "Collimator name", instrume))
02071         fors_pmos_extract_exit("Missing keyword INS COLL NAME in arc "
02072                      "lamp header");
02073 
02074         if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
02075                        "Chip identifier", instrume))
02076         fors_pmos_extract_exit("Missing keyword DET CHIP1 ID in arc "
02077                      "lamp header");
02078 
02079         if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
02080                        "Archive name of input data", 
02081                        instrume))
02082         fors_pmos_extract_exit("Missing keyword ARCFILE in arc "
02083                      "lamp header");
02084 
02085         pipefile = dfs_generate_filename_tfits(reduced_nul_v_tag);
02086         if (fors_qc_write_string("PIPEFILE", pipefile,
02087                      "Pipeline product name", instrume))
02088         fors_pmos_extract_exit("Cannot write PIPEFILE to QC log file");
02089         cpl_free(pipefile); pipefile = NULL;
02090 
02091 
02092         /*
02093          * QC1 parameters
02094          */
02095 
02096         keyname = "QC.NULL.V.MEAN";
02097             
02098         if (fors_qc_write_qc_double(qheader, mean_vnull,
02099                     keyname, NULL,
02100                     "Mean V null parameter",
02101                     instrume)) {
02102         fors_pmos_extract_exit("Cannot write mean Q null parameter "
02103                      "to QC log file");
02104         }
02105 
02106         fors_qc_end_group();
02107 
02108         if (dfs_save_image(frameset, pvnull_im, reduced_nul_v_tag, qheader, 
02109                    parlist, recipe, version))
02110         fors_pmos_extract_exit(NULL);
02111 
02112         cpl_propertylist_delete(qheader);
02113     }
02114 
02115     if (dfs_save_image(frameset, perr_im, reduced_error_v_tag, header, 
02116                parlist, recipe, version))
02117         fors_pmos_extract_exit(NULL);
02118 
02119     cpl_image_delete(pv_im);
02120     cpl_image_delete(pvnull_im);
02121     cpl_image_delete(perr_im);
02122     } else {
02123     cpl_image        *pq_im          = NULL;
02124     cpl_image        *pu_im          = NULL;
02125     cpl_image        *pl_im          = NULL;
02126 
02127     cpl_image        *pqnull_im      = NULL;
02128     cpl_image        *punull_im      = NULL;
02129 
02130     cpl_image        *pqerr_im        = NULL;
02131     cpl_image        *puerr_im        = NULL;
02132     cpl_image        *plerr_im        = NULL;
02133 
02134     cpl_image        *pang_im        = NULL;
02135     cpl_image        *pangerr_im        = NULL;
02136 
02137     double           *p_q            = NULL;
02138     double           *p_u            = NULL;
02139     double           *p_l            = NULL;
02140 
02141     double           *p_qnull        = NULL;
02142     double           *p_unull        = NULL;
02143 
02144     double           *pqerr           = NULL;
02145     double           *puerr           = NULL;
02146     double           *plerr           = NULL;
02147 
02148     double           *pang           = NULL;
02149     double           *pangerr           = NULL;
02150 
02151     int k, m;
02152 
02153     cpl_image * correct_im = cpl_image_new(nx, 1, CPL_TYPE_DOUBLE);
02154     double    * correct    = cpl_image_get_data_double(correct_im);
02155 
02156     double mean_unull, mean_qnull;
02157 
02158     int p = -1;
02159     int total = 0;
02160 
02161     pq_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02162     pu_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02163     pl_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02164 
02165     pqerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02166     puerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02167     plerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02168 
02169     pang_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02170     pangerr_im   = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02171 
02172     p_q = cpl_image_get_data_double(pq_im);
02173     p_u = cpl_image_get_data_double(pu_im);
02174     p_l = cpl_image_get_data_double(pl_im);
02175 
02176     if (nscience / 4 > 1) {
02177         pqnull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02178         punull_im = cpl_image_new(nx, nobjects, CPL_TYPE_DOUBLE);
02179 
02180         p_qnull = cpl_image_get_data_double(pqnull_im);
02181         p_unull = cpl_image_get_data_double(punull_im);
02182     } else {
02183         cpl_msg_warning(cpl_func, 
02184                 "Not enough pairs to compute null parameters");
02185     }
02186 
02187     pqerr = cpl_image_get_data_double(pqerr_im);
02188     puerr = cpl_image_get_data_double(puerr_im);
02189     plerr = cpl_image_get_data_double(plerr_im);
02190 
02191     pang = cpl_image_get_data_double(pang_im);
02192     pangerr = cpl_image_get_data_double(pangerr_im);
02193 
02194     if (chromatism) {
02195         cpl_table * chrotbl = 
02196         dfs_load_table(frameset, chrom_table_tag, 1);
02197 
02198         int      nrow   = cpl_table_get_nrow(chrotbl);
02199         float  * lambda = cpl_table_get_data_float(chrotbl, "lambda");
02200         float  * theta  = cpl_table_get_data_float(chrotbl, "eps_theta");
02201 
02202         for (j = 0; j < nx; j++) {
02203         double c_wave = 
02204             startwavelength + dispersion / 2 + j * dispersion;
02205         
02206         int found = 0;
02207 
02208         for (k = 0; k < nrow - 1; k++) {
02209             if (lambda[k] <= c_wave && c_wave < lambda[k + 1]) {
02210             found = 1;
02211             break;
02212             }
02213         }
02214 
02215         if (found) {
02216             correct[j] = (theta [k + 1] - theta [k]) /
02217                      (lambda[k + 1] - lambda[k]) *
02218                      (c_wave        - lambda[k])   + theta[k];
02219                     correct[j] *= M_PI / 180;   /* Radians */
02220         }
02221                 else if (j)
02222                     correct[j] = correct[j-1];
02223                 else
02224                     correct[j] = 0.0;
02225 
02226         }
02227 
02228         cpl_table_delete(chrotbl);
02229     }
02230 
02231     for (j = 0; j < nobjects; j++) {
02232         double * ip_q,     * ip_u, * ip_l, 
02233         * ip_qnull, * ip_unull, * ipqerr, * ipuerr, * iplerr,
02234         * ipang, * ipangerr;
02235 
02236         float * data;
02237         float * iffq,  * ierrq, * iffu, * ierru;
02238 
02239         int pos, pos_d;
02240 
02241         ip_q = p_q + (nobjects - 1 - j) * nx;
02242         ip_u = p_u + (nobjects - 1 - j) * nx;
02243         ip_l = p_l + (nobjects - 1 - j) * nx;
02244 
02245         if (nscience / 4 > 1) {
02246         ip_qnull = p_qnull + (nobjects - 1 - j) * nx;
02247         ip_unull = p_unull + (nobjects - 1 - j) * nx;
02248         }
02249 
02250         ipqerr = pqerr + (nobjects - 1 - j) * nx;
02251         ipuerr = puerr + (nobjects - 1 - j) * nx;
02252         iplerr = plerr + (nobjects - 1 - j) * nx;
02253 
02254         ipang = pang + (nobjects - 1 - j) * nx;
02255         ipangerr = pangerr + (nobjects - 1 - j) * nx;
02256 
02257         while (total < j + 1) {
02258         p++;
02259         total += nobjs_per_slit[p];
02260         }
02261 
02262         for (k = 0; k < nscience / 4; k++) {
02263         float * if_o,  * if_e,  * ifdelta_o, * ifdelta_e;
02264 
02265         /* First P_Q */
02266 
02267         pos   = fors_find_angle_pos(angles, nscience, 90 * k);
02268         pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 45);
02269 
02270         data = cpl_image_get_data_float(reduceds[pos]);
02271 
02272         if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02273                    + (total - j - 1)) * nx;
02274 
02275         if_e = data + (2 * (nobjects - total) 
02276                    + (total - j - 1)) * nx;
02277 
02278 //      if_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
02279 //      if_e = data +  2 * (nobjects - 1 - j)      * nx;
02280 
02281         data = cpl_image_get_data_float(reduceds[pos_d]);
02282 
02283         ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02284                    + (total - j - 1)) * nx;
02285 
02286         ifdelta_e = data + (2 * (nobjects - total) 
02287                    + (total - j - 1)) * nx;
02288 
02289 //      ifdelta_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
02290 //      ifdelta_e = data +  2 * (nobjects - 1 - j)      * nx;
02291 
02292         for (m = 0; m < nx; m++) {
02293 
02294             double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
02295             (if_o[m]      - if_e[m]     ) /
02296             (if_o[m]      + if_e[m]     ) -
02297             (ifdelta_o[m] - ifdelta_e[m]) /
02298             (ifdelta_o[m] + ifdelta_e[m]);
02299 
02300             quantity = isfinite(quantity) ? quantity : 0.0;
02301 
02302             /* PQ map computation */
02303             ip_q[m] += quantity * 0.5 / (nscience / 4);
02304 
02305             /* PQnull map computation */
02306             if (nscience / 4 > 1) {
02307             if (k % 2)
02308                 ip_qnull[m] += quantity * 0.5 / (nscience / 4);
02309             else
02310                 ip_qnull[m] -= quantity * 0.5 / (nscience / 4);
02311             }
02312         }
02313 
02314         /* Now P_U */
02315 
02316         pos   = fors_find_angle_pos(angles, nscience, 90 * k + 22.5);
02317         pos_d = fors_find_angle_pos(angles, nscience, 90 * k + 67.5);
02318 
02319         data = cpl_image_get_data_float(reduceds[pos]);
02320 
02321         if_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02322                    + (total - j - 1)) * nx;
02323 
02324         if_e = data + (2 * (nobjects - total) 
02325                    + (total - j - 1)) * nx;
02326 
02327 //      if_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
02328 //      if_e = data +  2 * (nobjects - 1 - j)      * nx;
02329 
02330         data = cpl_image_get_data_float(reduceds[pos_d]);
02331 
02332         ifdelta_o = data + (2 * (nobjects - total) + nobjs_per_slit[p] 
02333                    + (total - j - 1)) * nx;
02334 
02335         ifdelta_e = data + (2 * (nobjects - total) 
02336                    + (total - j - 1)) * nx;
02337 
02338 //      ifdelta_o = data + (2 * (nobjects - 1 - j) + 1) * nx;
02339 //      ifdelta_e = data +  2 * (nobjects - 1 - j)      * nx;
02340 
02341         for (m = 0; m < nx; m++) {
02342 
02343             double quantity = fabs(if_o[m] + if_e[m]) < FLT_MIN ? 0.0 :
02344             (if_o[m]      - if_e[m]     ) /
02345             (if_o[m]      + if_e[m]     ) -
02346             (ifdelta_o[m] - ifdelta_e[m]) /
02347             (ifdelta_o[m] + ifdelta_e[m]);
02348 
02349             quantity = isfinite(quantity) ? quantity : 0.0;
02350 
02351             /* PU map computation */
02352             ip_u[m] += quantity * 0.5 / (nscience / 4);
02353 
02354             /* PUnull map computation */
02355             if (nscience / 4 > 1) {
02356             if (k % 2)
02357                 ip_unull[m] += quantity * 0.5 / (nscience / 4);
02358             else
02359                 ip_unull[m] -= quantity * 0.5 / (nscience / 4);
02360             }
02361         }
02362         }
02363 
02364         /* Error map */
02365 
02366         pos   = fors_find_angle_pos(angles, nscience, 0.0);
02367 
02368         data = cpl_image_get_data_float(reduceds[pos]);
02369         iffq  = data +  2 * (nobjects - 1 - j)      * nx;
02370 
02371         data = cpl_image_get_data_float(rerrors[pos]);
02372         ierrq = data +  2 * (nobjects - 1 - j)      * nx;
02373         
02374         pos   = fors_find_angle_pos(angles, nscience, 22.5);
02375 
02376         data = cpl_image_get_data_float(reduceds[pos]);
02377         iffu  = data +  2 * (nobjects - 1 - j)      * nx;
02378 
02379         data = cpl_image_get_data_float(rerrors[pos]);
02380         ierru = data +  2 * (nobjects - 1 - j)      * nx;
02381 
02382         for (m = 0; m < nx; m++) {
02383 
02384         double radicand; 
02385 
02386         ipqerr[m] = iffq[m] <= 0.0 ? 
02387             0.0 : ierrq[m] / iffq[m] * 0.5 / sqrt (nscience / 4);
02388 
02389         ipuerr[m] = iffu[m] <= 0.0 ? 
02390             0.0 : ierru[m] / iffu[m] * 0.5 / sqrt (nscience / 4);
02391 
02392         iplerr[m] = CPL_MATH_SQRT1_2 * 0.5 * (ipqerr[m] + ipuerr[m]);
02393 
02394 /* Added: */
02395         if (chromatism) {
02396             ip_q[m] = ip_q[m] * cos(2 * correct[m]) - 
02397                               ip_u[m] * sin(2 * correct[m]);
02398 
02399             ip_u[m] = ip_q[m] * sin(2 * correct[m]) + 
02400                               ip_u[m] * cos(2 * correct[m]);
02401         }
02402 /* End added */
02403 
02404         /* PL computation */
02405         ip_l[m] = sqrt(ip_u[m] * ip_u[m] + ip_q[m] * ip_q[m]);
02406 
02407         /* P angle computation */
02408         ipang[m] = (ip_q[m] == 0.0 ?
02409             (ip_u[m] > 0.0 ? 45.0 : 135.0)
02410             : 0.5 * (atan2(ip_u[m], ip_q[m]) * 180 / M_PI + 
02411                  ((atan2(ip_u[m], ip_q[m]) > 0.0 ? 0.0 : 360.0))));
02412 
02413         /* Error on the angle computation */
02414         radicand = ip_q[m] * ip_q[m] * ipuerr[m] * ipuerr[m] + 
02415                    ip_u[m] * ip_u[m] * ipqerr[m] * ipqerr[m];
02416   
02417         ipangerr[m] = ip_l[m] == 0.0 ? 0.0 :
02418              sqrt(radicand) * 0.5 / (ip_l[m] * ip_l[m]) * 180 / M_PI;
02419 
02420                 /* 
02421                  * Note: no need to apply chromatism correction to angle,
02422                  * it is implicit in Q and U correction applied before.
02423                  */
02424 
02425 /* Removed: 
02426         if (chromatism) {
02427             ipang[m] -= correct[m];
02428         
02429             ip_q[m] = ip_q[m] * cos(2 * correct[m]) - 
02430                               ip_u[m] * sin(2 * correct[m]);
02431 
02432             ip_u[m] = ip_q[m] * sin(2 * correct[m]) + 
02433                               ip_u[m] * cos(2 * correct[m]);
02434         
02435         }
02436   end removed */
02437         }
02438 
02439         if (nscience / 4 > 1) {
02440         float * weights;
02441         float   max, sum, sum2, imean;
02442 
02443         int k;
02444 
02445         /* QC on U NULL */
02446         weights = cpl_malloc(sizeof(float) * nx);
02447 
02448         max = 0.0;
02449         for (k = 0; k < nx; k++) {
02450             if (max < iffq[k]) max = iffq[k];
02451         }
02452         
02453         for (k = 0; k < nx; k++) {
02454             weights[k] = iffq[k] < 0.0 ? 
02455             0.0 : iffq[k] * iffq[k] / (max * max);
02456         }
02457         
02458         sum  = 0.0;
02459         sum2 = 0.0;
02460         for (k = 0; k < nx; k++) {
02461             sum  += weights[k] * ip_qnull[k];
02462             sum2 += weights[k];
02463         }
02464 
02465         cpl_free(weights);
02466 
02467         imean = sum / sum2;
02468 
02469         mean_qnull += (imean - mean_qnull) / (j + 1.0);
02470           
02471         /* QC on U NULL */
02472         weights = cpl_malloc(sizeof(float) * nx);
02473         
02474         max = 0.0;
02475         for (k = 0; k < nx; k++) {
02476             if (max < iffu[k]) max = iffu[k];
02477         }
02478         
02479         for (k = 0; k < nx; k++) {
02480             weights[k] = iffu[k] < 0.0 ? 
02481             0.0 : iffu[k] * iffu[k] / (max * max);
02482         }
02483         
02484         sum  = 0.0;
02485         sum2 = 0.0;
02486         for (k = 0; k < nx; k++) {
02487             sum  += weights[k] * ip_unull[k];
02488             sum2 += weights[k];
02489         }
02490 
02491         cpl_free(weights);
02492 
02493         imean = sum / sum2;
02494 
02495         mean_unull += (imean - mean_unull) / (j + 1.0);
02496         }
02497     }
02498 
02499     cpl_image_delete(correct_im);
02500 
02501     if (dfs_save_image(frameset, pq_im, reduced_q_tag, header, 
02502                parlist, recipe, version))
02503         fors_pmos_extract_exit(NULL);
02504 
02505     if (dfs_save_image(frameset, pu_im, reduced_u_tag, header, 
02506                parlist, recipe, version))
02507         fors_pmos_extract_exit(NULL);
02508 
02509     if (dfs_save_image(frameset, pl_im, reduced_l_tag, header, 
02510                parlist, recipe, version))
02511         fors_pmos_extract_exit(NULL);
02512 
02513     if (nscience / 4 > 1) {
02514         char *pipefile; 
02515             char *keyname;
02516         cpl_propertylist *qheader = dfs_load_header(frameset, 
02517                                                         science_tag, 0);
02518 
02519             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
02520             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
02521             cpl_propertylist_update_double(qheader, "CRVAL1", 
02522                                startwavelength + dispersion/2);
02523             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
02524             /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
02525                cpl_propertylist_update_double(header, "CDELT2", 1.0); */
02526             cpl_propertylist_update_double(qheader, "CD1_1", dispersion);
02527             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
02528             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
02529             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
02530             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
02531             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
02532 
02533         fors_qc_start_group(qheader, "2.0", instrume);
02534 
02535         /*
02536          * QC1 group header
02537          */
02538 
02539         if (fors_qc_write_string("PRO.CATG", reduced_nul_q_tag,
02540                      "Product category", instrume))
02541         fors_pmos_extract_exit("Cannot write product category to "
02542                      "QC log file");
02543 
02544         if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
02545                        "DPR type", instrume))
02546         fors_pmos_extract_exit("Missing keyword DPR TYPE in arc "
02547                      "lamp header");
02548 
02549         if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
02550                        "Template", instrume))
02551         fors_pmos_extract_exit("Missing keyword TPL ID in arc "
02552                      "lamp header");
02553 
02554         if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
02555                        "Grism name", instrume))
02556         fors_pmos_extract_exit("Missing keyword INS GRIS1 NAME in arc "
02557                      "lamp header");
02558 
02559         if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
02560                        "Grism identifier", instrume))
02561         fors_pmos_extract_exit("Missing keyword INS GRIS1 ID in arc "
02562                      "lamp header");
02563 
02564         if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
02565         fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
02566                        "Filter name", instrume);
02567 
02568         if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
02569                        "Collimator name", instrume))
02570         fors_pmos_extract_exit("Missing keyword INS COLL NAME in arc "
02571                      "lamp header");
02572 
02573         if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
02574                        "Chip identifier", instrume))
02575         fors_pmos_extract_exit("Missing keyword DET CHIP1 ID in arc "
02576                      "lamp header");
02577 
02578         if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
02579                        "Archive name of input data", 
02580                        instrume))
02581         fors_pmos_extract_exit("Missing keyword ARCFILE in arc "
02582                      "lamp header");
02583 
02584         pipefile = dfs_generate_filename_tfits(reduced_nul_q_tag);
02585         if (fors_qc_write_string("PIPEFILE", pipefile,
02586                      "Pipeline product name", instrume))
02587         fors_pmos_extract_exit("Cannot write PIPEFILE to QC log file");
02588         cpl_free(pipefile); pipefile = NULL;
02589 
02590 
02591         /*
02592          * QC1 parameters
02593          */
02594 
02595         keyname = "QC.NULL.Q.MEAN";
02596             
02597         if (fors_qc_write_qc_double(qheader, mean_qnull,
02598                     keyname, NULL,
02599                     "Mean Q null parameter",
02600                     instrume)) {
02601         fors_pmos_extract_exit("Cannot write mean Q null parameter "
02602                      "to QC log file");
02603         }
02604 
02605         fors_qc_end_group();
02606 
02607         if (dfs_save_image(frameset, pqnull_im, reduced_nul_q_tag, qheader, 
02608                    parlist, recipe, version))
02609         fors_pmos_extract_exit(NULL);
02610 
02611         cpl_propertylist_delete(qheader);
02612 
02613         qheader = dfs_load_header(frameset, science_tag, 0);
02614 
02615             cpl_propertylist_update_double(qheader, "CRPIX1", 1.0);
02616             cpl_propertylist_update_double(qheader, "CRPIX2", 1.0);
02617             cpl_propertylist_update_double(qheader, "CRVAL1", 
02618                                startwavelength + dispersion/2);
02619             cpl_propertylist_update_double(qheader, "CRVAL2", 1.0);
02620             /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
02621                cpl_propertylist_update_double(header, "CDELT2", 1.0); */
02622             cpl_propertylist_update_double(qheader, "CD1_1", dispersion);
02623             cpl_propertylist_update_double(qheader, "CD1_2", 0.0);
02624             cpl_propertylist_update_double(qheader, "CD2_1", 0.0);
02625             cpl_propertylist_update_double(qheader, "CD2_2", 1.0);
02626             cpl_propertylist_update_string(qheader, "CTYPE1", "LINEAR");
02627             cpl_propertylist_update_string(qheader, "CTYPE2", "PIXEL");
02628 
02629         fors_qc_start_group(qheader, "2.0", instrume);
02630 
02631         /*
02632          * QC1 group header
02633          */
02634 
02635         if (fors_qc_write_string("PRO.CATG", reduced_nul_u_tag,
02636                      "Product category", instrume))
02637         fors_pmos_extract_exit("Cannot write product category to "
02638                      "QC log file");
02639 
02640         if (fors_qc_keyword_to_paf(qheader, "ESO DPR TYPE", NULL,
02641                        "DPR type", instrume))
02642         fors_pmos_extract_exit("Missing keyword DPR TYPE in arc "
02643                      "lamp header");
02644 
02645         if (fors_qc_keyword_to_paf(qheader, "ESO TPL ID", NULL,
02646                        "Template", instrume))
02647         fors_pmos_extract_exit("Missing keyword TPL ID in arc "
02648                      "lamp header");
02649 
02650         if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 NAME", NULL,
02651                        "Grism name", instrume))
02652         fors_pmos_extract_exit("Missing keyword INS GRIS1 NAME in arc "
02653                      "lamp header");
02654 
02655         if (fors_qc_keyword_to_paf(qheader, "ESO INS GRIS1 ID", NULL,
02656                        "Grism identifier", instrume))
02657         fors_pmos_extract_exit("Missing keyword INS GRIS1 ID in arc "
02658                      "lamp header");
02659 
02660         if (cpl_propertylist_has(qheader, "ESO INS FILT1 NAME"))
02661         fors_qc_keyword_to_paf(qheader, "ESO INS FILT1 NAME", NULL,
02662                        "Filter name", instrume);
02663 
02664         if (fors_qc_keyword_to_paf(qheader, "ESO INS COLL NAME", NULL,
02665                        "Collimator name", instrume))
02666         fors_pmos_extract_exit("Missing keyword INS COLL NAME in arc "
02667                      "lamp header");
02668 
02669         if (fors_qc_keyword_to_paf(qheader, "ESO DET CHIP1 ID", NULL,
02670                        "Chip identifier", instrume))
02671         fors_pmos_extract_exit("Missing keyword DET CHIP1 ID in arc "
02672                      "lamp header");
02673 
02674         if (fors_qc_keyword_to_paf(qheader, "ARCFILE", NULL,
02675                        "Archive name of input data", 
02676                        instrume))
02677         fors_pmos_extract_exit("Missing keyword ARCFILE in arc "
02678                      "lamp header");
02679 
02680         pipefile = dfs_generate_filename_tfits(reduced_nul_u_tag);
02681         if (fors_qc_write_string("PIPEFILE", pipefile,
02682                      "Pipeline product name", instrume))
02683         fors_pmos_extract_exit("Cannot write PIPEFILE to QC log file");
02684         cpl_free(pipefile); pipefile = NULL;
02685 
02686 
02687         /*
02688          * QC1 parameters
02689          */
02690 
02691         keyname = "QC.NULL.U.MEAN";
02692             
02693         if (fors_qc_write_qc_double(qheader, mean_unull,
02694                     keyname, NULL,
02695                     "Mean U null parameter",
02696                     instrume)) {
02697         fors_pmos_extract_exit("Cannot write mean U null parameter "
02698                      "to QC log file");
02699         }
02700 
02701         fors_qc_end_group();
02702 
02703         if (dfs_save_image(frameset, punull_im, reduced_nul_u_tag, qheader, 
02704                    parlist, recipe, version))
02705         fors_pmos_extract_exit(NULL);
02706 
02707         cpl_propertylist_delete(qheader);
02708     }
02709 
02710     if (dfs_save_image(frameset, pqerr_im, reduced_error_q_tag, header, 
02711                parlist, recipe, version))
02712         fors_pmos_extract_exit(NULL);
02713 
02714     if (dfs_save_image(frameset, puerr_im, reduced_error_u_tag, header, 
02715                parlist, recipe, version))
02716         fors_pmos_extract_exit(NULL);
02717 
02718     if (dfs_save_image(frameset, plerr_im, reduced_error_l_tag, header, 
02719                parlist, recipe, version))
02720         fors_pmos_extract_exit(NULL);
02721 
02722     if (dfs_save_image(frameset, pang_im, reduced_angle_tag, header, 
02723                parlist, recipe, version))
02724         fors_pmos_extract_exit(NULL);
02725 
02726     if (dfs_save_image(frameset, pangerr_im, reduced_error_angle_tag, 
02727                header, parlist, recipe, version))
02728         fors_pmos_extract_exit(NULL);
02729 
02730     cpl_image_delete(pq_im);
02731     cpl_image_delete(pu_im);
02732     cpl_image_delete(pl_im);
02733 
02734     cpl_image_delete(pqnull_im);
02735     cpl_image_delete(punull_im);
02736 
02737     cpl_image_delete(pqerr_im);
02738     cpl_image_delete(puerr_im);
02739     cpl_image_delete(plerr_im);
02740     cpl_image_delete(pang_im);
02741     cpl_image_delete(pangerr_im);
02742     }
02743 
02744     cpl_propertylist_delete(header);
02745 
02746     /* End of Stokes computation */
02747 
02748     for (j = 0; j < nscience; j++) {
02749     cpl_image_delete(reduceds[j]);
02750     cpl_image_delete(rerrors[j]);
02751     cpl_table_delete(slitss[j]);
02752     cpl_image_delete(mappeds[j]);
02753     }
02754 
02755     cpl_free(reduceds);
02756     cpl_free(rerrors);
02757     cpl_free(slitss);
02758     cpl_free(mappeds);
02759 
02760     cpl_free(instrume); instrume = NULL;
02761 
02762     cpl_free(skylocalmaps);
02763     cpl_free(nobjs_per_slit);
02764 
02765     if (cpl_error_get_code()) {
02766         cpl_msg_error(cpl_error_get_where(), cpl_error_get_message());
02767         fors_pmos_extract_exit(NULL);
02768     }
02769     else 
02770         return 0;
02771 }
02772 
02773 /*----------------------------------------------------------------------------*/
02784 /*----------------------------------------------------------------------------*/
02785 static float * fors_check_angles(cpl_frameset * frameset,
02786                  int pmos, const char *tag, int * circ)
02787 {
02788     float     *angles  = NULL;
02789     cpl_frame *c_frame = NULL;
02790     char      *ret_id  = NULL;
02791 
02792     int i = 0;
02793 
02794     angles = cpl_malloc(sizeof(float) * pmos);
02795 
02796     for (c_frame  = cpl_frameset_find(frameset, tag);
02797      c_frame != NULL; c_frame = cpl_frameset_find(frameset, NULL)) {
02798 
02799         cpl_propertylist * header =
02800         cpl_propertylist_load(cpl_frame_get_filename(c_frame), 0);
02801     
02802     if (!ret_id) {
02803         ret_id = cpl_strdup(cpl_propertylist_get_string(header, 
02804                                                         "ESO INS OPTI4 ID"));
02805 
02806         if (ret_id[1] != '5' && ret_id[1] != '4') {
02807         cpl_msg_error(cpl_func, 
02808                   "Unknown retarder plate id: %s", ret_id);
02809         return NULL;
02810         }
02811     } else {
02812         char * c_ret_id = (char *)
02813         cpl_propertylist_get_string(header, "ESO INS OPTI4 ID");
02814         if (ret_id[1] != c_ret_id[1]) {
02815         cpl_msg_error(cpl_func, "Input frames are not from the same "
02816                   "retarder plate");
02817         return NULL;
02818         }
02819     }
02820     
02821     if (ret_id[1] == '5') {  /* Linear polarimetry */
02822         angles[i] = (float)
02823         cpl_propertylist_get_double(header, "ESO INS RETA2 ROT");
02824         *circ = 0;
02825     } else {                 /* Circular polarimetry */
02826         angles[i] = (float)
02827         cpl_propertylist_get_double(header, "ESO INS RETA4 ROT");
02828         *circ = 1;
02829     }
02830 
02831         cpl_propertylist_delete(header);
02832     i++;
02833     }
02834 
02835     cpl_free(ret_id);
02836 
02837     if (*circ) {
02838     if (pmos != 2 && pmos != 4) {
02839         cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
02840                           "found, but either 2 or 4 are required for "
02841                           "circular polarization measurements!", pmos);
02842         return NULL;
02843     }
02844     } else {
02845     if (pmos != 4 && pmos != 8 && pmos != 16) {
02846         cpl_msg_error(cpl_func, "Wrong angle configuration: %d angles "
02847                           "found, but either 4, 8, or 16 are required for "
02848                           "linear polarization measurements!", pmos);
02849         return NULL;
02850     }
02851     }
02852     
02853     /* Check completeness */
02854 
02855     if (*circ) {
02856         for (i = 0; i < pmos; i++) {
02857             if (fors_find_angle_pos(angles, pmos, 90.0 * i - 45.0) < 0) {
02858                 const char *cangles;
02859                 switch (pmos) {
02860                 case 2: cangles  = "-45.0, 45.0"; break;
02861                 case 4: cangles  = "-45.0, 45.0, 135.0, 225.0"; break;
02862                 default: assert(0);
02863                 }  
02864 
02865             cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
02866                               "angle %.2f. All angles %s must be provided.",
02867                               angles[i], cangles);
02868                 return NULL;
02869             }
02870         }
02871     }
02872     else {
02873         for (i = 0; i < pmos; i++) {
02874             if (fors_find_angle_pos(angles, pmos, 22.5 * i) < 0) {
02875                 const char *cangles;
02876                 switch (pmos) {
02877                 case 4: cangles  = "0.0, 22.5, 45.0, 67.5"; break;
02878                 case 8: cangles  = "0.0, 22.5, 45.0, 67.5, "
02879                                    "90.0, 112.5, 135.0, 157.5"; break;
02880                 case 16: cangles = "0.0, 22.5, 45.0, 67.5, "
02881                                    "90.0, 112.5, 135.0, 157.5, "
02882                                    "180.0, 202.5, 225.0, 247.5, "
02883                                    "270.0, 292.5, 315.0, 337.5"; break;
02884                 default: assert(0);
02885                 }  
02886 
02887             cpl_msg_error(cpl_func, "Wrong angle configuration: missing "
02888                               "angle %.2f. All angles %s must be provided.",
02889                               angles[i], cangles);
02890                 return NULL;
02891             }
02892         }
02893     }
02894 
02895     return angles;
02896 }
02897 
02898 /*----------------------------------------------------------------------------*/
02906 /*----------------------------------------------------------------------------*/
02907 static int
02908 fors_find_angle_pos(float * angles, int nangles, float angle)
02909 {
02910     int i, match = 0;
02911 
02912     for (i = 0; i < nangles; i++) {
02913     if (fabs(angles[i]         - angle) < 1.0 || 
02914         fabs(angles[i] - 360.0 - angle) < 1.0) {
02915         match = 1;
02916         break;
02917     }
02918     }
02919 
02920     return match ? i : -1;
02921 }