FORS Pipeline Reference Manual 4.9.20
fors_calib.c
00001 /* $Id: fors_calib.c,v 1.28 2013/02/28 15:17:34 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:17:34 $
00024  * $Revision: 1.28 $
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 <cpl.h>
00035 #include <moses.h>
00036 #include <fors_stack.h>
00037 #include <fors_dfs.h>
00038 #include <fors_header.h>
00039 
00040 #ifdef __cplusplus
00041 extern "C"
00042 #endif
00043 int cpl_plugin_get_info(cpl_pluginlist * list);
00044 
00045 static int fors_calib_create(cpl_plugin *);
00046 static int fors_calib_exec(cpl_plugin *);
00047 static int fors_calib_destroy(cpl_plugin *);
00048 static int fors_calib(cpl_parameterlist *, cpl_frameset *);
00049 
00050 static char fors_calib_description[] =
00051 "This recipe is used to identify reference lines on LSS, MOS and MXU arc lamp\n"
00052 "exposures, and trace the spectral edges on the corresponding flat field\n"
00053 "exposures. This information is used to determine the spectral extraction\n"
00054 "mask to be applied in the scientific data reduction, performed with the\n"
00055 "recipe fors_science.\n"
00056 "This recipe accepts both FORS1 and FORS2 frames. The input arc lamp and\n"
00057 "flat field exposures are assumed to be obtained quasi-simultaneously, so\n"
00058 "that they would be described by exactly the same instrument distortions.\n"
00059 "A line catalog must be specified, containing the wavelengths of the\n"
00060 "reference arc lamp lines used for the wavelength calibration. A grism\n"
00061 "table (typically depending on the instrument mode, and in particular on\n"
00062 "the grism used) may also be specified: this table contains a default\n"
00063 "recipe parameter setting to control the way spectra are extracted for\n"
00064 "a specific instrument mode, as it is used for automatic run of the\n"
00065 "pipeline on Paranal and in Garching. If this table is specified, it\n"
00066 "will modify the default recipe parameter setting, with the exception of\n"
00067 "those parameters which have been explicitly modified on the command line.\n"
00068 "If a grism table is not specified, the input recipe parameters values\n"
00069 "will always be read from the command line, or from an esorex configuration\n"
00070 "file if present, or from their generic default values (that are rarely\n"
00071 "meaningful). Finally a master bias frame must be input to this recipe.\n" 
00072 "In the table below the MXU acronym can be read alternatively as MOS\n"
00073 "and LSS, with the exception of CURV_COEFF_LSS, CURV_TRACES_LSS,\n"
00074 "SPATIAL_MAP_LSS, SPECTRA_DETECTION_LSS, and and SLIT_MAP_LSS, which are\n" 
00075 "never created. The products SPECTRA_DETECTION_MXU, SLIT_MAP_MXU, and\n" 
00076 "DISP_RESIDUALS_MXU, are just created if the --check parameter is set to\n" 
00077 "true. The product GLOBAL_DISTORTION_TABLE is just created if more than 12\n" 
00078 "separate spectra are found in the CCD.\n\n"
00079 "Input files:\n\n"
00080 "  DO category:             Type:       Explanation:         Required:\n"
00081 "  SCREEN_FLAT_MXU          Raw         Flat field exposures    Y\n"
00082 "  LAMP_MXU                 Raw         Arc lamp exposure       Y\n"
00083 "  MASTER_BIAS or BIAS      Calib       Bias frame              Y\n"
00084 "  MASTER_LINECAT           Calib       Line catalog            Y\n"
00085 "  GRISM_TABLE              Calib       Grism table             .\n\n"
00086 "Output files:\n\n"
00087 "  DO category:             Data type:  Explanation:\n"
00088 "  MASTER_SCREEN_FLAT_MXU   FITS image  Combined (sum) flat field\n"
00089 "  MASTER_NORM_FLAT_MXU     FITS image  Normalised flat field\n"
00090 "  MAPPED_SCREEN_FLAT_MXU   FITS image  Wavelength calibrated flat field\n"
00091 "  MAPPED_NORM_FLAT_MXU     FITS image  Wavelength calibrated normalised flat\n"
00092 "  REDUCED_LAMP_MXU         FITS image  Wavelength calibrated arc spectrum\n"
00093 "  DISP_COEFF_MXU           FITS table  Inverse dispersion coefficients\n"
00094 "  DISP_RESIDUALS_MXU       FITS image  Residuals in wavelength calibration\n"
00095 "  DISP_RESIDUALS_TABLE_MXU FITS table  Residuals in wavelength calibration\n"
00096 "  DELTA_IMAGE_MXU          FITS image  Offset vs linear wavelength calib\n"
00097 "  WAVELENGTH_MAP_MXU       FITS image  Wavelength for each pixel on CCD\n"
00098 "  SPECTRA_DETECTION_MXU    FITS image  Check for preliminary detection\n"
00099 "  SLIT_MAP_MXU             FITS image  Map of central wavelength on CCD\n"
00100 "  CURV_TRACES_MXU          FITS table  Spectral curvature traces\n"
00101 "  CURV_COEFF_MXU           FITS table  Spectral curvature coefficients\n"
00102 "  SPATIAL_MAP_MXU          FITS image  Spatial position along slit on CCD\n"
00103 "  SPECTRAL_RESOLUTION_MXU  FITS table  Resolution at reference arc lines\n"
00104 "  SLIT_LOCATION_MXU        FITS table  Slits on product frames and CCD\n"
00105 "  GLOBAL_DISTORTION_TABLE  FITS table  Global distortions table\n\n";
00106 
00107 #define fors_calib_exit(message)                      \
00108 {                                                     \
00109 if (message != NULL ) cpl_msg_error(recipe, message);  \
00110 cpl_free(instrume);                                   \
00111 cpl_free(pipefile);                                   \
00112 cpl_free(fiterror);                                   \
00113 cpl_free(fitlines);                                   \
00114 cpl_image_delete(bias);                               \
00115 cpl_image_delete(master_bias);                        \
00116 cpl_image_delete(coordinate);                         \
00117 cpl_image_delete(checkwave);                          \
00118 cpl_image_delete(flat);                               \
00119 cpl_image_delete(master_flat);                        \
00120 cpl_image_delete(added_flat);                         \
00121 cpl_image_delete(norm_flat);                          \
00122 cpl_image_delete(rainbow);                            \
00123 cpl_image_delete(rectified);                          \
00124 cpl_image_delete(residual);                           \
00125 cpl_image_delete(smo_flat);                           \
00126 cpl_image_delete(spatial);                            \
00127 cpl_image_delete(spectra);                            \
00128 cpl_image_delete(wavemap);                            \
00129 cpl_image_delete(delta);                              \
00130 cpl_image_delete(rect_flat);                          \
00131 cpl_image_delete(rect_nflat);                         \
00132 cpl_image_delete(mapped_flat);                        \
00133 cpl_image_delete(mapped_nflat);                       \
00134 cpl_mask_delete(refmask);                             \
00135 cpl_propertylist_delete(header);                      \
00136 cpl_propertylist_delete(save_header);                 \
00137 cpl_propertylist_delete(qclist);                      \
00138 cpl_table_delete(grism_table);                        \
00139 cpl_table_delete(idscoeff);                           \
00140 cpl_table_delete(idscoeff_all);                       \
00141 cpl_table_delete(restable);                           \
00142 cpl_table_delete(maskslits);                          \
00143 cpl_table_delete(overscans);                          \
00144 cpl_table_delete(traces);                             \
00145 cpl_table_delete(polytraces);                         \
00146 cpl_table_delete(slits);                              \
00147 cpl_table_delete(restab);                             \
00148 cpl_table_delete(global);                             \
00149 cpl_table_delete(wavelengths);                        \
00150 cpl_vector_delete(lines);                             \
00151 cpl_msg_indent_less();                                \
00152 return -1;                                            \
00153 }
00154 
00155 #define fors_calib_exit_memcheck(message)              \
00156 {                                                      \
00157 if (message !=NULL ) cpl_msg_info(recipe, message);    \
00158 printf("free instrume (%p)\n", instrume);              \
00159 cpl_free(instrume);                                    \
00160 printf("free pipefile (%p)\n", pipefile);              \
00161 cpl_free(pipefile);                                    \
00162 printf("free fiterror (%p)\n", fiterror);              \
00163 cpl_free(fiterror);                                    \
00164 printf("free fitlines (%p)\n", fitlines);              \
00165 cpl_free(fitlines);                                    \
00166 printf("free bias (%p)\n", bias);                      \
00167 cpl_image_delete(bias);                                \
00168 printf("free master_bias (%p)\n", master_bias);        \
00169 cpl_image_delete(master_bias);                         \
00170 printf("free coordinate (%p)\n", coordinate);          \
00171 cpl_image_delete(coordinate);                          \
00172 printf("free checkwave (%p)\n", checkwave);            \
00173 cpl_image_delete(checkwave);                           \
00174 printf("free flat (%p)\n", flat);                      \
00175 cpl_image_delete(flat);                                \
00176 printf("free master_flat (%p)\n", master_flat);        \
00177 cpl_image_delete(master_flat);                         \
00178 printf("free norm_flat (%p)\n", norm_flat);            \
00179 cpl_image_delete(norm_flat);                           \
00180 printf("free mapped_flat (%p)\n", mapped_flat);        \
00181 cpl_image_delete(mapped_flat);                         \
00182 printf("free mapped_nflat (%p)\n", mapped_nflat);      \
00183 cpl_image_delete(mapped_nflat);                        \
00184 printf("free rainbow (%p)\n", rainbow);                \
00185 cpl_image_delete(rainbow);                             \
00186 printf("free rectified (%p)\n", rectified);            \
00187 cpl_image_delete(rectified);                           \
00188 printf("free residual (%p)\n", residual);              \
00189 cpl_image_delete(residual);                            \
00190 printf("free smo_flat (%p)\n", smo_flat);              \
00191 cpl_image_delete(smo_flat);                            \
00192 printf("free spatial (%p)\n", spatial);                \
00193 cpl_image_delete(spatial);                             \
00194 printf("free spectra (%p)\n", spectra);                \
00195 cpl_image_delete(spectra);                             \
00196 printf("free wavemap (%p)\n", wavemap);                \
00197 cpl_image_delete(wavemap);                             \
00198 printf("free delta (%p)\n", delta);                    \
00199 cpl_image_delete(delta);                               \
00200 printf("free rect_flat (%p)\n", rect_flat);            \
00201 cpl_image_delete(rect_flat);                           \
00202 printf("free rect_nflat (%p)\n", rect_nflat);          \
00203 cpl_image_delete(rect_nflat);                          \
00204 printf("free refmask (%p)\n", refmask);                \
00205 cpl_mask_delete(refmask);                              \
00206 printf("free header (%p)\n", header);                  \
00207 cpl_propertylist_delete(header);                       \
00208 printf("free save_header (%p)\n", save_header);        \
00209 cpl_propertylist_delete(save_header);                  \
00210 printf("free qclist (%p)\n", qclist);                  \
00211 cpl_propertylist_delete(qclist);                       \
00212 printf("free grism_table (%p)\n", grism_table);        \
00213 cpl_table_delete(grism_table);                         \
00214 printf("free idscoeff (%p)\n", idscoeff);              \
00215 cpl_table_delete(idscoeff);                            \
00216 printf("free idscoeff_all (%p)\n", idscoeff_all);      \
00217 cpl_table_delete(idscoeff_all);                        \
00218 printf("free restable (%p)\n", restable);              \
00219 cpl_table_delete(restable);                            \
00220 printf("free maskslits (%p)\n", maskslits);            \
00221 cpl_table_delete(maskslits);                           \
00222 printf("free overscans (%p)\n", overscans);            \
00223 cpl_table_delete(overscans);                           \
00224 printf("free traces (%p)\n", traces);                  \
00225 cpl_table_delete(traces);                              \
00226 printf("free polytraces (%p)\n", polytraces);          \
00227 cpl_table_delete(polytraces);                          \
00228 printf("free slits (%p)\n", slits);                    \
00229 cpl_table_delete(slits);                               \
00230 printf("free restab (%p)\n", restab);                  \
00231 cpl_table_delete(restab);                              \
00232 printf("free global (%p)\n", global);                  \
00233 cpl_table_delete(global);                              \
00234 printf("free wavelengths (%p)\n", wavelengths);        \
00235 cpl_table_delete(wavelengths);                         \
00236 printf("free lines (%p)\n", lines);                    \
00237 cpl_vector_delete(lines);                              \
00238 cpl_msg_indent_less();                                 \
00239 return 0;                                              \
00240 }
00241 
00242 
00254 int cpl_plugin_get_info(cpl_pluginlist *list)
00255 {
00256     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
00257     cpl_plugin *plugin = &recipe->interface;
00258 
00259     cpl_plugin_init(plugin,
00260                     CPL_PLUGIN_API,
00261                     FORS_BINARY_VERSION,
00262                     CPL_PLUGIN_TYPE_RECIPE,
00263                     "fors_calib",
00264                     "Determination of the extraction mask",
00265                     fors_calib_description,
00266                     "Carlo Izzo",
00267                     PACKAGE_BUGREPORT,
00268     "This file is currently part of the FORS Instrument Pipeline\n"
00269     "Copyright (C) 2002-2010 European Southern Observatory\n\n"
00270     "This program is free software; you can redistribute it and/or modify\n"
00271     "it under the terms of the GNU General Public License as published by\n"
00272     "the Free Software Foundation; either version 2 of the License, or\n"
00273     "(at your option) any later version.\n\n"
00274     "This program is distributed in the hope that it will be useful,\n"
00275     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00276     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00277     "GNU General Public License for more details.\n\n"
00278     "You should have received a copy of the GNU General Public License\n"
00279     "along with this program; if not, write to the Free Software Foundation,\n"
00280     "Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n",
00281                     fors_calib_create,
00282                     fors_calib_exec,
00283                     fors_calib_destroy);
00284 
00285     cpl_pluginlist_append(list, plugin);
00286     
00287     return 0;
00288 }
00289 
00290 
00301 static int fors_calib_create(cpl_plugin *plugin)
00302 {
00303     cpl_recipe    *recipe;
00304     cpl_parameter *p;
00305 
00306 
00307     /* 
00308      * Check that the plugin is part of a valid recipe 
00309      */
00310 
00311     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00312         recipe = (cpl_recipe *)plugin;
00313     else 
00314         return -1;
00315 
00316     /* 
00317      * Create the parameters list in the cpl_recipe object 
00318      */
00319 
00320     recipe->parameters = cpl_parameterlist_new(); 
00321 
00322 
00323     /*
00324      * Dispersion
00325      */
00326 
00327     p = cpl_parameter_new_value("fors.fors_calib.dispersion",
00328                                 CPL_TYPE_DOUBLE,
00329                                 "Expected spectral dispersion (Angstrom/pixel)",
00330                                 "fors.fors_calib",
00331                                 0.0);
00332     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
00333     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00334     cpl_parameterlist_append(recipe->parameters, p);
00335 
00336     /*
00337      * Peak detection level
00338      */
00339 
00340     p = cpl_parameter_new_value("fors.fors_calib.peakdetection",
00341                                 CPL_TYPE_DOUBLE,
00342                                 "Initial peak detection threshold (ADU)",
00343                                 "fors.fors_calib",
00344                                 0.0);
00345     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "peakdetection");
00346     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00347     cpl_parameterlist_append(recipe->parameters, p);
00348 
00349     /* 
00350      * Degree of wavelength calibration polynomial
00351      */
00352 
00353     p = cpl_parameter_new_value("fors.fors_calib.wdegree",
00354                                 CPL_TYPE_INT,
00355                                 "Degree of wavelength calibration polynomial",
00356                                 "fors.fors_calib",
00357                                 0);
00358     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wdegree");
00359     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00360     cpl_parameterlist_append(recipe->parameters, p);
00361 
00362     /*
00363      * Reference lines search radius
00364      */
00365 
00366     p = cpl_parameter_new_value("fors.fors_calib.wradius",
00367                                 CPL_TYPE_INT,
00368                                 "Search radius if iterating pattern-matching "
00369                                 "with first-guess method",
00370                                 "fors.fors_calib",
00371                                 4);
00372     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wradius");
00373     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00374     cpl_parameterlist_append(recipe->parameters, p);
00375 
00376     /*
00377      * Rejection threshold in dispersion relation polynomial fitting
00378      */
00379 
00380     p = cpl_parameter_new_value("fors.fors_calib.wreject",
00381                                 CPL_TYPE_DOUBLE,
00382                                 "Rejection threshold in dispersion "
00383                                 "relation fit (pixel)",
00384                                 "fors.fors_calib",
00385                                 0.7);
00386     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wreject");
00387     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00388     cpl_parameterlist_append(recipe->parameters, p);
00389 
00390     /*
00391      * Wavelength solution interpolation (for LSS data)
00392      */
00393 
00394     p = cpl_parameter_new_value("fors.fors_calib.wmode",
00395                                 CPL_TYPE_INT,
00396                                 "Interpolation mode of wavelength solution "
00397                                 "applicable to LSS-like data (0 = no "
00398                                 "interpolation, 1 = fill gaps, 2 = global "
00399                                 "model)",
00400                                 "fors.fors_calib",
00401                                 2);
00402     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wmode");
00403     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00404     cpl_parameterlist_append(recipe->parameters, p);
00405 
00406     /*
00407      * Wavelength solution interpolation (for MOS data)
00408      */
00409 
00410     p = cpl_parameter_new_value("fors.fors_calib.wmosmode",
00411                                 CPL_TYPE_INT,
00412                                 "Interpolation mode of wavelength solution "
00413                                 "(0 = no interpolation, 1 = local (slit) "
00414                                 "solution, 2 = global model)",
00415                                 "fors.fors_calib",
00416                                 0);
00417     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wmosmode");
00418     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00419     cpl_parameterlist_append(recipe->parameters, p);
00420 
00421     /*
00422      * Line catalog table column containing the reference wavelengths
00423      */
00424 
00425     p = cpl_parameter_new_value("fors.fors_calib.wcolumn",
00426                                 CPL_TYPE_STRING,
00427                                 "Name of line catalog table column "
00428                                 "with wavelengths",
00429                                 "fors.fors_calib",
00430                                 "WLEN");
00431     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
00432     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00433     cpl_parameterlist_append(recipe->parameters, p);
00434 
00435     /*
00436      * Degree of spectral curvature polynomial
00437      */
00438 
00439     p = cpl_parameter_new_value("fors.fors_calib.cdegree",
00440                                 CPL_TYPE_INT,
00441                                 "Degree of spectral curvature polynomial",
00442                                 "fors.fors_calib",
00443                                 0);
00444     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cdegree");
00445     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00446     cpl_parameterlist_append(recipe->parameters, p);
00447 
00448     /*
00449      * Curvature solution interpolation (for MOS-like data)
00450      */
00451  
00452     p = cpl_parameter_new_value("fors.fors_calib.cmode",
00453                                 CPL_TYPE_INT,
00454                                 "Interpolation mode of curvature solution "
00455                                 "applicable to MOS-like data (0 = no "
00456                                 "interpolation, 1 = fill gaps, 2 = global "
00457                                 "model)",
00458                                 "fors.fors_calib",
00459                                 1);
00460     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cmode");
00461     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00462     cpl_parameterlist_append(recipe->parameters, p);
00463 
00464     /*
00465      * Start wavelength for spectral extraction
00466      */
00467 
00468     p = cpl_parameter_new_value("fors.fors_calib.startwavelength",
00469                                 CPL_TYPE_DOUBLE,
00470                                 "Start wavelength in spectral extraction",
00471                                 "fors.fors_calib",
00472                                 0.0);
00473     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
00474     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00475     cpl_parameterlist_append(recipe->parameters, p);
00476 
00477     /*
00478      * End wavelength for spectral extraction
00479      */
00480 
00481     p = cpl_parameter_new_value("fors.fors_calib.endwavelength",
00482                                 CPL_TYPE_DOUBLE,
00483                                 "End wavelength in spectral extraction",
00484                                 "fors.fors_calib",
00485                                 0.0);
00486     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
00487     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00488     cpl_parameterlist_append(recipe->parameters, p);
00489 
00490     /*
00491      * Try slit identification
00492      */
00493 
00494     p = cpl_parameter_new_value("fors.fors_calib.slit_ident",
00495                                 CPL_TYPE_BOOL,
00496                                 "Attempt slit identification for MOS or MXU",
00497                                 "fors.fors_calib",
00498                                 TRUE);
00499     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "slit_ident");
00500     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00501     cpl_parameterlist_append(recipe->parameters, p);
00502 
00503     /*
00504      * Flat field frames stack parameters
00505      */
00506 
00507     fors_stack_define_parameters(recipe->parameters, "fors.fors_calib", 
00508                                  "average");
00509 
00510     /*
00511      * Degree of flat field fitting polynomial along spatial direction 
00512      * (used for LSS data)
00513      */
00514 
00515     p = cpl_parameter_new_value("fors.fors_calib.sdegree",
00516                                 CPL_TYPE_INT,
00517                                 "Degree of flat field fitting polynomial "
00518                                 "along spatial direction (used for LSS "
00519                                 "data only)",
00520                                 "fors.fors_calib",
00521                                 4);
00522     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sdegree");
00523     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00524     cpl_parameterlist_append(recipe->parameters, p);
00525 
00526     /*
00527      * Degree of flat field fitting polynomial along dispersion direction
00528      * (used for MOS and MXU data)
00529      */
00530 
00531     p = cpl_parameter_new_value("fors.fors_calib.ddegree",
00532                                 CPL_TYPE_INT,
00533                                 "Degree of flat field fitting polynomial "
00534                                 "along dispersion direction (used for MOS "
00535                                 "and MXU data only)",
00536                                 "fors.fors_calib",
00537                                 -1);
00538     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ddegree");
00539     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00540     cpl_parameterlist_append(recipe->parameters, p);
00541 
00542     /*
00543      * Smooth box radius for flat field along dispersion direction
00544      */
00545 
00546     p = cpl_parameter_new_value("fors.fors_calib.dradius",
00547                                 CPL_TYPE_INT,
00548                                 "Smooth box radius for flat field along "
00549                                 "dispersion direction",
00550                                 "fors.fors_calib",
00551                                 10);
00552     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dradius");
00553     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00554     cpl_parameterlist_append(recipe->parameters, p);
00555 
00556     /*
00557      * Smooth box radius for flat field along spatial direction
00558      * (used for LSS data only)
00559      */
00560 
00561     p = cpl_parameter_new_value("fors.fors_calib.sradius",
00562                                 CPL_TYPE_INT,
00563                                 "Smooth box radius for flat field along "
00564                                 "spatial direction",
00565                                 "fors.fors_calib",
00566                                 10);
00567     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sradius");
00568     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00569     cpl_parameterlist_append(recipe->parameters, p);
00570 
00571     /*
00572      * Computation of QC1 parameters
00573      */
00574 
00575     p = cpl_parameter_new_value("fors.fors_calib.qc",
00576                                 CPL_TYPE_BOOL,
00577                                 "Compute QC1 parameters",
00578                                 "fors.fors_calib",
00579                                 TRUE);
00580     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "qc");
00581     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00582     cpl_parameterlist_append(recipe->parameters, p);
00583 
00584     /*
00585      * Create check products
00586      */
00587 
00588     p = cpl_parameter_new_value("fors.fors_calib.check",
00589                                 CPL_TYPE_BOOL,
00590                                 "Create intermediate products",
00591                                 "fors.fors_calib",
00592                                 FALSE);
00593     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "check");
00594     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00595     cpl_parameterlist_append(recipe->parameters, p);
00596 
00597     return 0;
00598 }
00599 
00600 
00609 static int fors_calib_exec(cpl_plugin *plugin)
00610 {
00611     cpl_recipe *recipe;
00612     
00613     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00614         recipe = (cpl_recipe *)plugin;
00615     else 
00616         return -1;
00617 
00618     return fors_calib(recipe->parameters, recipe->frames);
00619 }
00620 
00621 
00630 static int fors_calib_destroy(cpl_plugin *plugin)
00631 {
00632     cpl_recipe *recipe;
00633     
00634     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00635         recipe = (cpl_recipe *)plugin;
00636     else 
00637         return -1;
00638 
00639     cpl_parameterlist_delete(recipe->parameters); 
00640 
00641     return 0;
00642 }
00643 
00644 
00654 static int fors_calib(cpl_parameterlist *parlist, cpl_frameset *frameset)
00655 {
00656 
00657     const char *recipe = "fors_calib";
00658 
00659 
00660     /*
00661      * Input parameters
00662      */
00663 
00664     double        dispersion;
00665     double        peakdetection;
00666     int           wdegree;
00667     int           wradius;
00668     double        wreject;
00669     int           wmode;
00670     int           wmosmode;
00671     const char   *wcolumn;
00672     int           cdegree;
00673     int           cmode;
00674     double        startwavelength;
00675     double        endwavelength;
00676     int           slit_ident;
00677     int           sdegree;
00678     int           ddegree;
00679     int           sradius;
00680     int           dradius;
00681     int           qc;
00682     int           check;
00683     const char   *stack_method;
00684     int           min_reject;
00685     int           max_reject;
00686     double        klow;
00687     double        khigh;
00688     int           kiter;
00689 
00690     /*
00691      * CPL objects
00692      */
00693 
00694     cpl_imagelist    *biases       = NULL;
00695     cpl_image        *bias         = NULL;
00696     cpl_image        *master_bias  = NULL;
00697     cpl_image        *multi_bias   = NULL;
00698     cpl_image        *flat         = NULL;
00699     cpl_image        *master_flat  = NULL;
00700     cpl_image        *added_flat   = NULL;
00701     cpl_image        *trace_flat   = NULL;
00702     cpl_image        *smo_flat     = NULL;
00703     cpl_image        *norm_flat    = NULL;
00704     cpl_image        *spectra      = NULL;
00705     cpl_image        *wavemap      = NULL;
00706     cpl_image        *delta        = NULL;
00707     cpl_image        *residual     = NULL;
00708     cpl_image        *checkwave    = NULL;
00709     cpl_image        *rectified    = NULL;
00710     cpl_image        *dummy        = NULL;
00711     cpl_image        *add_dummy    = NULL;
00712     cpl_image        *refimage     = NULL;
00713     cpl_image        *coordinate   = NULL;
00714     cpl_image        *rainbow      = NULL;
00715     cpl_image        *spatial      = NULL;
00716     cpl_image        *rect_flat    = NULL;
00717     cpl_image        *rect_nflat   = NULL;
00718     cpl_image        *mapped_flat  = NULL;
00719     cpl_image        *mapped_nflat = NULL;
00720 
00721     cpl_mask         *refmask      = NULL;
00722 
00723     cpl_table        *grism_table  = NULL;
00724     cpl_table        *overscans    = NULL;
00725     cpl_table        *wavelengths  = NULL;
00726     cpl_table        *idscoeff     = NULL;
00727     cpl_table        *idscoeff_all = NULL;
00728     cpl_table        *restable     = NULL;
00729     cpl_table        *slits        = NULL;
00730     cpl_table        *positions    = NULL;
00731     cpl_table        *maskslits    = NULL;
00732     cpl_table        *traces       = NULL;
00733     cpl_table        *polytraces   = NULL;
00734     cpl_table        *restab       = NULL;
00735     cpl_table        *global       = NULL;
00736 
00737     cpl_vector       *lines        = NULL;
00738 
00739     cpl_propertylist *header       = NULL;
00740     cpl_propertylist *save_header  = NULL;
00741     cpl_propertylist *qclist       = NULL;
00742 
00743     /*
00744      * Auxiliary variables
00745      */
00746 
00747     char        version[80];
00748     const char *arc_tag;
00749     const char *flat_tag;
00750     const char *master_screen_flat_tag;
00751     const char *master_norm_flat_tag;
00752     const char *reduced_lamp_tag;
00753     const char *disp_residuals_tag;
00754     const char *disp_coeff_tag;
00755     const char *wavelength_map_tag;
00756     const char *spectra_detection_tag;
00757     const char *spectral_resolution_tag;
00758     const char *slit_map_tag;
00759     const char *curv_traces_tag;
00760     const char *curv_coeff_tag;
00761     const char *spatial_map_tag;
00762     const char *slit_location_tag;
00763     const char *global_distortion_tag = "GLOBAL_DISTORTION_TABLE";
00764     const char *disp_residuals_table_tag;
00765     const char *delta_image_tag;
00766     const char *mapped_screen_flat_tag;
00767     const char *mapped_norm_flat_tag;
00768     const char *keyname;
00769     int         mxu, mos, lss;
00770     int         treat_as_lss = 0;
00771     int         nslits;
00772     float      *data;
00773     double      mxpos;
00774     double      mean_rms;
00775     double      mean_rms_err;
00776     double      alltime;
00777     int         nflats;
00778     int         nbias;
00779     int         nlines;
00780     int         rebin;
00781     double     *line;
00782     double     *fiterror = NULL;
00783     int        *fitlines = NULL;
00784     cpl_size    nx, ny;
00785     double      reference;
00786     double      gain;
00787     int         compute_central_wave;
00788     int         ccd_xsize, ccd_ysize;
00789     int         i;
00790 
00791     char       *instrume = NULL;
00792     char       *pipefile = NULL;
00793     char       *wheel4;
00794 
00795 
00796     snprintf(version, 80, "%s-%s", PACKAGE, PACKAGE_VERSION);
00797 
00798     cpl_msg_set_indentation(2);
00799 
00800     if (dfs_files_dont_exist(frameset))
00801         fors_calib_exit(NULL);
00802 
00803     /* 
00804      * Get configuration parameters
00805      */
00806 
00807     cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00808     cpl_msg_indent_more();
00809 
00810     if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00811         fors_calib_exit("Too many in input: GRISM_TABLE");
00812 
00813     grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00814 
00815     dispersion = dfs_get_parameter_double(parlist, 
00816                     "fors.fors_calib.dispersion", grism_table);
00817 
00818     if (dispersion <= 0.0)
00819         fors_calib_exit("Invalid spectral dispersion value");
00820 
00821     peakdetection = dfs_get_parameter_double(parlist, 
00822                     "fors.fors_calib.peakdetection", grism_table);
00823     if (peakdetection <= 0.0)
00824         fors_calib_exit("Invalid peak detection level");
00825 
00826     wdegree = dfs_get_parameter_int(parlist, 
00827                     "fors.fors_calib.wdegree", grism_table);
00828 
00829     if (wdegree < 1)
00830         fors_calib_exit("Invalid polynomial degree");
00831 
00832     if (wdegree > 5)
00833         fors_calib_exit("Max allowed polynomial degree is 5");
00834 
00835     wradius = dfs_get_parameter_int(parlist, "fors.fors_calib.wradius", NULL);
00836 
00837     if (wradius < 0)
00838         fors_calib_exit("Invalid search radius");
00839 
00840     wreject = dfs_get_parameter_double(parlist, 
00841                                        "fors.fors_calib.wreject", NULL);
00842 
00843     if (wreject <= 0.0)
00844         fors_calib_exit("Invalid rejection threshold");
00845 
00846     wmode = dfs_get_parameter_int(parlist, "fors.fors_calib.wmode", NULL);
00847 
00848     if (wmode < 0 || wmode > 2)
00849         fors_calib_exit("Invalid wavelength solution interpolation mode");
00850 
00851     wmosmode = dfs_get_parameter_int(parlist,
00852                                      "fors.fors_calib.wmosmode", NULL);
00853 
00854     if (wmosmode < 0 || wmosmode > 2)
00855         fors_calib_exit("Invalid wavelength solution interpolation mode");
00856 
00857     wcolumn = dfs_get_parameter_string(parlist, "fors.fors_calib.wcolumn", 
00858                                        NULL);
00859 
00860     cdegree = dfs_get_parameter_int(parlist, "fors.fors_calib.cdegree", 
00861                                     grism_table);
00862 
00863     if (cdegree < 1)
00864         fors_calib_exit("Invalid polynomial degree");
00865 
00866     if (cdegree > 5)
00867         fors_calib_exit("Max allowed polynomial degree is 5");
00868 
00869     cmode = dfs_get_parameter_int(parlist, "fors.fors_calib.cmode", NULL);
00870 
00871     if (cmode < 0 || cmode > 2)
00872         fors_calib_exit("Invalid curvature solution interpolation mode");
00873 
00874     startwavelength = dfs_get_parameter_double(parlist, 
00875                     "fors.fors_calib.startwavelength", grism_table);
00876     if (startwavelength > 1.0)
00877         if (startwavelength < 3000.0 || startwavelength > 13000.0)
00878             fors_calib_exit("Invalid wavelength");
00879 
00880     endwavelength = dfs_get_parameter_double(parlist, 
00881                     "fors.fors_calib.endwavelength", grism_table);
00882     if (endwavelength > 1.0) {
00883         if (endwavelength < 3000.0 || endwavelength > 13000.0)
00884             fors_calib_exit("Invalid wavelength");
00885         if (startwavelength < 1.0)
00886             fors_calib_exit("Invalid wavelength interval");
00887     }
00888 
00889     if (startwavelength > 1.0)
00890         if (endwavelength - startwavelength <= 0.0)
00891             fors_calib_exit("Invalid wavelength interval");
00892 
00893     slit_ident = dfs_get_parameter_bool(parlist, 
00894                     "fors.fors_calib.slit_ident", NULL);
00895 
00896     stack_method = dfs_get_parameter_string(parlist, 
00897                                             "fors.fors_calib.stack_method", 
00898                                             NULL);
00899 
00900     if (strcmp(stack_method, "minmax") == 0) {
00901         min_reject = dfs_get_parameter_int(parlist, 
00902                                        "fors.fors_calib.minrejection", NULL);
00903         if (min_reject < 0)
00904             fors_calib_exit("Invalid number of lower rejections");
00905 
00906         max_reject = dfs_get_parameter_int(parlist, 
00907                                        "fors.fors_calib.maxrejection", NULL);
00908         if (max_reject < 0)
00909             fors_calib_exit("Invalid number of upper rejections");
00910     }
00911 
00912     if (strcmp(stack_method, "ksigma") == 0) {
00913         klow  = dfs_get_parameter_double(parlist, 
00914                                          "fors.fors_calib.klow", NULL);
00915         if (klow < 0.1)
00916             fors_calib_exit("Invalid lower K-sigma");
00917 
00918         khigh = dfs_get_parameter_double(parlist, 
00919                                          "fors.fors_calib.khigh", NULL);
00920         if (khigh < 0.1)
00921             fors_calib_exit("Invalid lower K-sigma");
00922 
00923         kiter = dfs_get_parameter_int(parlist, 
00924                                       "fors.fors_calib.kiter", NULL);
00925         if (kiter < 1)
00926             fors_calib_exit("Invalid number of iterations");
00927     }
00928 
00929     sdegree = dfs_get_parameter_int(parlist, "fors.fors_calib.sdegree", NULL);
00930     ddegree = dfs_get_parameter_int(parlist, "fors.fors_calib.ddegree", NULL);
00931     sradius = dfs_get_parameter_int(parlist, "fors.fors_calib.sradius", NULL);
00932     dradius = dfs_get_parameter_int(parlist, "fors.fors_calib.dradius", NULL);
00933 
00934     if (sradius < 1 || dradius < 1)
00935         fors_calib_exit("Invalid smoothing box radius");
00936 
00937     qc = dfs_get_parameter_bool(parlist, "fors.fors_calib.qc", NULL);
00938 
00939     check = dfs_get_parameter_bool(parlist, "fors.fors_calib.check", NULL);
00940 
00941     cpl_table_delete(grism_table); grism_table = NULL;
00942 
00943     if (cpl_error_get_code())
00944         fors_calib_exit("Failure getting the configuration parameters");
00945 
00946 
00947     /* 
00948      * Check input set-of-frames
00949      */
00950 
00951     cpl_msg_indent_less();
00952     cpl_msg_info(recipe, "Check input set-of-frames:");
00953     cpl_msg_indent_more();
00954 
00955     if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID")) 
00956         fors_calib_exit("Input frames are not from the same grism");
00957 
00958     if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID")) 
00959         fors_calib_exit("Input frames are not from the same filter");
00960 
00961     if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID")) 
00962         fors_calib_exit("Input frames are not from the same chip");
00963 
00964     mxu = cpl_frameset_count_tags(frameset, "LAMP_MXU");
00965     mos = cpl_frameset_count_tags(frameset, "LAMP_MOS");
00966     lss = cpl_frameset_count_tags(frameset, "LAMP_LSS");
00967 
00968     if (mxu + mos + lss == 0)
00969         fors_calib_exit("Missing input arc lamp frame");
00970 
00971     if (mxu + mos + lss > 1)
00972         fors_calib_exit("Just one input arc lamp frame is allowed"); 
00973 
00974     if (mxu) {
00975         cpl_msg_info(recipe, "MXU data found");
00976         arc_tag                  = "LAMP_MXU";
00977         flat_tag                 = "SCREEN_FLAT_MXU";
00978         master_screen_flat_tag   = "MASTER_SCREEN_FLAT_MXU";
00979         master_norm_flat_tag     = "MASTER_NORM_FLAT_MXU";
00980         reduced_lamp_tag         = "REDUCED_LAMP_MXU";
00981         disp_residuals_tag       = "DISP_RESIDUALS_MXU";
00982         disp_coeff_tag           = "DISP_COEFF_MXU";
00983         wavelength_map_tag       = "WAVELENGTH_MAP_MXU";
00984         spectra_detection_tag    = "SPECTRA_DETECTION_MXU";
00985         spectral_resolution_tag  = "SPECTRAL_RESOLUTION_MXU";
00986         slit_map_tag             = "SLIT_MAP_MXU";
00987         curv_traces_tag          = "CURV_TRACES_MXU";
00988         curv_coeff_tag           = "CURV_COEFF_MXU";
00989         spatial_map_tag          = "SPATIAL_MAP_MXU";
00990         slit_location_tag        = "SLIT_LOCATION_MXU";
00991         disp_residuals_table_tag = "DISP_RESIDUALS_TABLE_MXU";
00992         delta_image_tag          = "DELTA_IMAGE_MXU";
00993         mapped_screen_flat_tag   = "MAPPED_SCREEN_FLAT_MXU";
00994         mapped_norm_flat_tag     = "MAPPED_NORM_FLAT_MXU";
00995     }
00996 
00997     if (lss) {
00998         cpl_msg_info(recipe, "LSS data found");
00999         arc_tag                  = "LAMP_LSS";
01000         flat_tag                 = "SCREEN_FLAT_LSS";
01001         master_screen_flat_tag   = "MASTER_SCREEN_FLAT_LSS";
01002         master_norm_flat_tag     = "MASTER_NORM_FLAT_LSS";
01003         reduced_lamp_tag         = "REDUCED_LAMP_LSS";
01004         spectral_resolution_tag  = "SPECTRAL_RESOLUTION_LSS";
01005         disp_residuals_tag       = "DISP_RESIDUALS_LSS";
01006         disp_coeff_tag           = "DISP_COEFF_LSS";
01007         slit_location_tag        = "SLIT_LOCATION_LSS";
01008         wavelength_map_tag       = "WAVELENGTH_MAP_LSS";
01009         slit_map_tag             = "SLIT_MAP_LSS";
01010         disp_residuals_table_tag = "DISP_RESIDUALS_TABLE_LSS";
01011         delta_image_tag          = "DELTA_IMAGE_LSS";
01012         mapped_screen_flat_tag   = "MAPPED_SCREEN_FLAT_LSS";
01013         mapped_norm_flat_tag     = "MAPPED_NORM_FLAT_LSS";
01014     }
01015 
01016     if (mos) {
01017         cpl_msg_info(recipe, "MOS data found");
01018         arc_tag                  = "LAMP_MOS";
01019         flat_tag                 = "SCREEN_FLAT_MOS";
01020         master_screen_flat_tag   = "MASTER_SCREEN_FLAT_MOS";
01021         master_norm_flat_tag     = "MASTER_NORM_FLAT_MOS";
01022         reduced_lamp_tag         = "REDUCED_LAMP_MOS";
01023         disp_residuals_tag       = "DISP_RESIDUALS_MOS";
01024         disp_coeff_tag           = "DISP_COEFF_MOS";
01025         wavelength_map_tag       = "WAVELENGTH_MAP_MOS";
01026         spectra_detection_tag    = "SPECTRA_DETECTION_MOS";
01027         spectral_resolution_tag  = "SPECTRAL_RESOLUTION_MOS";
01028         slit_map_tag             = "SLIT_MAP_MOS";
01029         curv_traces_tag          = "CURV_TRACES_MOS";
01030         curv_coeff_tag           = "CURV_COEFF_MOS";
01031         spatial_map_tag          = "SPATIAL_MAP_MOS";
01032         slit_location_tag        = "SLIT_LOCATION_MOS";
01033         disp_residuals_table_tag = "DISP_RESIDUALS_TABLE_MOS";
01034         delta_image_tag          = "DELTA_IMAGE_MOS";
01035         mapped_screen_flat_tag   = "MAPPED_SCREEN_FLAT_MOS";
01036         mapped_norm_flat_tag     = "MAPPED_NORM_FLAT_MOS";
01037     }
01038 
01039     nbias = 0;
01040     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") == 0) {
01041         if (cpl_frameset_count_tags(frameset, "BIAS") == 0)
01042             fors_calib_exit("Missing required input: MASTER_BIAS or BIAS");
01043         nbias = cpl_frameset_count_tags(frameset, "BIAS");
01044     }
01045 
01046     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") > 1)
01047         fors_calib_exit("Too many in input: MASTER_BIAS");
01048 
01049     if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
01050         fors_calib_exit("Missing required input: MASTER_LINECAT");
01051 
01052     if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
01053         fors_calib_exit("Too many in input: MASTER_LINECAT");
01054 
01055     nflats = cpl_frameset_count_tags(frameset, flat_tag);
01056 
01057     if (nflats < 1) {
01058         cpl_msg_error(recipe, "Missing required input: %s", flat_tag);
01059         fors_calib_exit(NULL);
01060     }
01061 
01062     cpl_msg_indent_less();
01063 
01064     if (nflats > 1)
01065         cpl_msg_info(recipe, "Load %d flat field frames and stack them "
01066                      "with method \"%s\"", nflats, stack_method);
01067     else
01068         cpl_msg_info(recipe, "Load flat field exposure...");
01069 
01070     cpl_msg_indent_more();
01071 
01072     header = dfs_load_header(frameset, flat_tag, 0);
01073 
01074     if (header == NULL)
01075         fors_calib_exit("Cannot load flat field frame header");
01076 
01077     /*
01078      * Insert here a check on supported filters:
01079      */
01080 
01081     wheel4 = (char *)cpl_propertylist_get_string(header, "ESO INS OPTI9 TYPE");
01082     if (cpl_error_get_code() != CPL_ERROR_NONE) {
01083         fors_calib_exit("Missing keyword ESO INS OPTI9 TYPE in flat header");
01084     }
01085 
01086     if (strcmp("FILT", wheel4) == 0) {
01087         wheel4 = (char *)cpl_propertylist_get_string(header, 
01088                                                      "ESO INS OPTI9 NAME");
01089         cpl_msg_error(recipe, "Unsupported filter: %s", wheel4);
01090         fors_calib_exit(NULL);
01091     }
01092 
01093     alltime = cpl_propertylist_get_double(header, "EXPTIME");
01094 
01095     if (cpl_error_get_code() != CPL_ERROR_NONE)
01096         fors_calib_exit("Missing keyword EXPTIME in flat field frame header");
01097 
01098     cpl_propertylist_delete(header);
01099 
01100     for (i = 1; i < nflats; i++) {
01101 
01102         header = dfs_load_header(frameset, NULL, 0);
01103 
01104         if (header == NULL)
01105             fors_calib_exit("Cannot load flat field frame header");
01106 
01107         alltime += cpl_propertylist_get_double(header, "EXPTIME");
01108 
01109         if (cpl_error_get_code() != CPL_ERROR_NONE)
01110             fors_calib_exit("Missing keyword EXPTIME in flat field "
01111                             "frame header");
01112 
01113         cpl_propertylist_delete(header);
01114 
01115     }
01116 
01117     master_flat = dfs_load_image(frameset, flat_tag, CPL_TYPE_FLOAT, 0, 0);
01118 
01119     if (master_flat == NULL)
01120         fors_calib_exit("Cannot load flat field");
01121 
01122     if (nflats > 1) {
01123         if (strcmp(stack_method, "average") == 0) {
01124             for (i = 1; i < nflats; i++) {
01125                 flat = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01126                 if (flat) {
01127                     cpl_image_add(master_flat, flat);
01128                     cpl_image_delete(flat); flat = NULL;
01129                 }
01130                 else
01131                     fors_calib_exit("Cannot load flat field");
01132             }
01133 
01134         /***
01135             if (nflats > 1)
01136                 cpl_image_divide_scalar(master_flat, nflats);
01137         ***/
01138 
01139         }
01140         else {
01141             cpl_imagelist *flatlist = NULL;
01142             double rflux, flux;
01143 
01144             /*
01145              * added_flat is needed for tracing (masters obtained with
01146              * rejections are not suitable for tracing)
01147              */
01148 
01149             added_flat = cpl_image_duplicate(master_flat);
01150 
01151             flatlist = cpl_imagelist_new();
01152             cpl_imagelist_set(flatlist, master_flat, 
01153                               cpl_imagelist_get_size(flatlist));
01154             master_flat = NULL;
01155 
01156             /*
01157              * Stacking with rejection requires normalization
01158              * at the same flux. We normalise according to mean
01159              * flux. This is equivalent to determining the
01160              * flux ration for each image as the average of the
01161              * flux ratio of all pixels weighted on the actual
01162              * flux of each pixel.
01163              */
01164 
01165             rflux = cpl_image_get_mean(added_flat);
01166 
01167             for (i = 1; i < nflats; i++) {
01168                 flat = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01169                 if (flat) {
01170                     cpl_image_add(added_flat, flat);
01171                     flux = cpl_image_get_mean(flat);
01172                     cpl_image_multiply_scalar(flat, rflux / flux);
01173                     cpl_imagelist_set(flatlist, flat, 
01174                                       cpl_imagelist_get_size(flatlist));
01175                     flat = NULL;
01176                 }
01177                 else {
01178                     fors_calib_exit("Cannot load flat field");
01179                 }
01180             }
01181 
01182             if (strcmp(stack_method, "median") == 0) {
01183                 master_flat = cpl_imagelist_collapse_median_create(flatlist);
01184             }
01185 
01186             if (strcmp(stack_method, "minmax") == 0) {
01187                 master_flat = cpl_imagelist_collapse_minmax_create(flatlist, 
01188                                                                    min_reject,
01189                                                                    max_reject);
01190             }
01191 
01192             if (strcmp(stack_method, "ksigma") == 0) {
01193                 master_flat = mos_ksigma_stack(flatlist, 
01194                                                klow, khigh, kiter, NULL);
01195             }
01196 
01197             cpl_imagelist_delete(flatlist);
01198         }
01199     }
01200 
01201     /*
01202      * Get the reference wavelength and the rebin factor along the
01203      * dispersion direction from the arc lamp exposure
01204      */
01205 
01206     header = dfs_load_header(frameset, arc_tag, 0);
01207 
01208     if (header == NULL)
01209         fors_calib_exit("Cannot load arc lamp header");
01210 
01211     instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
01212     if (instrume == NULL)
01213         fors_calib_exit("Missing keyword INSTRUME in arc lamp header");
01214     instrume = cpl_strdup(instrume);
01215 
01216     if (instrume[4] == '1')
01217         snprintf(version, 80, "%s/%s", "fors1", VERSION);
01218     if (instrume[4] == '2')
01219         snprintf(version, 80, "%s/%s", "fors2", VERSION);
01220 
01221     reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
01222 
01223     if (cpl_error_get_code() != CPL_ERROR_NONE)
01224         fors_calib_exit("Missing keyword ESO INS GRIS1 WLEN in arc lamp "
01225                         "frame header");
01226 
01227     if (reference < 3000.0)   /* Perhaps in nanometers... */
01228         reference *= 10;
01229 
01230     if (reference < 3000.0 || reference > 13000.0) {
01231         cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
01232                       "keyword ESO INS GRIS1 WLEN in arc lamp frame header",
01233                       reference);
01234         fors_calib_exit(NULL);
01235     }
01236 
01237     cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
01238 
01239     rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
01240 
01241     if (cpl_error_get_code() != CPL_ERROR_NONE)
01242         fors_calib_exit("Missing keyword ESO DET WIN1 BINX in arc lamp "
01243                         "frame header");
01244 
01245     if (rebin != 1) {
01246         dispersion *= rebin;
01247         cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
01248                         "working dispersion used is %f A/pixel", rebin, 
01249                         dispersion);
01250     }
01251 
01252     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
01253 
01254     if (cpl_error_get_code() != CPL_ERROR_NONE)
01255         fors_calib_exit("Missing keyword ESO DET OUT1 CONAD in arc lamp "
01256                         "frame header");
01257 
01258     cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
01259 
01260     if (mos || mxu) {
01261 
01262         int nslits_out_det = 0;
01263 
01264         cpl_msg_info(recipe, "Produce mask slit position table...");
01265         if (mos)
01266             maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
01267         else
01268             maskslits = mos_load_slits_fors_mxu(header);
01269 
01270         /*
01271          * Check if all slits have the same X offset: in such case, 
01272          * treat the observation as a long-slit one!
01273          */
01274 
01275         mxpos = cpl_table_get_column_median(maskslits, "xtop");
01276         nslits = cpl_table_get_nrow(maskslits);
01277 
01278         
01279         treat_as_lss = fors_mos_is_lss_like(maskslits,  nslits_out_det);
01280 
01281         if (treat_as_lss) {
01282             cpl_msg_warning(recipe, "All MOS slits have the same offset: %.2f\n"
01283                             "The LSS data reduction strategy is applied!", 
01284                             mxpos);
01285             cpl_table_delete(maskslits); maskslits = NULL;
01286             if (mos) {
01287                 master_screen_flat_tag   = "MASTER_SCREEN_FLAT_LONG_MOS";
01288                 master_norm_flat_tag     = "MASTER_NORM_FLAT_LONG_MOS";
01289                 disp_residuals_table_tag = "DISP_RESIDUALS_TABLE_LONG_MOS";
01290                 delta_image_tag          = "DELTA_IMAGE_LONG_MOS";
01291                 spectral_resolution_tag  = "SPECTRAL_RESOLUTION_LONG_MOS";
01292                 reduced_lamp_tag         = "REDUCED_LAMP_LONG_MOS";
01293                 disp_coeff_tag           = "DISP_COEFF_LONG_MOS";
01294                 wavelength_map_tag       = "WAVELENGTH_MAP_LONG_MOS";
01295                 slit_location_tag        = "SLIT_LOCATION_LONG_MOS";
01296                 mapped_screen_flat_tag   = "MAPPED_SCREEN_FLAT_LONG_MOS";
01297                 mapped_norm_flat_tag     = "MAPPED_NORM_FLAT_LONG_MOS";
01298             }
01299             else {
01300                 master_screen_flat_tag   = "MASTER_SCREEN_FLAT_LONG_MXU";
01301                 master_norm_flat_tag     = "MASTER_NORM_FLAT_LONG_MXU";
01302                 disp_residuals_table_tag = "DISP_RESIDUALS_TABLE_LONG_MXU";
01303                 delta_image_tag          = "DELTA_IMAGE_LONG_MXU";
01304                 spectral_resolution_tag  = "SPECTRAL_RESOLUTION_LONG_MXU";
01305                 reduced_lamp_tag         = "REDUCED_LAMP_LONG_MXU";
01306                 disp_coeff_tag           = "DISP_COEFF_LONG_MXU";
01307                 wavelength_map_tag       = "WAVELENGTH_MAP_LONG_MXU";
01308                 slit_location_tag        = "SLIT_LOCATION_LONG_MXU";
01309                 mapped_screen_flat_tag   = "MAPPED_SCREEN_FLAT_LONG_MXU";
01310                 mapped_norm_flat_tag     = "MAPPED_NORM_FLAT_LONG_MXU";
01311             }
01312         }
01313     }
01314 
01315     if (slit_ident == 0) {
01316         cpl_table_delete(maskslits); maskslits = NULL;
01317     }
01318 
01319 
01320     /* Leave the header on for the next step... */
01321 
01322 
01323     /*
01324      * Remove the master bias
01325      */
01326 
01327     if (nbias) {
01328 
01329         /*
01330          * Set of raw BIASes in input, need to create master bias!
01331          */
01332 
01333         cpl_msg_info(recipe, "Generate the master from input raw biases...");
01334 
01335         if (nbias > 1) {
01336 
01337             biases = cpl_imagelist_new();
01338 
01339             bias = dfs_load_image(frameset, "BIAS", CPL_TYPE_FLOAT, 0, 0);
01340     
01341             if (bias == NULL)
01342                 fors_calib_exit("Cannot load bias frame");
01343 
01344             cpl_imagelist_set(biases, bias, 0); bias = NULL;
01345     
01346             for (i = 1; i < nbias; i++) {
01347                 bias = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01348                 if (bias) {
01349                     cpl_imagelist_set(biases, bias, i); bias = NULL;
01350                 }
01351                 else
01352                     fors_calib_exit("Cannot load bias frame");
01353             }
01354     
01355             master_bias = cpl_imagelist_collapse_median_create(biases);
01356 
01357             cpl_imagelist_delete(biases);
01358         }
01359         else {
01360             master_bias = dfs_load_image(frameset, "BIAS", 
01361                                          CPL_TYPE_FLOAT, 0, 1);
01362             if (master_bias == NULL)
01363                 fors_calib_exit("Cannot load bias");
01364         }
01365 
01366     }
01367     else {
01368         master_bias = dfs_load_image(frameset, "MASTER_BIAS", 
01369                                      CPL_TYPE_FLOAT, 0, 1);
01370         if (master_bias == NULL)
01371             fors_calib_exit("Cannot load master bias");
01372     }
01373 
01374     cpl_msg_info(recipe, "Remove the master bias...");
01375 
01376     overscans = mos_load_overscans_vimos(header, 1);
01377     cpl_propertylist_delete(header); header = NULL;
01378 
01379     if (nbias) {
01380         int xlow = cpl_table_get_int(overscans, "xlow", 0, NULL);
01381         int ylow = cpl_table_get_int(overscans, "ylow", 0, NULL);
01382         int xhig = cpl_table_get_int(overscans, "xhig", 0, NULL);
01383         int yhig = cpl_table_get_int(overscans, "yhig", 0, NULL);
01384         dummy = cpl_image_extract(master_bias, xlow+1, ylow+1, xhig, yhig);
01385         cpl_image_delete(master_bias); master_bias = dummy;
01386 
01387         if (dfs_save_image(frameset, master_bias, "MASTER_BIAS",
01388                            NULL, parlist, recipe, version))
01389             fors_calib_exit(NULL);
01390     }
01391 
01392     if (nflats > 1) {
01393         multi_bias = cpl_image_multiply_scalar_create(master_bias, nflats);
01394         dummy = mos_remove_bias(master_flat, multi_bias, overscans);
01395         if (added_flat)
01396             add_dummy = mos_remove_bias(added_flat, multi_bias, overscans);
01397         cpl_image_delete(multi_bias);
01398     }
01399     else {
01400         dummy = mos_remove_bias(master_flat, master_bias, overscans);
01401     }
01402     cpl_image_delete(master_flat);
01403     master_flat = dummy;
01404 
01405     if (master_flat == NULL)
01406         fors_calib_exit("Cannot remove bias from flat field");
01407 
01408     if (added_flat) {
01409         cpl_image_delete(added_flat);
01410         added_flat = add_dummy;
01411 
01412         if (added_flat == NULL)
01413             fors_calib_exit("Cannot remove bias from added flat field");
01414 
01415         trace_flat = added_flat;
01416     }
01417     else
01418         trace_flat = master_flat;
01419 
01420     cpl_msg_indent_less();
01421     cpl_msg_info(recipe, "Load arc lamp exposure...");
01422     cpl_msg_indent_more();
01423 
01424     spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0);
01425 
01426     if (spectra == NULL)
01427         fors_calib_exit("Cannot load arc lamp exposure");
01428 
01429     cpl_msg_info(recipe, "Remove the master bias...");
01430 
01431     dummy = mos_remove_bias(spectra, master_bias, overscans);
01432     cpl_table_delete(overscans); overscans = NULL;
01433     cpl_image_delete(master_bias); master_bias = NULL;
01434     cpl_image_delete(spectra); spectra = dummy;
01435 
01436     if (spectra == NULL)
01437         fors_calib_exit("Cannot remove bias from arc lamp exposure");
01438 
01439     cpl_msg_indent_less();
01440     cpl_msg_info(recipe, "Load input line catalog...");
01441     cpl_msg_indent_more();
01442 
01443     wavelengths = dfs_load_table(frameset, "MASTER_LINECAT", 1);
01444 
01445     if (wavelengths == NULL)
01446         fors_calib_exit("Cannot load line catalog");
01447 
01448 
01449     /*
01450      * Cast the wavelengths into a (double precision) CPL vector
01451      */
01452 
01453     nlines = cpl_table_get_nrow(wavelengths);
01454 
01455     if (nlines == 0)
01456         fors_calib_exit("Empty input line catalog");
01457 
01458     if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
01459         cpl_msg_error(recipe, "Missing column %s in input line catalog table",
01460                       wcolumn);
01461         fors_calib_exit(NULL);
01462     }
01463 
01464     line = cpl_malloc(nlines * sizeof(double));
01465     
01466     for (i = 0; i < nlines; i++)
01467         line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
01468 
01469     cpl_table_delete(wavelengths); wavelengths = NULL;
01470 
01471     lines = cpl_vector_wrap(nlines, line);
01472 
01473 
01474     if (lss || treat_as_lss) {
01475 
01476         cpl_size first_row, last_row;
01477         cpl_size ylow, yhig;
01478 
01479         /* FIXME:
01480          * The LSS data calibration is still dirty: it doesn't apply
01481          * any spatial rectification, and only in future an external
01482          * spectral curvature model would be provided in input. Here
01483          * and there temporary solutions are adpted, such as accepting
01484          * the preliminary wavelength calibration.
01485          */
01486 
01487         /*
01488          * Flat field normalisation is done directly on the master flat
01489          * field (without spatial rectification first). The spectral
01490          * curvature model may be provided in input, in future releases.
01491          */
01492 
01493         cpl_msg_indent_less();
01494         cpl_msg_info(recipe, "Perform LSS flat field normalisation...");
01495         cpl_msg_indent_more();
01496 
01497         norm_flat = cpl_image_duplicate(master_flat);
01498 
01499         smo_flat = mos_normalise_longflat(norm_flat, sradius, dradius,
01500                                           sdegree);
01501 
01502         cpl_image_delete(smo_flat); smo_flat = NULL; /* It may be a product */
01503 
01504         if (1) {
01505             save_header = dfs_load_header(frameset, flat_tag, 0);
01506             cpl_propertylist_update_int(save_header, 
01507                                         "ESO PRO DATANCOM", nflats);
01508 
01509             if (dfs_save_image(frameset, master_flat, master_screen_flat_tag,
01510                                save_header, parlist, recipe, version))
01511                 fors_calib_exit(NULL);
01512 //%%%%%            cpl_image_delete(master_flat); master_flat = NULL;
01513         }
01514 
01515         if (dfs_save_image(frameset, norm_flat, master_norm_flat_tag,
01516                            save_header, parlist, recipe, version))
01517             fors_calib_exit(NULL);
01518 
01519         cpl_propertylist_delete(save_header); save_header = NULL;
01520 //%%%%%        cpl_image_delete(norm_flat); norm_flat = NULL;
01521 
01522 
01523         /*
01524          * In the case of LSS data, extract the spectra directly
01525          * on the first attempt. The spectral curvature model may
01526          * be provided in input, in future releases.
01527          */
01528 
01529         cpl_msg_indent_less();
01530         cpl_msg_info(recipe, "Perform wavelength calibration...");
01531         cpl_msg_indent_more();
01532 
01533         nx = cpl_image_get_size_x(spectra);
01534         ny = cpl_image_get_size_y(spectra);
01535 
01536         wavemap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01537         idscoeff_all = cpl_table_new(ny);
01538 
01539     if (mos_saturation_process(spectra))
01540         fors_calib_exit("Cannot process saturation");
01541 
01542     if (mos_subtract_background(spectra))
01543         fors_calib_exit("Cannot subtract the background");
01544 
01545         rectified = mos_wavelength_calibration_raw(spectra, lines, dispersion,
01546                                                    peakdetection, wradius,
01547                                                    wdegree, wreject, reference,
01548                                                    &startwavelength,
01549                                                    &endwavelength, NULL,
01550                                                    NULL, idscoeff_all, wavemap,
01551                                                    NULL, NULL, NULL);
01552 
01553         if (rectified == NULL)
01554             fors_calib_exit("Wavelength calibration failure.");
01555 
01556         if (!cpl_table_has_valid(idscoeff_all, "c0"))
01557             fors_calib_exit("Wavelength calibration failure.");
01558 
01559         cpl_image_delete(rectified); rectified = NULL;
01560 
01561         first_row = 0;
01562         while (!cpl_table_is_valid(idscoeff_all, "c0", first_row))
01563             first_row++;
01564 
01565         last_row = ny - 1;
01566         while (!cpl_table_is_valid(idscoeff_all, "c0", last_row))
01567             last_row--;
01568 
01569         ylow = first_row + 1;
01570         yhig = last_row + 1;
01571 
01572         if (ylow >= yhig) {
01573             cpl_error_reset();
01574             fors_calib_exit("No spectra could be detected.");
01575         }
01576 
01577         cpl_msg_info(recipe, 
01578                      "Spectral pattern was detected on %"CPL_SIZE_FORMAT
01579                      " out of %"CPL_SIZE_FORMAT" CCD rows", 
01580                      yhig - ylow, ny);
01581 
01582         dummy = cpl_image_extract(spectra, 1, ylow, nx, yhig);
01583         cpl_image_delete(spectra); spectra = dummy;
01584 
01585         ccd_ysize = (int)ny;
01586         ny = cpl_image_get_size_y(spectra);
01587 
01588         if (check)
01589             residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01590 
01591         fiterror = cpl_calloc(ny, sizeof(double));
01592         fitlines = cpl_calloc(ny, sizeof(int));
01593         idscoeff = cpl_table_new(ny);
01594         restable = cpl_table_new(nlines);
01595 
01596     if (mos_saturation_process(spectra))
01597         fors_calib_exit("Cannot process saturation");
01598 
01599     if (mos_subtract_background(spectra))
01600         fors_calib_exit("Cannot subtract the background");
01601 
01602         rectified = mos_wavelength_calibration_raw(spectra, lines, dispersion,
01603                                                    peakdetection, wradius,
01604                                                    wdegree, wreject, reference,
01605                                                    &startwavelength, 
01606                                                    &endwavelength, fitlines, 
01607                                                    fiterror, idscoeff, NULL,
01608                                                    residual, restable, NULL);
01609 
01610         if (rectified == NULL)
01611             fors_calib_exit("Wavelength calibration failure.");
01612 
01613         if (!cpl_table_has_valid(idscoeff, "c0"))
01614             fors_calib_exit("Wavelength calibration failure.");
01615 
01616         /*
01617          * A dummy slit locations table
01618          */
01619 
01620         slits = cpl_table_new(1);
01621         cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
01622         cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
01623         cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
01624         cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
01625         cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
01626         cpl_table_new_column(slits, "position", CPL_TYPE_INT);
01627         cpl_table_new_column(slits, "length", CPL_TYPE_INT);
01628         cpl_table_set_column_unit(slits, "xtop", "pixel");
01629         cpl_table_set_column_unit(slits, "ytop", "pixel");
01630         cpl_table_set_column_unit(slits, "xbottom", "pixel");
01631         cpl_table_set_column_unit(slits, "ybottom", "pixel");
01632         cpl_table_set_column_unit(slits, "position", "pixel");
01633         cpl_table_set_column_unit(slits, "length", "pixel");
01634         cpl_table_set_int(slits, "slit_id", 0, 0);
01635         cpl_table_set_double(slits, "xtop", 0, 0);
01636         cpl_table_set_double(slits, "ytop", 0, (double)last_row);
01637         cpl_table_set_double(slits, "xbottom", 0, 0);
01638         cpl_table_set_double(slits, "ybottom", 0, (double)first_row);
01639         cpl_table_set_int(slits, "position", 0, 0);
01640         cpl_table_set_int(slits, "length", 0, (int)ny);
01641 
01642         if (dfs_save_table(frameset, slits, slit_location_tag, NULL,
01643                            parlist, recipe, version))
01644             fors_calib_exit(NULL);
01645 
01646         cpl_table_delete(slits); slits = NULL;
01647 
01648         if (dfs_save_table(frameset, restable, disp_residuals_table_tag, NULL,
01649                            parlist, recipe, version))
01650             fors_calib_exit(NULL);
01651 
01652         cpl_table_delete(restable); restable = NULL;
01653 
01654         if (wmode) {
01655             cpl_image_delete(rectified); rectified = NULL;
01656             cpl_image_delete(wavemap); wavemap = NULL;
01657             mos_interpolate_wavecalib(idscoeff, wavemap, wmode, 2);
01658             mos_interpolate_wavecalib(idscoeff_all, wavemap, wmode, 2);
01659             wavemap = mos_map_idscoeff(idscoeff_all, nx, reference,
01660                                        startwavelength, endwavelength);
01661             rectified = mos_wavelength_calibration(spectra, reference,
01662                                                    startwavelength, 
01663                                                    endwavelength, dispersion, 
01664                                                    idscoeff, 0);
01665         }
01666 
01667         cpl_table_delete(idscoeff_all); idscoeff_all = NULL;
01668 
01669         cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
01670         cpl_table_set_column_unit(idscoeff, "error", "pixel");
01671         cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
01672 
01673         for (i = 0; i < ny; i++)
01674             if (!cpl_table_is_valid(idscoeff, "c0", i))
01675                 cpl_table_set_invalid(idscoeff, "error", i);
01676 
01677         delta = mos_map_pixel(idscoeff, reference, startwavelength,
01678                               endwavelength, dispersion, 2);
01679 
01680 //%%%%%
01681         dummy = cpl_image_extract(master_flat, 1, ylow, nx, yhig);
01682         cpl_image_delete(master_flat); master_flat = dummy;
01683 
01684         mapped_flat = mos_wavelength_calibration(master_flat, reference,
01685                                       startwavelength, endwavelength,
01686                                       dispersion, idscoeff, 0);
01687 
01688         cpl_image_delete(master_flat); master_flat = NULL;
01689 
01690         dummy = cpl_image_extract(norm_flat, 1, ylow, nx, yhig);
01691         cpl_image_delete(norm_flat); norm_flat = dummy;
01692 
01693         mapped_nflat = mos_wavelength_calibration(norm_flat, reference,
01694                                       startwavelength, endwavelength,
01695                                       dispersion, idscoeff, 0);
01696 
01697         cpl_image_delete(norm_flat); norm_flat = NULL;
01698 
01699         header = cpl_propertylist_new();
01700         cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01701         cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01702         cpl_propertylist_update_double(header, "CRVAL1",
01703                                        startwavelength + dispersion/2);
01704         cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01705         /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
01706         cpl_propertylist_update_double(header, "CDELT2", 1.0); */
01707         cpl_propertylist_update_double(header, "CD1_1", dispersion);
01708         cpl_propertylist_update_double(header, "CD1_2", 0.0);
01709         cpl_propertylist_update_double(header, "CD2_1", 0.0);
01710         cpl_propertylist_update_double(header, "CD2_2", 1.0);
01711         cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01712         cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01713 
01714         if (dfs_save_image(frameset, delta, delta_image_tag,
01715                            header, parlist, recipe, version))
01716             fors_calib_exit(NULL);
01717 
01718         cpl_image_delete(delta); delta = NULL;
01719 
01720         if (dfs_save_image(frameset, mapped_flat, mapped_screen_flat_tag,
01721                            header, parlist, recipe, version))
01722             fors_calib_exit(NULL);
01723 
01724         cpl_image_delete(mapped_flat); mapped_flat = NULL;
01725 
01726         if (dfs_save_image(frameset, mapped_nflat, mapped_norm_flat_tag,
01727                            header, parlist, recipe, version))
01728             fors_calib_exit(NULL);
01729 
01730         cpl_image_delete(mapped_nflat); mapped_nflat = NULL;
01731 
01732         cpl_propertylist_delete(header); header = NULL;
01733 
01734         cpl_msg_info(recipe, "Valid solutions found: %"CPL_SIZE_FORMAT" out of %"CPL_SIZE_FORMAT" rows", 
01735                      ny - cpl_table_count_invalid(idscoeff, "c0"), ny);
01736 
01737         cpl_image_delete(spectra); spectra = NULL;
01738 
01739         mean_rms = mos_distortions_rms(rectified, lines, startwavelength,
01740                                        dispersion, 6, 0);
01741 
01742         cpl_msg_info(recipe, "Mean residual: %f pixel", mean_rms);
01743 
01744         mean_rms = cpl_table_get_column_mean(idscoeff, "error");
01745         mean_rms_err = cpl_table_get_column_stdev(idscoeff, "error");
01746 
01747         cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
01748                      mean_rms, mean_rms * dispersion);
01749 
01750         restab = mos_resolution_table(rectified, startwavelength, dispersion,
01751                                       60000, lines);
01752 
01753         if (restab) {
01754             cpl_msg_info(recipe, "Mean spectral resolution: %.2f",
01755                   cpl_table_get_column_mean(restab, "resolution"));
01756             cpl_msg_info(recipe, 
01757                   "Mean reference lines FWHM: %.2f +/- %.2f pixel",
01758                   cpl_table_get_column_mean(restab, "fwhm") / dispersion,
01759                   cpl_table_get_column_mean(restab, "fwhm_rms") / dispersion);
01760 
01761             if (qc) {
01762 
01763                 header = dfs_load_header(frameset, arc_tag, 0);
01764 
01765                 if (header == NULL)
01766                     fors_calib_exit("Cannot reload arc lamp header");
01767 
01768                 qclist = cpl_propertylist_new();
01769 
01770 
01771                 /*
01772                  * QC1 parameters
01773                  */
01774                 keyname = "QC.DID";
01775 
01776                 if (fors_header_write_string(qclist,
01777                                              keyname,
01778                                              "2.0",
01779                                              "QC1 dictionary")) {
01780                     fors_calib_exit("Cannot write dictionary version "
01781                                     "to QC log file");
01782                 }
01783    
01784                 if (mos)
01785                     keyname = "QC.MOS.RESOLUTION";
01786                 else
01787                     keyname = "QC.LSS.RESOLUTION";
01788 
01789                 if (fors_header_write_double(qclist, 
01790                                            cpl_table_get_column_mean(restab,
01791                                                                  "resolution"),
01792                                            keyname, NULL, 
01793                                            "Mean spectral resolution")) {
01794                     fors_calib_exit("Cannot write mean spectral resolution to "
01795                                     "QC log file");
01796                 }
01797 
01798                 if (mos)
01799                     keyname = "QC.MOS.RESOLUTION.RMS"; 
01800                 else
01801                     keyname = "QC.LSS.RESOLUTION.RMS";
01802 
01803                 if (fors_header_write_double(qclist,
01804                                            cpl_table_get_column_stdev(restab,
01805                                                                 "resolution"),
01806                                            keyname, NULL, 
01807                                            "Scatter of spectral resolution")) {
01808                     fors_calib_exit("Cannot write spectral resolution scatter "
01809                                     "to QC log file");
01810                 }
01811 
01812                 if (mos)
01813                     keyname = "QC.MOS.RESOLUTION.NWAVE";
01814                 else
01815                     keyname = "QC.LSS.RESOLUTION.NWAVE";
01816 
01817                 if (fors_header_write_int(qclist, cpl_table_get_nrow(restab) -
01818                                         cpl_table_count_invalid(restab,
01819                                                                 "resolution"),
01820                                         keyname, NULL,
01821                                         "Number of examined wavelengths "
01822                                         "for resolution computation")) {
01823                     fors_calib_exit("Cannot write number of lines used in "
01824                                     "spectral resolution computation "
01825                                     "to QC log file");
01826                 }
01827 
01828                 if (mos)
01829                     keyname = "QC.MOS.RESOLUTION.MEANRMS";
01830                 else
01831                     keyname = "QC.LSS.RESOLUTION.MEANRMS";
01832 
01833                 if (fors_header_write_double(qclist, 
01834                                            cpl_table_get_column_mean(restab,
01835                                                          "resolution_rms"),
01836                                         keyname, NULL,
01837                                         "Mean error on spectral "
01838                                         "resolution computation")) {
01839                     fors_calib_exit("Cannot write mean error in "
01840                                     "spectral resolution computation "
01841                                     "to QC log file");
01842                 }
01843 
01844                 if (mos)
01845                     keyname = "QC.MOS.RESOLUTION.NLINES";
01846                 else
01847                     keyname = "QC.LSS.RESOLUTION.NLINES";
01848 
01849                 if (fors_header_write_int(qclist, 
01850                          cpl_table_get_column_mean(restab, "nlines") *
01851                                            cpl_table_get_nrow(restab),
01852                                         keyname, NULL,
01853                                         "Number of lines for spectral "
01854                                         "resolution computation")) {
01855                     fors_calib_exit("Cannot write number of examined "
01856                          "wavelengths in spectral resolution computation "
01857                          "to QC log file");
01858                 }
01859 
01860 //                fors_qc_end_group();
01861 
01862             }  /* End of QC1 computation */
01863 
01864             if (dfs_save_table(frameset, restab, spectral_resolution_tag, 
01865                                qclist, parlist, recipe, version))
01866                 fors_calib_exit(NULL);
01867 
01868             cpl_table_delete(restab); restab = NULL;
01869             cpl_propertylist_delete(qclist); qclist = NULL;
01870 
01871         }
01872         else
01873             fors_calib_exit("Cannot compute the spectral resolution table");
01874 
01875         cpl_vector_delete(lines); lines = NULL;
01876 
01877 
01878         /*
01879          * Save rectified arc lamp spectrum to disk
01880          */
01881 
01882         header = cpl_propertylist_new();
01883         cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01884         cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01885         cpl_propertylist_update_double(header, "CRVAL1", 
01886                                        startwavelength + dispersion/2);
01887         cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01888         /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
01889         cpl_propertylist_update_double(header, "CDELT2", 1.0); */
01890         cpl_propertylist_update_double(header, "CD1_1", dispersion);
01891         cpl_propertylist_update_double(header, "CD1_2", 0.0);
01892         cpl_propertylist_update_double(header, "CD2_1", 0.0);
01893         cpl_propertylist_update_double(header, "CD2_2", 1.0);
01894         cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01895         cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01896         cpl_propertylist_update_int(header, "ESO PRO DATANCOM", 1);
01897 
01898         if (dfs_save_image(frameset, rectified, reduced_lamp_tag, header, 
01899                            parlist, recipe, version))
01900             fors_calib_exit(NULL);
01901 
01902         cpl_image_delete(rectified); rectified = NULL;
01903         cpl_propertylist_delete(header); header = NULL;
01904 
01905         if (dfs_save_table(frameset, idscoeff, disp_coeff_tag, NULL, 
01906                            parlist, recipe, version))
01907             fors_calib_exit(NULL);
01908 
01909         cpl_table_delete(idscoeff); idscoeff = NULL;
01910 
01911         header = dfs_load_header(frameset, arc_tag, 0);
01912 
01913         if (header == NULL)
01914             fors_calib_exit("Cannot reload arc lamp header");
01915 
01916         if (qc) {
01917 
01918             compute_central_wave = 0;
01919             if (lss) {
01920 /***
01921                 if (fabs(1.0 - cpl_propertylist_get_double(header,
01922                                              "ESO INS SLIT WID")) < 0.05)
01923  ***/
01924                 compute_central_wave = 1;
01925             }
01926             else {
01927                 if (fabs(mxpos) < 0.05)
01928                     compute_central_wave = 1;
01929             }
01930 
01931             /*
01932              * QC1 parameters
01933              */
01934             keyname = "QC.DID";
01935 
01936             if (fors_header_write_string(header,
01937                                          keyname,
01938                                          "2.0",
01939                                          "QC1 dictionary")) {
01940                 fors_calib_exit("Cannot write dictionary version "
01941                                 "to QC log file");
01942             }
01943 
01944             if (fors_header_write_double(header,
01945                                        mean_rms,
01946                                        "QC.WAVE.ACCURACY",
01947                                        "pixel",
01948                                        "Mean accuracy of wavecalib model")) {
01949                 fors_calib_exit("Cannot write mean wavelength calibration "
01950                                 "accuracy to QC log file");
01951             }
01952 
01953             if (fors_header_write_double(header,
01954                                        mean_rms_err,
01955                                        "QC.WAVE.ACCURACY.ERROR",
01956                                        "pixel",
01957                                        "Error on accuracy of wavecalib model")) {
01958                 fors_calib_exit("Cannot write error on wavelength calibration "
01959                                 "accuracy to QC log file");
01960             }
01961 
01962             if (compute_central_wave) {
01963 
01964                 data = cpl_image_get_data(wavemap);
01965     
01966                 if (lss) {
01967                     if (fors_header_write_double(header, 
01968                                                data[nx/2 + ccd_ysize*nx/2],
01969                                                "QC.LSS.CENTRAL.WAVELENGTH",
01970                                                "Angstrom", 
01971                                                "Wavelength at CCD center")) {
01972                         fors_calib_exit("Cannot write central wavelength to QC "
01973                                         "log file");
01974                     }
01975                 }
01976                 else {
01977                     if (fors_header_write_double(header, 
01978                                                data[nx/2 + ccd_ysize*nx/2],
01979                                                "QC.MOS.CENTRAL.WAVELENGTH",
01980                                                "Angstrom", 
01981                                                "Wavelength at CCD center")) {
01982                         fors_calib_exit("Cannot write central wavelength to QC "
01983                                         "log file");
01984                     }
01985                 }
01986             }
01987         }
01988 
01989         if (dfs_save_image(frameset, wavemap, wavelength_map_tag, header,
01990                            parlist, recipe, version))
01991             fors_calib_exit(NULL);
01992 
01993         cpl_image_delete(wavemap); wavemap = NULL;
01994 
01995         cpl_propertylist_erase_regexp(header, "^ESO QC ", 0);
01996 
01997         if (check) {
01998             cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01999             cpl_propertylist_update_double(header, "CRVAL2", 1.0);
02000             /* cpl_propertylist_update_double(header, "CDELT2", 1.0); */
02001             cpl_propertylist_update_double(header, "CD1_1", 1.0);
02002             cpl_propertylist_update_double(header, "CD1_2", 0.0);
02003             cpl_propertylist_update_double(header, "CD2_1", 0.0);
02004             cpl_propertylist_update_double(header, "CD2_2", 1.0);
02005             cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
02006             cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
02007 
02008             if (dfs_save_image(frameset, residual, disp_residuals_tag, header,
02009                                parlist, recipe, version))
02010                 fors_calib_exit(NULL);
02011 
02012             cpl_image_delete(residual); residual = NULL;
02013         }
02014 
02015         cpl_propertylist_delete(header); header = NULL;
02016         cpl_free(instrume); instrume = NULL;
02017 
02018         return 0;         /* Successful LSS data reduction */
02019 
02020     }   /* End of LSS data reduction section */
02021 
02022 
02023     /*
02024      * Here the MOS and MXU calibration is carried out.
02025      */
02026 
02027     /*
02028      * Detecting spectra on the CCD
02029      */
02030 
02031     cpl_msg_indent_less();
02032     cpl_msg_info(recipe, "Detecting spectra on CCD...");
02033     cpl_msg_indent_more();
02034 
02035     ccd_xsize = nx = cpl_image_get_size_x(spectra);
02036     ccd_ysize = ny = cpl_image_get_size_y(spectra);
02037 
02038     refmask = cpl_mask_new(nx, ny);
02039 
02040     if (mos_saturation_process(spectra))
02041     fors_calib_exit("Cannot process saturation");
02042 
02043     if (mos_subtract_background(spectra))
02044     fors_calib_exit("Cannot subtract the background");
02045  
02046     checkwave = mos_wavelength_calibration_raw(spectra, lines, dispersion, 
02047                                                peakdetection, wradius, 
02048                                                wdegree, wreject, reference,
02049                                                &startwavelength, &endwavelength,
02050                                                NULL, NULL, NULL, NULL, NULL, 
02051                                                NULL, refmask);
02052 
02053     if (checkwave == NULL)
02054         fors_calib_exit("Wavelength calibration failure.");
02055 
02056     /*
02057      * Save check image to disk
02058      */
02059 
02060     header = cpl_propertylist_new();
02061     cpl_propertylist_update_double(header, "CRPIX1", 1.0);
02062     cpl_propertylist_update_double(header, "CRPIX2", 1.0);
02063     cpl_propertylist_update_double(header, "CRVAL1", 
02064                                    startwavelength + dispersion/2);
02065     cpl_propertylist_update_double(header, "CRVAL2", 1.0);
02066     /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
02067     cpl_propertylist_update_double(header, "CDELT2", 1.0); */
02068     cpl_propertylist_update_double(header, "CD1_1", dispersion);
02069     cpl_propertylist_update_double(header, "CD1_2", 0.0);
02070     cpl_propertylist_update_double(header, "CD2_1", 0.0);
02071     cpl_propertylist_update_double(header, "CD2_2", 1.0);
02072     cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
02073     cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
02074 
02075     if (check) {
02076         if (dfs_save_image(frameset, checkwave, spectra_detection_tag, header, 
02077                            parlist, recipe, version))
02078             fors_calib_exit(NULL);
02079     }
02080 
02081     cpl_image_delete(checkwave); checkwave = NULL;
02082     cpl_propertylist_delete(header); header = NULL;
02083 
02084     cpl_msg_info(recipe, "Locate slits at reference wavelength on CCD...");
02085     slits = mos_locate_spectra(refmask);
02086 
02087     if (!slits) {
02088         cpl_msg_error(cpl_error_get_where(), cpl_error_get_message());
02089         fors_calib_exit("No slits could be detected!");
02090     }
02091 
02092     refimage = cpl_image_new_from_mask(refmask);
02093     cpl_mask_delete(refmask); refmask = NULL;
02094 
02095     if (check) {
02096         save_header = dfs_load_header(frameset, arc_tag, 0);
02097         if (dfs_save_image(frameset, refimage, slit_map_tag, save_header,
02098                            parlist, recipe, version))
02099             fors_calib_exit(NULL);
02100         cpl_propertylist_delete(save_header); save_header = NULL;
02101     }
02102 
02103     cpl_image_delete(refimage); refimage = NULL;
02104 
02105     if (slit_ident) {
02106 
02107         /*
02108          * Attempt slit identification: this recipe may continue even
02109          * in case of failed identification (i.e., the position table is 
02110          * not produced, but an error is not set). In case of failure,
02111          * the spectra would be still extracted, even if they would not
02112          * be associated to slits on the mask.
02113          * 
02114          * The reason for making the slit identification an user option 
02115          * (via the parameter slit_ident) is to offer the possibility 
02116          * to avoid identifications that are only apparently successful, 
02117          * as it would happen in the case of an incorrect slit description 
02118          * in the data header.
02119          */
02120 
02121         cpl_msg_indent_less();
02122         cpl_msg_info(recipe, "Attempt slit identification (optional)...");
02123         cpl_msg_indent_more();
02124 
02125         positions = mos_identify_slits(slits, maskslits, NULL);
02126 
02127         if (positions) {
02128             cpl_table_delete(slits);
02129             slits = positions;
02130 
02131             /*
02132              * Eliminate slits which are _entirely_ outside the CCD
02133              */
02134 
02135             cpl_table_and_selected_double(slits, 
02136                                           "ybottom", CPL_GREATER_THAN, ny-1);
02137             cpl_table_or_selected_double(slits, 
02138                                           "ytop", CPL_LESS_THAN, 0);
02139             cpl_table_erase_selected(slits);
02140 
02141             nslits = cpl_table_get_nrow(slits);
02142 
02143             if (nslits == 0)
02144                 fors_calib_exit("No slits found on the CCD");
02145 
02146             cpl_msg_info(recipe, "%d slits are entirely or partially "
02147                          "contained in CCD", nslits);
02148 
02149         }
02150         else {
02151             slit_ident = 0;
02152             cpl_msg_info(recipe, "Global distortion model cannot be computed");
02153             if (cpl_error_get_code() != CPL_ERROR_NONE) {
02154                 fors_calib_exit(NULL);
02155             }
02156         }
02157     }
02158 
02159 
02160     /*
02161      * Determination of spectral curvature
02162      */
02163 
02164     cpl_msg_indent_less();
02165     cpl_msg_info(recipe, "Determining spectral curvature...");
02166     cpl_msg_indent_more();
02167 
02168     cpl_msg_info(recipe, "Tracing master flat field spectra edges...");
02169     traces = mos_trace_flat(trace_flat, slits, reference, 
02170                             startwavelength, endwavelength, dispersion);
02171 
02172     if (!traces)
02173         fors_calib_exit("Tracing failure");
02174 
02175     cpl_image_delete(added_flat); added_flat = NULL;
02176 
02177     cpl_msg_info(recipe, "Fitting flat field spectra edges...");
02178     polytraces = mos_poly_trace(slits, traces, cdegree);
02179 
02180     if (!polytraces)
02181         fors_calib_exit("Trace fitting failure");
02182 
02183     if (cmode) {
02184         cpl_msg_info(recipe, "Computing global spectral curvature model...");
02185         mos_global_trace(slits, polytraces, cmode);
02186     }
02187 
02188     if (dfs_save_table(frameset, traces, curv_traces_tag, NULL, parlist,
02189                        recipe, version))
02190         fors_calib_exit(NULL);
02191 
02192     cpl_table_delete(traces); traces = NULL;
02193 
02194     coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02195     spatial = mos_spatial_calibration(spectra, slits, polytraces, reference, 
02196                                       startwavelength, endwavelength, 
02197                                       dispersion, 0, coordinate);
02198 
02199     if (!slit_ident) {
02200         cpl_image_delete(spectra); spectra = NULL;
02201     }
02202 
02203     /*
02204      * Flat field normalisation is done directly on the master flat
02205      * field (without spatial rectification first). The spectral
02206      * curvature model may be provided in input, in future releases.
02207      */
02208 
02209     cpl_msg_indent_less();
02210     cpl_msg_info(recipe, "Perform flat field normalisation...");
02211     cpl_msg_indent_more();
02212 
02213     norm_flat = cpl_image_duplicate(master_flat);
02214 
02215     smo_flat = mos_normalise_flat(norm_flat, coordinate, slits, polytraces, 
02216                                   reference, startwavelength, endwavelength,
02217                                   dispersion, dradius, ddegree);
02218 
02219     cpl_image_delete(smo_flat); smo_flat = NULL;  /* It may be a product */
02220 
02221  
02222     save_header = dfs_load_header(frameset, flat_tag, 0);
02223     cpl_propertylist_update_int(save_header, "ESO PRO DATANCOM", nflats);
02224 //%%%
02225     rect_flat = mos_spatial_calibration(master_flat, slits, polytraces, 
02226                                         reference, startwavelength, 
02227                                         endwavelength, dispersion, 0, NULL);
02228     rect_nflat = mos_spatial_calibration(norm_flat, slits, polytraces, 
02229                                         reference, startwavelength, 
02230                                         endwavelength, dispersion, 0, NULL);
02231 //%%%
02232 
02233     if (dfs_save_image(frameset, master_flat, master_screen_flat_tag,
02234                        save_header, parlist, recipe, version))
02235         fors_calib_exit(NULL);
02236 
02237     cpl_image_delete(master_flat); master_flat = NULL;
02238 
02239     if (dfs_save_image(frameset, norm_flat, master_norm_flat_tag,
02240                        save_header, parlist, recipe, version))
02241         fors_calib_exit(NULL);
02242 
02243     cpl_image_delete(norm_flat); norm_flat = NULL;
02244     cpl_propertylist_delete(save_header); save_header = NULL;
02245 
02246 
02247     /*
02248      * Final wavelength calibration of spectra having their curvature
02249      * removed
02250      */
02251 
02252     cpl_msg_indent_less();
02253     cpl_msg_info(recipe, "Perform final wavelength calibration...");
02254     cpl_msg_indent_more();
02255 
02256     nx = cpl_image_get_size_x(spatial);
02257     ny = cpl_image_get_size_y(spatial);
02258 
02259     idscoeff = cpl_table_new(ny);
02260     restable = cpl_table_new(nlines);
02261     rainbow = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02262     if (check)
02263         residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02264     fiterror = cpl_calloc(ny, sizeof(double));
02265     fitlines = cpl_calloc(ny, sizeof(int));
02266 
02267     rectified = mos_wavelength_calibration_final(spatial, slits, lines, 
02268                                                  dispersion, peakdetection, 
02269                                                  wradius, wdegree, wreject,
02270                                                  reference, &startwavelength, 
02271                                                  &endwavelength, fitlines, 
02272                                                  fiterror, idscoeff, rainbow, 
02273                                                  residual, restable);
02274 
02275 /*
02276 dfs_save_image(frameset, rainbow, "rainbow_calib", NULL, parlist, recipe, version);
02277 */
02278 
02279     if (rectified == NULL)
02280         fors_calib_exit("Wavelength calibration failure.");
02281 
02282     if (dfs_save_table(frameset, restable, disp_residuals_table_tag, NULL,
02283                        parlist, recipe, version))
02284         fors_calib_exit(NULL);
02285 
02286     cpl_table_delete(restable); restable = NULL;
02287 
02288     cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
02289     cpl_table_set_column_unit(idscoeff, "error", "pixel");
02290     cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
02291 
02292     for (i = 0; i < ny; i++)
02293         if (!cpl_table_is_valid(idscoeff, "c0", i))
02294             cpl_table_set_invalid(idscoeff, "error", i);
02295 
02296     if (wmosmode > 0) {
02297         mos_interpolate_wavecalib_slit(idscoeff, slits, 1, wmosmode - 1);
02298 
02299         cpl_image_delete(rectified);
02300 
02301         rectified = mos_wavelength_calibration(spatial, reference,
02302                                            startwavelength, endwavelength,
02303                                            dispersion, idscoeff, 0);
02304     }
02305 
02306     cpl_image_delete(spatial); spatial = NULL;
02307 
02308     delta = mos_map_pixel(idscoeff, reference, startwavelength,
02309                           endwavelength, dispersion, 2);
02310 
02311     header = cpl_propertylist_new();
02312     cpl_propertylist_update_double(header, "CRPIX1", 1.0);
02313     cpl_propertylist_update_double(header, "CRPIX2", 1.0);
02314     cpl_propertylist_update_double(header, "CRVAL1",
02315                                    startwavelength + dispersion/2);
02316     cpl_propertylist_update_double(header, "CRVAL2", 1.0);
02317     /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
02318     cpl_propertylist_update_double(header, "CDELT2", 1.0); */
02319     cpl_propertylist_update_double(header, "CD1_1", dispersion);
02320     cpl_propertylist_update_double(header, "CD1_2", 0.0);
02321     cpl_propertylist_update_double(header, "CD2_1", 0.0);
02322     cpl_propertylist_update_double(header, "CD2_2", 1.0);
02323     cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
02324     cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
02325 
02326     if (dfs_save_image(frameset, delta, delta_image_tag,
02327                        header, parlist, recipe, version))
02328         fors_calib_exit(NULL);
02329 
02330     cpl_image_delete(delta); delta = NULL;
02331     cpl_propertylist_delete(header); header = NULL;
02332 
02333     mean_rms = mos_distortions_rms(rectified, lines, startwavelength, 
02334                                    dispersion, 6, 0);
02335 
02336     cpl_msg_info(recipe, "Mean residual: %f pixel", mean_rms);
02337 
02338     mean_rms = cpl_table_get_column_mean(idscoeff, "error");
02339     mean_rms_err = cpl_table_get_column_stdev(idscoeff, "error");
02340 
02341     cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)", 
02342                  mean_rms, mean_rms * dispersion);
02343 
02344     restab = mos_resolution_table(rectified, startwavelength, dispersion, 
02345                                   60000, lines);
02346 
02347     if (restab) {
02348         cpl_msg_info(recipe, "Mean spectral resolution: %.2f", 
02349                    cpl_table_get_column_mean(restab, "resolution"));
02350         cpl_msg_info(recipe, "Mean reference lines FWHM: %.2f +/- %.2f pixel",
02351                    cpl_table_get_column_mean(restab, "fwhm") / dispersion,
02352                    cpl_table_get_column_mean(restab, "fwhm_rms") / dispersion);
02353 
02354         if (qc) {
02355 
02356             qclist = cpl_propertylist_new();
02357 
02358             /*
02359              * QC1 parameters
02360              */
02361             keyname = "QC.DID";
02362 
02363             if (fors_header_write_string(qclist,
02364                                          keyname,
02365                                          "2.0",
02366                                          "QC1 dictionary")) {
02367                 fors_calib_exit("Cannot write dictionary version "
02368                                 "to QC log file");
02369             }
02370 
02371             if (mos)
02372                 keyname = "QC.MOS.RESOLUTION";
02373             else
02374                 keyname = "QC.MXU.RESOLUTION";
02375 
02376             if (fors_header_write_double(qclist, 
02377                                        cpl_table_get_column_mean(restab,
02378                                                                  "resolution"),
02379                                        keyname,
02380                                        "Angstrom",
02381                                        "Mean spectral resolution")) {
02382                 fors_calib_exit("Cannot write mean spectral resolution to QC "
02383                                 "log file");
02384             }
02385 
02386             if (mos)
02387                 keyname = "QC.MOS.RESOLUTION.RMS";
02388             else
02389                 keyname = "QC.MXU.RESOLUTION.RMS";
02390 
02391             if (fors_header_write_double(qclist, 
02392                                        cpl_table_get_column_stdev(restab, 
02393                                                                   "resolution"),
02394                                        keyname,
02395                                        "Angstrom", 
02396                                        "Scatter of spectral resolution")) {
02397                 fors_calib_exit("Cannot write spectral resolution scatter "
02398                                 "to QC log file");
02399             }
02400 
02401             if (mos)
02402                 keyname = "QC.MOS.RESOLUTION.NWAVE";
02403             else
02404                 keyname = "QC.MXU.RESOLUTION.NWAVE";
02405 
02406             if (fors_header_write_int(qclist, cpl_table_get_nrow(restab) -
02407                                     cpl_table_count_invalid(restab, 
02408                                                             "resolution"),
02409                                     keyname,
02410                                     NULL,
02411                                     "Number of examined wavelengths "
02412                                     "for resolution computation")) {
02413                 fors_calib_exit("Cannot write number of lines used in "
02414                                 "spectral resolution computation "
02415                                 "to QC log file");
02416             }
02417 
02418             if (mos)
02419                 keyname = "QC.MOS.RESOLUTION.MEANRMS";
02420             else
02421                 keyname = "QC.MXU.RESOLUTION.MEANRMS";
02422 
02423             if (fors_header_write_double(qclist,
02424                                        cpl_table_get_column_mean(restab,
02425                                                      "resolution_rms"),
02426                                     keyname, NULL,
02427                                     "Mean error on spectral "
02428                                     "resolution computation")) {
02429                 fors_calib_exit("Cannot write mean error in "
02430                                 "spectral resolution computation "
02431                                 "to QC log file");
02432             }
02433 
02434             if (mos)
02435                 keyname = "QC.MOS.RESOLUTION.NLINES";
02436             else
02437                 keyname = "QC.MXU.RESOLUTION.NLINES";
02438 
02439             if (fors_header_write_int(qclist,
02440                      cpl_table_get_column_mean(restab, "nlines") *
02441                                        cpl_table_get_nrow(restab),
02442                                     keyname, NULL,
02443                                     "Number of lines for spectral "
02444                                     "resolution computation")) {
02445                 fors_calib_exit("Cannot write number of examined "
02446                      "wavelengths in spectral resolution computation "
02447                      "to QC log file");
02448             }
02449         }
02450 
02451         if (dfs_save_table(frameset, restab, spectral_resolution_tag, qclist,
02452                            parlist, recipe, version))
02453             fors_calib_exit(NULL);
02454 
02455         cpl_table_delete(restab); restab = NULL;
02456         cpl_propertylist_delete(qclist); qclist = NULL;
02457 
02458     }
02459     else
02460         fors_calib_exit("Cannot compute the spectral resolution table");
02461 
02462     cpl_vector_delete(lines); lines = NULL;
02463 
02464     if (dfs_save_table(frameset, idscoeff, disp_coeff_tag, NULL,
02465                        parlist, recipe, version))
02466         fors_calib_exit(NULL);
02467 //%%%
02468 
02469     mapped_flat = mos_wavelength_calibration(rect_flat, reference,
02470                                       startwavelength, endwavelength,
02471                                       dispersion, idscoeff, 0);
02472 
02473     mapped_nflat = mos_wavelength_calibration(rect_nflat, reference,
02474                                       startwavelength, endwavelength,
02475                                       dispersion, idscoeff, 0);
02476 
02477     cpl_image_delete(rect_flat); rect_flat = NULL;
02478     cpl_image_delete(rect_nflat); rect_nflat = NULL;
02479 //%%%
02480 
02481     /*
02482      * Global distortion models
02483      */
02484 
02485     if (slit_ident) {
02486 
02487         cpl_msg_info(recipe, "Computing global distortions model");
02488         global = mos_global_distortion(slits, maskslits, idscoeff, 
02489                                        polytraces, reference);
02490 
02491         if (global && 0) {
02492             cpl_table *stest;
02493             cpl_table *ctest;
02494             cpl_table *dtest;
02495             cpl_image *itest;
02496 
02497             stest = mos_build_slit_location(global, maskslits, ccd_ysize);
02498 
02499             ctest = mos_build_curv_coeff(global, maskslits, stest);
02500             if (dfs_save_table(frameset, ctest, "CURVS", NULL,
02501                                parlist, recipe, version))
02502                 fors_calib_exit(NULL);
02503 
02504             itest = mos_spatial_calibration(spectra, stest, ctest, 
02505                                             reference, startwavelength, 
02506                                             endwavelength, dispersion, 
02507                                             0, NULL);
02508             cpl_table_delete(ctest); ctest = NULL;
02509             cpl_image_delete(itest); itest = NULL;
02510             if (dfs_save_table(frameset, stest, "SLITS", NULL,
02511                                parlist, recipe, version))
02512                 fors_calib_exit(NULL);
02513 
02514             dtest = mos_build_disp_coeff(global, stest);
02515             if (dfs_save_table(frameset, dtest, "DISPS", NULL,
02516                                parlist, recipe, version))
02517                 fors_calib_exit(NULL);
02518 
02519             cpl_table_delete(dtest); dtest = NULL;
02520             cpl_table_delete(stest); stest = NULL;
02521         }
02522 
02523         if (global) {
02524             if (dfs_save_table(frameset, global, global_distortion_tag, NULL,
02525                                parlist, recipe, version))
02526                 fors_calib_exit(NULL);
02527             cpl_table_delete(global); global = NULL;
02528         }
02529 
02530         cpl_image_delete(spectra); spectra = NULL;
02531         cpl_table_delete(maskslits); maskslits = NULL;
02532     }
02533 
02534     cpl_table_delete(idscoeff); idscoeff = NULL;
02535 
02536     header = cpl_propertylist_new();
02537     cpl_propertylist_update_double(header, "CRPIX1", 1.0);
02538     cpl_propertylist_update_double(header, "CRPIX2", 1.0);
02539     cpl_propertylist_update_double(header, "CRVAL1", 
02540                                    startwavelength + dispersion/2);
02541     cpl_propertylist_update_double(header, "CRVAL2", 1.0);
02542     /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
02543     cpl_propertylist_update_double(header, "CDELT2", 1.0); */
02544     cpl_propertylist_update_double(header, "CD1_1", dispersion);
02545     cpl_propertylist_update_double(header, "CD1_2", 0.0);
02546     cpl_propertylist_update_double(header, "CD2_1", 0.0);
02547     cpl_propertylist_update_double(header, "CD2_2", 1.0);
02548     cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
02549     cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
02550     cpl_propertylist_update_int(header, "ESO PRO DATANCOM", 1);
02551 
02552     if (dfs_save_image(frameset, rectified, reduced_lamp_tag, header,
02553                        parlist, recipe, version))
02554         fors_calib_exit(NULL);
02555 
02556     cpl_image_delete(rectified); rectified = NULL;
02557 //%%%
02558     cpl_propertylist_update_int(header, "ESO PRO DATANCOM", nflats);
02559 
02560     if (dfs_save_image(frameset, mapped_flat, mapped_screen_flat_tag, header,
02561                        parlist, recipe, version))
02562         fors_calib_exit(NULL);
02563 
02564     cpl_image_delete(mapped_flat); mapped_flat = NULL;
02565 
02566     if (dfs_save_image(frameset, mapped_nflat, mapped_norm_flat_tag, header,
02567                        parlist, recipe, version))
02568         fors_calib_exit(NULL);
02569 
02570     cpl_image_delete(mapped_nflat); mapped_nflat = NULL;
02571 
02572     cpl_propertylist_delete(header); header = NULL;
02573 
02574     if (check) {
02575         save_header = dfs_load_header(frameset, arc_tag, 0);
02576 
02577         cpl_propertylist_update_double(save_header, "CRPIX2", 1.0);
02578         cpl_propertylist_update_double(save_header, "CRVAL2", 1.0);
02579         /* cpl_propertylist_update_double(save_header, "CDELT2", 1.0); */
02580         cpl_propertylist_update_double(save_header, "CD1_1", 1.0);
02581         cpl_propertylist_update_double(save_header, "CD1_2", 0.0);
02582         cpl_propertylist_update_double(save_header, "CD2_1", 0.0);
02583         cpl_propertylist_update_double(save_header, "CD2_2", 1.0);
02584         cpl_propertylist_update_string(save_header, "CTYPE1", "LINEAR");
02585         cpl_propertylist_update_string(save_header, "CTYPE2", "PIXEL");
02586 
02587         if (dfs_save_image(frameset, residual, disp_residuals_tag, save_header,
02588                            parlist, recipe, version))
02589             fors_calib_exit(NULL);
02590 
02591         cpl_image_delete(residual); residual = NULL;
02592         cpl_propertylist_delete(save_header); save_header = NULL;
02593     }
02594 
02595     wavemap = mos_map_wavelengths(coordinate, rainbow, slits, polytraces, 
02596                                   reference, startwavelength, endwavelength, 
02597                                   dispersion);
02598 
02599     cpl_image_delete(rainbow); rainbow = NULL;
02600 
02601     save_header = dfs_load_header(frameset, arc_tag, 0);
02602 
02603     if (qc) {
02604 
02605         /*
02606          * QC1 parameters
02607          */
02608         keyname = "QC.DID";
02609 
02610         if (fors_header_write_string(save_header,
02611                                      keyname,
02612                                      "2.0",
02613                                      "QC1 dictionary")) {
02614             fors_calib_exit("Cannot write dictionary version "
02615                             "to QC log file");
02616         }
02617 
02618         if (fors_header_write_double(save_header,
02619                                    mean_rms,
02620                                    "QC.WAVE.ACCURACY",
02621                                    "pixel",
02622                                    "Mean accuracy of wavecalib model")) {
02623             fors_calib_exit("Cannot write mean wavelength calibration "
02624                             "accuracy to QC log file");
02625         }
02626 
02627 
02628         if (fors_header_write_double(save_header,
02629                                    mean_rms_err,
02630                                    "QC.WAVE.ACCURACY.ERROR",
02631                                    "pixel",
02632                                    "Error on accuracy of wavecalib model")) {
02633             fors_calib_exit("Cannot write error on wavelength calibration "
02634                             "accuracy to QC log file");
02635         }
02636     }
02637 
02638     if (dfs_save_image(frameset, wavemap, wavelength_map_tag, save_header,
02639                        parlist, recipe, version))
02640         fors_calib_exit(NULL);
02641 
02642     cpl_image_delete(wavemap); wavemap = NULL;
02643 
02644     cpl_propertylist_erase_regexp(save_header, "^ESO QC ", 0);
02645 
02646     if (dfs_save_image(frameset, coordinate, spatial_map_tag, save_header,
02647                        parlist, recipe, version))
02648         fors_calib_exit(NULL);
02649 
02650     cpl_image_delete(coordinate); coordinate = NULL;
02651     cpl_propertylist_delete(save_header); save_header = NULL;
02652 
02653     header = NULL;    /* To be really, really, REALLY sure... */
02654 
02655     if (qc) {
02656 
02657         double maxpos, maxneg, maxcurve, maxslope;
02658 
02659         header = dfs_load_header(frameset, arc_tag, 0);
02660 
02661         /*
02662          * QC1 parameters
02663          */
02664         keyname = "QC.DID";
02665 
02666         if (fors_header_write_string(header,
02667                                      keyname,
02668                                      "2.0",
02669                                      "QC1 dictionary")) {
02670             fors_calib_exit("Cannot write dictionary version "
02671                             "to QC log file");
02672         }
02673 
02674         maxpos = fabs(cpl_table_get_column_max(polytraces, "c2"));
02675         maxneg = fabs(cpl_table_get_column_min(polytraces, "c2"));
02676         maxcurve = maxpos > maxneg ? maxpos : maxneg;
02677         if (fors_header_write_double(header,
02678                                    maxcurve,
02679                                    "QC.TRACE.MAX.CURVATURE",
02680                                    "Y pixel / X pixel ^2",
02681                                    "Max observed curvature in "
02682                                    "spectral tracing")) {
02683             fors_calib_exit("Cannot write max observed curvature in spectral "
02684                             "tracing to QC log file");
02685         }
02686 
02687         maxpos = fabs(cpl_table_get_column_max(polytraces, "c1"));
02688         maxneg = fabs(cpl_table_get_column_min(polytraces, "c1"));
02689         maxslope = maxpos > maxneg ? maxpos : maxneg;
02690         if (fors_header_write_double(header,
02691                                    maxslope,
02692                                    "QC.TRACE.MAX.SLOPE",
02693                                    "Y pixel / X pixel",
02694                                    "Max observed slope in spectral tracing")) {
02695             fors_calib_exit("Cannot write max observed slope in spectral "
02696                             "tracing to QC log file");
02697         }
02698 
02699 //        fors_qc_end_group();
02700     }
02701 
02702     cpl_free(instrume); instrume = NULL;
02703 
02704     if (dfs_save_table(frameset, polytraces, curv_coeff_tag, header,
02705                        parlist, recipe, version))
02706         fors_calib_exit(NULL);
02707 
02708     cpl_propertylist_delete(header); header = NULL;
02709     cpl_table_delete(polytraces); polytraces = NULL;
02710 
02711     if (dfs_save_table(frameset, slits, slit_location_tag, NULL,
02712                        parlist, recipe, version))
02713         fors_calib_exit(NULL);
02714 
02715     cpl_table_delete(slits); slits = NULL;
02716 
02717     if (cpl_error_get_code()) {
02718         cpl_msg_error(cpl_error_get_where(), cpl_error_get_message());
02719         fors_calib_exit(NULL);
02720     }
02721 
02722     return 0;
02723 }
02724