fors_calib.c

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

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